20 November 2014

Upgrading a gem for newer Rails versions

How I Upgrade a Gem when Rails Upgrades

Rails is continually being improved and upgraded and goes through a moderate upgrade (Rails 4.0 to 4.1, for example) at least once a year. For maintainers of gems which others use, this also means that the gem must be upgraded and tested against the latest Rails version. I typically do my upgrades annually, in the Fall, and by that time, I've forgotten the process I used twelve months ago.
So my blog today will try to capture this process. I maintain the Milia gem for basic multi-tenanting in a Rails environment. I recently updated it for Rails 4.1. Rails 4.2 is already at release candidate status, so I will record my steps in upgrading from 4.1 to 4.2.

Process in a nutshell

  1. Read up on what has changed: release notes, upgrade blog, tutorials.
  2. Make sure that the gem's unit and functional tests are working for the older Rails version.
  3. Check out the "edge" branch in your git repository for working on the upgrade.
  4. Bump the gem version number.
  5. In your gem test area, empty the gemset, delete the Gemfile.lock.
  6. Update any Ruby, Rails version references in Gemfile, generators, docs, shell scripts.
  7. Create a new Rails project (in the new version); Gemfile references the edge branch for gem.
  8. Try to start the server for the new app; deal with deprecations, errors, references.
  9. Manually test the new app enough to be confident that most things have been caught.
  10. Repeat steps 7 through 9 until no more changes.
  11. rake test:units
  12. rake test:functionals
  13. update gem README.
  14. build gem; publish when ready

Each step explained


What has changed?

There are four primary places to get information about new versions of Rails
Each of these has helpful information to know prior to starting an upgrade process. Take notes if needed and dig deeper if any areas seem unclear or confusing.
For Milia, the upgrade was fairly straightforward from Rails 4.0 to 4.1. But in looking at the 4.1 to 4.2 upgrade, I'm noting the following possible impacts:
  • Web Console
  • Responders (might be used in the test app?)
  • Rails html scanner (test app?)
  • render with a String (test app?)
  • railties changes (test app?)

unit and functional tests working?

Might be a good idea, before going any further, to make sure that the current released gem version's unit and functional tests are working.

$ git checkout edge

Actually Milia uses a branch called "newdev" ... but same thing. Prior to this step, you might also want to freeze a branch for the current version (say, version 1.1.0) as well:
  $ git checkout master
  $ git checkout -b v1.1.0
  $ git push origin v1.1.0
  $ git checkout edge

Bump gem version number

Now that we're in edge, I like to symbolically bump the version number, so I have a record in the git trail of when work started. In this case, Milia is going to 1.2.0.

empty testing gemset; delete test Gemfile.lock

You might not have these, but Milia does because there's a stub of a Rails application which is required for running the unit tests. Best to do the deletes now so you won't forget and become confused later when entering the testing phase.
  $ rvm gemset empty miliatest
  Are you SURE you wish to remove the installed gems for /home/xxxxx/.rvm/gems/ruby-2.1.3@miliatest?
  (anything other than 'yes' will cancel) > yes
  installing gem /home/xxxxx/.rvm/gem-cache/gem-empty-1.0.0.gem --local --no-ri --no-rdoc.
  :
  $ rm Gemfile.lock


Update any Ruby, Rails version references

Update any Ruby, Rails, or other gems' version references in milia.gemspec, Gemfile, generators, docs, shell scripts, etc.
This can be a bit tricky. For example, Milia has auto generators for installing milia within a sample web app. But for that to work most correctly, the latest version of Rails also has be installed in the gemset in my development project directory.
So this stage is the time to make the first draft of potential changes based on your notes.

Here's a list of possible things to check for:

  • does rvm need to be bumped to the latest version (for latest Ruby version)?
  • latest Rails in the project-space gemset?
  • what are the potential latest versions of any key gem or app dependencies?

And here's a list of files which sometimes change when Rails changes:

  • config/boot.rb
  • config/application.rb
  • config/routes.rb
  • config/initializers/*.rb
  • config/environments/*.rb
  • any railtie.rb files in the gem


Create a new Rails project

You want to create a new project so that you'll be working with the default application scaffolding for the Rails version to which you're upgrading. Otherwise, you sometimes run into situations where you're testing on an app built against an older scaffolding, which is still supported, but might be dropped in the future. So you want to make sure that everything that defines a new version is in the app you'll be testing.
And make sure that it will reference the new version: go into the Gemfile references the edge branch for gem. There's two ways to do this.

If you're the only developer, you can have the Gemfile reference the gem (assuming the local branch is at edge):
  gem 'milia', :path => "../../milia"

Or, you can specify the edge branch on gitbhub:
  gem 'milia', :git => 'git://github.com/dsaronin/milia', :branch => 'edge'

Start and try out the new app


Try to start the server for the new app. Invariably it will be flagged with many deprecation and error references. Don't ignore these in your haste. Fix each one and try again until there are none showing. Because you're providing a gem, you want something which has been correctly upgraded for the Rails version you're supporting. Gem users don't like getting deprecation messages for things which they cannot directly control.

There's two different ways I try to get these errors. The easiest is just to run the console; it forces the initialization process and doesn't complicate anything with trying to get a web server started.
  $ rails c

After that comes up correctly, then I'll try to actually start the app via a web server.

Do some minor manual testing just to feel confident that things are running. Then move on to the unit and functional tests.

Unit & functional tests

Obviously, now is a good time to try these out, fix any deprecations and errors which arise.

Revise README and any other documentation


Build & publish the gem


Consistent way to scaffold a new milia app

Note: this is reference for me to remember how I like to start and test milia on my dev system.
  $ cd projectspace/
  $ gem install rails --pre
  $ new_project sample-milia-app
  $ cd sample-milia-app/
  $ bundle install
  $ rails g milia:install --org_email='conjugalis@gmail.com'
  $ rails g web_app_theme:milia
  $ rake db:create
  $ rake db:migrate
  $ foreman start