#225 Upgrading to Rails 3 Part 1
The first release candidate for Rails 3.0 has just been released. This makes it a great time to try out Rails 3, if you haven’t already, and to see what changes will need to be made to your current Rails 2 applications go get them working. In this episode we’ll show you how to upgrade a Rails 2.3 application to Rails 3.0.
There are already a number of episodes on Rails 3.0 that you can watch or read and we’ll refer to some relevant episodes as we work through upgrading. The application we’re going to start upgrading is the railscasts.com site, the source code of which is available on Github.
Even if you’re not ready to use Rails 3 in production yet it’s still worth trying to upgrade your Rails 2 applications just to see what problems you might run in to. If your applications use Git for source control it’s easy to create a separate branch to experiment on so having downloaded the code we’ll create a new branch called rails3
.
$ git checkout -b rails3 Switched to a new branch 'rails3'
Before We Start Upgrading
Before upgrading an application to Rails 3 we should upgrade it to the latest version of Rails 2.3 which at the time of writing is Rails 2.3.8. Likewise, any gems that the application uses should be upgraded to their latest version. This way we’ll ensure that there are no compatibility problems with the latest versions.
Once we’ve upgraded Rails and the gems we should run our tests. Having a good automated test suite will help ensure that everything is still working as it should.
$ rake spec (in /Users/eifion/rails/apps_for_asciicasts/ep225/railscasts) DEPRECATION WARNING: Rake tasks in vendor/plugins/hoptoad_notifier/tasks ........................................................................................................................................................ 152 examples, 0 failures
Everything passes so we’re ready to start the upgrade.
Updating Ruby and Installing Rails 3
For this update we want to use the latest version of Ruby and have a clean slate for our Rails 3 environment and so we’ll use rvm to install the latest version of Ruby 1.9.2 and install Rails 3.0 on top of that. There are frequent updates to rvm so we’ll ensure that we have the latest version by running rvm update before installing Ruby.
$ rvm update rvm 0.1.40 by Wayne E. Seguin (wayneeseguin@gmail.com) [http://rvm.beginrescueend.com/] info: fetching rvm-0.1.43.tar.gz info: Extracting rvm-0.1.43.tar.gz ... info: Installing rvm-0.1.43...
Once rvm has updated itself we’ll need to reload it. We can do that by running
$ rvm reload
Now we can use rvm to install Ruby 1.9.2.
$ rvm install 1.9.2
This command will download, compile and install the latest version of Ruby 1.9.2, currently rc2. When this has completed (it may take a few minutes) we can switch to the new version and check that it works.
$ rvm 1.9.2 $ ruby -v ruby 1.9.2dev (2010-07-11 revision 28618) [x86_64-darwin10.4.0]
Now that we know that we’re running the correct version of Ruby we can install the Rails 3.0 release candidate gem.
gem install rails --pre
Note that as we using an rvm-installed version of Ruby we don’t need to use sudo to install gems.
Beginning The Upgrade
We now have a fresh install of Ruby and Rails 3.0 so we’re ready to begin upgrading our application. To help us there’s an official rails_upgrade plugin that we can use to see what changes we need to make. To install it we’ll first need to swap back to our Rails 2 environment. This is installed on our system version of Ruby so we first need to swap back to it by running.
$ rvm system
We can now install the plugin.
$ script/plugin install git://github.com/rails/rails_upgrade.git
This will show some helpful documentation after it installs. One of the key commands it adds is rake rails:upgrade:check
. When we run this command from our application’s directory it will show a list of everything that needs to be updated in our application. The output is too long to show here, but as an example the first part is shown.
$ rake rails:upgrade:check (in /Users/eifion/rails/apps_for_asciicasts/ep225/railscasts) Old router API The router API has totally changed. More information: http://yehudakatz.com/2009/12/26/the-rails-3-router-rack-it-up/ The culprits: - config/routes.rb
Before we start the upgrade we’ll run rake rails:upgrade:backup
. This will back up some of the key files that are likely to be overwritten during the upgrade.
$ rake rails:upgrade:backup (in /Users/eifion/rails/apps_for_asciicasts/ep225/railscasts) DEPRECATION WARNING: Rake tasks in vendor/plugins/hoptoad_notifier/tasks are deprecated. Use lib/tasks instead. (called from /Library/Ruby/Gems/1.8/gems/rails-2.3.8/lib/tasks/rails.rb:10) * backing up .gitignore to .gitignore.rails2 * backing up app/controllers/application_controller.rb to app/controllers/application_controller.rb.rails2 * backing up app/helpers/application_helper.rb to app/helpers/application_helper.rb.rails2 * backing up config/routes.rb to config/routes.rb.rails2 * backing up config/environment.rb to config/environment.rb.rails2 * backing up config/environments/development.rb to config/environments/development.rb.rails2 * backing up config/environments/production.rb to config/environments/production.rb.rails2 * backing up config/database.yml to config/database.yml.rails2 This is a list of the files analyzed and backed up (if they existed); you will probably not want the generator to replace them since you probably modified them (but now they're safe if you accidentally do!). - .gitignore - app/controllers/application_controller.rb - app/helpers/application_helper.rb - config/routes.rb - config/environment.rb - config/environments/development.rb - config/environments/production.rb - config/environments/staging.rb - config/database.yml - config.ru - doc/README_FOR_APP - test/test_helper.rb
We start the upgrade by creating a new Rails 3 application in the application’s directory. In order to do that we need to swap back to our Ruby 1.9.2 environment.
$ rvm 1.9.2
We can now create the new Rails 3 application with
$ rails new .
As we’re creating a new application in the same directory as an existing one we’ll be asked if we want to overwrite some of the existing ones. If we’re not sure if a file should be overwritten we can use the d
option to see the differences between the existing file and the one that will be overwritten.
We we’ve just made backups of most of the important files in our application so we can choose to overwrite all of the conflicting files with the exception of the following:
/README
/app/views/layouts/application.html.erb
/public/javascripts/application.js
The other files will either not have changed from their defaults or will have been backed up, allowing us to move any changes back into the new versions as we go along.
Our application has now been upgraded to Rails 3.0 but we still have to go through the files that we backed up and move over any code that is specific to our application. Each backed-up file will be in its original directory with a .rails2
extension so we just need to open up each backup file along with the new version and copy any code we want to keep over.
We’ll start with the routes file. The routing syntax has changed but the old syntax still works in Rails 3.0, although it has been deprecated. For now we can just paste in the routes from the backup file into the new routes file and we’ll test it later.
Next we’ll take a look at the /config/environment.rb
file. Fixing this will involve a little more work as the configuration has changed quite a lot in Rails 3. The default Rails 3 environment file is quite short as the configuration options don’t belong there any more, going instead into the /config/application.rb
file.
There are three sections of the backed-up environment.rb
file that we need to consider. The first is the time zone configuration code.
config.time_zone = 'Pacific Time (US & Canada)'
This line can be moved into /config/application.rb
.
The second part is the session code.
config.action_controller.session = { :session_key => APP_CONFIG['session_key'], :secret => APP_CONFIG['session_secret'] }
In Rails 3 this code belongs in an initializer file and if we look at that file we’ll see the code for configuring the application’s session data.
# Be sure to restart your server when you modify this file. Railscasts::Application.config.session_store :cookie_store, :key => '_railscasts_session' # Use the database for sessions instead of the cookie-based default, # which shouldn't be used to store highly confidential information # (create the session table with "rake db:sessions:create") # Railscasts::Application.config.session_store :active_record_store
We can leave this code as it is for now. It might need to be changed later on but that won’t be necessary in order to get the site up and running.
Finally, at the bottom of the backed-up envronment.rb
file is the code that configures the application’s gems.
config.gem "RedCloth", :lib => 'redcloth', :version => ">= 4.0" config.gem "coderay" config.gem 'acts-as-list' config.gem 'will_paginate' config.gem 'thinking-sphinx', :lib => 'thinking_sphinx' config.gem 'whenever', :lib => false
This code can be copied into our application’s Gemfile
, which is in the application’s root directory. The syntax has changed slightly so the code will need to be changed to look like this:
gem "RedCloth", ">=4.0", :require => 'redcloth' gem "coderay" gem 'acts-as-list' gem 'will_paginate' gem 'thinking-sphinx', :require => 'thinking_sphinx' gem 'whenever', :require => false
As well as changing config.gem
to gem
on each line there are a couple of other changes that we need to make. Version numbers are now just supplied as a second argument and the :lib
option is now :require
.
Once we have done that we need to run bundle install
to make sure that all of the required gems are installed. For more information about bundler and installing gems you can watch or read episode 201. We won’t go into moving code from the other backup files but it should be fairly straightforward to transfer the necessary code.
With the gems installed we’ll try starting the server to see if the application will start.
$ rails s /Users/eifion/.rvm/gems/ruby-1.8.7-p299/gems/will_paginate-2.3.14/lib/will_paginate.rb:39:in `enable_activerecord': undefined method `returning' for WillPaginate:Module (NoMethodError) # rest of error truncated…
It won’t and the reason is that there’s an error in the will_paginate
gem’s code where we have an undefined returning
method. This method was available in Rails 2 but has been removed in Rails 3.
In cases like these it’s worth checking the RubyGems website to see if there is a newer prerelease version of the gem available that is compatible with Rails 3.0. In the case of will_paginate
there is and we can use it by adding a version number for that gem in our Gemfile
.
gem 'will_paginate', '>=3.0.pre'
If we run bundle install
again the prerelease version of the gem will be installed and we’ll be able to use it in our application. Now that we have that newer version installed we can try running the server again.
$ rails s /Users/eifion/.rvm/gems/ruby-1.8.7-p299/gems/after_commit-1.0.7/lib/after_commit/active_record.rb:15:in `include_after_commit_extensions': undefined method `subclasses_of' for Object:Class (NoMethodError) from /Users/eifion/.rvm/gems/ruby-1.8.7-p299/gems/after_commit-1.0.7/lib/after_commit.rb:81 from /Users/eifion/.rvm/gems/ruby-1.8.7-p299/gems/thinking-sphinx-1.3.18/lib/thinking_sphinx.rb:2:in `require' from /Users/eifion/.rvm/gems/ruby-1.8.7-p299/gems/thinking-sphinx-1.3.18/lib/thinking_sphinx.rb:2
This error appears to be caused by the Thinking Sphinx gem. Again there is a prerelease version available that we can use instead so we can make another change to the Gemfile
and then run bundle install again.
gem 'thinking-sphinx', '>=2.0.0.rc1', :require => 'thinking_sphinx'
Let’s try running the server again.
$ rails s DEPRECATION WARNING: railtie_name is deprecated and has no effect. (called from <class:Railtie> at /Users/eifion/.rvm/gems/ruby-1.9.2-rc2/gems/will_paginate-3.0.pre/lib/will_paginate/railtie.rb:6) => Booting WEBrick => Rails 3.0.0.rc application starting in development on http://0.0.0.0:3000 => Call with -d to detach => Ctrl-C to shutdown server Exiting /Users/eifion/rails/apps_for_asciicasts/ep225/railscasts/config/routes.rb:2:in `block in <top (required)>': undefined local variable or method `map' for #<ActionDispatch::Routing::Mapper:0x00000101dffde8> (NameError)
This time we get a little further. The server does try to start but then complains about an undefined variable or method called map in the routes. As we’ve pasted the old routing code over into the new routes file we’re calling a map
variable but that variable isn’t passed to the block as it isn’t required in Rails 3. We can still use the old routing syntax though and pass that variable in.
Railscasts::Application.routes.draw do |map| map.resources :spam_questions map.resources :spam_checks map.with_options :controller => 'info' do |info| info.about 'about', :action => 'about' info.contest 'contest', :action => 'contest' info.feeds 'feeds', :action => 'feeds' info.give_back 'give_back', :action => 'give_back' end map.login 'login', :controller => 'sessions', :action => 'new' map.logout 'logout', :controller => 'sessions', :action => 'destroy' map.resources :sponsors map.resources :comments map.resources :tags map.resources :episodes, :collection => { :archive => :get } map.resources :sessions map.resources :spam_reports, :member => { :confirm => :post }, :collection => { :confirm => :post } map.root :episodes end
When we try to start the server up again it’s fourth-time lucky and it boots up without any errors.
$ rails s DEPRECATION WARNING: railtie_name is deprecated and has no effect. (called from <class:Railtie> at /Users/eifion/.rvm/gems/ruby-1.9.2-rc2/gems/will_paginate-3.0.pre/lib/will_paginate/railtie.rb:6) => Booting WEBrick => Rails 3.0.0.rc application starting in development on http://0.0.0.0:3000 => Call with -d to detach => Ctrl-C to shutdown server [2010-08-05 19:30:22] INFO WEBrick 1.3.1 [2010-08-05 19:30:22] INFO ruby 1.9.2 (2010-07-11) [x86_64-darwin10.4.0] [2010-08-05 19:30:22] INFO WEBrick::HTTPServer#start: pid=9933 port=3000
If we visit the home page in a browser now we’ll see the default home page as it will have been recreated in the /public
directory when we created the Rails 3 application. We can see from this page that the correct environment is being used our app.
Even though we have our application booting successfully there’s still a lot we need to fix as we’ll see if we run rails:upgrade:check again. We also need to run our specs again to see if they pass. We’ll cover these in the next episode.