Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mobile version of views for Ruby on Rails

I'm after some validation that I'm doing the right thing. I have my Ruby on Rails application in the following structure:

/home
   about.rhtml
   index.rhtml
/display
   index.rhtml
/data <--This is called by jQuery from the display\index page to provide the data to render
   push.js.erb
   pull.js.erb
/layout
   home.rhtml
   display.rhtml

Everything is working fine, but I now want to add a site targeted for mobile devices. While the iPhone renders the website correctly, it would be nice to provide a more targeted experience. Ideally, I'm thinking about having an iPhone.domain.com which would be redirected to via .htaccess.

For this, I was thinking about adding another view for each device
/iPhone
   home.rhtml
   about.rhtml
   display.rhtml

However, it feels like a lot of the data would be duplicated, for example the about page would be in two places. I guess I could have a partial and do something like render :partial => 'home/about' but that seems a little hacky.

How can I develop my site to support this?

I was thinking about a structure such as, but again not sure how to structure the code - how do I tell it to render the view in the iPhone directory... while not having the master layout applied
/display
   /iphone
      index.rhtml

I would really like some advice on the best way to approach this and structure the application. While the applications follow a structure at the moment, they could go off in different directions..

Thank you

Ben

like image 844
Ben Hall Avatar asked Aug 16 '09 12:08

Ben Hall


3 Answers

I would strongly recommend leaving the controller structure the same across all device types. Particularly if you are using Rails' RESTful routes your controllers should be closely matched to the domain model of your data. Whether that data is then presented to a desktop browser, to an iPhone, to a different type of mobile device, to a JSON/XML REST API client etc. is mostly a matter of the presentation layer, not the controller/routing layer.

So an elegant solution would be:

  1. Detect device type based on User Agent (you may want to refer to the WURFL User Agent database);
  2. use Rails' respond_to mechanism to render a different view format for each device type;
  3. define a layout for each device type (e.g. using the XHTML Mobile Profile doctype for mobile devices);
  4. include different CSS files depending on device type.

There are some plugins which try to make this easier: have a look at brendanlim's Mobile Fu and noelrappin's Rails iUI (both on GitHub). Also Brendan Lim's presentation at Rails Underground has a few ideas.

What you should be aiming for is something like:

def show
  @foo = Foo.find(params[:id])
  respond_to do |format|
    format.html       # => show.html.erb
    format.iphone     # => show.iphone.erb
    format.blackberry # => show.blackberry.erb
  end
end

You should also allow users on mobile devices to override the user agent detection if they really want to see the desktop version of the site. A cookie with a long expiry time is probably the best way to do this, so that the site remembers the choice next time the user returns. Some mobile devices have rubbish cookie support, but then they probably won't want the desktop version of the site anyway because it probably won't work.

like image 64
Martin Kleppmann Avatar answered Nov 05 '22 12:11

Martin Kleppmann


Rails 4.1 includes Variants, a great new feature that:

Allows you to have different templates and action responses for the same mime type (say, HTML). This is a magic bullet for any Rails app that's serving mobile clients. You can now have individual templates for the desktop, tablet, and phone views while sharing all the same controller logic.

In your case, you only need to set the variant for the iphone in a before_action, e.g:

class HomeController < ApplicationController
  before_action :detect_iphone
  def index

    respond_to do |format|
      format.html               # /app/views/home/index.html.erb
      format.html.phone         # /app/views/home/index.html+phone.erb
    end
  end

  private
    def detect_iphone
      request.variant = :iphone if request.user_agent =~ /iPhone/
    end
end

What's new in Rails 4.1

like image 22
Sergio A. Avatar answered Nov 05 '22 14:11

Sergio A.


The Iphone actually does a pretty good job of rendering web pages without any special formatting.

However on my Android phone floated content seems to get cut off and so a custom view for that phone is required. To achieve this you need to create a different layout (e.g. mobile_application.html.erb) and in your application_controller add the following:

layout :select_layout

  def select_layout
    session.inspect # force session load
    if session.has_key? "layout"
      return (session["layout"] == "mobile") ? "mobile_application" : "application"
    end
    return detect_browser
  end

  def detect_browser
    agent = request.headers["HTTP_USER_AGENT"].downcase
    MOBILE_BROWSERS.each do |m|
      return "mobile_application" if agent.match(m)
    end
    return "application"
  end

where MOBILE_BROWSERS is a an array of user agent strings you want to match as a mobile device.

I wrote a blog about this here:

http://www.arctickiwi.com/blog/2-mobile-enable-your-ruby-on-rails-site-for-small-screens

Cheers

like image 1
Jonathon Horsman Avatar answered Nov 05 '22 12:11

Jonathon Horsman