short for entity tag,
is a part of HTTP header and is used for web cache validation.
ETag is a digest of the resource that uniquely identifies specific version of the resource.
This helps browser and web servers determine
if resource in the browser’s cache
is exactly same as
the resource on the server.
Strong ETag indicates that resource content is same for response body
the response headers.
Weak ETag indicates that the two representations are semantically equivalent.
It compares only the response body.
Weak ETags are prefixed withW\
thus one can easily distinguish between Weak ETags and Strong ETags.
page to illustrate how ETag matching works.
When server receives a request, it returns an ETag header as part of HTTP response.
This ETag represents state of the resource.
For the subsequent HTTP requests,
client sends this ETag via If-None-Match header to identify if the resource is changed or not.
The server will compare the current ETag and the one sent by the client.
If ETag matches, server responds with 304 Not modified.
This means resource content in the client’s cache is up-to-date.
If resource is changed, server will send updated resource along with the new ETag.
Let’s see it in action.
ETags in Rails 4.x
Rails 4.x generates strong ETags by default i.e without W/ prefix.
We are making first request to the server.
For the next request,
we will send ETag that was sent
by the sever.
And notice that server
304 Not Modified.
Now for the second request, server will return 304 Not Modified response as before,
but the ETag is weak ETag.
Why this change?
Rails does not perform strong validation of ETags
as implied by strong ETags spec.
Rails just checks
whether the incoming ETag from the request headers
matches with the
ETag of the generated response.
It does not do byte by byte comparison of the response.
This was true even before Rails 5.
this change is more of a course correction.
Rack also generates weak ETags
by default because of similar reasons.
Rails 4.x returns error information in HTML page
whenever there is any exception,
in the development environment.
This is fine for normal HTML requests.
But traditionally, Rails always returned with HTML response
for exceptions for all requests, including JSON on XML requests
We can now generate API only apps in Rails 5.
In case of such apps,
to have the error message
in the format in which request was made.
Having an HTML response for a JSON endpoint
is not going to help in debugging why the exception happened.
New config option debug_exception_response_format
Rails 5 has introduced new configuration
to respond with proper format for exceptions.
Let’s see an example of the response received with this configuration.
The status key will represent HTTP status code
and error key will represent the corresponding Rack HTTP status.
exception will print the output of actual exception in inspect format.
traces will contain application and framework traces similar to how they are displayed in
HTML error page.
By default, config.debug_exception_response_format is set to :api
so as to render responses in the same format as requests.
If you want the original behavior of rendering HTML pages,
you can configure this option as follows.
We will see how migrations in Rails 5 differ by looking at different
In Rails 4.x command
will generate migration
as shown below.
In Rails 5 the same command
will generate following migration.
Let’s see the generated schema after running migration generated in
Rails 5 added the NOT NULL constraints on the timestamps columns
even though not null constraint was not specified in the migration.
Let’s look at another example.
In Rails 4.x command
would generate following migration.
In Rails 5.0, same command will generate following migration.
There is no mention of index: true in the above migration.
Let’s see the generated schema after running Rails 5 migration.
As you can see, an index on user_id column is added even though it’s not
present in the migration.
Migration API has changed in Rails 5
Rails 5 has changed migration API because of which
even though null: false options is not passed to timestamps
when migrations are run then not null is
automatically added for
Similarly, we want indexes for referenced columns
in almost all cases.
So Rails 5 does not need references to have index: true.
When migrations are run then index is automatically created.
Now let’s assume that an app was created in Rails 4.x. It has a bunch of
migrations. Later the app was upgraded to Rails 5. Now when older
migrations are run then those migrations will behave differently and
will create a different schema file. This is a problem.
Solution is versioned migrations.
Versioned migrations in Rails 5
Let’s look at the migration generated in Rails 5 closely.
In this case CreateUsers class is now inheriting from
Here [5.0] is Rails version that generated this migration.
Solving the issue with older migrations
Whenever Rails 5 runs migrations, it checks the class of the current
migration file being run.
If it’s 5.0, it uses the new migration API which has changes
like automatically adding null: false to timestamps.
But whenever the class of migration file is other than
Rails will use a compatibility layer of migrations API.
compatibility layer is present for Rails 4.2.
What it means is that all migration generated prior to usage of Rails 5
will be treated as if they were generate in Rails 4.2.
You will also see a
asking user to add the version of the migration to the class name for older migrations.
So if you are migrating a Rails 4.2 app, all of your migrations will have class
ActiveRecord::Migration. If you run those migrations in Rails 5, you will see
a warning asking to add version name to the class name so that class name looks like
In Rails 4.2,
Active Job was integrated with Action Mailer
to send emails asynchronously.
Rails provides deliver_later method to enqueue mailer jobs.
Note that the task of delivering email was put in queue called
In Rails 4.x, all background jobs are given queue named “default” except
for mailers. All outgoing mails are given the queue named “mailers” and
we do not have the option of changing this queue name from “mailers” to
Since Rails 4.x comes with minimum of two queues it makes difficult to
use queuing services like que which
relies on applications having only one queue.
Customizing queue name in Rails 5
In Rails 5, we can now
change queue name
for mailer jobs
using following configuration.
To add precision on datetime column
we need to add limit option to it.
By default it is set to 0.
This adds precision(6) to last_seen_at column
in users table.
Rails 4.x behavior
Let’s look at the some of the examples
with different precision values.
The task here is to set end_of_day value
to updated_at column.
With precision set to 6
Everything looks good here.
But let’s look at what happens when precision is set to 0.
With precision set to 0
So far everything looks good here too.
Now let’s see what happens when we reload this object.
As we can clearly see
after the reload
updated_at value has been rounded off from 2016-01-18 23:59:59.999999
to 2016-01-19 00:00:00. It might seem like a small issue but notice
that date has changed from 01/18 to 01/19 because of this rounding.
Improvement in Rails 5
Rails team fixed
by removing fractional part if
mysql adapter does not support precision.
Before Rails 5,
returning false from any before_ callback
in ActiveModel or ActiveModel::Validations,
ActiveRecord and ActiveSupport
resulted in halting of callback chain.
In this case the code is
attempting to set the value of eligibility_for_rebate to false.
However the side effect of the way Rails callbacks work is that
the callback chain will be halted
one of the callbacks returned false.
Right now, to fix this we need to return true
from before_ callbacks,
so that callbacks are not halted.
Improvements in Rails 5
Rails 5 fixed this issue
by addingthrow(:abort) to
explicitly halt callbacks.
Now, if any before_ callback returns false
then callback chain is not halted.
To explicitly halt the callback chain,
we need to use throw(:abort).
Opting out of this behavior
The new Rails 5 application comes up
with initializer named callback_terminator.rb.
helps in managing gem dependencies of ruby projects.
You can specify which gems and versions you need, bundler will install them and load them at runtime.
Bundler ensures that gems you need are present in the environment you
Bundler gets its configurations from local application (app/.bundle/config), environment variables and user’s home directory (~/.bundle/config) in the order of priority.
To list all bundler configurations for the current bundle, run bundle config without any parameters. You will also get the location where the value is set.
You might see different result based on configuration of bundler on your
To get value for the specific configuration setting, run bundle config with name.
To set the value of the configuration setting, use bundle config with name and value. Configuration will be stored in ~/.bundle/config.
If any config already has a value, it will be overwritten directly and user will be warned.
Application level configuration
By default setting configuration value will set it for all projects on the machine. You can set configurations specific to local application with --local option. This will store value in app/.bundle/config.
You can run bundle config with --global.
This will set values at global level i.e. across all applications on the machine.
It will be similar to running bundle config without any options.
You can delete configuration with --delete option.
This is not compatible with --local and --global. This will delete configuration from local and global resources.
You can pass the flags required for installing particular gem to bundler with bundle config.
Many El Capitan users face an
while installing eventmachine gem.
The issue can be resolved by providing path to OpenSSL include directory while installing eventmachine.
As location of configuration will vary from machine to machine, we can set this with bundle config.
Now bundler will pick this configuration while installing eventmachine gem.
Various configuration keys are available with bundler.
These keys are available in two forms.
You can specify them in canonical form with bundle config or set them in environment variable form.
Following are canonical forms and their usage.
Corresponding environment variables are specified in bracket.
Setting auto_install config will enable automatic installing of gems instead of raising an error.
This applies to show, binstubs, outdated, exec, open, console, license, clean commands.
Example, When you try to run bundle show for the gem which is not yet installed you will get an error.
You can set auto_install to remove this error and install gem.
You can specify location to install your gems. Default path is $GEM_HOME for development and vendor/bundle when –deployment is used.
You can freeze changes to your Gemfile.
If frozen is set and you try to run bundle install with changed Gemfile, you will get following warning.
You can skip installing groups of gems with bundle install. Specify : separated group names whose gems bundler should not install.
You can set the directory to install executables from gems in the bundle.
You can set the file which bundler should use as theGemfile. By default, bundler will use Gemfile. The location of this file also sets the root of the project, which is used to resolve relative paths in the Gemfile.
This specifies path to a designated CA certificate file or folder containing multiple certificates for trusted CAs in PEM format. You can specify your own https sources in Gemfile with corresponding certificates specified via bundle config.
This specifies path to a designated file containing a X.509 client certificate and key in PEM format.
You can set the path to place cached gems while running bundle package.
When set, Gemfiles containing multiple sources will produce an error instead of a warning.
When you try to run bundle install, you will get warning.
To ignore all bundle config on the machine and run bundle install, set BUNDLE_IGNORE_CONFIG environment variable.
Often while developing a Rails application you may look to have one of these
to boost the performance.
Along with these, Rails 5 now provides a way of caching a collection of records,
thanks to the introduction of the following method:
What is collection caching?
Consider the following example where we are fetching a collection of all users belonging to city of Miami.
Here @users is a collection of records and is an object of class ActiveRecord::Relation.
Whether the result of the above query would be same depends on following
The query statement doesn’t change. If we change city name from
“Miami” to “Boston” then result might change.
No record is deleted. The count of records in the collection should be same.
No record is added. The count of records in the collection should be same.
implemented caching for a collection of records .
Method cache_key was added to ActiveRecord::Relation which
takes into account many factors including
query statement, updated_at column value and the count of the records in collection.
We have object @users of class ActiveRecord::Relation.
Now let’s execute cache_key method on it.
Let’s try to understand each piece of the output.
users represents what kind of records we are holding.
In this example we have collection of records of class User.
Hence users is to illustrate that we are holding users records.
query- is hardcoded value and it will be same in all cases.
is a digest of the query statement that will be executed.
In our example it is MD5( "SELECT "users".* FROM "users" WHERE "users"."city" = 'Miami'")
3 is the size of collection.
is timestamp of the most recently updated record in the
collection. By default, the timestamp column considered is updated_at
hence the value will be the most recent updated_at value in the collection.
Let’s see how to use cache_key to actually cache data.
In our Rails application, if we want to cache records of users belonging to “Miami”
then we can take following approach.
From above, we can see that
for the first hit,
a count query is fired to get the
the latest updated_at and size from the users collection.
Rails will write a new cache entry with a cache_key
generated from above count query.
Now on second hit,
it again fires count query and
checks if cache_key for this query exists or not.
If cache_key is found, it loads data without firing SQL query.
What if your table doesn’t have updated_at column?
Previously we mentioned that cache_key method uses
updated_at column. cache_key also provides an option of
passing custom column as a parameter
then the highest value of that column among the records in the collection will be considered.
For example if your business logic considers
a column named last_bought_at in products table as a factor to decide caching, then you can use the following code.
Edge cases to watch out for
Before you start using cache_key there are some edge cases to watch
Consider you have an application where there are
5 entries in users table with city Miami.
Using limit puts incorrect size in cache key if collection is not loaded.
If you want to fetch three users belonging to city “Miami” then you would execute following query.
Here users contains only three records
hence the cache_key has 3 for size of collection.
Now let’s try to execute same query without fetching the records first.
You can see that the count in the cache is 5 this time even though we have set a limit to 3.
This is because the implementation of
executes query without limit
to fetch the size of the collection.
Cache key doesn’t change when an existing record from a collection is replaced
I want 3 users in the descending order of ids.
Above statement will give us
users with ids [5, 4, 3].
Now let’s remove the user with id = 3.
Note that cache_key both users1 and users2 is exactly same.
This is because none of the parameters that affect the cache key is changed
i.e., neither the number of records,
nor the query statement,
nor the timestamp of the latest record.
a discussion undergoing
about adding ids of the collection records as part of the cache key.
This might help solve the problems discussed above.
Using group query gives incorrect size in the cache key
Just like limit case discussed above cache_key behaves differently
when data is loaded and when data is not loaded in memory.
Let’s say that we have two users with first_name “Sam”.
First let’s see a case where collection is not loaded in memory.
In the above case, the size is 1 in cache_key.
For the system mentioned above, the sizes that you will get shall either be 1 or 5.
That is, it is size of an arbitrary group.
Now let’s see when collection is first loaded.
In the above case, the size is 2 in cache_key.
You can see that the count in the cache key here
is different compared to that where the collection was unloaded
even though the query output in both the cases will be exactly same.
In case where the collection is loaded,
the size that you get is equal to the total number of groups.
So irrespective of what the records in each group are,
we may have possibility of having the same cache key value.