Architecture


5
Jul 11

The Mobile Experience – Mobile Layouts With Rails

So, you have this great web app and you’re targeting mobile users (for our purposes, this means Android, iOS, and anything small/hand-held with a full browser and a touch screen). How do you make sure your app is usable on small, hand-held devices?

There are a number of interface strategies, including:

  1. Design only for mobile, and particularly the phone (iPhone/Android phone only).
  2. Design for desktop only, and assume mobile users will zoom as needed.
  3. Design separately for mobile and desktop, and figure out which interface to use somehow.

and there are permutations here based on whether you want to have a different experience on tablets versus phones, tablets versus desktops, Android vs. iOS vs. Blackberry, and so on.

For our system, we’re starting small – designing for iPhone – and scaling up to the tablet and desktop as we go. This will allow us to make sure the interface really works when all you have is your phone. We’ll test the app on both the iOS simulator and the Android SDK simulator on several versions of Android and the two latest iOS, and again in various Desktop browsers (Safari, Chrome, Firefox, and some reasonably intelligent version of Internet Explorer – just as soon as Microsoft releases one).

For our initial purposes, we need different layouts for both desktop and mobile. So we need to figure out how to serve mobile differently. Again, there are a couple different strategies:

  1. User-agent testing on the server-side (check HTTP_USER_AGENT or its equivalent).
  2. User-agent testing on the client-side, via JavaScript and DOM manipulation.
  3. User-agent testing on the client-side, via specialized CSS calls. A List Apart has a great explanation of how this is done.

#2 and #3 both seem overly complicated to me. For now, we’re going with sniffing the user-agent string. The strategy for implementing this in Rails is to do something like this:

# app/controllers/application_controller.rb
before_filter :check_layout

def check_layout
case params["layout"]
when "mobile" then session[:layout] = "mobile"
when "full" then session[:layout] = "full"
when nil then
  session[:layout] = ua_is_mobile ? "mobile" : "full"
else session[:layout] = "full"
end
end

def ua_is_mobile
uastring =
  request.env["HTTP_USER_AGENT"].downcase rescue nil
# you could check several patterns here
return true if uastring =~ /mobile/
return false
end

and then you can either use layout :mobile or the default layout depending on the value of session[:layout]. In our case we have separate layout files entirely, which reduces the number of if statements in the layout. We can then DRY up the layouts using shared partials and helpers. I also seek to minimize repetition in the views by having view snippets be device agnostic, and leaving it to CSS to make things look right for mobile.

Alright then, so that works just fine, but how do we test it? A major problem we run into is in making sure the right layout is rendered for the right user agent. You have to adjust your test to pass in the right user-agent string:

# test/functional/users_controller_test.rb

  test "should detect mobile" do
    @request.env["HTTP_USER_AGENT"] = "Mozilla/5.0 " +
      "(iPhone; U; CPU like Mac OS X; en) AppleWebKit/420" +
      "+ (KHTML, like Gecko) Version/3.0 Mobile/1A543a " +
      "Safari/419.3"
    get :index
    assert_template :layout => "application.mobile"
  end

which will assert the appropriate layout is used. See the description of assert_template for more goodies.

TwitterFacebookLinkedInXINGViadeoRedditDiggEmailShare

8
Apr 11

Why We’re Using Ruby on Rails

Because it’s faster to get started. Also we’ll probably be able to keep my work once we hire the next person.

Really, it’s that simple. The web framework decision tree is surprisingly straightforward, assuming you even need a framework at all. For our purposes at Resume Everywhere, we’re building a multi-user transactional application that helps track things, their states, and their attributes through a workflow. Most of the critical data is highly structured and can be expressed easily in a normalized database. We want the app to be accessible via the web (in a browser) and feature a RESTful API.

This screams Model-View-Controller (as do most transactional web apps). A good framework will provide RESTful routing resources and make it easy to dummy up views and manage a database.

Aside from Ruby on Rails, there are dozens of web-based MVC frameworks. So why Rails? When it comes down to it, of all the possibilities, it’s the one I know the best, and it’s my problem to solve. This means I can be productive with it from day 1. And it’s easy enough to find people who know the framework – and may know it better than I do – that when we add someone else to the team, they should be able to make progress right away. In other words, using Rails reduces the intellectual start-up costs.

That’s a pretty shallow (if accurate) assessment.  But using Rails is a good choice for a couple other reasons:

  1. RESTful API out of thed box (or nearly so) using respond_to do |format| blocks.
  2. Use of convention in place of configuration means I spend less time configuring things at the outset (although this might come back to bite us later).
  3. Rapid deployment via Capistrano, which is surprisingly easy to set up.
  4. Rapid develop-test cycle: make a change, reload a browser window. This means code gets written in small, testable chunks that work right the first time.
  5. Existing libraries for oAuth, OpenID, RESTful authentication, and just about anything you might need.

Not that other frameworks or languages don’t offer these advantages (they do). But we only need the framework to be “good enough” on the critical points. Actually, the respond_to do |format| and routes together get us much of the way toward a workable API with almost no effort on the programmer’s (i.e., me) part. I’m all about judicious use of laziness.

TwitterFacebookLinkedInXINGViadeoRedditDiggEmailShare