Blog

Variants With Ruby on Rails

By Glen Crawford, 20 Jun 2014

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 :v2, then <%= 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.

Template files:

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.

Conclusion

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.

blog comments powered by Disqus