What's new in Rails 5.1

By Mario Alberto Chavez March 9, 2017

rails ruby upgrade minitest security vulnerabilities performance capybara react javascript

Rails 5.1 beta 1 was just announced by the Rails team and it includes important changes on how Javascript will be used by Rails applications , it even says ”Loving JavaScript” in the section title to describe these changes. This is probably the biggest change since the Asset Pipeline was introduced in Rails 4.0. It also acknowledges how modern web applications are being built in the present.

Ruby 2.4 support

This new version of Rails is compatible with Ruby 2.4, for cases where Ruby 2.4 provides methods defined in ActiveSupport core extensions, Ruby’s implementation will be favored, if you are running Rails with the previous version of Ruby then the ActiveSupport core extension code will be used.

An example of this is Hash#compact and Hash#compact!. Also, Rails 5.1 supports Ruby’s unification of Fixnum and Bignum into the Integer class. Unicode version 9.0.0 from Ruby 2.4 is available as well, so now you have more emojis to name your controller and models.

Removed deprecated functionality

It couldn’t be a new Rails release without removing deprecated code or marking code as deprecated.


Following methods have been removed #uniq, #uniq!, #uniq_value, #insert_sql, #update_sql, and #delete_sql.

If you use fixtures #use_transactional_fixtures has been removed as well.

The following rake db tasks were also removed db:test:clone, db:test:clone_schema,db:test:clone_structure`.


Don’t call env anymore from your controllers, since ActionController::Metal#env was removed, use request instead.

If you are doing Integration tests, ActionDispatch::IntegrationTest now requires you to use the Keyword arguments versions of #get, #post, #patch, #put, #delete, and #head.

In your controller you can’t userender :text or :nothing anymore. Also redirect_to doesn’t support the :back option anymore.

Controller methods skip_action_callback, skip_filter, before_filter, prepend_before_filter, skip_before_filter, append_before_filter, around_filter, prepend_around_filter, skip_around_filter, append_around_filter, after_filter, prepend_after_filter, skip_after_filter and append_after_filter were removed too.


rake tasks rails:update, rails:template, rails:template:copy, rails:update:configs and rails:update:bin were removed.

Configuration options config.serve_static_files and config.static_cache_control were also removed .

So, what is new?

Here is the list of features that you can expect with the new release.

Adiós jQuery

If you have been in Rails world for a while, you probably remember the time when Scriptaculous was replaced by jQuery, well now is time to say Adiós to jQuery.

Rails doesn’t depend on jQuery anymore, rails-ujs was rewritten to be plain vanilla Javascript. You can still add jQuery as a dependency in your app but is not installed by default with new Rails applications.

New way to work with Javascript.

The Javascript world has changed a lot since Rails 4.0 introduced the Asset Pipeline. Now it is almost impossible to simply download a Javascript library from its Git repository, place it under vendor/assets/javascripts, require it in the application.js manifest and expect it to work.

Today, Javascript libraries are packed for Node.js and configured to work with Node’s module loading system making impossible for Rails to work with them without additional configuration to include Node.js and npm into the picture.

Rails team acknowledges this and with Rails 5.1 they made it easy for us to work with the Javascript world with the introduction of Yarn.

Every new Rails application will include Yarn, which is a package manager for Javascript. To use Yarn you must install it first if you don’t already have it. In MacOS, you can just use Homebrew.

$ brew install yarn

This command will install Yarn and Nodejs. By creating a new Rails application with Rails 5.1.0 a new binstub, yarn, will be placed in the bin directory. You should always use this binstub when working with Yarn from a Rails application.

To add a Javascript package to your Rails app, you simply use the add command inYarn.

$ bin/yarn add moment

Yarn will create a package.json and yarn.lock files, it will also download the Javascript package into node_modules directory. Yarn will manage versions and dependencies for us.

Once a Javascript package has been installed you can reference it from the Asset Pipeline manifest. In this example, to use the moment library, you can require it by adding the following line in application.js:

//= require moment/moment

Then you’re free to use it on your own Javascript files. By running rails assets:precompile your generated application-{digest}.js file will include the moment library.

If you are into single page applications, Rails now comes with setup options for React, Angular, and Vue. In this case, Rails depends on Webpack to manage modules and dependencies.

To setup Webpack, Rails depends on the gem Webpacker which setups required configuration for Webpack to work with Rails.

To start a Rails/React project you can run the command:

$ rails new single_page --webpack=react

For an existing project, run the following command but first add the web packer gem in your Gemfile. As of today, it is better if you pull the gem directly from its repository.

Webpacker gem is compatible with Rails 4.2 or better.

$ rails webpacker:install
$ rails webpacker:install:react

The first command will setup Webpack for development and production environments, this configuration can be found in config/webpack and it is a standard Webpack configuration, something that you might expect by using Webpack in a standalone or Nodejs application.

Two new stubs are added to the bin directory: webpack-watcher and webpack-dev-server, both of them are scripts that need to be running in development along with rails s. The first one is a watcher that will automatically recompile your Javascript as you make changes. The second one does the same but it supports advanced features of Webpack. The later runs on http://localhost:8080 and you need to configure the option config.x.webpacker[:dev_server_host] in your config/environments/development.rb to allow rails to serve Javascript files properly. Only one of the scripts needs to be running at a time.

Your Javascript code must be placed in app/javascript, in there you must define packs, a pack is a Javascript application, it needs to have an entry point like signup.js and a directory app/javascript/signup must exist with all related files to this application.

Then in your Rails layout, you just add the javascript_pack_tag to load your application.

<%= javascript_pack_tag ‘signup’ %>

javascript_pack_tag will reference your pack and in production mode it will insert the digested file.

If you need to reference Sprockets files in your Javascript packs, just add the .erb extension to your file, include the helpers and use them.

// app/javascript/signup/form.js.erb

<% helpers = ActionController::Base.helpers %>
var logoPath = "<%= helpers.image_path('logo.png') %>";

rails webpacker:install:react command tells Yarn to setup decencies to use React in your application but you can also pass angular or vue as parameters.

Once you are ready to deploy your application just run the following commands to compile all your assets and Javascripts.

$ rails asset:precompile
$ rails webpacker:compile

Now is up to you to decide if you just need Yarn or if you want to include Webpack into your development environment. Rails and Asset Pipeline will not get in your way.

Keep your secrets safe.

It is a very common practice to keep your production secrets in environment variables, the ones that later are loaded from secrets.ymlwhen you boot your Rails application. Another approach is to set your private keys in your secrets.yml and exclude it from your source control while keeping a separate file for production. Either way has its pros and cons.

Rails now include a way to encrypt your secrets file. To use encrypted secrets you need to setup your project first by running:

$ bin/rails secrets:setup

This will generate a config/secrets.yml.key which will have the encryption key, you need to secure this file and don’t add it to your source control system. A new file config/secrets.yml.enc will be added, this is the encrypted file that will keep your secrets safe.

If you need to change a value, you will need to run the command:

$ bin/rails secrets:edit

This will open your default editor and will allow you to make changes to your encrypted file. The structure of this file is the same as config/secrets.yml, once you make your changes and save the file, everything will be encrypted again.

To tell Rails to use this encrypted file instead the regular config/secrets.yml you will need to modify your config/production.rb file to include the line config.read_encrypted_secrets = true.

From now on, when you deploy your application to a production environment and given that config/secrets.yml.enc is checked in your source control, just set the environment variable RAILS_MASTER_KEY with the encryption key.

The cryptography method used by Rails was reported to be weak, but it was quickly updated to use a stronger method.

Direct and resolve routes

Rails now allow you to define custom URLs that override or replace default behavior.

The first example of this is direct, which allows us to define custom URLs like

direct(:dev_shop) { "https://michelada.io" }

Which we can use in our code like:

<%= link_to "michelada.io", dev_shop_url %> #=> <a href="https://michelada.io" />

You can also define routes to polymorphic models using an array definition like:

direct :model_section do |model|
  [model, anchor: "#{model.model_name.singular}_#{model.id}"]

And then use the helper as follow:

<%= link_to "User section", user_section_path(@user) %> #=> <a href="/users/1#user_1">User section</a>

Also, you can pass a hash with controller and action as direct’s block.

direct :current_users do
  { action: "index", controller: "users", active: true }

<%= link_to "Current users", current_users_path %> #=> <a href="/users?active=true">Current users</a>

direct can also have default parameters like:

direct :browse, page: 1, page_size: 10 do |klass, options|
 [ klass, options ]

<%= link_to "Browse users", browse_path(:users) %> #=> <a href="/users?page=1&amp;page_size=10">Browse users</a>
<%= link_to "Browse users", browse_path(:users, page: 2) %> #=> <a href="/users?page=2&amp;page_size=10">Browse users</a>

resolve helps us map a model to a resource. Here is an example for the User model being mapped to theprofile resource.

resource :profile
resolve("User") { [:profile] }

By doing this mapping, we are telling to the router to not use paths based on the model name, but resolve it to profile. Doing this when you write the following code will generate the correct resource paths.

<%= link_to @user.name, @user %> #=> <a href="/profile">Sample user</a>

<%= form_with model: User.new do |f| %>
  <%= f.text_field :name %>
<% end %>
<form action="/profile" accept-charset="UTF-8" data-remote="true" method="post"><input name="utf8" type="hidden" value="✓">
  <input type="text" name="user[name]">

Both direct and resolve work on the global scope of the routes, they can’t be used inside a scope or namespace.

form_for and form_tag unified into form_with

form_for and form_tag in Rails were very similar, both allowed you to create a form tag but the first one uses model’s attributes to build create or update form, while the second one simply create an HTML form tag with the passed URL as action.

Given their similarities, with Rails 5.1 both were unified into form_with. If we want to create a form based on model attributes you just need to pass the model: param.

<%= form_with model: User.new do |user| %>
  <%= user.text_field :name %>
<% end %>
<form action="/profile" accept-charset="UTF-8" data-remote="true" method="post"><input name="utf8" type="hidden" value="&#x2713;"/>
  <input type="text" name="user[name]" />

If you just want a form without the model’s attribute, just pass the url: param.

<%= form_with url: "/users" do %>
  <%= text_field_tag :name %>
<% end %>
<form action="/user" accept-charset="UTF-8" data-remote="true" method="post"><input name="utf8" type="hidden"value="&#x2713;" />
  <input type="text" name="name" id="name" />

form_with accepts the same parameters as form_for and form_tag for scopes, data attributes, HTML attributes, method, etc.

With form_with be aware that the data-remote attribute is set to true by default. The syntax for nested models was also changed. Instead of using fields_for you must use now the fields method and set model: param with the nested model instance.

<%= form_with model: User.new do |user| %>
  <%= user.text_field :name %>
  <%= user.fields model: Profile.new do |profile| %>
    <%= profile.text_field :age %>
  <% end %>
<% end %>
<form action="/profile" accept-charset="UTF-8" data-remote="true" method="post"><input name="utf8" type="hidden" value="✓">
  <input type="text" name="user[name]">

    <input type="text" name="user[profile][age]">

System tests

There is a good chance that you are currently using Capybara for your features or acceptance testing in your Rails applications. Using it currently involves setting up Capybara in your project, along with a strategy for cleaning your database with tools like database_cleaner, and launchy gem to help you to debug when an error happens.

With Rails embracing Javascript the need to support full testing, including the javascript part, sparked what is called System Tests with Rails 5.1. Now Capybara is setup by default and a change in the framework is included to deal with transactional fixtures without the need to have something like database_cleaner.

A new file is included, it is the base for systems tests test/application_system_test_case.rb , this file setups by default use of selenium and chrome driver to run your test.

require "test_helper"

class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
  driven_by :selenium, using: :chrome, screen_size: [1400, 1400]

You can change the parameters passed to driven_by to use firefox or to use a headless server like poltergeist. Capybara DSL and assertions are available in your system tests. You can create these tests files by hand our use rails’ generator rails g test_unit:system

require "application_system_test_case"

class UsersTest < ApplicationSystemTestCase
  test "visiting the index" do
    visit root_url

    assert_selector "h1", text: "Hello Rails"

When a system test fails, a screenshot is taken and saved into tmp directory, minitest will report the file that contains the screenshot for you to review.

[Screenshot]: tmp/screenshots/failures_test_visiting_the_index.png


Probably the feature that stands out in this new version, is the change on how Rails deals with Javascript and npm. Like I mentioned earlier, this acknowledges how web development has changed over time, but also states that using Rails to build web applications is still an option, no matter if your application is HTML, an API or a Javascript single page application.

Rails is removing all the friction and helping you to design and build the kind of application that makes sense for your immediate needs.

This new version might be released this same year, probably around the Railsconf, which is relevant not only because you want to use the new features, but because version 4.2.x might become unsupported. This might be a good time to start planning the upgrade in your applications.

If you need help on how to upgrade older versions of Rails, please refer to our previous blog post “Upgrading a Ruby on Rails application”.

Thanks to David Padilla and Gil Villa for helping to review this post content.

Mario Alberto Chavez

Software Engineer