For the past month I’ve been updating Conductor from Ruby on Rails 2 to Ruby on Rails 3. It has proven to be both a great learning experience and an exercise in patience. I’ve worked with Rails 2 for about 3 ½ years; I’ve grown accustomed to it’s idioms and its quirks. On the surface, Rails 3 is very similar, but under the hood is where the most stark changes can be seen. Mercifully, over the various minor revisions (i.e. Rails 2.3.1), the system has clearly identified what has been deprecated and would be removed in future versions.
Conductor is comprised of about 10,000 lines of production code and 15,000 lines of test code. The production code is what everyone else interacts with: viewing pages, uploading images, managing templates, etc. The test code is what the Conductor development team interacts with: verifying that pages can be viewed, verifying that images are uploaded, verifying any additional behavior, etc. When running Conductor’s test suite, the deprecation notices look like such:
DEPRECATION WARNING: reorder is deprecated. Please use except(:order).order(...) instead. (called from RAILS_ROOT/conductor/config/application.rb:7)
Step 1: Make sure the original “works”
The first step was to make sure that all deprecation notices for the Rails 2 version were taken care of. This was fairly straight-forward as the file and line numbers were given.It was also something I’d done as part of the ongoing routine maintenance of Conductor. With the deprecation notices gone and a fully functional test suite, I proceeded to the next step.
Step 2: It’s Worse than that, He’s Dead Jim
Using git source control, I made a branch try-rails-3 and flipped to Rails 3…and the local application wouldn’t start. It was dead, fortunately that was to be expected. With the help of the Rails 3 release notes and the Rails Upgrade plugin, I slowly brought the system back to life. This involved moving some configuration files, updating some plugins, and a patience.
Step 3: The Devil’s in the Details
Once the system booted up, I then turned to the test suite. I pushed play and waited for the results (sadly the test suite takes quite a bit of time to run). There were plenty of errors, some related to the sequence of loading files, others related to broken plugins, and some simply needing attention. I picked through the tests, and eventually got all of them running.
With the test suite running, I focused on manually verifying the system. That is where I found the next batch of trouble. With Rails 3, all text sent to the browser was now automatically HTML escaped as a security measure. This resulted in some pages rendering broken HTML. I had broken pages, even though my tests worked. So the answer was to write some tests to verify the page wasn’t broken.
Step 3 ¾: Camera Two, Camera Three
Applying the Red, Green, Refactor principal, I decided that I would write test code against the Rails 2 base, as I wanted to ensure the current Rails 2 code was behaving as expected. Then, via git’s awesome cherry-pick command, I grabbed the commit that had the functioning test and added it to the Rails 3 branch. I ran the test, and as expected, it failed…Good. I went in and fixed the failure. I reran the test and it passed…Good. That particular bug was gone, and moving forward, I wouldn’t need to worry about it. As I encountered this problem, I repeated the solution, adding several more tests to the system.
Step 3 ⅞: Meanwhile Back at the Hall of Justice
While I was working on the Rails 3 changes, I was also maintaining the Rails 2 version of Conductor. Some of these changes were more substantial, others were not. What was happening, however, is that the Rails 2 and Rails 3 branches of work were diverging, and there would be a reckoning.
Step 4: Construction Paper, Scissors and Glue
All of the tests were working. I had manually verified all of the administrative functions in the Rails 3 branch. Any problems I had encountered were fixed with an underlying test to verify the behavior. I felt confident that I had gotten most of the problems. So I merged the Rails 2 branch into the Rails 3 branch.
Since the branches had diverged, there were merge conflicts that I needed to resolve; Git is amazing and tracking changes, but when changes happen to the same line of a file in different branches, a merge conflict results. I resolved these to the best of my ability…And then ran the tests.
There were some broken tests, but eventually, I got things working. I again manually verified the administrative functions in the Rails 3 branch.