Upgrade Rails from 3.2 to 4.0

Upgrade Rails from 3.2 to 4.0

This article is part of our Upgrade Rails series. To see more of them, click here.

A previous post covered some general tips to take into account for this migration. This article will try to go a bit more in depth. We will first go from 3.2 to 4.0, then to 4.1 and finally to 4.2. Depending on the complexity of your app, a Rails upgrade can take anywhere from one week for a single developer, to a few months for two developers.

  1. Preparations
  2. Ruby version
  3. Gems
  4. Config files (config/)
  5. Application code
  6. Models (app/models/)
  7. Controllers (app/controllers/)
  8. Tests
  9. Miscellaneous
  10. Next steps

1. Preparations

Before beginning with the upgrade process, we have some recommended preparations:

  • Your Rails app should have the latest patch version before you move to the next major/minor version.
  • You should have at least 80% test coverage unless you have a dedicated QA team.
  • Follow a Git flow workflow to actively manage at least two environments: staging and production.
  • Check your Gemfile.lock for incompatibilities by using RailsBump.
  • Create a dual boot mechanism, the fastest way to do this is installing the handy gem next_rails.

For full details check out our article on How to Prepare Your App for a Rails Upgrade.

2. Ruby version

Rails 3.2.x is the last version to support Ruby 1.8.7. If you’re using Ruby 1.8.7, you’ll need to upgrade to Ruby 1.9.3 or newer. The Ruby upgrade is not covered in this guide, but check out this guide for more details on that.

3. Gems

You can add the aptly named rails4_upgrade gem to your Rails 3 project’s Gemfile and find gems which you’ll need to update:

➜  myproject git:(develop) ✗ bundle exec rake rails4:check

| Dependency Path                          | Rails Requirement          |
| devise 2.1.4                             | railties ~> 3.1            |
| devise-encryptable 0.2.0 -> devise 2.1.4 | railties ~> 3.1            |
| friendly_id                     | activerecord < 4.0, >= 3.0 |
| strong_parameters 0.2.3                  | actionpack ~> 3.0          |
| strong_parameters 0.2.3                  | activemodel ~> 3.0         |
| strong_parameters 0.2.3                  | activesupport ~> 3.0       |
| strong_parameters 0.2.3                  | railties ~> 3.0            |

Instead of going through your currently bundled gems or Gemfile.lock manually, you get a report of the gems you need to upgrade.

4. Config files

Rails includes the rails:update task. You can use this task as a guideline as explained thoroughly in this post. It will help you get rid of unnecessary code or monkey-patches in your config files and initializers, specially if your Rails 3 app was running on Rails 2.

As an alternative, check out RailsDiff, which provides an overview of the changes in a basic Rails app between 3.2 and 4.0 (or any other source/target versions).

If you’re feeling adventurous, you can give this script a try. It attempts to apply this git patch (similar to the patch shown on RailsDiff) to your Rails app to migrate from 3.2 to 4.0. However, I don’t recommend this for complex or mature apps, as there will be plenty of conflicts.

5. Application code

a. Models

  • All dynamic finder methods except for .find_by_... are deprecated:
# before:
Authentication.find_all_by_provider_and_uid(provider, uid)

# after:
Authentication.where(provider: provider, uid: uid)

You can regain usage of these finders by adding the gem activerecord-deprecated_finders

  • ActiveRecord scopes now need a lambda:
# before:
default_scope where(deleted_at: nil)

# after:
default_scope { where(deleted_at: nil) }

# before:
has_many :posts, order: 'position'

# after:
has_many :posts, -> { order('position') }

(Friendly reminder: beware when using default_scope)

  • Protected attributes is deprecated, but you can still add the protected_attributes gem. However, since the Rails core team dropped its support since Rails 5.0, you should begin migrating your models to Strong Parameters anyway.

To do so, you will need to remove calls to attr_accessible from your models, and add a new method to your model’s controller with a name like user_params or your_model_params:

class UsersController < ApplicationController
  def user_params
    params.require(:user).permit(:name, :email)

Finally, change (most) references to params[:user] to user_params in your controller’s actions. If the reference is for an update or a creation, like user.update_attributes(params[:user]), change it to user.update_attributes(user_params). This new method permits using the name and email attributes of the user model and disallows writing any other attribute the user model may have (like id).

  • ActiveRecord Observers were removed from the Rails 4.0 codebase and extracted into a gem. You can regain usage by adding the gem to your Gemfile:
gem 'rails-observers' # https://github.com/rails/rails-observers

As an alternative, you can take a look at the wisper gem, or Rails’ Concerns (which were added in Rails 4.0) for a slightly different approach.

  • ActiveResource was removed and extracted into its own gem:
gem 'active_resource' # https://github.com/rails/activeresource

b. Controllers

  • ActionController Sweeper was extracted into the rails-observers gem, you can regain usage by adding the gem to your Gemfile:
gem 'rails-observers' # https://github.com/rails/rails-observers
  • Action caching was extracted into its own gem, so if you’re using this feature through either:
caches_page   :public


caches_action :index, :show

You will need to add the gem:

gem 'actionpack-action_caching' # https://github.com/rails/actionpack-action_caching

6. Tests

From Ruby 1.9.x onwards, you have to include the test-unit gem in your Gemfile as it was removed from the standard lib. As an alternative, migrate to Minitest, RSpec or your favorite test framework.

7. Miscellaneous

  • Routes now require you to specify the request method, so you no longer can rely on the catch-all default.
# change:
match '/home' => 'home#index'

# to:
match '/home' => 'home#index', via: :get

# or:
get '/home' => 'home#index'
  • Rails 4.0 dropped support for plugins, so you’ll need to replace them with gems, either by searching for the project on RubyGems/Github, or by moving the plugin to your lib directory and require it from somewhere within your Rails app.

8. Next steps

If you successfully followed all of these steps, by now you should be running Rails 4.0!

To fine-tune your app, check out FastRuby.io, and feel free to tell us how your upgrade went.

If you’re not on Rails 4.0 yet, we can help! Download our free eBook: The Complete Guide to Upgrade Rails.

Get the book