Skip to content

Instantly share code, notes, and snippets.

@chasestubblefield
Last active December 16, 2015 02:00
Show Gist options
  • Save chasestubblefield/5359655 to your computer and use it in GitHub Desktop.
Save chasestubblefield/5359655 to your computer and use it in GitHub Desktop.
Mobile templates

https://rpm.newrelic.com/accounts/839/applications/15372/traced_errors/370951312?original_error_id=371022380

The "It Just Works But It Doesn't" Way

config/initializers/mime_types.rb

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

app/controllers/application_controller.rb

class ApplicationController < ActionController::Base
  before_filter :prepare_mobile
  
  private
  
  def prepare_mobile
    request.format = :mobile if request_from_mobile_device?
  end
  
  def request_from_mobile_device?
    env["HTTP_USER_AGENT"].present? && env["HTTP_USER_AGENT"].match(/android/).present?
  end
end

app/controllers/foos_controller.rb

class FoosController < ApplicationController
  def foo
    respond_to do |format|
      format.html   # foos/foo.html.haml
      format.mobile # foos/foo.mobile.haml
      format.json   { render json: {'success': true} }
    end
  end
end

Issues

No fallback format for partials

Since the format is set to :mobile, when rendering partials the engine will only look for files with suffix mobile.haml. This is solvable by patching ActionView like below, but I don't like that.

class ActionView::LookupContext
  module DetailsWithHTMLFormat
    def formats=(values)
      values << :html if values && values.exclude?(:html)
      super(values)
    end
  end

  include DetailsWithHTMLFormat
end

Mobile devices cannot request formats that aren't html

With the above code, a mobile device will always get html, even if they request json for example. This can be partially solved by changing should_set_format_to_mobile? like below.

def prepare_mobile
  request.format = :mobile if request_from_mobile_device? && request.format == :html
end

This says that if a mobile device comes in asking for html, give them mobile html. But what if text/html is not the first entry of the Accept header? What if a request comes in requesting */*?

A Better Way

config/initializers/lookup_context.rb

LookupContext.register_detail(:from_mobile_device)

app/controllers/application_controller.rb

class ApplicationController < ActionController::Base
  before_filter :prepare_mobile
  prepend_view_path MobileViewsResolver.new(Rails.root.join('app/views'))
  
  private
  
  def prepare_mobile
    lookup_context.from_mobile_device = true if request_from_mobile_device?
  end
  
  def request_from_mobile_device?
    env["HTTP_USER_AGENT"].present? && env["HTTP_USER_AGENT"].match(/android/).present?
  end
end

app/controllers/foos_controller.rb

class FoosController < ActionController::Base
  def foo
    respond_to do |format|
      format.html   # foos/foo.mobile.haml if mobile device, else foos/foo.html.haml
      format.json   { render json: {'success': true} }
    end
  end
end

lib/mobile_views_resolver.rb

class MobileViewsResolver < ActionView::FileSystemResolver
  def find_templates(name, prefix, partial, details)
    return [] unless details[:from_mobile_device]
    # lookup files with ".mobile"
  end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment