Onwards to Rails 5: Additions, Changes, and Deprecations
Rails 5, a new major release of the popular web framework, is coming soon and, of course, we are looking forward to it. Refer to this article if you want to read on overview of some new features that will be introduced. Today, however, we are going to dig into some technical details, discuss which methods were removed or deprecated, which settings have been changed, and how to migrate your existing Rails 4 application to version 5.
Don’t forget that after the release of Rails 5, all bug fixes will only be introduced for Rails 5.0.x, whereas regular security fixes will be applied to Rails 4.2.x, as stated in the official maintenance policy.
The source code for this app is available at GitHub.
Preparations
The first thing to take into consideration before proceeding is that Rails 5 requires Ruby 2.2.2 or higher, so if you don’t have this version installed, go ahead and download it now.
Before migrating your existing application to Rails 5, I really recommend you having a solid test suite, otherwise the migration process may become much more complex and painful. For this article I will be using a demo application, called MusicalShop, that was introduced in my screencast series RSpec with Rails. This is a very simple Rails 4 app, but it does have automated tests written in RSpec. You can clone the latest version of the app from this branch.
Also it is advised to migrate to the latest patch version of Rails 4 (which is currently 4.2.5.1). Additionally, you may check that the latest possible versions of gems are used in your project by running
$ bundle outdated
Be careful, though, not to introduce any conflicts by explicitly requiring the latest versions for all your gems (especially for dependencies).
Another thing to consider is some gems may not yet be compatible with Rails 5, so be prepared for that. Browse gem’s dependencies and skim through open issues on GitHub to find out whether other developers are facing problems while using the library with Rails 5. Some gems (like Devise, for example) may need to be included directly from the master
branch.
Okay, so go ahead and check that the tests are running successfully:
$ rspec .
Don’t forget that, as Edsger Dijkstra said, “testing can never demonstrate the absence of errors in software, only their presence”. But we’ll hope for the best.
The last thing to do is to switch to another branch so that you can always return to an older version if things don’t go as planned:
$ git checkout -b rails5
Migrating
Take a deep breath and modify your Gemfile:
Gemfile
[...]
gem 'rails', '>= 5.0.0.beta3', '< 5.1'
[...]
Currently Rails is in beta, but a release candidate should be published pretty soon.
Run
$ bundle update
You may also run
$ rake rails:update
which creates the new files introduced in Rails 5, as well as, modifies the old ones.
All in all, Rails 5 configuration files are very similar to Rails 4, so chances are that you’ll be able to boot your app right away while seeing some deprecation messages. Some of you may remember that the migration process from Rails 3 to 4 was much more painful.
Still, many deprecated methods were removed, so if you were using any of them, you’ll have to modify the code base accordingly. Let’s discuss the major changes so that you understand which parts of the app will most require your attention.
Controllers
Okay, first of all, let’s observe changes that relate to controllers.
-
The
skip_action_callback
method is removed completely. -
The
:nothing
option for therender
method is deprecated. -
The
*_filter
methods (before_filter
, for example) are still supported, but deprecated and will be removed in Rails 5.1. Use*_action
instead. -
A nice method
redirect_back
is being introduced in favor ofredirect_to :back
. It tries to redirect to an HTTP_REFERER or to the provided location, so you may use it inside your methods:
redirect_back root_path
render :text
is deprecated, as it does not render atext/plain
response. Userender :plain
instead orrender :html
to display atext/html
page:
render plain: 'My text'
-
The
respond_to
andrespond_with
methods have been extracted to the responders gem, so be sure to include it in the Gemfile if you use those methods. -
XML serialization has been extracted to a separate activemodel-serializers-xml gem as well.
Views
Every form now has its own CSRF token by default. This is to prevent form hijacking when an attacker injects his own form on the page pointing to some malicious website.
Create an initializer file to control this behavior globally:
config/initializers/per_form_csrf_tokens.rb
Rails.application.config.action_controller.per_form_csrf_tokens = true
Or use the following setting inside individual controllers:
self.per_form_csrf_tokens = true
content_tag_for
and div_for
methods were removed from ActionView
and extracted to the recordtaghelper gem.
Models
Rails 5 introduces a new abstract class (meaning, that it cannot be instantiated) called ApplicationRecord
and all models inherit from it by default rather than from ActiveRecord::Base
. This is done to prevent monkey-patching ActiveRecord::Base
directly. So, you can introduce all extensions inside ApplicationRecord
. If you want to employ this functionality, create application_record.rb file inside your models directory:
models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
end
Next ensure that all models inherit from this new class, for example:
models/user.rb
class User < ApplicationRecord
[...]
end
Another addition is related to belongs_to
associations: in Rails 5, the associated record must be present by default. The required
option is now deprecated and a new optional
parameter is being introduced. This means that, by default, Rails validates that the “parent” record is present before saving the object. If you do not wish to employ this behavior for some association, use
belongs_to :some_parent, optional: true
Rails 5 also comes with a activerecordbelongstorequiredbydefault.rb initializer file that controls this behavior for the whole application. You may create it as well:
config/initializers/active_record_belongs_to_required_by_default.rb
Rails.application.config.active_record.belongs_to_required_by_default = true
before_*
callbacks that return false
inside your models do not halt the callback chain anymore. If you wish to explicitly halt the callback chain, use throw(:abort)
instead:
def my_callback
throw(:abort) if something_bad_happened
end
New Rails 5 apps comes with a callback_terminator.rb initializer file that controls this behavior:
config/inititalizers/callback_terminator.rb
ActiveSupport.halt_callback_chains_on_return_false = false
Also keep in mind that if you are going to use Rails with PostgreSQL, only version 9.1 above is supported.
Cache
Caching management in development was made more convenient. In Rails 4, we used to have the config.action_controller.perform_caching
setting that accepts either true
or false
. In Rails 5, however, the following code snippet is now present:
config/environments/development.rb
[...]
if Rails.root.join('tmp/caching-dev.txt').exist?
config.action_controller.perform_caching = true
config.cache_store = :memory_store
config.public_file_server.headers = {
'Cache-Control' => 'public, max-age=172800'
}
else
config.action_controller.perform_caching = false
config.cache_store = :null_store
end
[...]
To enable or disable caching, you can run the rake dev:cache
command that creates caching-dev.txt file inside the tmp directory.
Routing
Multiple root
routes may exist in the same scope now (previously, an error was raised). You can provide constraints and choose which root to use:
root 'blog#show', constraints: ->(req) { some_condition }
root 'pages#show'
The rails routes
command now accepts the following options to
search for specific routes:
-c
returns all routes for the specified controller-g
returns routes based on the specified pattern
Tests
ActionController::TestCase
HTTP request methods like get
and post
are deprecated. You should use process
instead. For example, in my tests I have calls like:
spec/controllers/albums_controller_spec.rb
[...]
get :index
[...]
post :create, album: {title: ''}
[...]
These should be re-written as
spec/controllers/albums_controller_spec.rb
[...]
process :index, method: :get
[...]
process :create, method: :post, params: { album: {title: ''} }
[...]
The assigns
and assert_template
methods have been extracted to a separate rails-controller-testing gem. Add it into your Gemfile:
Gemfile
[...]
group :test do
gem 'rails-controller-testing'
end
[...]
If you are using RSpec like me, add the following lines into your rails_helper.rb file as a temporary fix (RSpec does not support Rails 5 officially yet):
spec/rails_helper.rb
[...]
RSpec.configure do |config|
config.include Rails::Controller::Testing::TestProcess
config.include Rails::Controller::Testing::TemplateAssertions
config.include Rails::Controller::Testing::Integration
[...]
end
Some Other Stuff
Another cool feature of Rails 5 is that all rake
commands now live in rails
(for example, rake db:migrate
becomes rails db:migrate
).
ActiveRecord::Base.raise_in_transactional_callbacks
setting is deprecated and has no effect. It will be removed without replacement, so you can safely delete it from config/application.rb file.
ActiveJob now inherits from an intermediate ApplicationJob
class. Create an application_job.rb file inside the jobs directory:
jobs/application_job.rb
class ApplicationJob < ActiveJob::Base
end
Make sure that your jobs inherit from ApplicationJob
.
ActionDispatch::Static
accepts custom HTTP headers, so you may specify custom cache-control options.
The last thing to note is that ActionCable
does not require Redis, EventMachine, and Celluloid to be present anymore.
Conclusion
So, Rails 5 promises a great set of new features and additions and I really encourage you to give it a try. Also, be sure to browse the complete changelog.
Are you planning to migrate to Rails 5 in the nearest future? How many of your apps are still running Rails 3 (or even Rails 2)? Share your opinion in the comments!