|
LATEST POSTS
Friday, February 26, 2010
More and more often these days, I’ve been having a crisis of faith conscience when it comes to TDD, or rather, interaction testing in general. It happens more so on projects where I’m the only developer or there is a very small team of strong developers. And with the latest project being in GWT, where you are practically beaten down by all the guidance espousing separation of concerns, it’s even more pronounced. Consider a screen that display a list of pet names for your truck. When you click the “add” button, it displays a dialogue box with a blank form. If you’re using an MVP pattern, this is likely what would happen when the page is loaded, from a testing point of view: - Load a list of data from the datastore
- When it’s retrieved, send the list to the view
- Add a click handler to the “add” button that does something
In this case, I’m using gwt-dispatch, which is a command pattern implementation for GWT. So the first point is executed by creating a command and dispatching it like so: dispatcher.execute( new GetPetNamesCommand(), new GetPetNamesCallback( ));
The GetPetNamesCallback class looks like so:
public class GetPetNamesCallback extends AsyncCallback<GetPetNamesResult>
{
@Override
public void callback( GetPetNamesResult result ) {
display.showList( result.getPetNames( ) );
}
}
And the code to add the click handler when the page is loaded:
display.getAddNewLink().addClickHandler(new AddNewClickHandler( ));
(This is a passive view, I think, so the presenter handles all the event wire-up.)
Going to the testing side of things, I have a test context class called When_initializing_pet_names_list_presenter and within it, I have these tests:
- should_request_data_from_data_store
- should_wire_up_add_new_button
In each of these tests, I dutifully go about my business mocking out a view and a dispatcher and creating a presenter. I set up expectations and the real meat of the test is in this line (using Mockito syntax):
verify( dispatcher ).execute( isA( GetPetNamesCommand.class ), isA( GetPetNamesCallback.class ) );
That is, we verify that we executed the dispatcher with appropriate arguments. There’s a similar test for the second method and in another class, When_pet_names_have_been_retrieved, there’s a method that tests the GetPetNamesCallback class to ensure, also dutifully, that display.showList was called.
When creating this code, I did it the other way around, writing the tests first and fleshing out the details. That makes no never mind here. The end result is the same no matter which way you go about it.
Where my crisis of conscience comes in is when I review the code I’ve written based on these tests. This screen isn’t very complicated. If I were to bang out the code for it without tests, I can’t imagine doing it any other way.
But someone else could, of course. And maybe they aren’t as disciplined as I am about keeping concerns separated. Fair enough. At the moment, they aren’t working on my project though. It’s just me and it will be me for the foreseeable future. Besides which, I’ve done enough thinking on how smart or dumb the next guy is going to be.
Furthermore, these tests feel too granular. Like they’re too focused on implementation and that they’d break at the slightest refactoring. Like say, if we switched to an architecture where the view wires up its click handler and raises events. Now I have to go and fix all these tests.
Maybe that’s short-term thinking and it will lead to a lapse in judgment one day where I write untestable code to get something out the door.
Maybe that’s true but maybe it’s not. I don’t think I’m advocating against interaction testing in any case. Just wonder what value it holds once you’ve been doing it long enough that you can’t write untestable code if you tried, even if there aren’t any actual tests.
In any case, this is just interaction testing. There are plenty of cases where unit testing can be useful in verifying complex business rules (like whether it’s okay to have duplicate pet names for both your truck and your sister-wife).
What I see more value in is a higher level integration tests. Having recently taking Selenium for a spell, it seems more useful. What happens when the user lands on the “Pet Names List” page? Why, it should have this text showing in its HTML somewhere. What about when you click the “add” button? Well, then the page should include a form in it.
In this case, if anything changes in the bowels of the application, these tests remain untouched and, more importantly, still very relevant. If you have to change these tests, it’s as the result of a user interaction change and I think that warrants a change in your tests more than which flavour of MVP you decide to use.
This isn’t really a position I’ll defend to the death, or even to the slightly injured. As it is, I’m not altogether happy about how I’ve explained my position. Should probably let it sit for a bit but I don’t have that kind of patience. In any case, more and more when I run my test suite and see everything running green, I feel a little bit Pavlovian.
Kyle the Trained
Friday, February 19, 2010
I didn’t necessarily mean to piggyback off Greg’s two posts on ORMs but c’mon, what’s a hillbilly to do when he perpetuates such negative stereotypes? I mean, before you start knocking it, have any of you *tried* kissing your sister? He also has some blather in there on RDMSs and ORMs. So I suppose I should hide my indignation behind something technical. We’re using BigTable for our project by way of Google App Engine. The decision to use it was pretty easy once we landed on GWT as our platform. The integration ‘twixt GWT and App Engine is pretty seamless and hey, App Engine uses Big Table. I’m glossing over the dozens of times we’ve second-guessed ourselves since making the decision though. The most recent was just yesterday as a matter of fact when my friend expressed a couple of concerns: - How do we back it up?
- How do we do ad hoc reports against it?
Over the course of the conversation, these boiled down to: How can we work with BigTable in a way we’re used to with RDMSs? The inevitable option came up. Maybe we shouldn’t use BigTable. Maybe MySQL is more suitable if we’re unsure. That means moving off App Engine though and we like what we’ve seen so far with it. It was a bit of an uncomfortable conversation actually and this was between two very seasoned developers who have never shied away from new tech. I think the reason for the awkwardness though is that we aren’t dealing with someone else’s money. This is a startup so it’s a decision that he and I are going to have to live with. In the end, being seasoned developers, we recognized that moving to a new development platform will just substitute one set of problems with another. For basic transactions (I hesitate to say OLTP because that will imply I know more about the term than I do), like getting some objects and saving them again, BigTable just plain works. There’s no ORM behind the scenes to map your data structure to the domain model. You create a User and you save it. Any relationships are automatically dealt with by some magic that is buried in the documentation somewhere, I’m sure. It really is like working with an ORM without actually having to deal with the mapping. As for our two questions above, we have a tentative solution that I still like a day later and it will solve both problems. Let’s take the second one: How do we do ad hoc reports against it? See this is where RDMSs shine, I think. So breaking down the question we get: How can we get the benefits of BigTable for transactional stuff and the benefits of RDMSs for reporting and ad hoc querying? Funny how CQRS starts to make sense when you have the right problem staring you in the face. We’ll have a separate relational database for querying. As requests are sent to BigTable, we’ll also dump them out to another service elsewhere that queues them up to be processed into the relational database. This also addresses our first question: How do we back it up? The nice thing about this approach is that we now have our offline backup though of course, backing up is only half the solution. We also need some way to restore BigTable from our relational database easily. But the idea seems sound enough even if the mechanics may prove otherwise. Maybe this sounds unduly complicated. It really doesn’t to me. App Engine and BigTable offer a lot of advantages. They solve problems I don’t want to deal with, most notably, scalability. The ones they introduce, backing up and querying, by contrast, are pretty simple. Besides which, I’m scheduled for Udi’s course in a couple of months anyway. And for the record, I don’t have any sisters. Just three adventurous brothers. Kyle the Restored
Tuesday, February 16, 2010
Almost three years ago to the day, I concluded a post with this: I hate security and all software and hardware related to it, including but not limited to: anti-virus, spam, phishing, SSL, permissions, LDAP, NTLM, forms authentication, SecurID tokens, VPNs, swipe cards, PIN numbers, security deposits, car alarms, bike locks, and cell phones for seven-year-olds.I don't like that the major upgrade to Windows XP was a firewall that broke a bunch of apps. And that among IE7's features is not to let me into websites because I'm not smart enough to figure out if they're dangerous. And that Vista's main differentiation from XP is that it's harder to play my music. I will concede, however, that retinal scanners are pretty cool. Now, three years later and my position has changed slightly in that now I also don’t like OAuth, RSA, salts, Kerberos, MD5, SHA, IPSec, OpenID, HTTP sessions, cookies, cross-site request forgery, fingerprint scanners, parental controls, deadbolts, my mother’s maiden name, my favorite pet, the name of my elementary school, and “forgot my password” links that all but announce my password on Twitter. Again, though, retinal scanners rock. With that background, today’s topic is authentication with Google Web Toolkit. In the .NET world, I had my head somewhat wrapped around authentication. Or rather, I knew which code to cut and paste from previous apps to make it work in new ones. But having thrown myself headlong into GWT, I’ve had to actually research the concepts. A little. With an AJAX application like one built with GWT, there are actually two scenarios that require authentication: - Navigating to a page
- Performing an action on a page
Navigation has traditionally been fairly easy for me to work out. You fiddle with the web.config, add a login page, toss around a FormsAuthenticationTicket or two and call it a day. With GWT, this needs to be done via gwt-presenter. But I’ll leave it at that for now because I haven’t actually got around to doing that part of the authentication yet. Instead, I’ll focus on the second item: performing an action on a page. In GWT, I’m using gwt-dispatch, a command pattern implementation. It’s nice in that it provides a centralized interface for making RPC calls ‘twixt client and server. Nicer still is the latest version on the trunk which includes support for securing these calls. As David Peterson points out in this thread, there isn’t much you need to do in order to add security to your command calls. That said, it isn’t altogether obvious to a Java greenhorn how to implement it. So forthwith are my current thoughts in as plain English as I can realistically make it. The main issue I had when researching all this is that it seemed fairly academic. There is talk of sessions and session IDs and I couldn’t make the leap from that discussion to what I really wanted: to log in to the application. As I lamented on the OpenRasta forum, I can get a username and password from the screen, validate it against a database, and create a random session ID. What happens after that is still a little foggy. So here is the workflow as I see it now: - User enters username and password and clicks submit
- Login command is created and sent/dispatched to its appropriate handler on the server
- LoginHandler validates the user, creates a user object, and dumps it into the HTTP Session
- Server also creates a random session ID and dumps it into an appropriately-named cookie.
- Server returns login success
Next, the user performs some action that requires authentication: - Client creates an appropriate command and sends it to the server
- The session ID is attached to the command and sent to the server
- The server checks to see if the action being performed requires authentication.
- If not, it executes the action
- If so, it checks to see if a user has been added to the session. It also verifies that the session ID it received matches the one it generated earlier (by also checking the cookies)
- If the checks fail, throw an InvalidSessionException. Otherwise, execute the action.
- Back on the client, we use a custom AsyncCallback class that specifically checks for InvalidSessionException. If it catches one, we raise a Logout event which is configured elsewhere to redirect to the login page.
The session ID check seems kind of superfluous on the surface. But from what I gather, it’s necessary to prevent cross-site request forgery attacks. As far as I can tell, it’s not used for any other purpose. The vast majority of this is taken from various sources and I hope I remember them all. First is the Hupa, a web mail app written in GWT. Much assistance has come from the guy behind this site, which is shaping up to be one of the best tutorials I’ve seen in either Java or .NET. A good chunk of the explanation is from this post and I should note that most of the code in it has been incorporated into gwt-dispatch already. I had planned to incorporate some code in here but didn’t for a couple of reasons. One, this is kind of wordy and I’d rather do a follow-up that is more code-centric. Two, I haven’t finished the code. For now, the act of typing all this out has served it’s purpose in that it’s helped clarify things in my head. The nice thing is that much of this is already incorporated into gwt-dispatch already. I’m still a little fuzzy on where to implement the parts I need to implement (e.g. where do I check to see if an action can be performed anonymously?) but I have something that works. Which will serve as the basis for future posts. Kyle the Dispatched
Monday, February 15, 2010
If you’re the type that skips titles, we’re using Google App Engine in our project, mostly because it’s there. I jest. We’re using it because it’s scalable and high performance and reliable and whatever other reason you can think of that will keep me from having a boring debate on the difference between Google and Amazon and Microsoft. Along with Google App Engine, we’re also using TeamCity for our continuous integration server. And generally speaking, these are two very disparate topics. I.e. I’m not finding App Engine to be very CI-friendly. Deploying to App Engine from Eclipse or IntelliJ is dead simple. There are plug-ins that manage it and all you do is provide credentials. Doing it from the command line is not too bad. There’s a console utility, appcfg, that lets you upload to App Engine. So on the surface, it appears CI and App Engine play nicely. But there are a couple of quirks that don’t sit well. We’ll start with the Ant tasks I’m using from the CI server: <target name="setAppEngineVersionToDayOfWeek">
<tstamp>
<format property="day.of.week" pattern="E" />
</tstamp>
<stringutil string="${day.of.week}" property="day.of.week">
<lowercase/>
</stringutil>
<replaceregexp file="war/web-inf/appengine-web.xml"
match="<version>(.*)</version>"
replace="<version>${day.of.week}</version>"
byline="true" />
</target>
<target name="setAppEngineVersionToCurrentDate">
<tstamp>
<format property="current.date" pattern="yyyy-MM-dd" />
</tstamp>
<replaceregexp file="war/web-inf/appengine-web.xml"
match="<version>(.*)</version>"
replace="<version>${current.date}</version>"
byline="true" />
</target>
<target name="update"
description="Uploads the application to App Engine.">
<java classname="com.google.appengine.tools.admin.AppCfg"
inputstring="s1sterW1f3">
<sysproperty key="appengine.sdk.root"
value="${appengine.folder}" />
<classpath>
<fileset dir="${appengine.folder}/lib"
includes="**/*.jar" />
</classpath>
<arg value="--email=betty.lou@plaidWorld.com" />
<arg value="--passin" />
<arg value="update" />
<arg value="war" />
</java>
</target>
The first two targets are nearly identical. In App Engine projects, there is an XML file that contains the config
Side note: Versions in App Engine either can’t start with a capital or can’t contain capital letters altogether. This was one of my more aggravating annoyances because it ain’t ‘zactly trivial to make a string lowercase in Ant. I had to import a whole new Ant add-on, Antelope, to do it. Hence the <stringutil> task in setAppEngineVersionToDayOfWeek.
uration for uploading to App Engine. One of the tags is <version /> which is the name of the version in App Engine. These two targets will configure the version name to be the name of the day of the week, and the current date (in yyyy-MM-dd format) respectively.
The update target does the actual work of executing appcfg to upload to App Engine. In TeamCity, I have two build configurations. One executes nightly and calls the setAppEngineVersionToDayOfWeek target followed by the update target. The second configuration executes weekly and calls setAppEngineVersionToCurrentDate, then update.
The first crack in the veneer appears in the update target. App Engine comes with an Ant task, <appcfg>. But it requires you to be logged in and as far as I can tell, you can’t pass in that info to the <appcfg> task. So the target calls AppCfg using the <java> task and with a hard-coded email and password.
There’s an alternative. You can log into the CI server, run appcfg from the command line, and provide your email and password. This will be cached for 24 hours. As long as you hit App Engine at least once in 24 hours, your credentials will remain cached.
Problem with that is that you have to provide the credentials while logged in with the user account that runs the TeamCity agent, which in our case is the local system account. The other problem is that it’s a nightly build. Which means it runs roughly every 24 hours. Didn’t take long for the credentials to get evicted from the cache. So we created a special account so that we could hard-code the credentials into the build file (because, y’know, no one wanted their e-mail addresses and passwords stored in the version control system).
Other suggestions are included here. I went with the least resistance approach because frankly, a nighly build on a team of two in a start-up company shouldn’t require a custom Ant task.
The next issue we hit is that App Engine has a limit of ten versions per application. Put your fingers down, I’ll do the math for you. It means that after one week of nightly builds, that leaves three slots available for weekly builds. With none left over for others.
Ideally, I’d like to keep maybe only the last three nightly builds and possibly the last three weekly builds. But as far as I can tell, there’s no automated way to delete versions from App Engine. So this has forced us to examine exactly what we want to accomplish with our automated builds in App Engine.
Finally, if anyone knows how to secure versions of the app from prying eyes, I’d love to see that feature implemented as well. You need to log in to administer the application on App Engine along with all the versions. But as far as I’ve been able to figure out, if you know the URL to a version of the app, it’s available to the public. Which has stepped up the priority of our “Customer should be able to log in” story.
Kyle the Engineered
Friday, February 12, 2010
My daughter’s report card came home last week. The grading system is as follows: - E: Exceeds expectations
- A: Achieving expectations
- P: Progressing towards expectations
- N: Needs support
There are also a couple of places for comments, which is what I’m usually most interested in. Before I continue, there’s a very good chance my opinion is clouded by a certain “back in my day” attitude. That said, I have given this some thought, enough to start a discussion at least. The confusion I have with this grading system is the word “expectations”. It sounds kind of benign but what’s missing is context. Is it the expectations at this current moment in time or is it year-end expectations? If it’s year-end expectations, then does she need help to bring her up to speed? Or do they continue to get taught the concept throughout the year? If they don’t, what’s the plan to bring her up to expectations? All this boils down to a single question for me: Is there anything actionable I, as a parent, need to do? This is something I posed to the teacher and she was very helpful. She explained the rationale behind the grades and clarified my questions. In the end, I left feeling like I was doing my job, which was basically to support hers. (Side note: I’ve come to expect this level of professionalism of the teachers at my daughter’s school. Despite the somewhat new-age grading system, she has had some fantastic teachers through the years. The first month of each year is always a little anxious for me because I keep thinking her streak can’t continue. But I guess if Pixar can bat a thousand, so can a school.) But still this system nags at me. If my kid is progressing toward expectations in a concept and yet, I don’t need to do anything different, then what meaning does the grade provide? Worse yet: what if she’s still progressing toward expectations at the end of the year? What are my options at that point? I get the impression that this grading system wouldn’t identify the problem earlier in the year. There’s another angle to this. If your child is a good student and is grasping the concepts and is basically on the right track, there’s a psychological hit you take seeing P’s on the report card. It gives the message that your child is not quite at the level he or she is supposed to be at. The counter-argument, of course, is that this is mid-term. There’s no reason to expect them to be at year-level expectations yet. But again, if they’re on track, what good does it do to tell me “she’s not there yet but we have high hopes”? This psychological aspect also affects parents. For me personally, I have a good idea what kind of student my daughter is. I help her with her homework and know what she’s good at and what she struggles with. So all I really needed was to have a chat with her teacher to see if there were any problem areas and to see if I needed to do anything differently. But I can imagine other parents putting undue emphasis on the “lower” P grade, and perhaps pressuring their children to work harder, even if they are already good students. I suspect the grading process is a stressful one for teachers. Particularly for students whose parents are more apt to blame the teachers for their child’s poor grades. Maybe this system was in response to that. Or maybe the old, A, B, C, D, F system had some inherent problems on its own. But I’m picturing a report card that has a bunch of As and Bs with one or two Cs. That, to me, is something I can do something about. It sends a clear message of where your child needs support. “At this exact point in time, your daughter is pretty good in all these subjects, but she could step up her efforts in comprehension.” To re-iterate, this is mostly moot for me because I have a decent grasp on my daughter’s education and on how she’s achieving my own expectations. I just wonder if this grading system adds an unnecessary level of stress when a simple twenty minute conversation with the teacher would tell me what I need to know more than this report card. Kyle the Graded
Thursday, February 11, 2010
A couple of months ago, I threw up a little PSA for people wanting to make project requests for http://teamcity.codebetter.com. I’m going to expand on it a little here in response to the most common errors and omissions I see. Firstly, here is an updated list of what we need in order to set up a project: - Your username on the TeamCity server
- The usernames of any other people you’d like to be administrators of the build
- The name of the project
- A *brief* description
- The project’s URL
- The URL to the source code
- The name of the build runner (e.g. NAnt, Rake, MSBuild, command line, etc.)
- The path to the build file (relative to the source code URL above)
- Any other details required by the build runner (e.g. the name of the target if different from the default)
The general rule of thumb that summarizes all this is: assume I know nothing of your project. Furthermore, assume I have approximately three minutes available to devote to setting it up. The less guessing I need to do about how it’s built, the faster it’ll get up and running. (Unless it’s hosted on Google Code and has a NAnt file named build.xml at the root. I know SVN ain’t the new hotness anymore and XML files rank just below living next to a convicted felon with bad taste in lawn gnomes but MAN, those projects are easy to whip up.) Next: test your build process. By my estimate, at least 80% of the projects I set up fail the first time I click Run. Almost all of them because they are assuming something is on the build server that isn’t. If you’re using something other than the standard BCL, chances are we haven’t installed it. So you’ll need to include it in your source code and reference it there. If there’s something you need installed in order to make the actual build process run (e.g. a Rake gem or NCover), then include those with your request. While the build server is relatively free from client libraries, we do have various gems, Mercurial, and Git installed. But things like ASP.NET MVC aren’t necessary. We don’t want the hassle of dealing with different versions as they get released. If at all possible, find a clean machine that just has the .NET SDK on it, download your code, and run your build process. If it fails there, it’ll likely fail on the TeamCity server. If it does, my debug process consists solely of reading the build log. That’s only to make my response to your request more personal and to make me sound like I know what I’m talking about. Couple of housekeeping notes: - .NET 4.0 RC will be installed shortly. Probably within the next week
- TeamCity 5.0 upgrade is on the radar but not a high priority as we don’t have an immediate need for it.
- We have a Twitter account: @CodeBetterCI. Follow to be notified of outages and announcements.
Closing note: I double dog-DARE you to submit a request that uses CodePlex’s new Mercurial support. Kyle the Volatile
Wednesday, February 10, 2010
I’m in week four of my Google Web Toolkit adventure and things are heating up nicely. I’ve already switched to IntelliJ and back to Eclipse at least once (not really counting the two times I switched in my head while I was laying in bed; let it not be said that being spoiled for choice is always a good thing). Reason I’m currently sticking with Eclipse: it seems better suited for GWT development. As an editor, IntelliJ rocks for .NET/ReSharper developers. All the shortcuts are familiar and things seem more organized and intuitive. Generally speaking, Eclipse feels like an IDE developed by committee, which it kinda was from what I’ve read. But the GWT and Google AppEngine plug-ins for Eclipse are pretty solid and there’s no confusing terminology around libraries and facets and artifacts and modules like there is with IntelliJ. After all, I’m just a .NET developer. The gwt-presenter library has been a godsend in the short time I’ve been on the project. Its implementation of MVP is cleaner than my kids’ hides after they’ve had a good whoopin’. Unlike the ASP.NET version of MVP I’ve used over the years, this one has a very decoupled link ‘twixt presenters and views. Communication is managed through dependency injection to wire up view interfaces to their implementations and through an event bus, which comes standard with the gwt-presenter lib rary. Say you have a link in a left sidebar that users click to launch the Settings page in the main content window. With gwt-presenter, the workflow to wire this up is as follows: - In the left sidebar’s presenter, attach a click handler to the Settings link exposed as a method on the Display interface, which the view implements. (gwt-presenter appears to use a Passive View approach to MVP.)
- In the click handler, raise a ShowSettingsWindow event on the event bus.
- Elsewhere (for example, in the main AppPresenter), we register a handler on the event bus to listen for this event and swap out whatever is being displayed in the content window with the SettingsPresenter.
Here’s where it gets even better. I don’t even need to create a ShowSettingsWindow event. gwt-presenter has history management built-in. This means that in order to navigate to a new “place” in the app, all I need to do is raise a PlaceRequestEvent. Furthermore, I also don’t need to handle the event anywhere. The PlaceRequestEvent includes a parameter indicating the name of the place you want to navigate to. (I believe I’m simplifying but then, I’m a simple person.) As long as that’s mapped to the SettingsPresenter (typically, exposed as a property on the presenter itself), gwt-presenter handles all that for you including adding it to the browser’s history. So to sum up, out of the box, gwt-presenter gives you: - A view and presenter decoupled thanks to dependency injection and a built-in event bus
- navigation between views
- browser history management and bookmarking
What makes this all the more amazing to me is something to which I alluded in the previous post: it’s nearly impossible not to use all of this in a GWT application. Yes, gwt-presenter is optional but it’s not optional the way ASP.NET MVC is optional to WebForms developers. The GWT documentation itself encourages the use of it as well as an event bus. Note that I’m being sparse with implementation details. That’s because there’s no point. The absolute definitive guide for this is already underway over at Andreas Borglin’s pad. As much as I love David Chandler’s articles (and believe me, they’re golden), Andreas’s step-by-step guide to the various pieces, along with detailed source code for each step, is the type of teaching guide I can only aspire to. The purpose for this post is two-fold: - Get people over to Andreas’s site to join in the awesomeness
- To help clarify my own thoughts on how this all works together.
As always, if any of this is helpful or interesting to you, that’s not my fault. Kyle the Presented
Wednesday, February 03, 2010
The hillbilly be all Java’d up these days. For coming on three weeks, I’ve been the lone developer on a project based on Google Web Toolkit. It’s a web framework (naturally) that allows developers to build applications in Java and compiles it down to JavaScript, CSS, and HTML. Scoff all you want, if it were any other company behind it, I’d probably be right there with you. We considered quite a few technologies before landing on this one. We went through Ruby, Sharp Architecture, Open Rasta, Moo Tools, and probably several others. I can defend ending up Google Web Toolkit privately if you want but putting the thought process up in a public forum is going to lead to comments that I’m too tired to moderate these days. And yes, I’ve heard of Script #, too. Coming at this from a .NET developer’s standpoint has been interesting. It’s surprising how much we take for granted in our IDE, for example. I’m still meandering my way through the keyboard shortcuts in Eclipse (after a brief fling with IntelliJ IDEA). One thing I’d really like is a keyboard shortcut for the GWT Compile button, which currently is available only via a toolbar button or a context menu, as far as I can tell. Was happy to find a VIM plugin for it too that works as advertised. Not sure if this applies to Java in general but within the confines of the Google Web Toolkit, the ecosystem is freakin’ phenomenal. The documentation page alone encourages unit testing and the use of an MVP architecture. Once you start reading about it, you can’t help but stumble on a number of other projects: - gwt-presenter: A passive view implementation for GWT
- gwt-dispatch: A command pattern implementation for GWT
- Guice: Google’s dependency injection framework
- Gin: A dependency injection framework for the client part of GWT
The shocking thing from a .NET perspective is that these concepts are just sort of assumed. There’s little debate on whether dependency injection is necessary or whether passive view is overkill. Someone watched a presentation from Google IO last year, whipped up a command pattern project based on it, and lo! the people said it was good. Between support for Hibernate and the App Engine data store, I think people would look at you bug-eyed if you even suggested writing your own data access layer. The downside to all this as that the tooling seems to be several steps behind what I’m used to in the Microsoft world. It’s still too early to make a fair comparison but even after digging a little deeper into Eclipse, I can’t imagine being quite as productive as I currently am in Visual Studio. Yes, there’s also IntelliJ but our anecdotal evidence suggests that neither Java IDE is seen as a de facto standard. For the moment, I’m sticking with Eclipse simply because all the documentation refers to it and the GWT and App Engine plug-ins for it work out of the box. Couple of final shout-outs to two blogs that have been invaluable during the learning curve phase. First is Hive Development (currently featuring two posts on how to unit test MVP applications in GWT). Second is TurboManage, whose sample code is more detailed than many apps I’ve written. Anyway, it’s been an interesting couple of weeks. Kyle the Decaffeinated
Disclaimer
The opinions expressed herein are my own personal opinions and do not represent
my employer's view in any way.
Copyright © 2010 Kyle Baley. All rights reserved.
|
|
|
CATEGORIES
|
|
LATEST POSTS
POPULAR POSTS
|
|
ARCHIVE
| March, 2010 (1) |
| February, 2010 (8) |
| January, 2010 (2) |
| December, 2009 (3) |
| November, 2009 (2) |
| September, 2009 (5) |
| August, 2009 (4) |
| July, 2009 (2) |
| June, 2009 (5) |
| May, 2009 (6) |
| April, 2009 (5) |
| March, 2009 (6) |
| February, 2009 (2) |
| January, 2009 (6) |
| December, 2008 (5) |
| November, 2008 (2) |
| October, 2008 (5) |
| September, 2008 (9) |
| August, 2008 (5) |
| July, 2008 (7) |
| June, 2008 (6) |
| May, 2008 (11) |
| April, 2008 (13) |
| March, 2008 (13) |
| February, 2008 (12) |
| January, 2008 (19) |
| December, 2007 (16) |
| November, 2007 (8) |
| October, 2007 (23) |
| September, 2007 (15) |
| August, 2007 (8) |
| July, 2007 (6) |
| June, 2007 (11) |
| May, 2007 (19) |
| April, 2007 (14) |
| March, 2007 (3) |
| February, 2007 (4) |
| January, 2007 (7) |
| December, 2006 (5) |
| November, 2006 (9) |
| October, 2006 (11) |
| September, 2006 (14) |
| August, 2006 (11) |
| July, 2006 (15) |
| June, 2006 (8) |
| May, 2006 (10) |
| April, 2006 (12) |
| March, 2006 (3) |
| February, 2006 (7) |
|
|
|