Rails 5 adds ignored_columns for Active Record

This blog is part of our Rails 5 series.

Somtimes we need to ignore a database column. However Rails 4.x doesn’t have any officially defined method which ignores a database column from Active Record. We can apply our patch on model to ignore certain columns.

class User < ActiveRecord::Base

  # Ignoring employee_email column
  def self.columns
    super.reject {|column| column.name == 'employee_email'}
  end

end

Rails 5 added ignored_columns

Rails 5 has added ignored_columns to ActiveRecord::Base class.

Here is revised code after using ignored_columns method.

class User < ApplicationRecord
  self.ignored_columns = %w(employee_email)
end

Rails 5 adds a hidden field on collection radio buttons

This blog is part of our Rails 5 series.

Consider the following form which has only one input role_id which is accepted through collection_radio_button.

<%= form_for(@user) do |f| %>
  <%= f.collection_radio_buttons :role_id, @roles, :id, :name %>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

In the controller, we can access role_id using the strong parameters.

def user_params
  params.require(:user).permit(:role_id)
end

When we try to submit this form without selecting any radio button in Rails 4.x, we will get 400 Bad Request error with following message.

ActionController::ParameterMissing (param is missing or the value is empty: user):.

This is because following parameters were sent to server in Rails 4.x .

Parameters: {"utf8"=>"✓", "authenticity_token"=>"...", "commit"=>"Create User"}

According to HTML specification, when multiple parameters are passed to collection_radio_buttons and no option is selected, web browsers do not send any value to the server.

Solution in Rails 5

Rails 5 adds hidden field on the collection_radio_buttons to avoid raising an error when the only input on the form is collection_radio_buttons. The hidden field has the same name as collection radio button and has blank value.

Following parameters will be sent to server in Rails 5 when above user form is submitted:

Parameters: {"utf8"=>"✓", "authenticity_token"=>"...", "user"=>{"role_id"=>""}, "commit"=>"Create User"}

In case we don’t want the helper to generate this hidden field, we can specify include_hidden: false.

<%= f.collection_radio_buttons :role_id, Role.all, :id, :name, include_hidden: false %>

Rails 5 extracted attributes assignment from Active Record to Active Model

This blog is part of our Rails 5 series.

Before Rails 5, we could use assign_attributes and have bulk assignment of attributes only for objects whose classes are inherited from ActiveRecord::Base class.

In Rails 5 we can make use of assign_attributes method and have bulk assignment of attributes even for objects whose classes are not not inherited from ActiveRecord::Base.

This is possible because the attributes assignment code is now moved from ActiveRecord::AttributeAssignment to ActiveModel::AttributeAssignment module.

To have this up and running, we need to include ActiveModel::AttributeAssignment module to our class.

class User
  include ActiveModel::AttributeAssignment
  attr_accessor :email, :first_name, :last_name
end

user = User.new
user.assign_attributes({email:      'sam@example.com',
                        first_name: 'Sam',
                        last_name:  'Smith'})
> user.email
#=> "sam@example.com"
> user.first_name
#=> "Sam"

Rails 5 supports configuring Active Job backend adapter for each job

This blog is part of our Rails 5 series.

Before Rails 5 we had ability to configure Active Job queue_adapter at an application level. If we want to use sidekiq as our backend queue adapter we would configure it as following.

config.active_job.queue_adapter = :sidekiq

This queue_adapter would be applicable to all jobs.

Rails 5 provides ability to configure queue_adapter on a per job basis. It means queue_adapter for one job can be different to that of the other job.

Let’s suppose we have two jobs in our brand new Rails 5 application. EmailJob is responsible for processing basic emails and NewsletterJob sends out news letters.

class EmailJob < ActiveJob::Base
  self.queue_adapter = :sidekiq
end

class NewletterJob < ActiveJob::Base
end

EmailJob.queue_adapter
 => #<ActiveJob::QueueAdapters::SidekiqAdapter:0x007fb3d0b2e4a0>

NewletterJob.queue_adapter
 => #<ActiveJob::QueueAdapters::AsyncAdapter:0x007fb3d0c61b88>

We are now able to configure sidekiq queue adapter for EmailJob. In case of NewsletterJob we fallback to the global default adapter which in case of a new Rails 5 app is async.

Moreover, in Rails 5, when one job inherits other job, then queue adapter of the parent job gets persisted in the child job unless child job has configuration to change queue adapter.

Since news letters are email jobs we can make NewsLetterJob inherit from EmailJob.

Below is an example where EmailJob is using rescue while NewsLetterJob is using sidekiq.

class EmailJob < ActiveJob::Base
  self.queue_adapter = :resque
end

class NewsletterJob < EmailJob
end

EmailJob.queue_adapter
 => #<ActiveJob::QueueAdapters::ResqueAdapter:0x007fe137ede2a0>

NewsletterJob.queue_adapter
 => #<ActiveJob::QueueAdapters::ResqueAdapter:0x007fe137ede2a0>

class NewsletterJob < EmailJob
  self.queue_adapter = :sidekiq
end

NewsletterJob.queue_adapter
 => #<ActiveJob::QueueAdapters::SidekiqAdapter:0x007fb3d0b2e4a0>

Rails 5 accepts 1 or true for acceptance validation

This blog is part of our Rails 5 series.

validates_acceptance_of is a good validation tool for asking users to accept “terms of service” or similar items.

Before Rails 5, the only acceptable value for a validates_acceptance_of validation was 1.

class User < ActiveRecord::Base
  validates_acceptance_of :terms_of_service
end

> user = User.new(terms_of_service: "1")
> user.valid?
#=> true

Having acceptable value of 1 does cause some ambiguity because general purpose of acceptance validation is for attributes that hold boolean values.

So in order to have true as acceptance value we had to pass accept option to validates_acceptance_of as shown below.

class User < ActiveRecord::Base
  validates_acceptance_of :terms_of_service, accept: true
end

> user = User.new(terms_of_service: true)
> user.valid?
#=> true

> user.terms_of_service = '1'
> user.valid?
#=> false

Now this comes with the cost that 1 is no longer an acceptable value.

In Rails 5, we have true as a default value for acceptance along with the already existing acceptable value of 1.

In Rails 5 the previous example would look like as shown below.

class User < ActiveRecord::Base
  validates_acceptance_of :terms_of_service
end

> user = User.new(terms_of_service: true)
> user.valid?
#=> true

> user.terms_of_service = '1'
> user.valid?
#=> true

Rails 5 allows user to have custom set of acceptable values

In Rails 5, :accept option of validates_acceptance_of method supports an array of values unlike a single value that we had before.

So in our example if we are to validate our terms_of_service attribute with any of true, "y", "yes" we could have our validation as follows.

class User < ActiveRecord::Base
  validates_acceptance_of :terms_of_service, accept: [true, "y", "yes"]
end

> user = User.new(terms_of_service: true)
> user.valid?
#=> true

> user.terms_of_service = 'y'
> user.valid?
#=> true

> user.terms_of_service = 'yes'
> user.valid?
#=> true

Rails 5 supports bi-directional destroy dependency

This blog is part of our Rails 5 series.

In Rails 4.x, it is not possible to have destroy dependency on both sides of a bi-directional association between the two models as it would result in an infinite callback loop causing SystemStackError: stack level too deep.

class User < ActiveRecord::Base
  has_one :profile, dependent: :destroy
end

class Profile < ActiveRecord::Base
  belongs_to :user, dependent: :destroy
end

Calling User#destroy or Profile#destroy would lead to an infinite callback loop.

>> user = User.first
=> <User id: 4, name: "George">

>> user.profile
=> <Profile id: 4>

>> user.destroy
=> DELETE FROM `profiles` WHERE `profiles`.`id` = 4
   ROLLBACK
SystemStackError: stack level too deep

Rails 5 supports bi-directional destroy dependency without triggering infinite callback loop.

>> user = User.first
=> <User id: 4, name: "George">

>> user.profile
=> <Profile id: 4, about: 'Rails developer', works_at: 'ABC'>

>> user.destroy
=> DELETE FROM "profiles" WHERE "posts"."id" = ?  [["id", 4]]
   DELETE FROM "users" WHERE "users"."id" = ?  [["id", 4]]
=> <User id: 4, name: "George">

There are many instances like above where we need to destroy an association when it is destroying itself, otherwise it may lead to orphan records.

This feature adds responsibility on developers to ensure adding destroy dependency only when it is required as it can have unintended consequences as shown below.

class User < ApplicationRecord
  has_many :posts, dependent: :destroy
end

class Post < ApplicationRecord
  belongs_to :user, dependent: :destroy
end

>> user = User.first
=> <User id: 4, name: "George">

>> user.posts
=> <ActiveRecord::Associations::CollectionProxy [<Post id: 11, title: 'Ruby', user_id: 4>, #<Post id: 12, title: 'Rails', user_id: 4>]>

As we can see “user” has two posts. Now we will destroy first post.

>> user.posts.first.destroy
=> DELETE FROM "posts" WHERE "posts"."id" = ?  [["id", 11]]
   SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = ?  [["user_id", 4]]
   DELETE FROM "posts" WHERE "posts"."id" = ?  [["id", 12]]
   DELETE FROM "users" WHERE "users"."id" = ?  [["id", 4]]

As we can see, we wanted to remove post with id “11”. However post with id “12” also got deleted. Not only that but user record got deleted too.

In Rails 4.x this would have resulted in SystemStackError: stack level too deep .

So we should use this option very carefully.

Rails 5 adds after_{create,update,delete}_commit callbacks aliases

This blog is part of our Rails 5 series.

Rails 4.x has after_commit callback. after_commit is called after a record has been created, updated or destroyed.

class User < ActiveRecord::Base
  after_commit :send_welcome_mail, on: create
  after_commit :send_profile_update_notification, on: update
  after_commit :remove_profile_data, on: destroy

  def send_welcome_mail
    EmailSender.send_welcome_mail(email: email)
  end
end

Rails 5 added new aliases

Rails 5 had added following three aliases.

  • after_create_commit
  • after_update_commit
  • after_destroy_commit

Here is revised code after using these aliases.

class User < ApplicationRecord
  after_create_commit	:send_welcome_mail
  after_update_commit	:send_profile_update_notification
  after_destroy_commit	:remove_profile_data

  def send_welcome_mail
    EmailSender.send_welcome_mail(email: email)
  end
end

Note

We earlier stated that after_commit callback is executed at the end of transaction. Using after_commit with a transaction block can be tricky. Please checkout our earlier post about Gotcha with after_commit callback in Rails .

Rails 5 allows updating a record without updating timestamps

This blog is part of our Rails 5 series.

In Rails 4.x, when we save an ActiveRecord object then Rails automatically updates fields updated_at or updated_on.

>> user = User.new(name: 'John', email: 'john@example.com')
>> user.save
 INSERT INTO "users" ("name", "created_at", "updated_at", "email") VALUES (?, ?, ?, ?)  [["name", "John"], ["created_at", 2016-03-16 09:12:44 UTC], ["updated_at", 2016-03-16 09:12:44 UTC], ["email", "john@example.com"]]
=> true

>> user.updated_at
=> Wed, 16 Mar 2016 09:12:44 UTC +00:00

>> user.name = "Mark"
>> user.save
  UPDATE "users" SET "name" = ?, "updated_at" = ? WHERE "users"."id" = ?  [["name", "Mark"], ["updated_at", 2016-03-16 09:15:30 UTC], ["id", 12]]
=> true

>> user.updated_at
=> Wed, 16 Mar 2016 09:15:30 UTC +00:00

Addition of touch option in ActiveRecord::Base#save

In Rails 5, by passing touch: false as an option to save, we can update the object without updating timestamps. The default option for touch is true.

>> user.updated_at
=> Wed, 16 Mar 2016 09:15:30 UTC +00:00

>> user.name = "Dan"
>> user.save(touch: false)
  UPDATE "users" SET "name" = ? WHERE "users"."id" = ?  [["name", "Dan"], ["id", 12]]
=> true

>> user.updated_at
=> Wed, 16 Mar 2016 09:15:30 UTC +00:00

This works only when we are updating a record and does not work when a record is created.

>> user = User.new(name: 'Tom', email: 'tom@example.com')
>> user.save(touch: false)
 INSERT INTO "users" ("name", "created_at", "updated_at", "email") VALUES (?, ?, ?, ?)  [["name", "Tom"], ["created_at", 2016-03-21 06:57:23 UTC], ["updated_at", 2016-03-21 06:57:23 UTC], ["email", "tom@example.com"]])

>> user.updated_at
=> Mon, 21 Mar 2016 07:04:04 UTC +00:00

Rails 5 adds a way to get information about types of failed validations

This blog is part of our Rails 5 series.

Let’s look at a validation example in Rails 4.x.

class User < ActiveRecord::Base
  validates :email, presence: true
end

>> user = User.new
>> user.valid?
=> false

>> user.errors.messages
=> {:email=>["can't be blank"]}

In this case, we do not get any information about the type of failed validation as ActiveModel#Errors only gives the attribute name and the translated error message.

This works out well for normal apps. But in case of API only applications, sometimes we want to allow the client consuming the API to generate customized error message as per their needs. We don’t want to send the final translated messages in such cases. Instead if we could just send details that presence validation failed for :name attribute, the client app would be able to customize the error message based on that information.

In Rails 5, it is now possible to get such details about which validations failed for a given attribute.

We can check this by calling details method on the ActiveModel#Errors instance.

class User < ApplicationRecord
  validates :email, presence: true
end

>> user = User.new
>> user.valid?
=> false

>> user.errors.details
=> {:email=>[{:error=>:blank}]}

We can also add custom validator types as per our need.

# Custom validator type
>> user = User.new
>> user.errors.add(:name, :not_valid, message: "The name appears invalid")

>> user.errors.details
=> {:name=>[{:error=>:not_valid}]}

# Custom error with default validator type :invalid

>> user = User.new
>> user.errors.add(:name)

>> user.errors.details
=> {:name=>[{:error=>:invalid}]}

# More than one error on one attribute

>> user = User.new
>> user.errors.add(:password, :invalid_format, message: "Password must start with an alphabet")
>> user.errors.add(:password, :invalid_length, message: "Password must have atleast 8 characters")

>> user.errors.details
=> {:password=>[{:error=>:invalid_format}, {:error=>:invalid_length}]}

Passing contextual information about the errors

We can also send contextual data for the validation to the Errors#add method. This data can be later accessed via Errors#details method because Errors#add method forwards all options except :message, :if, :unless, and :on to details.

For eg. we can say that the password is invalid because ! is not allowed, as follows.

class User < ApplicationRecord
  validate :password_cannot_have_invalid_character

  def password_cannot_have_invalid_character
    if password.scan("!").present?
      errors.add(:password, :invalid_character, not_allowed: "!")
    end
  end
end

>> user = User.create(name: 'Mark', password: 'Ra!ls')
>> user.errors.details
=> {:password=>[{:error=>:invalid_character, :not_allowed=>"!"}]}

We can also use this feature in our Rails 4.x apps by simply installing gem active_model-errors_details.

Using Image as a Container in React Native

Adding a nice looking background to a screen makes an app visually appealing. It also makes the app look more sleek and elegant. Let us see how we can leverage this technique in React Native and add an image as a background.

We’ll need to create different sizes of the background image, which we’re going to use as a container. React Native will pick up appropriate image based on the device’s dimension (check Images guide for more information).

- login-background.png (375x667)
- login-background@2x.png (750x1134)
- login-background@3x.png (1125x2001)

Now we’ll use these images in our code as container.

//...
render() {
    return (
      <Image
        source={require('./images/login-background.png')}
        style={styles.container}>
        <Text style={styles.welcome}>
          Welcome to React Native!
        </Text>
        <Text style={styles.instructions}>
          To get started, edit index.ios.js
        </Text>
        <Text style={styles.instructions}>
          Press Cmd+R to reload,{'\n'}
          Cmd+D or shake for dev menu
        </Text>
      </Image>
    );
  }
//...

const styles = StyleSheet.create({
  container: {
    flex: 1,
    width: undefined,
    height: undefined,
    backgroundColor:'transparent',
    justifyContent: 'center',
    alignItems: 'center',
  },
});

We’ve intentionally left height and width of the image as undefined. This will let React Native take the size of the image from the image itself. This way, we can use Image component as View and add other components as a children to build UI. image as container in react native

Rails 5 - What's in it for me?

This blog is part of our Rails 5 series.

I recently did a webinar with Srijan on upcoming changes in Rails 5. In this webinar I discussed various features and additions coming up in Rails 5.

Major Features

Ruby 2.2.2+ dependency.

Action Cable.

API only apps.

Features for Development mode

Puma as default web server.

rails CLI over rake.

Restarting app using rails restart.

Enable caching using rails dev:cache.

Enhanced filtering of routes using rails routes -g

Evented file system monitor.

Features for Test mode

Test Runner.

Changes to controller tests.

Cache content forever using http_cache_forever.

Collection caching using ActiveRecord#cache_key.

Partials caching using multi_fetch_fragments.

Caching in Action Mailer views.

Changes in Active Record

Introduction of ApplicationRecord.

ActiveRelation#or.

has_secure_token for generating secure tokens.

Versioned migrations for backward compatibility.

Changes in Active Support

Improvements to Date/Time.

Enumerable#pluck, Enumerable#without.

Change in behavior related to halting callback chains.

Rails 5 officially supports MariaDB

This blog is part of our Rails 5 series.

MariaDB is an open source fork of the MySQL database and it acts as a drop-in replacement for MySQL.

After the Oracle’s take over of MySQL there was some confusion about the future of MySQL. To remove any ambiguity about whether in future MySQL will remain free or not MariaDB was started .

Some of you might be wondering what advantages MariaDB offers over MySQL.Here is an article which lists 10 reasons to migrate to MariaDB from MySQL.

MariaDB is bundled as default on systems like Redhat’s RHEL 7+, Archlinux, Slackware and OpenBSD.

Some of the users of MariaDB are Google, Mozilla, Facebook and Wikipedia. Later we found out that Basecamp has already been using MariaDB for a while.

Active Record support for MariaDB

Recently, Ian Gilfillan from MariaDB Foundation sent a Pull Request to include MariaDB as part Rails Documentation.

Accepting that pull request means Rails is committing to supporting MariaDB along with MySQL, PostgreSQL and SQLite.

The tests revealed an issue related to micro-precision support on time column.

If a column has time field and if we search on that column then the search was failing for MariaDB.

time = ::Time.utc(2000, 1, 1, 12, 30, 0, 999999)
Task.create!(start: time)
Task.find_by(start: time) # => nil

In the above case we created a record. However query yielded no record.

Now let’s see why the query did not work for MariaDB.

MariaDB vs MySQL time column difference

First let’s examine the tasks table.

 mysql> desc tasks;
+--------+---------+------+-----+---------+----------------+
| Field  | Type    | Null | Key | Default | Extra          |
+--------+---------+------+-----+---------+----------------+
| id     | int(11) | NO   | PRI | NULL    | auto_increment |
| start  | time    | YES  |     | NULL    |                |
+--------+---------+------+-----+---------+----------------+
2 rows in set (0.00 sec)

In the above case column start is of type time.

Let’s insert a record in tasks table.

mysql> INSERT INTO `tasks` (`start`) VALUES ('2000-01-01 12:30:00');

Now let’s query the table.

mysql> SELECT  `tasks`.* FROM `tasks` WHERE `tasks`.`start` = '2000-01-01 12:30:00' LIMIT 1;
Empty set (0.00 sec)

In the above case query is passing date part(2000-01-01) along with the time part(12:30:00) for column start and we did not get any result.

Now let’s query again but this time we will pass only the time part to the start column.

mysql> SELECT  `tasks`.* FROM `tasks` WHERE `tasks`.`start` = '12:30:00' LIMIT 1;
+----+----------+
| id | start    |
+----+----------+
|  1 | 12:30:00 |
+----+----------+
1 row in set (0.00 sec)

So in the query if we pass 2000-01-01 12:30:00 to a column which is of type time then MariaDB fails.

Passing 2000-01-01 12:30:00 to MySQL, PostgreSQL and SQLite will work fine. That’s because the adapters for those databases will drop the date part if date is passed in the query string.

For MariaDB similar action was needed and soon enough a Pull Request, to take care of this behavior from Rails side, was landed. MariaDB is itself, working on supporting this behavior now.

Summary

In summary Rails 5 officially supports MariaDB and MariaDB can now safely be used as an alternative to MySQL for Ruby on Rails Applications.

Changes to test controllers in Rails 5

This blog is part of our Rails 5 series.

In Rails 5, controller tests have undergone some major changes. In this blog post, we will walk through some of those changes.

ActionController::TestCase is deprecated

In Rails 5, controller tests are generated with superclass ActionDispatch::IntegrationTest instead of ActionController::TestCase which is deprecated . It will be moved into a separate gem in Rails 5.1 .

Rails 5 will use ActionDispatch::IntegrationTest by default for generating scaffolds as well controller tests stubs.

Use URL instead of action name with request methods in Rails 5

In Rails 4.x, we pass controller action as shown below.

class ProductsControllerTest < ActionController::TestCase

  def test_index_response
    get :index
    assert_response :success
  end
end

But in Rails 5, controller tests expect to receive URL instead of action. Otherwise test will throw exception URI::InvalidURIError: bad URI.

class ProductsControllerTest < ActionDispatch::IntegrationTest

  def test_index
    get products_url
    assert_response :success
  end
end

If we are upgrading an older Rails 4.x app to Rails 5, which have test cases with superclass ActionController::TestCase, then they will continue to work as it is without requiring to change anything from above.

Deprecation of assigns and assert_template in controller tests

In Rails 4.x, we can test instance variables assigned in a controller action and which template a particular controller action renders using assigns and assert_template methods.

class ProductsControllerTest < ActionController::TestCase
  def test_index_template_rendered
    get :index
    assert_template :index
    assert_equal Product.all, assigns(:products)
  end
end

But in Rails 5, calling assert_template or assigns will throw an exception.

class ProductsControllerTest < ActionDispatch::IntegrationTest
  def test_index_template_rendered
    get products_url
    assert_template :index
    assert_equal Product.all, assigns(:products)
  end
end

# Throws exception
NoMethodError: assert_template has been extracted to a gem. To continue using it,
  add `gem 'rails-controller-testing'` to your Gemfile.

These two methods have now been removed from the core and moved to a separate gem rails-controller-testing. If we still want to use assert_template and assigns, then we can do this by adding this gem in our applications.

Reasons for removing assigns and assert_template

The idea behind the removal of these methods is that instance variables and which template is rendered in a controller action are internals of a controller, and controller tests should not care about them.

According to Rails team, controller tests should be more concerned about what is the result of that controller action like what cookies are set, or what HTTP code is set rather than testing of the internals of the controller. So, these methods are removed from the core.

Use of Keywords arguments in HTTP request methods in Rails 5

In Rails 4.x, we pass various arguments like params, flash messages and session variables to request method directly.

class ProductsControllerTest < ActionController::TestCase

  def test_show
    get :show, { id: user.id }, { notice: 'Welcome' }, { admin: user.admin? }
    assert_response :success
  end
end

Where { id: user.id } are params, { notice: 'Welcome' } is flash and { admin: user.admin? } is session.

This becomes confusing sometimes, as it is not clear which argument belongs to which part.

Now in Rails 5, request methods accept only keyword arguments.

class ProductsControllerTest < ActionDispatch::IntegrationTest

  def test_create
    post product_url, params: { product: { name: "FIFA" } }
    assert_response :success
  end
end

This makes it easier to understand what arguments are being passed.

When we pass arguments without keywords arguments, then Rails logs a deprecation warning.

class ProductsControllerTest < ActionDispatch::IntegrationTest

  def test_create
    post product_url, { product: { name: "FIFA" } }
    assert_response :success
  end
end

DEPRECATION WARNING: ActionDispatch::IntegrationTest HTTP request methods will accept
only the following keyword arguments in future Rails versions:
params, headers, env, xhr

Rails 5 has added accessed_fields to find the fields that are actually being used in the application

This blog is part of our Rails 5 series.

Rails makes it very easy to select all the fields of a table.

@users = User.all

Above code is selecting all the columns of the table users. This might be ok in most cases. However in some cases we might want to select only certain columns for performance reason. The difficult task is finding what all columns are actually used in a request.

To help in this task, Rails 5 has added accessed_fields method which lists attributes that were actually used in the operation.

This is helpful in development mode in determining what all fields are really being used by the application.

class UsersController < ApplicationController
  def index
    @users = User.all
  end
end
# app/views/users/index.html.erb

<table>
  <tr>
    <th>Name</th>
    <th>Email</th>
  </tr>
  <% @users.each do |user| %>
    <tr>
      <td><%= user.name %></td>
      <td><%= user.email %></td>
    </tr>
  <% end %>

</table>

Now, in order to find all the fields that were actually used, let’s add after_action to the controller.

class UsersController < ApplicationController

  after_action :print_accessed_fields

  def index
    @users = User.all
  end

  private

  def print_accessed_fields
    p @users.first.accessed_fields
  end
end

Let’s take a look at the log file.

Processing by UsersController#index as HTML
  User Load (0.1ms) SELECT "users".* FROM "users"
  Rendered users/index.html.erb within layouts/application (1.0ms)
  ["name", "email"]

As we can see, it returns ["name", "email"] as attributes which were actually used.

If users table has 20 columns then we do not need to load values all those other columns. We are using only two columns. So let’s change code to reflect that.

class UsersController < ApplicationController
  def index
    @users = User.select(:name, :email)
  end
end

Rails 5 adds warning when fetching big result set with Active Record

This blog is part of our Rails 5 series.

With large data set we can run into memory issue. Here is an example.

>> Post.published.count
=> 25000

>> Post.where(published: true).each do |post|
     post.archive!
   end

# Loads 25000 posts in memory

Rails 5 adds warning when loading large data set

To mitigate issue shown above Rails 5 has added config.active_record.warn_on_records_fetched_greater_than.

When this configuration is set to an integer value, any query that returns the number of records greater than the set limit, logs a warning.

config.active_record.warn_on_records_fetched_greater_than = 1500

>> Post.where(published: true).each do |post|
     post.archive!
   end

=> Query fetched 25000 Post records: SELECT "posts".* FROM "posts" WHERE "posts"."published" = ? [["published", true]]
   [#<Post id: 1, title: 'Rails', user_id: 1, created_at: "2016-02-11 11:32:32", updated_at: "2016-02-11 11:32:32", published: true>, #<Post id: 2, title: 'Ruby', user_id: 2, created_at: "2016-02-11 11:36:05", updated_at: "2016-02-11 11:36:05", published: true>,....]

This helps us find areas where potential problems exist and then we can replace inefficient queries with better ones.

config.active_record.warn_on_records_fetched_greater_than = 1500

>> Post.where(published: true).find_each do |post|
     post.archive!
   end

# No warning is logged

Rails 5 allows to send log to STDOUT via environment variable

This blog is part of our Rails 5 series.

By default, Rails creates log directory in a file that is named after the environment in which the application is running. So in production environment, logs are by default directed to production.log file.

We will have to define custom loggers if these logs are to be directed to another file or to standard output. Presence of such custom logic is what enables Rails to direct logs to STDOUT along with development.log file in development environment.

Rails 5, however, supports logging to STDOUT in production environment through introduction of new environment variable RAILS_LOG_TO_STDOUT.

In a brand new Rails app, we can see the following snippet in production.rb file.

if ENV["RAILS_LOG_TO_STDOUT"].present?
  config.logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
end

By setting RAILS_LOG_TO_STDOUT to any value we should have the production logs directed to STDOUT.

We can see in the snippet above config.logger is overwritten. Therefore now the logs will not be directed to production.log file.

To opt out of this and revert to the original functionality, we can either assign a blank value to this environment constant or remove RAILS_LOG_TO_STDOUT from the list of environment constants.

Validate multiple contexts together in Rails 5

This blog is part of our Rails 5 series.

Active Record validation is a well-known and widely used functionality of Rails. Slightly lesser popular is Rails’s ability to validate on custom context.

If used properly, contextual validations can result in much cleaner code. To understand validation context, we will take example of a form which is submitted in multiple steps:

class MultiStepForm < ActiveRecord::Base
  validate :personal_info
  validate :education, on: :create
  validate :work_experience, on: :update
  validate :final_step, on: :submission

  def personal_info
    # validation logic goes here..
  end

  # Smiliary all the validation methods go here.
end

Let’s go through all the four validations one-by-one.

1. personal_info validation has no context defined (notice the absence of on:). Validations with no context are executed every time a model save is triggered. Please go through all the triggers here.

2. education validation has context of :create. It is executed only when a new object is created.

3. work_experience validation is in :update context and gets triggered for updates only. :create and :update are the only two pre-defined contexts.

4. final_step is validated using a custom context named :submission. Unlike above scenarios, it needs to be explicitly triggered like this:

form = MultiStepForm.new

# Either
form.valid?(:submission)

# Or
form.save(context: :submission)

valid? runs the validation in given context and populates errors. save would first call valid? in the given context and persist the changes if validations pass. Otherwise populates errors.

One thing to note here is that when we validate using an explicit context, Rails bypasses all other contexts including :create and :update.

Now that we understand validation context, we can switch our focus to validate multiple context together enhancement in Rails 5.

Let’s change our contexts from above example to

class MultiStepForm < ActiveRecord::Base
  validate :personal_info, on: :personal_submission
  validate :education, on: :education_submission
  validate :work_experience, on: :work_ex_submission
  validate :final_step, on: :final_submission

  def personal_info
    # code goes here..
  end

  # Smiliary all the validation methods go here.
end

For each step, we would want to validate the model with all previous steps and avoid all future steps. Prior to Rails 5, this can be achieved like this:

class MultiStepForm < ActiveRecord::Base
  #...

  def save_personal_info
    self.save if self.valid?(:personal_submission)
  end

  def save_education
    self.save if self.valid?(:personal_submission)
              && self.valid?(:education_submission)
  end

  def save_work_experience
    self.save if self.valid?(:personal_submission)
              && self.valid?(:education_submission)
              && self.valid?(:work_ex_submission)
  end

  # And so on...
end

Notice that valid? takes only one context at a time. So we have to repeatedly call valid? for each context.

This gets simplified in Rails 5 by enhancing valid? and invalid? to accept an array. Our code changes to:

class MultiStepForm < ActiveRecord::Base
  #...

  def save_personal_info
    self.save if self.valid?(:personal_submission)
  end

  def save_education
    self.save if self.valid?([:personal_submission,
                              :education_submission])
  end

  def save_work_experience
    self.save if self.valid?([:personal_submission,
                              :education_submission,
                              :work_ex_submission])
  end
end

A tad bit cleaner I would say.

Rails 5 changes protect_from_forgery execution order

This blog is part of our Rails 5 series.

What makes Rails a great framework to work with is its sane conventions over configuration. Rails community is always striving to keep these conventions relevant over time. In this blog, we will see why and what changed in execution order of protect_from_forgery.

protect_from_forgery protects applications against CSRF. Follow that link to read up more about CSRF.

What

If we generate a brand new Rails application in Rails 4.x then application_controller will look like this.

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
end

Looking it at the code it does not look like protect_from_forgery is a before_action call but in reality that’s what it is. Since protect_from_forgery is a before_action call it should follow the order of how other before_action are executed. But this one is special in the sense that protect_from_forgery is executed first in the series of before_action no matter where protect_from_forgery is mentioned. Let’s see an example.

class ApplicationController < ActionController::Base
  before_action :load_user
  protect_from_forgery with: :exception
end

In the above case even though protect_from_forgery call is made after load_user, the protection execution happens first. And we can’t do anything about it. We can’t pass any option to stop Rails from doing this.

Rails 5 changes this behavior by introducing a boolean option called prepend. Default value of this option is false. What it means is, now protect_from_forgery gets executed in order of call. Of course, this can be overridden by passing prepend: true as shown below and now protection call will happen first just like Rails 4.x.

class ApplicationController < ActionController::Base
  before_action :load_user
  protect_from_forgery with: :exception, prepend: true
end

Why

There isn’t any real advantage in forcing protect_from_forgery to be the first filter in the chain of filters to be executed. On the flip side, there are cases where output of other before_action should decide the execution of protect_from_forgery. Let’s see an example.

class ApplicationController < ActionController::Base
  before_action :authenticate
  protect_from_forgery unless: -> { @authenticated_by.oauth? }

  private
    def authenticate
      if oauth_request?
        # authenticate with oauth
        @authenticated_by = 'oauth'.inquiry
      else
        # authenticate with cookies
        @authenticated_by = 'cookie'.inquiry
      end
    end
end

Above code would fail in Rails 4.x, as protect_from_forgery, though called after :authenticate, actually gets executed before it. Due to which we would not have @authenticated_by set properly.

Whereas in Rails 5, protect_from_forgery gets executed after :authenticate and gets skipped if authentication is oauth.

Upgrading to Rails 5

Let’s take an example to understand how this change might affect the upgrade of applications from Rails 4 to Rails 5.

class ApplicationController < ActionController::Base
  before_action :set_access_time
  protect_from_forgery

  private
    def set_access_time
      current_user.access_time = Time.now
      current_user.save
    end
end

In Rails 4.x, set_access_time is not executed for bad requests. But it gets executed in Rails 5 because protect_from_forgery is called after set_access_time.

Saving data (current_user.save) in before_action is anyways a big enough violation of the best practices, but now those persistences would leave us vulnerable to CSRF if they are called before protect_from_forgery is called.

Rails 5 provides application config to use UUID as primary key

This blog is part of our Rails 5 series.

UUIDs are a popular alternative to auto-incremental integer primary keys.

create_table :users, id: :uuid do |t|
  t.string :name
end

Notice that id: :uuid is passed to create_table. This is all we need to do to have UUID as primary key for users.

Now, if an application is designed to use UUID instead of Integer, then chances are that new tables too would use UUID as primary key. And it can easily get repetitive to add id: :uuid in create_table , every time a new model is generated.

Rails 5 comes up with a solution. We need to set primary key as UUID in config/application.rb.

config.generators do |g|
  g.orm :active_record, primary_key_type: :uuid
end

This automatically adds id: :uuid to create_table in all future migrations.

One small recurrence off our plate.

Rails 5 changed Active Job default adapter from Inline to Async

This blog is part of our Rails 5 series.

Active Job has built-in adapters for multiple queuing backends among which two are intended for development and testing. They are Active Job Inline Adapter and Active Job Async Adapter.

These adapters can be configured as follows.

# for Active Job Inline
Rails.application.config.active_job.queue_adapter = :inline

# for Active Job Async
Rails.application.config.active_job.queue_adapter = :async

In Rails 4.x the default queue adapter is :inline. In Rails 5 it has been changed to :async by DHH.

Asynchronous Execution

In case of inline, as the name suggests, execution of the job happens in the same process that invokes the job. In case of Async adapter, the job is executed asynchronously using in-process thread pool.

AsyncJob makes use of a concurrent-ruby thread pool and the data is retained in memory. Since the data is stored in memory, if the application restarts, this data is lost. Hence, AsyncJob should not be used in production.

Running in future

AsyncJob supports running the job at some time in future through perform_later. Inline executes the job immediately and does not support running the job in future.

Both Active Job Async and Active Job Inline do not support configuring priorities among queues, timeout in execution and retry intervals/counts.

Advantage of having Async as default adapter

In Rails 4.x where Inline is the default adapter, the test cases were mistakenly dependent on job’s behavior that happens synchronously in development/testing environment. Using Async adapter ,by default, will help users have tests not rely on such synchronous behavior.

It’s a step closer to simulating your production environment where jobs are executed asynchronously with more persistent backends.

Consider an example, where in an e-commerce site upon every order placed an email is sent.

test "order is created successfully" do
  # Code to test record in orders table is created
end

test "order email is sent" do
  # Code to test order email is sent
end

The process of sending email can be part of a job which is invoked from an after_create callback in Order model.

class Order < ActiveRecord::Base

  after_create :send_order_email

  def send_order_email
    # Invoke the job of sending an email asynchronously.
  end

end

When Inline adapter is used, any wrongly configured email settings will cause both the above tests to fail. This is because the process of sending the email happens within the process of order creation and any error in sending the email would kill the process if unhandled.