Upgrading GWW from Ruby 1.8.7 to Ruby 1.9.2 and RVM
Having already upgraded GWW from Rails 1 to Rails 2 and then Rails 3, the last step to bring it fully up to date was to upgrade to Ruby 1.9.2. It wasn’t difficult, but it took enough puzzling out to be worth writing down, and I even found a regression in Ruby.
Succinct specs for Rails named routes
I like all of my routes tested, and I like all of my routes named, and I like all of my named routes tested. I even like to test my RESTful routes; even though it feels a bit like testing Rails, it more than paid for itself when migrating to Rails 3. How, then, to spec a named route succinctly?
Rails’ named routes considered helpful
The current Rails best practice is to use RESTful routes. One nice thing about a RESTful route is that it comes with a name. Similarly, :member and :collection routes are automatically named. But sometimes you need a plain old arbitrary route, which you can then name or not as you wish. It may seem like overkill to give every route a name, especially if the route is referred to infrequently in templates. But there’s another place where you’re virtually guaranteed to need that named route again: in tests.
Do dynamic languages need more tests than static languages?
Recently I made an error in a Ruby program (a Rails application) that I wouldn’t have been able to make in a Java program. I renamed a model method manually — because another class had a method with the same name, and RubyMine’s Rename Method refactoring would have renamed both methods — and although I renamed the method itself and uses of the method in the model object’s spec, I missed uses of the method in a controller and mocks of it in the controller’s spec. All of the tests passed, but the application broke. “Gee,” I thought, “if I’d had to compile everything first, that couldn’t have happened.”
Avoiding extra queries in ActiveRecord 3
ActiveRecord 3 introduces a new query interface based on Arel. The new query methods are more succinct than the old :conditions
etc. syntax, and they’re easier to combine into complex queries. Scopes (formerly known as named scopes) are still present and useful, but there’s much less difference between a scope and a simple method that returns a query than there was before. Along with these carrots encouraging you to rewrite all your queries there is the stick of deprecation of the old syntax in Rails 3.1 and removal in Rails 3.2, so most people migrate to the new interface when they migrate to Rails 3.
Migrating GWW to the new interface went smoothly, but when I was reviewing some pages for performance recently I noticed ActiveRecord issuing some extra queries that I didn’t expect, and that weren’t necessary. Fortunately, once noticed, they were easy to eliminate.
Upgrading GWW from Rails 2, RSpec 1 and prototype to Rails 3, RSpec 2 and jQuery
GWW originated on Rails 1; I upgraded it to Rails 2 last year. I’d been putting off the upgrade to Rails 3 until a bug in Phusion Passenger which breaks redirects in Apache httpd was fixed, but I want to use a database adapter which supports MySQL spatial features, and that requires Rails 3. I said goodbye to page caching until (crossing fingers) Passenger 3.0.6, made a branch and started editing my Gemfile.
Although Rails 3 has been released for more than half a year, upgrading involved many hiccups and a couple of landslides. I’d have been happy to just update my gems and be done with it, but I had to hunt around enough that it seemed worth logging what I did in case it helped someone else. I’ve kept it as short and to-the-point as I could. Although I fixed all of the deprecations that Rails 3 reported as I encountered them, I won’t mention them further, since in every case they were completely clear and fixing them only required following instructions. I’ve also left out a handful of odd little incorrectnesses in GWW which worked in Rails 2 but broke in Rails 3, since the details probably won’t interest anyone else and the fixes were obvious. Otherwise, here’s how the upgrade went.
Migrating Palm application data to Android
There can’t be many other people left in the world who have a Palm device whose data they want to move to something newer. I mostly used my series of Palm devices for their built-in productivity applications (Contacts, Calendar, Memos, Tasks) and I was happy with the most recent, a Treo, for six years. Although it’s surprisingly tough, having survived many falls onto concrete sidewalks with only a few scratches, I finally managed to break a few pixels in its screen recently. And the Palm Desktop software doesn’t run on Mac OS later than 10.4, so when I finally retire the older of my Mac laptops I won’t be able to sync the Treo. Time to move on.
Another reason I kept the Treo as long as I did is that I wasn’t looking forward to the migration. I’ve used Palms for twelve years, and I intend to keep my data. I expected it would be a significant project to move it all over. It wasn’t as bad as I feared — Palm Desktop was prepared to bow out gracefully, in that it could export all of its data into standard or at least reasonable formats — but it took enough experimentation and fiddling that I’ve written it down here on the off chance that it will help someone else who still has this job ahead of them.
Page caching vs. RESTful routes in Rails
GWW is almost six years old. It began life on Rails 1. It’s picked up on many of the improvements to Rails made since then, but not all of them. One upgrade that remains to be done is to take more advantage of Rails’ RESTful routing. (Yes, that’s a link to the Rails 2.x docs; GWW is waiting on a Phusion Passenger bug to move to Rails 3.)
I recently added a new model type and set of page to GWW — it now explicitly tracks ScoreReports — and took the opportunity to begin using RESTful routes. It worked beautifully; I really appreciate being able to lay down a whole set of standard, named routes with so little code. But when I pushed the score reports tracking feature to production, it broke: creating a new score report (done by pushing a button to submit a form) did nothing, only refreshed to the index view with no new score report at the top. After a little head-scratching and watching the form submission with Live HTTP Headers, I realized that, like seemingly every only-in-production bug, the problem was due to caching.
Homebrewed ActiveRecord test object factories for rspec
I recently took GWW from a handful of Test::Unit tests to 100% test coverage. It took some good tools (rspec and RR (Double Ruby) are both terrific), but what I didn’t do was use either of the widely used test object factory libraries, factory_girl or Machinist. I wasn’t going to use Rails fixtures, either — fixtures are just wrong — but the factory libraries rubbed me the wrong way. I could point to bits of each tool that weren’t exactly what I wanted, but the real reason I didn’t use either one was that they both seemed a little much when all I wanted to do was construct a few objects. Yes, I recognized the sound of a wheel about to be reinvented, but I went ahead anyway, and I ended up with a small amount of code very well tuned to my needs, and a couple of nice features that I don’t think the off-the-shelf solutions provide.
Controller instance variables in Rails partials
A model value in a Rails partial can be a controller @instance variable or a local variable passed in by the calling template. What should it be?
Some model values must vary within a single page, so can’t be instance variables. Even when instance variables can be used, they still smell bad to the careful coder, because they have broad scope — they’re visible to every template that makes up the page. However, the controlled scope of a local comes with a cost: the template that sets it must know about it, which means more coupling between the partial and the calling template. If the partial’s locals change, so must the calling template, and perhaps its caller and its caller. For a widely-used partial, this is a lot of work. With instance variables, the caller knows nothing, and the partial can change without ripples.
So, when can you take advantage of the convenience and reduced coupling of passing model values to a partial via instance variables?