#400 What's New in Rails 4
- Download:
- source codeProject Files in Zip (42.4 KB)
- mp4Full Size H.264 Video (47.9 MB)
- m4vSmaller H.264 Video (23.6 MB)
- webmFull Size VP8 Video (24.5 MB)
- ogvFull Size Theora Video (52.5 MB)
The next major version of Rails is just around the corner and as this is episode 400 it seems like a good time to discuss what’s coming in Rails 4.0. At the time of writing there’s no gem pre-release yet so to take a look at it we’ll have to check out the master branch, which we can do by cloning it from Github. Once we’ve done that we’ll need to move into its directory and run bundle to install its dependencies. Note that Rails 4 requires Ruby 1.9.3 or higher to run.
$ git clone https://github.com/rails/rails.git $ cd rails $ bundle
If you see an error message that says that the version of Bundler is incorrect you might need to install the prerelease version which we can do like this:
$ gem install bundler --pre
After this has installed we can try running bundle
again and once it completes we can create our first Rails 4 application. We do this by running the rails
command from the railties/bin/
directory of the version of Rails that we’ve downloaded to create a new application called blog
and telling it to use Edge Rails. We’ll also specify the Postgres database as there are some new Postgres features that we’ll be demonstrating later.
$ railties/bin/rails new ~/code/blog --edge -d postgresql
Once a prerelease version of the Rails 4 gem is released this will become much easier. We’ll be able to run something like gem install rails --pre
and then create a Rails 4 application in the usual way. Once we’ve installed this gem we can create a new Rails 3.2 application by passing in the version number.
$ rails _3.2.10_ new my_app
New Postgres Features
Now that we know how to create a new Rails 4 application let’s take a look at it. Before we look at any new features we’ll set up the database. The first thing we’ll do is remove the username option from each database in the database.yml
file so that the application can connect to our Postgres database. We should then be able to create the application’s databases by running rake db:create
. (Setting up a Rails application to use Postgresql was covered in more detail back in episode 342.)
Next we’ll generate create an Article
scaffold so that we have something in our application to work with. Rails 4 has support for native datatypes in Postgres and we’ll show two of these here, although a lot more are supported: array and hstore. We can store arrays in a string-type column and specify the type for hstore.
$ rails g scaffold article name content:text published_on:date tags properties:hstore
Before we migrate the database we’ll need to make a small change to the migration so that the tags
column is treated as an array. We do this by adding the array
option and setting it to true
. To get hstore working we need to run execute "create extension hstore"
before creating the table.
class CreateArticles < ActiveRecord::Migration def change execute "create extension hstore" create_table :articles do |t| t.string :name t.text :content t.date :published_on t.string :tags, array: true t.hstore :properties t.timestamps end end end
Running rake db:migrate
will create this table. Running migrations for Postgres databases in Rails 4 isn’t as noisy as in previous versions as the log messages aren’t shown. Let’s look now at how these datatypes are handled in the console. We’ll start by creating a new Article
. For the tags
we can pass in a Ruby array which will be converted to a Postgres array while for the properties
we can pass in a hash.
>> Article.create! name:"Hello", tags: %w[ruby rails], properties: {author:"Eifion"}
When we fetch these properties back later we get an array and a hash that we can interact with just like any other. While it’s true that we could do something similar by serializing a text column this approach allows us to interact with these columns in a much more efficient way. Episode 345 covers hstore in more detail.
New ActiveRecord Features
Next we’ll explore some of the other new features in ActiveRecord, starting with the all
method. This now returns an ActiveRecord relation instead of an array of results which means that it’s now lazily loaded and only triggers a database query when it needs to. This also gets rid of the need for the scoped
as it replaces this functionality. If we do need to old functionality where we get an array of results we can all to_a
on any ActiveRecord relation. This will do the same thing as all did in previous versions of Rails.
Another new method which goes along with this is load
. This will trigger the database query but return an ActiveRecord relation instead of an array of results. This is useful if we want to trigger the query, say for some kind of caching behaviour, but still want to return a relation. There’s also a new none
method which will return a ActiveRecord relation with no records. This is useful if we don’t want to return any records but want something that we can call other scopes on. Speaking of scopes, we can now call not on the where scope to invert it.
>> Article.where.not(name: "Hello") Article Load (107.6ms) SELECT "articles".* FROM "articles" WHERE ("articles"."name" != 'Hello')
The order
clause now accepts a hash so we can pass in a column name and the order that we want the results to be in. We can also pass in multiple column names to order by multiple columns. Another useful new method is find_by
. We’ve always been able to use dynamic finders, such as find_by_name
, but these rely on method_missing
and the implementation isn’t very clean. Rails 4 introduces a find_by
method that we can pass a hash to, like this:
>> Article.find_by name: "Hello"
There are also new find_or_create_by
and find_or_inititalize_by
methods and you’re encouraged to use these instead of the other ones.
ActiveModel::Model
Another interesting new feature is a new module called ActiveModel::Model
. Say that we want to create a new class and be able to update its attributes through a form, like an ActiveModel object, but not persist it to the database. If we include ActiveModel::Model
in the class we can create attr_accessors
for any method we want and also add validations just like we would with ActiveRecord. We can then instantiate instances of this class and interact with its attributes just like we would with an ActiveRecord model, even though this is just a plain Ruby class.
>> class Message >> include ActiveModel::Model >> attr_accessor :name, :content >> end => nil >> m = Message.new(name: "foo") => #<Message:0x007ff5dca1e760 @name="foo"> >> m.name => "foo"
This was possible to do with ActiveModel before but it required including a lot of different modules. In Rails 4 this is much easier. It’s especially great if you want to pass a custom class into a form_for
call.
Changes To The Gemfile
We’ll shift gears away from the console now and take a look at the welcome page that’s displayed when we visit a new application’s home page. This page looks much the same as it did before but it’s now dynamically generated by Rails internals. This means that we no longer have to delete the public/index.html
page from our applications. Next we’ll take a look at the gemfile. The default gemfile looks like this:
source 'https://rubygems.org' gem 'rails', github: 'rails/rails' gem 'arel', github: 'rails/arel' gem 'activerecord-deprecated_finders', github: 'rails/activerecord-deprecated_finders' gem 'pg' # Gems used only for assets and not required # in production environments by default. group :assets do gem 'sprockets-rails', github: 'rails/sprockets-rails' gem 'sass-rails', github: 'rails/sass-rails' gem 'coffee-rails', github: 'rails/coffee-rails' # See https://github.com/sstephenson/execjs#readme for more supported runtimes # gem 'therubyracer', platforms: :ruby gem 'uglifier', '>= 1.0.3' end gem 'jquery-rails' # Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks gem 'turbolinks' # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder gem 'jbuilder', '~> 1.0.1' # To use ActiveModel has_secure_password # gem 'bcrypt-ruby', '~> 3.0.0' # Use unicorn as the app server # gem 'unicorn' # Deploy with Capistrano # gem 'capistrano', group: :development # To use debugger # gem 'debugger'
The top three gems are only included here because we’re using Edge Rails. In the assets group the sprockets-rails
gem is new as part of a Sprockets refactoring to move part of it out into a separate gem. The gemfile also has a new turbolinks
gem which we covered in detail in episode 390. Even though turbolinks is enabled by default it’s easy to remove if we want to. All we need to do is remove the gem from the gemfile, remove the turbolinks line from the app/assets/javascripts/application.js
file and finally remove the references to data-turbolinks-track
from the application’s layout file. This attribute is something new that’s been added since episode 390 was written. Turbolinks will track assets with this attribute and will reload the entire page if they change.
Next we’ll take a look at the controller and see what was generated by the scaffolding.
class ArticlesController < ApplicationController before_action :set_article, only: [:show, :edit, :update, :destroy] # Some methods omitted. # PATCH/PUT /articles/1 # PATCH/PUT /articles/1.json def update respond_to do |format| if @article.update(article_params) format.html { redirect_to @article, notice: 'Article was successfully updated.' } format.json { head :no_content } else format.html { render action: 'edit' } format.json { render json: @article.errors, status: :unprocessable_entity } end end end private # Use callbacks to share common setup or constraints between actions. def set_article @article = Article.find(params[:id]) end # Never trust parameters from the scary internet, only allow the white list through. def article_params params.require(:article).permit(:name, :content, :published_at) end end
Quite a lot has changed has changed here. The scaffolding now loads the record using a before filter, except that it’s no longer called before_filter
but before_action
. This does exactly the same thing (and we can still use before_filter
if we want) but before_action
seems to be the new convention. The next thing of note is that the update action now responds to either a PUT request or a PATCH request. PATCH seems to fit the behaviour of an update action more clearly, though it’ll be a while before PUT disappears. In the action the update_attributes
method on the model has been renamed to update
although we can still use update_attributes
without deprecation warnings. Finally we have support for strong parameters built in. This approach is a much better solution than attr_accessible
. This was covered in episode 371 and it’s worth taking a look at this for more information about strong parameters. This means that in our model we no longer need the attr_accessible
line, although if we want this functionality back we can adding with the Protected Attributes gem.
While we’re talking about the model we’ll mention scopes. Previously we could pass in another scope as a second argument, like this:
class Article < ActiveRecord::Base scope :sorted, order(:name) end
We now need to pass in a callable object as a second object, such as a lambda. This is because it was too common to accidentally use something dynamic, such as the current time, in the second argument and this prevents that problem as it will always be evaluated at runtime.
class Article < ActiveRecord::Base scope :sorted, -> { order(:name) } end
Concerns
The controllers
and models
directories now have a concerns
directory. This is designed for modules that we can include in our models to help reduce their size. There are alternative approaches to doing this, such as service objects and these were discussed in episode 398. There’s no compulsion to use concerns if we don’t want to and we can remove these directories if we want.
Changes to Views
What about the views? Quite a lot has been added here with a lot of new helper methods that can be used to generate form fields, including ones for collections. You’re probably familiar with the collection_select
helper method to create a select menu for an association. We can now use collection_check_boxes
and collection_radio_buttons
to generate lists of checkboxes or radio buttons for an association. The ability to generate a list of checkboxes is useful for many-to-many associations or we can use it on an array attribute such as our Article
’s tags. We pass in the attribute’s name, a collection of values, and a name and id.
<div class="field"> <%= f.collection_check_boxes :tags, %w[ruby rails design], :to_s, :to_s %> </div>
Now when we edit or create an article we’ll have a list of checkboxes that we can use to set the tags.
This makes many-to-many associations a lot easier to handle with checkboxes.
There are also helper methods for the different HTML 5 input types. For example we have a date_select
field that we can change to a date
field. This will display a text field in some browsers, but others will handle it with a date selector. Chrome, for example, will give us a dropdown calendar that we can use to select a date from. Rails doesn’t do anything special here, just renders out an input
element with a type of date
, any special handling is done by the browser.
Template handlers are another useful new view feature. If we rename our edit.html.erb
file to edit.html.ruby
(this is ruby
not rb
as that conflicted with Mustache templating) we can use plain Ruby code in the template and any string that is returned will be rendered out.
content_tag(:h1, "Edit") + render("form")
This will give us the same edit page as before. The only difference is that it’s generated using straight Ruby code. This might have limited uses for rendering HTML but it can be really useful for handling JSON formats. See episode 379 for more details about this.
Another new feature of views in Rails 4 is the cache digest, also known as Russian doll caching. We won’t cover that here, but it was covered in detail in episode 387.
Routes
Let’s take a look at our application’s routes file next as there are some noticeable changes here. The biggest new feature is the addition of concerns which can help us reduce duplication in the routes. Let’s say that we have a nested comments
resource under articles
and that we also have a photos resource
that has nested comments
too. In a Rails 3 app, the routes for these resources would look like this:
resources :articles do resources :comments end resources :photos do resources :comments end
In Rails 4 we can remove this duplication by moving the comments into a concern that we’ll call commentable
. We put the concern’s behaviour into a block then add it to resources by calling concerns
.
concern :commentable do resources :comments end resources :articles, concerns: :commentable resources :photos, concerns: :commentable
This can be really useful if we have a complicated route set with a lot of APIs with duplication between them. For simpler examples such as this it may be better to stick with using rested resources, however.
The match
method has been used as a way to support any type of request, be it GET, POST or any other verb, but this is no longer supported. This is being done to encourage you to specify exactly what type of request you want to accept so that there are no wildcard matches. We should now instead use the get
or post
methods (or the newly supported patch
).
We’ve left one of the best new features of routing in Rails 4 to last. If we supply constraints to a route these will automatically turn into default attributes when we generate the URL. Here’s an example:
get 'foo', to: 'articles#index', constraints: {protocol: "https", subdomain: "test"}
Before, if we called foo_url
in our application these restraints wouldn’t be included, but now they are.
>> app.foo_url
=> "https://test.example.com/foo"
A Few Last Changes
There are just a few more things left to show. We’ll start in the application’s configuration file. In here we now can pass in a call to console
with a block. The contents of the block will be evaluated when we load the Rails environment through the console. We can add some console-specific configuration here such as using Pry. (Note that we’ll need to add Pry to our gemfile for this to work).
console do require 'pry' config.console = Pry end
Next we’ll look at the production config file, specifically at the following line.
config.eager_load = true
This will eager-load our entire Rails application and all its classes instead of using autoloading, which makes it thread-safe. Previously this behaviour was enabled using an option called threadsafe!
but this has now been deprecated as a way to encourage you to use this behaviour instead. Episode 365 has more details on this.
Testing
The next place we’ll look is in the test
directory. This is now structured a little differently: instead of having unit
and functional
directories we have controllers
, models
and so on making it structured more like RSpec is. Also, this now uses MiniTest.
We’ll finish up by looking at the new exception page in development. This now has a different look and if a routing error is thrown it shows a list of the valid routes which is useful.
Another way to get the routing information in development is to go to the /rails/info
path. This will display the same routing info table and we can toggle between this and our application’s properties.
That brings us to the end of our look at Rails 4. There are a number of great features and we haven’t covered all of them. There’s more information in this excellent blog post by Santiago Pastorino which compiles a long list of various links that cover the new additions in Rails 4.
There were several things that were removed from Rails 4 such as ActiveResource, model observers, page and action caching and disabling Rack::Cache
by default. All of these can be brought back by using various gems if you can’t live without them. One feature that we haven’t covered here is ActionController::Live
. This will be covered in detail in a future episode as it’s big enough to deserve one to itself. If you were expecting a queueing API with asynchronous mailing support this has been pulled from Rails 4.0 but should be included in Rails 4.1.