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

前端 未结 5 517
说谎
说谎 2021-01-14 18:36

I\'m creating a mobile site next to our normal html site. Using rails 3.1. Mobile site is accessed in subdomain m.site.com.

I have defined mobile format (Mime::Type.

相关标签:
5条回答
  • 2021-01-14 19:11

    I think I've found the best way to do this. I was attempting the same thing that you were, but then I remembered that in rails 3.1 introduced template inheritance, which is exactly what we need for something like this to work. I really can't take much credit for this implementation as its all laid out there in that railscasts link by Ryan Bates.

    So this is basically how it goes.

    Create a subdirectory in app/views. I labeled mine mobile.

    Nest all view templates you want to override in the same structure format that they would be in the views directory. views/posts/index.html.erb -> views/mobile/posts/index.html.erb

    Create a before_filter in your Application_Controller and do something to this effect.

     before_filter :prep_mobile
     def is_mobile?
       request.user_agent =~ /Mobile|webOS|iPhone/
     end 
     def prep_mobile
       prepend_view_path "app/views/mobile" if is_mobile?
     end
    

    Once thats done, your files will default to the mobile views if they are on a mobile device and fallback to the regular templates if a mobile one is not present.

    0 讨论(0)
  • 2021-01-14 19:13

    Rails 4.1 includes a pretty neat feature:

    Variants

    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.

    Now you can do something like this:

    class PostController < ApplicationController
      def show
        @post = Post.find(params[:id])
    
        respond_to do |format|
          format.json
          format.html               # /app/views/posts/show.html.erb
          format.html.phone         # /app/views/posts/show.html+phone.erb
          format.html.tablet do
            @show_edit_link = false
          end
        end
      end
    end
    

    You simply need to set the variant depending on your needs, for example within a before_filter:

    class ApplicationController < ActionController::Base
      before_action :detect_device_variant
    
      private
    
        def detect_device_variant
          case request.user_agent
          when /iPad/i
            request.variant = :tablet
          when /iPhone/i
            request.variant = :phone
          end
        end
    end
    

    What's new in Rails 4.1

    0 讨论(0)
  • 2021-01-14 19:15

    You can register new format for the whole application in your mime type initializers:

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

    Now you can do something like this in your templates to specify format priority(see How do I render a partial of a different format in Rails?):

    <% self.formats = [:mobile, :html] %>
    

    In this case if there is mobile template it will be used for rendering with fallback to ordinary html template. Now you should only determine if user is browsing via mobile browser and conditionally execute this code. Or you can just assign formats value in ApplicationController filter so correct template will be chosen automaticaly.

    UPDATE:

    It seems like by this time there is no "legal" way to solve this problem using Rails. Here is unclosed issue in the rails repository. There you can find patch that can solve your problem, but it uses private Rails API, so it can be unstable.

    Also you can try implement your own view resolver that possibly can solve the problem: http://jkfill.com/2011/03/11/implementing-a-rails-3-view-resolver/

    0 讨论(0)
  • 2021-01-14 19:15

    How can I set that it's always [:mobile, :html] inside the mobile site? Can I set it somehow in the controller already?

    Yes.

    Here's a simple solution, but it's a bit gross.

    class ApplicationController
        ...
        def formats=(values)
            values << :html if values == [:mobile]
            super(values)
        end
        ...
    end
    

    It turns out Rails (3.2.11) already adds an :html fallback for requests with the :js format. Here's ActionView::LookupContext#formats=

    # Override formats= to expand ["*/*"] values and automatically
    # add :html as fallback to :js.
    def formats=(values)
      if values
        values.concat(default_formats) if values.delete "*/*"
        values << :html if values == [:js]
      end
      super(values)
    end
    

    So you can override formats yourself and it will be conceivably no more gross and hacky than the existing Rails implementation.

    Where does the self.formats come in views (what sets it originally)

    ActionController::Rendering#process_action assigns the formats array from the request (see action_controller/metal/rendering.rb)

    # Before processing, set the request formats in current controller formats.
    def process_action(*) #:nodoc:
      self.formats = request.formats.map { |x| x.ref }
      super
    end
    

    Why isn't it the same as i set in controller before_filter?

    It's not the same as what you set in your before_filter because before filters are run before #process_action (as you'd expect). So whatever you set gets clobbered by what #process_action pulls off the request.

    0 讨论(0)
  • 2021-01-14 19:26

    Why don't you look into Responsive Web Design instead, use CSS and media queries to render the page. That way your controller need never know whether the view is mobile or not.

    0 讨论(0)
提交回复
热议问题