Tobias Cohen

Hi, I'm Tobias, and I'm a web developer in Melbourne, Australia, specializing in Ruby on Rails and Coffeescript

Creating a mobile version of your site in Rails 3.1

01 Jul 2012

I recently added a mobile layout for Chess Microbase. To ensure the mobile content was just as optimized as the desktop version, and to help reduce the number of interactions (and therefore conflicts) between the desktop and mobile versions, I chose to make the mobile site run from separate view files. Here, I'll give an explanation of how I made that work using Rails 3.1.

I started with this helpful solution by Winfield Peterson on StackOverflow:

Define a custom 'mobile' Rails MIME type in config/initializers/mime_types.rb that's just HTML:

Mime::Type.register_alias "text/html", :mobile

Add a helper/filter to respond to mobile users:

def mobile_user_agent?
  @mobile_user_agent ||= ( request.env["HTTP_USER_AGENT"] && request.env["HTTP_USER_AGENT"][/(Mobile\/.+Safari)/] )


before_filter :handle_mobile

def handle_mobile
  request.format = :mobile if mobile_user_agent?

Make custom mobile template:

app/views/users/show.html.erb => app/views/users/

Dispatch mobile or regular in controllers:

respond_to do |format|
  format.html { }   # Regular stuff { } # other stuff

This was a great starting solution, although there are a few changes needed, most importantly to allow the user to manually switch between modes, following usability best practices.

To begin with, I changed the user agent RegEx to better match the mobile environments I'm familiar with (my apologies to the ones I'm not):

def mobile_user_agent?
  request.env["HTTP_USER_AGENT"].try :match, /(iphone|ipod|android)/i

In particular, I'm deliberately not including the iPad, as the 10" screen size tends to be better suited to desktop layouts than mobile.

At this point, you can safely start making some mobile views, so I'd like to share a few notes before I get to the switching functionality:

With that noted, I'll show you how I added layout switching.

I started by adding a switch_layout action to my SessionsController (part of authentication system), but you can add it wherever makes most sense in your application. This action simply sets a session variable with the preferred layout and redirects back to the referring page:

# routes.rb
resources :sessions do
  get :switch_format, on: :collection

# sessions_controller.rb
def switch_format
  session[:preferred_format] = params[:preferred_format]
  redirect_to :back

I added links to switch layouts in the footer of both my mobile and desktop application layouts:

# application.html.slim
li Layout: <strong>Desktop</strong> | #{link_to 'Mobile', \
  switch_format_sessions_path(preferred_format: 'mobile')}

li Layout: #{link_to 'Desktop', switch_format_sessions_path(\
  preferred_format: 'html')} | <strong>Mobile</strong>

Finally, I added another before_filter to my ApplicationController to apply the selected layout where appropriate:

# application_controller.rb

before_filter :handle_mobile
before_filter :use_preferred_format


def use_preferred_format
  if request.format == 'html' && session[:preferred_format] == 'mobile'
    request.format = 'mobile'
  if request.format == 'mobile' && session[:preferred_format] == 'html'
    request.format = 'html'

In this code I made a point of detecting that I'm only switching between mobile and html formats, to ensure the user doesn't get stuck unable to access any other formats generated by the site due to their session layout preference.