What’s new in Rails 5.1
This was originally posted on http://blog.michelada.io/whats-new-in-rails-51
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.
ActiveRecord
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`.
ActionPack
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.
Railties
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.yml
when 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}"]
end
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 }
end<%= 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 ]
end<%= link_to "Browse users", browse_path(:users) %> #=> <a href="/users?page=1&page_size=10">Browse users</a>
<%= link_to "Browse users", browse_path(:users, page: 2) %> #=> <a href="/users?page=2&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]">
</form>
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="✓"/>
<input type="text" name="user[name]" />
</form>
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="✓" />
<input type="text" name="name" id="name" />
</form>
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]">
</form>
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]
end
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"
end
end
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
Conclusions
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.