Upgrade Rails from 4.0 to 4.1
This article is part of our Upgrade Rails series. To see more of them, click here .
This article will cover the most important aspects that you need to know to get your Ruby on Rails application from version 4.0 to 4.1 .
- Preparations
- Ruby version
- Gems
- Config files (config/)
- Application code
- Callbacks
- ActiveRecord
- Tests
- Miscellaneous
- 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 4.1 requires Ruby 1.9.3 or later. Ruby 1.8.7 support was dropped in Rails 4.0, so you should already be running 1.9.3 or later. For Rails 4.1, Ruby 2.0 (or newer) is preferred according to the official upgrade guide .
3. Gems
If your application relies on MultiJSON
,
you will need to add the gem to your Gemfile
(gem 'multi_json'
) if it’s not
already there, since it was removed from Rails 4.1.
Alternatively, stop using MultiJSON and migrate your application to use
to_json
and JSON.parse
.
4. Config files
Rails includes the rails:update
task .
You can use this task as a guideline as explained thoroughly in
this post .
As an alternative, check out RailsDiff , which provides an overview of the changes in a basic Rails app between 4.0 and 4.1 (or any other source/target versions).
5. Application code
a. Callbacks
- Return from callbacks is no longer allowed:
Before:
before_save { return false }
After:
before_save { false }
See: https://github.com/rails/rails/pull/13271
b. ActiveRecord
- Removal of deprecated finders:
activerecord-deprecated_finders
(https://github.com/rails/activerecord-deprecated_finders)
was removed as a dependency from Rails 4.1. From the gem’s README:
To migrate dynamic finders to Rails 4.1+:
find_all_by_... should become where(...).
find_last_by_... should become where(...).last.
scoped_by_... should become where(...).
find_or_initialize_by_... should become find_or_initialize_by(...).
find_or_create_by_... should become find_or_create_by(...).
If you can’t afford to upgrade the finders now, then add the gem back yourself
into the Gemfile
:
gem 'activerecord-deprecated_finders'
See our last upgrade post for more information.
- Default scopes are now chained to other scopes:
If you thought default scopes on models could be confusing, there’s even another (un?)expected twist to it:
class User < ActiveRecord::Base
default_scope { where active: true }
scope :inactive, -> { where active: false }
# ...
end
# Rails < 4.1
> User.all
SELECT "users".* FROM "users" WHERE "users"."active" = 'true'
> User.inactive
SELECT "users".* FROM "users" WHERE "users"."active" = 'false'
# Rails >= 4.1:
> User.all
SELECT "users".* FROM "users" WHERE "users"."active" = 'true'
> User.inactive
SELECT "users".* FROM "users" WHERE "users"."active" = 'true'
AND "users"."active" = 'false'
If you depended on this behavior, you will need to work around it using
unscoped
, unscope
or the new rewhere
method (source )
(Friendly reminder: beware when using default_scope )
- No more mutable methods on ActiveRecord relations:
ActiveRecord::Relation
no longer has access to mutator methods like #map!
,
#delete_if
or #compact!
. If you need to use them, you will need to convert
the Relation
to an Array
by calling #to_a
first.
Before:
Project.where(title: 'Rails Upgrade').compact!
After:
projects = Project.where(name: 'Rails Upgrade').to_a
projects.compact!
- Implicit joins are removed:
Before Rails 4.1, if you had this code:
Post.includes(:comments).where("comments.title = 'foo'")
ActiveRecord 4.0 would know to join posts
and comments
in the executed SQL,
and it would work just fine.
However, Rails shouldn’t have to be smart and parse your where
with a regular
expression to figure out what tables you want to join, since it leads to
bugs (for example: https://github.com/rails/rails/issues/9712 ).
To fix this problem, you need to use an explicit join:
Post.joins(:comments).where("comments.title = 'foo'")
Unless your intention was to actually eager load the post’s comments. In that case, you can use the following syntax:
Post.eager_load(:comments).where("comments.title = 'foo'")
Or:
Post.includes(:comments).where("comments.title = 'foo'").references(:comments)
Both are equivalent and produce the same SQL.
For a more in depth explanation of the differences between joins
, includes
,
references
, eager_load
and even preload
, check out this post .
Finally, if you get this deprecation warning:
DEPRECATION WARNING: Implicit join references were removed with Rails 4.1. Make sure to remove this configuration because it does nothing.
Just remove config.active_record.disable_implicit_join_references
from your
config files.
6. Tests
- CSRF protection now covers GET requests with JS responses:
If your tests hit JS URLs, you’ll need to use xhr
instead of get
:
Before:
post :create, format: :js
After:
xhr :post, :create, format: :js
See: https://github.com/rails/rails/pull/13345
7. Miscellaneous
- Flash message keys are strings now:
If you were using the flash
hash or its keys and expected symbols,
you will need to use strings now:
Before:
flash.to_hash.except(:notify)
After:
flash.to_hash.except("notify")
8. Next steps
If you successfully followed all of these steps, you should now be running Rails 4.1! Do you have any other tips? Share it with us in the comments section.
If you’re not on Rails 4.1 yet, we can help! Download our free eBook: The Complete Guide to Upgrade Rails .