Martin
Martin

Reputation: 11336

In Rails, how to make :mobile views to fallback default views when not found?

This question is a follow up to my previous unanswered question: ActionView::MissingTemplate: Missing template (Trying to render nonexistent :mobile format )

Since doesn't seem to be a Rails approach consensus with this, Is there any way that when accessing from a mobile device to render default :html when :mobile format is not available? (If a :mobile view is present should have priority over those who are not mobile formatted).

Upvotes: 4

Views: 1621

Answers (3)

Mike P.
Mike P.

Reputation: 198

I need the same thing. I researched this including this stack overflow question (and the other similar one) as well as followed the rails thread (as mentioned in this question) at https://github.com/rails/rails/issues/3855 and followed its threads/gists/gems.

Heres what I ended up doing that works with Rails 3.1 and engines. This solution allows you to place the *.mobile.haml (or *.mobile.erb etc.) in the same location as your other view files with no need for 2 hierarchies (one for regular and one for mobile).

Engine and preparation Code

in my 'base' engine I added this in config/initializers/resolvers.rb:

    module Resolvers
      # this resolver graciously shared by jdelStrother at
      # https://github.com/rails/rails/issues/3855#issuecomment-5028260
      class MobileFallbackResolver < ::ActionView::FileSystemResolver
        def find_templates(name, prefix, partial, details)
          if details[:formats] == [:mobile]
            # Add a fallback for html, for the case where, eg, 'index.html.haml' exists, but not 'index.mobile.haml'
            details = details.dup
            details[:formats] = [:mobile, :html]
          end
          super
        end
      end
    end

    ActiveSupport.on_load(:action_controller) do
      tmp_view_paths = view_paths.dup # avoid endless loop as append_view_path modifies view_paths
      tmp_view_paths.each do |path|
        append_view_path(Resolvers::MobileFallbackResolver.new(path.to_s))
      end
    end

Then, in my 'base' engine's application controller I added a mobile? method:

    def mobile?
        request.user_agent && request.user_agent.downcase =~ /mobile|iphone|webos|android|blackberry|midp|cldc/ && request.user_agent.downcase !~ /ipad/
    end

And also this before_filter:

    before_filter :set_layout

    def set_layout
      request.format = :mobile if mobile?
    end

Finally, I added this to the config/initializers/mime_types.rb:

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

Usage

Now I can have (at my application level, or in an engine):

  • app/views/layouts/application.mobile.haml
  • and in any view a .mobile.haml instead of a .html.haml file.

I can even use a specific mobile layout if I set it in any controller: layout 'mobile'

which will use app/views/layouts/mobile.html.haml (or even mobile.mobile.haml).

Upvotes: 0

Viktor Tr&#243;n
Viktor Tr&#243;n

Reputation: 8884

assume you got a mobile_request? controller instance method to detect mobile requests, then you should be able to set format fallback chains:

# application_controller.rb
before_filter :set_request_format, :set_format_fallbacks

respond_to :html, :mobile # etc

def set_request_format
  request.format = :mobile if mobile_request?
end

def set_format_fallbacks
  if request.format == :mobile
    self.formats = [:mobile, :html]
  end
end

This should work but apparently it doesn't completely. https://github.com/rails/rails/issues/3855 If you have a mobile template, the format seems to get locked and it will not find a partial with only html.

Hopefully it will be fixed some way or other. In the meantime you can put this <% controller.set_format_fallbacks %> in each template (ouch) or write your own resolver. http://jkfill.com/2011/03/11/implementing-a-rails-3-view-resolver/

also look at:

Can a mobile mime type fall back to "html" in Rails?

Changing view formats in rails 3.1 (delivering mobile html formats, fallback on normal html)

Upvotes: 3

sgrif
sgrif

Reputation: 3822

Try this:

if File.exists?('app/views/object/mobile.file.erb')
  render :mobile
else
  render :html
end

Upvotes: 0

Related Questions