Mobile Pages in Sinatra

By Lachlan Hardy
1800h Sunday, 17 January 2010 Permalink

I’m building a Sinatra app that needs to be iPhone and Android capable. So I spent a few hours nutting out the best way I could come up with do it. I welcome suggestions for improving it!

To make it easier to explain, I’ve extracted my solution into a simplified Sinatra app. I only show the main file below, but you can grab the whole app from the download link to the left or on GitHub. The README has instructions on how to run it for those who aren’t Ruby-orientated.

The comments have the details covered, I think. Basically, it checks the User Agent string of each request to see if it matches two previously defined strings - extracts of the UA strings from the iPhone and Android browsers.

Given a match, it checks for a mobile version of the relevant views, layouts and partials needed for that request and serves them up.

Ruby

require 'sinatra'
require 'haml'

helpers do
  
  # Regexes to match identifying portions of UA strings from iPhone and Android
  def mobile_user_agent_patterns
    [
      /AppleWebKit.*Mobile/,
      /Android.*AppleWebKit/
    ]
  end
  
  # Compares User Agent string against regexes of designated mobile devices
  def mobile_request? 
    mobile_user_agent_patterns.any? {|r| request.env['HTTP_USER_AGENT'] =~ r}
  end
  
  # If there is a mobile version of the view, use that. Otherwise revert to normal view
  def mobile_file(name)
    mobile_file = "#{options.views}/#{name}#{@mobile}.haml"
    if File.exist? mobile_file
      view = "#{name}#{@mobile}"
    else
      view = name
    end
  end
  
  # Set up rendering for partials
  def partial(name)
    haml mobile_file("_#{name}").to_sym, :layout => false
  end
  
  # Render appropriate file, with mobile layout if needed
  def deliver(name)
    haml mobile_file(name).to_sym, :layout => :"layout#{@mobile}"
  end
end

# Before responding to each request, verify if it came from a designated mobile device and set @mobile appropriately
before do
  mobile_request? ? @mobile = ".mobile" : @mobile = ""
end

# homepage
get '/' do
  deliver :index
end

# content page (like Other)
get '/:page/' do
  deliver :"#{params[:page]}"
end
Download file: mobile.rb

Credit

I should note that I nicked the UA string matching idea from Tim Lucas’s sweet work on the Webjam site.

Summary

It’s quite a simple technique and I think the code is pretty neat. The fact I can deliver that much functionality in a 50-odd lines of neatly commented and spaced code is a tribute to Sinatra.

Obviously, I’m bound to hit edgecases as I go and I really should account for those who occasionally want to browse the full site, but I’ll worry about those as when I need to. I can always post improvements later!