A while back, I got around to upgrading memory (my spaced repetition memorization project) to Rails 3.0. Then Rails 3.1 hit; I upgraded to that in the sense that I was using a 3.1.x version of the Rails gems, but Rails 3.1 came with a bunch of new defaults: jQuery instead of Prototype, the asset pipeline, CoffeeScript, and SASS. And I wasn’t using any of those new defaults: I put in a couple of hours towards upgrading at the time, but I ran into trouble and I’d just let things sit. Since then, Rails 3.2 had appeared, and Rails 4 was on the horizon, so it was clearly high time for me to switch. (My unplanned switch to Ruby 1.9.3 / RVM was a factor nudging me along, too.) Conveniently, I took a hiatus from work for part of December, and while I wanted to spend much of that time playing guitar, I was happy to spend some of it getting this upgrade out of the way.
The first question, then, is sequencing the changes. Clearly switching to CoffeeScript and SASS should come after the asset pipeline, and they also felt more optional than switching to jQuery. I think that, the last time I’d tried this, I’d tried switching to the asset pipeline while leaving Prototype in place, but that ran into trouble. (One of the issues here is that my code for keyboard shortcuts had required me to tweak the existing Prototype integration.)
I thought about switching the order of those two steps, switching to jQuery first while not turning on the asset pipeline, but ultimately I decided that turning on the asset pipeline was simple enough that I would do that together with the jQuery switch. (After all, that would save me the trouble of figuring out how to get jQuery included without grabbing it via the asset pipeline.) So that was my plan of record.
First, though, I spent a little while getting a development system set up on my Mac, so that I didn’t have to do all of the testing over an ssh tunnel to a Linux box. That was a pleasant enough diversion; a few things took a little more fiddling than I expected, but not a lot, and it was good to start from a clean slate so I knew exactly what was in the environment.
Then came the basic asset pipeline / jQuery switch. Fortunately, memory is a very small program, so there wasn’t much I had to do to get it to work with jQuery: add #
in a few places, call .html()
instead of .update()
. And the asset pipeline part of the switch was as simple as I expected. I broke the keyboard shortcuts as part of converting to jQuery, but other than that, all the functionality was there with a quite simple change.
Then I turned my attention to the keyboard shortcut. That actually turned out to be very straightforward: I had one selector that was selecting the wrong element (a form instead of a button), so it’s actually possible that all the problems I had before were self-inflicted; even if they weren’t, once I selected the button and translated to jQuery’s way of doing things, everything worked. That was a huge relief—one of the lessons of this project (along with some self-observations at work) is that I’m so immersed in the TDD way of doing things that I get physically nervous if functionality doesn’t work for as much as an hour. So getting things back to working, albeit with somewhat unstylish code, was a huge relief.
Of course, the next step was to make the code more stylish—working is good, but it’s only a start. I spent a bit of time refactoring that keyboard event handler; then, prompted by the existence of the asset pipeline, I moved it out of the .html.erb file into a separate .js file. (The rest of the project was in general structured reasonably well, but not that part of it…) I only wanted that handler on one of my pages, though; I’m not sure what the best practice is for that sort of conditional behavior in Rails, but I ended up deciding that I would have one .js file per controller, leaving it empty for all controllers other than the quiz controller.
With that, I had a JavaScript story; next, time to clean up my CSS. It had been split across two files (a legacy of the scaffold generation); I unified that. I also started looking at asset precompilation; I ran into some problems there, and for the time being I just stuck everything in my list of files to precompile.
That got the first two items checked off of the list, at least in a basic form; next came CoffeeScript and SASS. The only JavaScript I had was that event handler; I converted it to CoffeeScript, making a few stupid mistakes along the way but liking the results. I’d never used SASS before; but it also seems like a clear improvement, in particular its nesting support is an obviously good idea.
At this point, everything on my initial list was done; but a next level of improvements were on the horizon. I wanted to think about proper organization of these files (e.g. how to handle the mobile versus full versions of the site, which I’d been doing by swapping out CSS files), I wanted to make it look as much like a new Rails 3.1 project as possible instead of something that had evolved over years (looking back, I guess the initial version was Rails 2.1?), I wanted to be able to do push-button deployment from my Mac at home to my Linux server, and I wanted to bring it up to the most recent Rails 3.2 version.
I started by comparing various files in my project with those of a freshly generated single-controller Rails 3.1 project, and I copied over all the changes that made sense. (It was amusing to see that mine was so old that the default formats the controller spit out were HTML and XML instead of HTML and JSON!) I also fiddled with routing a bit, and switched over to Ruby 1.9.3 hash syntax in several places. In terms of organization, the main change that I made was sticking a class in the body
element that said whether I was on the mobile or full version of the device, and then including all the CSS for both versions all the time instead of swapping out CSS files conditionally. (SASS nesting was super useful here, of course.)
Then came push-button deployment; I eventually got a script that I was quite happy with. The main issue there was figuring out where to precompile the assets (which, incidentally, is something that gave me rather more trouble than I was expecting in general; I think I even ran into some bugs on gems with the latest 3.1.x version, but I can’t remember the details, it’s all clean now at any rate); I eventually settled on a two-phase approach where I check things out in a staging directory, bundle update, run all the unit tests and precompile the assets, then rsync the assets to the deployment directory and just do a git pull in that directory without compiling any assets or running tests. That gave me an acceptably small window of time when the deployment was in a slightly inconsistent state. (And I do control when Passenger restarts, anyways, so that window isn’t a problem.)
Switching to Rails 3.2 actually ended up hitting one wrinkle: the JavaScript escaping protection ran up against some code where I was intentionally sending from the backend, because the ampersand kept on getting transformed into & no matter what I did. Eventually I realized that I should just get over my 90’s instinct of using character entities and instead specify it as a unicode character: \u00A0 worked just fine.
So yay, things are all up to date. Which I’ve been very glad of twice over the last month—there have been two Rails security vulnerabilities that seemed fairly serious, but in both cases I could just bump a version number, do ‘bundle update’, and run the deployment script I’d spent the time to get working solidly, getting the problem fixed in maybe two minutes. It really does feel a lot better to not only be up to date on your third-party software versions but to have spent the time to switch your code over to using the latest idioms and to have firmed up your deployment infrastructure. I’m now actively looking forward to Rails 4, as an excuse to nudge things along still further; and I have some changes that I’d like to make to the memory software itself as well.
Post Revisions:
This post has not been revised since publication.