This post covers my experiences of upgrading a small Rails 2 application to boot with Rails 3.
First, we’ll need to install the Rails 3 beta.
As per the official instructions, we perform:
> [sudo] gem install tzinfo builder memcache-client
rack rack-test rack-mount erubis mail text-format
thor bundler i18n
> [sudo] gem install rails --pre
Before touching an existing app, it’s wise to commit any changes, and switch to a new branch. Using git, for example:
> git branch rails3
> git checkout rails3
rails_upgrade is a wonderful plugin that eases the transition to Rails 3. We can install it in an existing Rails 2 application with the following:
> script/plugin install git://github.com/rails/rails_upgrade.git
rails_upgrade adds a few handy rake tasks. Let’s start with:
> rails:upgrade:check
You’ll see a list of files that rails_upgrade has determined will need updating to work with Rails 3. Let’s stash our current version of these files using rails_upgrade:
> rails:upgrade:backup
We should commit now, as the next step will overwrite a bunch of our existing files.
Perhaps the simplest way to ensure an app will boot with Rails 3 is to rerun the Rails generator on it. We’ll do that now:
> rails .
The generator will prompt us before overwriting any files with Rails 3 versions. We’ll overwrite any files haven’t been modified since our app was first generated (e.g. config/boot.rb). For files that have been modified, we’ll take a backup (if rails_upgrade didn’t generate one already), and then tell Rails to overwrite. We’ll suffix our backup files with .rails2, following the rails_upgrade convention.
Now, we’ll take a good look at the backup files. Firstly, some might be of files that we don’t need or don’t use. For example, test/test_helper has changed, but I don’t use Test::Unit, so that change won’t affect me. I can safely delete the test_helper backup.
In other cases, the backups will need to be merged into their newly generated Rails 3 counterparts. For example, I needed to merge some custom application logic into app/controller/application_controller. We don’t need to merge config/routes, config/environment or config/environments/* just yet.
rails_upgrade can generate routes that conform to the new Rails 3 routing API from our old config/routes.rb. We can invoke the migration with:
> rails:upgrade:routes
Before running this rake task, ensure that config/routes.rb contains Rails 2 style routes. We’ll receive some output, such as:
SampleApp::Application.routes do
resources :users do
resources :posts
end
match '/' => 'site#index'
end
We can paste this into our config/routes.rb to start using the new Rails 3 routing API.
rails_upgrade can also generate a Gemfile to define our gem dependencies using bundler. In Rails 2, these dependencies were defined as config.gem statements in environment.rb. We can invoke the migration with:
> rails:upgrade:routes
Again, be sure that config/environment.rb contains Rails 2 code. We’ll receive output such as:
path "/path/to/rails", :glob => "{*/,}*.gemspec"
git "git://github.com/rails/rack.git"
gem "Rails", "3.0.pre"
gem 'authlogic'
gem 'cancan'
gem 'friendly_id'
In fact, the first few lines are no longer correct as we’re using Rails 3 beta (not the prerelease version). The above should read:
source 'http://gemcutter.org'
gem "rails", "3.0.0.beta"
gem 'authlogic'
gem 'cancan'
gem 'friendly_id'
We can go ahead and copy the latter into our app’s new Gemfile file.
In Rails 2, each environment could add further gem dependencies. For example, a Rails 2 config/environments/test.rb file might contain the following:
config.gem 'webrat', :lib => false, :version => '>=0.7.0'
config.gem 'rspec', :lib => false, :version => '>=1.2.9'
In Rails 3, environment-specific gem dependencies are also defined in the Gemfile. The above code becomes:
group :test do
gem 'webrat', '0.7.0'
gem 'rspec', '1.2.9'
end
There’s some fairly good documentation in the comments of the Gemfile generated by Rails 3. I recommend taking a couple of minutes to read it, and to check out the documentation for bundler.
The final step is to ensure each of the files in config/environments now uses the new configure module method. The contents of each file should now be executed inside a configure block. Here’s an example config/environments/development.rb, specified in the new Rails 3 style:
SampleApp::Application.configure do
config.cache_classes = false
config.whiny_nils = true
config.consider_all_requests_local = true
config.action_view.debug_rjs = true
config.action_controller.perform_caching = false
config.action_mailer.raise_delivery_errors = false
end
Recall that gem dependencies should be moved to Gemfile, rather than be specified with a config.gem call.
And we’re done. The next step is to start the Rails server, and see which dependency breaks first. Note the new command:
> rails server
And the winner is… (for me at least):
authlogic-2.1.3/lib/authlogic/session/timeout.rb:26:in
'included': undefined method `before_persisting'
for Authlogic::Session::Base:Class (NoMethodError)
Fixing Authlogic, and the other gems I’m using will be covered in part 2.