A quick post to talk about a new feature in Rails 4.1: Action Pack variants.
Variants allow your app to easily serve different templates in different cases, for example, different templates for phones, tablets and desktops. In other words, variants allow you to arbitrarily respond with different templates within the same MIME type (e.g. HTML).
To use Action Pack variants, you set the
request.variant to a symbol for the name of the variant in a controller, probably in a
before_action. You can then respond to each of those variants in your actions in the same way that you respond to different formats.
Example: different templates for devices
Using the different devices use case as the obvious example, we can set the request variant like so (using the awesome browser gem):
class ApplicationController < ActionController::Base before_action do if browser.tablet? request.variant = :tablet elsif browser.mobile? request.variant = :phone end end end
Now when no template can be found for a particular action, you will see something like
:variants => [:tablet] along with the formats and handlers.
Now you can go ahead and add new templates to your app/views/* directory, one for each variant. The variant is specified in the file name with a + symbol, such as show.html+tablet.erb. Your views directory for a resource would now look something like this:
glen@~/projects/rails_app > ls app/views/graphs/ -rw-r--r-- 1 glen staff 0 30 May 14:47 show.html+tablet.erb -rw-r--r-- 1 glen staff 0 30 May 14:47 show.html+phone.erb -rw-r--r-- 1 glen staff 0 30 May 14:47 show.html.erb
With the show.html.erb file being a catch-all for when there is no
request.variant is set, or when there is no matching template for the
request.variant. So you probably wouldn't have a desktop variant; you would likely just let your apps render the catch-all show.html.erb template for desktops.
The correct template will now be rendered for each variant. You don’t need to explicitly specify each format in a
respond_to; if a template for the variant exists, it will be rendered.
As for partials, if a template attempts to find a partial and
request.variant is set, then a partial with the variant in the file name will be prioritised and rendered if one is found, and the non-variant partial will be rendered if not. For example, if
request.variant is set to
<%= render "sidebar" %> would render _sidebar.html+v2.erb if it exists, and _sidebar.html.erb if it doesn't. However, if
request.variant is not set, only _sidebar.html.erb would be tried, and not the v2 variant.
You can also execute variant-specific code in your actions within the standard
respond_to block, like so:
class GraphsController < ApplicationController def show respond_to do |format| format.html do |html| html.phone do # Do something specific for phones here. end end end end end
There is also a shorthand for this, which I'll let you read for yourself.
Another example: beta testers
Rendering different templates for different types of devices is the typical example use for variants, but there are a lot of other cool uses, such as rendering different templates for different classes of users, for example, beta testers of a new design for your site.
glen@~/projects/rails_app > ls app/views/graphs/ -rw-r--r-- 1 glen staff 0 30 May 14:47 show.html+v2.erb -rw-r--r-- 1 glen staff 0 30 May 14:47 show.html.erb
And setting the variant:
class ApplicationController < ActionController::Base before_action do request.variant = :v2 if current_user.beta_tester? end end
It’s that simple. Just a couple of lines of code is all that you need to render an entirely different set of templates to a different group of users.
And that's all there is to it. Variants are an extremely powerful new feature of Rails, particularly because of their flexibility, and the fact that more and more companies are building mobile interfaces for their site. While the obvious use case for variants (and what you will find in all the blogs and articles on the topic) is rendering different templates for mobile and desktop users, the feature itself is agnostic from any particular use case, allowing you to use variants for any reason you can think of.