What’s new in Rails 5?

Mario Alberto Chávez
michelada.io
Published in
10 min readSep 23, 2015

Rails 5 was announced on RailsConf past April. David Heinemeier Hansson highlighted a few of the features that we can expect with the new release of Ruby on Rails.

He also talked about his vision on how building monolithic Ruby on Rails application is not that bad. You can agree or disagree with David’s opinions, but the truth is that modern Web Applications are more than just HTML and CSS and this is clear in the Ruby on Rails world, proof of this is the inclusion of Rails API and ActionCable.

The web has changed and, with it, Ruby on Rails also needs to change. So let us take a quick look at what is changing for the new version, Rails 5.

Support for Ruby 2.2.2 or newer

Rails 5 is a new major version of Ruby On Rails, this implicates that the Rails API and requirements can change to deprecate, add new, and/or improve existing APIs, but also to take advantage of important changes in the Ruby language as well.

The later is the most important reason on why Rails 5 will only work with Ruby 2.2.2 or better.

In Ruby On Rails applications, we usually pass symbols all over the place, doing this open the possibility of DOS attacks when our memory is consumed by symbols that never get garbage collected.

Ruby 2.2.0 introduced changes in its garbage collector to be able to collect symbols.

Another reason for Rails 5 to support Ruby 2.2.2 is to take advantage of new Incremental GC which will help to reduce memory consumption by our Rails applications.

Rails 5 also started to leak Ruby 2.0 syntax into its source. Keyword Arguments and Module#prepend are examples of the adoption of newer Ruby.

API Deprecation and Cleanup

Going through Rails commits, we will find changes related to removing code that was marked as deprecated in previous versions of Rails.

Notable removed APIs are:

ActionMailer

  • #deliver and #deliver! methods have been removed
  • *_path helper in email views

ActiveRecord

  • Support for protected_attributes gem
  • Support for activerecord-deprecated_finders gem

ActionPack assertions

Clean up in Rails 5 also includes dead code and unnecessary tests. Also, mocha is being removed from Rails tests in favor of plain Minitest stubbing.

Performance improvements

With Ruby 2.2.2, Rails 5 should get an improvement in performance, less memory usage, and less time spent in GC. This alone should help alone to bring this performance boost, but it’s not the only reason on why Rails 5 should perform better.

Seems that core team is determined to have a better/faster framework. Reducing object allocations, freezing immutable strings (BTW there is a discussion about having immutable strings by default in Ruby 3), removing unnecessary dependencies, and optimizing common operations have all helped to make Rails 5 faster.

The following are examples of commits focused on performance, it is worth to check them out, study, and understand the changes that have helped to reduce object allocations or code optimization because some of the lessons that can be learned in those commits can be applied to our own apps.

So, what is new in Rails 5?

So far we have seen how Rails 5 will be faster with the introduced changes but we haven’t seen (yet) what is new in terms of functionality or new API.

#or method in ActiveRecord::Relation

Finally ActiveRecord::Relation is getting #or method, this will allow us to write queries with ActiveRecord DSL as follows:

Book.where('status = 1').or(Book.where('status = 3'))
\# => SELECT * FROM books WHERE (status = 1) OR (status = 3)

#or method accepts a second relation as a parameter that is combined with an or.

#belongs_to is required by default

From now on every Rails application will have a new configuration option config.active_record.belongs_to_required_by_default = true, it will trigger a validation error when trying to save a model where belongs_to associations are not present.

config.active_record.belongs_to_required_by_default can be changed to false and with this keep old Rails behavior or we can disable this validation on each belongs_to definition, just passing an additional option optional: true as follows:

class Book < ActiveRecord::Base
belongs_to :author, optional: true
end

ActiveRecord’s attribute API

This new API adds functionality on top of ActiveRecord models, it made possible to override an attribute type to be a different type.

Consider the case where we have a field in the database defined as decimal but in our app we only care for the integer part of the number. We can in our app just ignore the decimal part and format our number everywhere we need to use it to only display the integer part.

With attribute API we can do this in an easy way:

class Book < ActiveRecord::Base
end
book.quantity # => 12.0class Book < ActiveRecord::Base
attribute :quantity, :integer
end
book.quantity # => 12

Here we are overriding the automatically generated attribute from the database schema to be cast in our model as an integer instead the original decimal. For every interaction of our model with the database, the attribute will be treated as a decimal as it should be.

We can even define our own custom types just by creating a class derived from ActiveRecord::Type::Value and implementing its contract to #cast, #serialize, and #deserialize values.

Custom attributes will honor ActiveModel::Dirty to track changes in our models. Also, these new attributes can be virtual, so there is no need to be backed by a table column.

has_secure_token landed in ActiveRecord

An ActiveRecord model now can have token attributes in an easy way. Common scenarios for token attributes are for cases when we need to create an invitation token or a password reset token for example.

Here is an example on how this works:

class Invite < ActiveRecord::Base
has_secure_token :invitation_code
end
invite = Invite.new
invite.save
invite.invitation_code # => 44539a6a59835a4ee9d7b112
invite.regenerate_invitation_code # => true

SecureRandom is being used to generate a 24-character token.

We can use the model generator to create needed migration for a token attribute, it requires from us to pass attribute type as token.

$ rails g invite invitation_code:token

Within the migration, a unique index will be created for our token column.

MySQL ActiveRecord adapter gets JSON support

If you happen to run your Rails application on top of MySQL 5.7.8 then your database have a new native JSON data type.

From Rails 5 you should be able to use this new data type within yourActiveRecord models.

Render a template outside controllers

Rails 5 allows you to render templates or inline code outside controllers. This feature is important and useful for ActiveJob and the new ActionCable (we will discuss this one later).

ActionController::Renderer is what makes this happen and it’s available in our ApplicationController class.

Let’s dig into few examples on how this works.

# render inline code
ApplicationController.render inline: '<%= "Hello Rails" %>' # => "Hello Rails"
# render a template
ApplicationController.render 'sample/index' # => Rendered sample/index.html.erb within layouts/application (0.0ms)
# render an action
SampleController.render :index # => Rendered sample/index.html.erb within layouts/application (0.0ms)
# render a file
ApplicationController.render file: ::Rails.root.join('app', 'views', 'sample', 'index.html.erb') # => Rendered sample/index.html.erb within layouts/application (0.8ms)

This is really nice but what if we need to pass assigns or locals to our template?

# Pass assigns
ApplicationController.render assigns: { rails: 'Rails' }, inline: '<%= "Hello #{@rails}" %>' # => "Hello Rails"
# Pass locals
ApplicationController.render locals: { hello: 'Hello' }, assigns: { rails: 'Rails' }, inline: '<%= "#{hello} #{@rails}" %>' # => "Hello Rails"

Now if we have to use route helper’s functions like root_url or any other helper that needs access to the environment we can do it, but what ActionController::Renderer will receive is not a real environment. Take in account that we might use this functionality outside HTTP request.

ApplicationController.render inline: '<%= root_url %>' # => "http://example.org/"# Inspect ActionController::Renderer environment
ApplicationController.renderer.defaults # => {:http_host=>"example.org", :https=>false, :method=>"get", :script_name=>"", "rack.input"=>""}
# To modify this environment we have to explicitly create a renderer
renderer = ApplicationController.renderer.new(
http_host: 'michelada.io'
) # => #<#<Class:0x007fdf9985a338>:0x007fdf947981c0 @env={"HTTP_HOST"=>"michelada.io", "HTTPS"=>"off", "SCRIPT_NAME"=>"", "rack.input"=>"", "REQUEST_METHOD"=>"GET", "action_dispatch.routes"=>#<ActionDispatch::Routing::RouteSet:0x007fdf93d29450>}>

renderer.render inline: '<%= root_url %>' # => "http://michelada.io/"

Better Minitest test runner

I have been using Minitest with Rails since test_unit was removed from Rails. I really like the simplicity of Minitest and I never missed anything from RSpec, pretty much Minitest fulfilled all my needs for testing.

The only issue that I had in the past with Minitest in Rails, was that the runner seems so basic. This is not the case anymore, its integration with Rails has been improved.

Now when you execute bin/rails test -h you get the following help screen with a few nice things:

  • Run tests filtered by a pattern
  • Execute a single test by specifying file and line number
  • Execute tests in specific files/directories
$ bin/rails test -h
minitest options:
-h, --help Display this help.
-s, --seed SEED Sets random seed. Also via env. Eg: SEED=n rake
-v, --verbose Verbose. Show progress processing files.
-n, --name PATTERN Filter run on /regexp/ or string.
Known extensions: pride, rails
-p, --pride Pride. Show your testing pride!
Usage: bin/rails test [options] [files or directories]
You can run a single test by appending a line number to a filename:
bin/rails test test/models/user_test.rb:27You can run multiple files and directories at the same time: bin/rails test test/controllers test/integration/login_test.rbRails options:
-e, --environment ENV Run tests in the ENV environment
-b, --backtrace Show the complete backtrace

Finally, the reporter gives you a command to rerun failed tests

$ bin/rails test
Run options: --seed 41988
# Running:FFinished in 0.028552s, 35.0239 runs/s, 35.0239 assertions/s. 1) Failure:
UserTest#test_the_truth [/Users/marioch/Development/proyectos/edge/cinco/test/models/user_test.rb:5]:
Failed assertion, no message given.
1 runs, 1 assertions, 1 failures, 0 errors, 0 skipsFailed tests:bin/rails test test/models/user_test.rb:4

Turbolinks 3

Turbolinks has been part of Rails since version 4, probably one of the features that people hate it or love; there is no middle ground here.

With Rails 5 we will be receiving a new version that, with the help of HTML5 custom data attributes, we will expect better speed and rendering in our Rails applications.

The most significative change in this new version is the Partial Replacement feature. From the client side, we will be able to tell Turbolinks what content do we need to change/replace and what we don’t.

Turbolinks will look for HTML5 custom attributes data-turbolinks-permanent and data-turbolinks-temporary to decide the replacement strategy in our DOM.

To trigger a replacement in the client side we could use Turbolinks.visit or Turbolinks.replace to update our DOM. The difference between visit and replace is that the first one will issue a GET to the server to obtain the HTML that must be used to replace our DOM while replace expects from us the HTML that should be used for its operation.

With both functions, we can pass a hash with an id or an array of id of HTML elements to change or keep.

  • Turbolinks.visit(url, { change: [‘entries’] }): Will replace any DOM element with custom attribute data-turbolinks-temporary and any element with its id listed in change.
  • Turbolinks.visit(url): Will keep only DOM elements with custom attribute data-turbolinks-permanent and replace everything.
  • Turbolinks.visit(url, { keep: [‘flash’] }): Will keep only DOM elements with custom attribute data-turbolinks-permanent and any element with its id listed in keep, everything else will be replaced.
  • Turbolinks.visit(url, { flush: true }): Will replace everything.

We can trigger the same functionality from the server-side with redirect_to and render, both can receive change, keep and flush as options but redirect_to can also receive turbolinks with true or false to force a redirect with or without Turbolinks.

Whether you like Turbolinks or not, this might be a good time to try out and find out if it could be a good fit somewhere in your application.

Rails API

Rails API was the opinionated way to build APIs with Ruby on Rails. Rails API was a separate gem with its own generator to build the skeleton of API applications.

Its goal was to help to create fast Rails API application by removing unnecessary middleware while providing sensitive defaults for these kinds of applications.

Starting with Rails 5, Rails API is integrated into the framework, there is no need to include additional gems. To create a new API only Rails app we just pass the --api option to rails command.

$ rails new michelada-api --api

The new application will include ActiveModelSerializers and will remove JQuery and Turbolinks gems from our Gemfile. Also, config/application.rb and aplication_controller.rb files to have config.api_only = true option and to not check for CSRF protection, also it will be inherited from ActionController::API.

# application.rb file
module MicheladaApi
class Application < Rails::Application
config.api_only = true
end
end
# application_controller.rb file
class ApplicationController < ActionController::API
end

If you want to learn more about creating Rails API application, here is a starter post from WyeWorks: “HOW TO BUILD A RAILS 5 API ONLY AND EMBER APPLICATION”

ActionCable

ActionCable is the new thing in Rails 5, it is a framework for real-time communication over web sockets.

An ActionCable server will expose one or more channels into where a consumer will be able to subscribe via web socket connection, each channel broadcast messages to all subscribers in real-time.

Current ActionCable dependencies include the need of Redis and its feature PubSub. It also requires on the Ruby side faye-websocket and celluloid although these may change in the future.

An application with ActionCable support requires of 3 basic components:

  • Connection: Is a class derived from ActionCable::Connection::Base, this is where authorization and connection establishments happen.
  • Channel: This is what we will expose via web sockets and it is derived from ActionCable::Channel::Base
  • Client: Is a javascript library to become an ActionCable client.

ActionCable will require from us to start an additional server to respond to web sockets connections.

If you want to explore ActionCable, the Rails team has put together a Github repository with a sample application.

Conclusions

This is not the definitive list of changes or new features in Rails 5 but is just a quick glimpse on what can we expect on the new version.

There is no definitive date for when this might happen, but it still might happen this year. There is still 68 open pull request for Rails 5 milestone in Github.

Without a doubt, it is time to prepare our applications for the upgrade; just because of the performance improvements are worth it. If you are on Rails 4.x, the upgrade is almost painless.

Originally published at blog.michelada.io. Check out Michelada.io at michelada.io.

--

--

michelada.io
michelada.io

Published in michelada.io

We plan, code and launch awesome web and mobile products.

Mario Alberto Chávez
Mario Alberto Chávez

Written by Mario Alberto Chávez

Rubyist and software engineer. @micheladaio co-founder. Author of Aprendiendo Ruby on Rails @railsenespanol. Photography http://mario_chavez.500px.com