How to restrict public access to a rails app - ruby-on-rails

Is there a way to upload an app, but only have it accessible by me? Or perhaps by a specific set of IP's?
Reason being, we want to run a few private online tests before opening the app up to the general public. So far I have come up with the following code:
class ApplicationController < ActionController::Base
before_filter :restrict_access
def restrict_access
whitelist = ['127.0.0.123', '10.0.1.7', '10.0.1.8'].freeze
unless( whitelist.include? request.env['REMOTE_ADDR'] )
render :file => "#{Rails.public_path}/500.html", :status => :unauthorized
return
end
end
end
However, the above code still renders the main layout file (app/views/layouts/application.html.erb) which exposes the logo and footer. For un-authorised access we want to display a page that says something like "Ooops, we are still doing a few tests and will be public soon!". No logo of the site, no nothing. Just a simple message.
We are using devise as our authentication gem. We don't want to add authentication functionality just to restrict access for private beta testing. We want to do it by IP instead.
Is such a thing possible? Perhaps the code above just needs working on? Or is there a gem that we can use solely for this requirement?

If you're deploying on Apache or Nginx, this should be easy enough to configure in the relevant site config files. Doesn't need to be in the app itself, in that case.
I'm not totally sure what the issue is with your existing code. Are you saying that the filter seems to be ignored, or that the file renders within the layout? If it's the latter, specifying :layout => false as a render option should take care of that.

class ApplicationController < ActionController::Base
before_filter :check, :except=>:unauth
def unauth
render(:layout=>false)
end
private
def check
whitelist = ['127.0.0.123', '10.0.1.7', '10.0.1.8'].freeze
if !(whitelist.include? request.env['REMOTE_ADDR'])
redirect_to('/unauth')
return
end
end
end
untested but should do it, now you can place a plain error msg or whatever in unauth.rhtml

I will suggest of use some Rack middelware a.e. rack-rewrite
require 'rack-rewrite'
#in your environment
config.middleware.insert_before(Rack::Lock, Rack::Rewrite) do
r301 %r{.*}, "YOUR REDIRECTPAGE$&" ,:if => Proc.new {|rack_env|
#puts here your conditions basing on rack_env
}
end
NOT TESTED

Related

How to require manual 'Admin' approval of an OAuth application when using Doorkeeper?

Let's say we have Users (who can create Doorkeeper::Applications). On the other hand our app has Admins who ideally need to check each Application that is created (and maybe do a background check on the creating User and what not) as well as its scopes. They would #approve! or #reject! the Application and only once it is approved, can the Application make calls to the API.
NOTE: #approve!, #reject!, and approved do not come with Doorkeeper, from what I know. They are hypothetical so my question is clearer.
Is this a behavior that can be achieved with Doorkeeper (or an extension)? I don't think something like this is described in the config file. If not, do you have any general steps on how this could be done?
I'm thinking that something like this could work
class Api::V1::TransactionalBaseController < Api::V1::AuthableController
before_action :doorkeeper_authorize!
before_action :check_application_status!
private
def check_application_status!
application = doorkeeper_token.application
unless application.approved?
raise Doorkeeper::Errors::ApplicationForbidden.new
end
end
end
If this is something that may help other users of the gem, I'm open to possibly opening a PR or developing an extension to achieve this.
rails g migration AddApprovedAtRejectedAtToOauthApplications approved_at:datetime rejected_at:datetime
Edit the file to reflect the correct table.
Keeping in mind that Ruby lets you modify a class from anywhere... In an initializer (or similar); from https://github.com/doorkeeper-gem/doorkeeper/issues/153:
doorkeeper_extend_dir = File.join(Rails.root, "app", "models", "doorkeeper")
Dir.glob("#{doorkeeper_extend_dir}/*.rb").each { |f| require(f) }
# app/models/doorkeeper/application.rb
module Doorkeeper
class Application < ActiveRecord::Base
def approved?
approved_at?
end
def rejected?
rejected_at?
end
def approve!
update_column(:approved_at, DateTime.now)
end
def reject!
update_column(:rejected_at, DateTime.now)
end
end
end

Rails securing letter_opener

Alright, I'm actually using letter_opener_web as a way to preview emails I'm about to send. Understand, I'm not on the developer side (letter_opener is originally meant for dev environment) but on the side of the user of my website. I am using letter_opener as a production component.
I need to generare an access to letter_opener views, however the default implementation of letter_opener is done in routes.rb
mount LetterOpenerWeb::Engine, at: "/letter_opener"
I have for now used a trick of generating a random path and only communicating the URL in the web views I render, however, well, it's just bad :D
if Rails.env.development?
mount LetterOpenerWeb::Engine, at: "/letter_opener"
else
mount LetterOpenerWeb::Engine, at: "/#{SecureRandom.hex(12)}"
end
Is there a way I can override some controller to add my own access control implementation ? My current access control (which I won't change) is done this way
class ApplicationController
before_action :access_denied
end
class SomeController < ApplicationController
prepend_before_action :allow_access_to_xxxx
end
Which is why I need some way to override a controler of letter_opener while still making its views available (if possible by keeping the simple syntax to add in routes !)
EDIT : my user case. Only a couple lines of code to preview instead of sending an email
...
mail(
to: dst,
from: message[:from],
subject: #subject_full,
delivery_method: real_or_preview(message))
...
# Switch delivery method to letter_opener if preview
def real_or_preview(message)
if message[:preview]
:letter_opener
else
ActionMailer::Base.delivery_method
end
end

What should I be using for sitemap generation for rails on heroku?

As a beginner to rails, I'm finding the generation of sitemaps on Heroku to be extremely daunting due to its read-only limitations. However, a sitemap is fundamental to my website as its success is based on SEO.
I have tried dynamic_sitemaps gem however soon removed it as I realised it had no documentation for heroku use. I then used the sitemap_generator gem which had coverage of heroku integration using several gems and external platforms such as Amazon S3. The problem however is that as a beginner I'm running into issues and finding it hard to get past them.
Is there a solution I can use easily generate sitemaps for consistent content such as blog posts on the heroku platform? I really want to get up and running and feel this could take a while to configure if I have to use the methods I've already attempted.
Thanks!
I figured out a small trick that makes it possible to dynamically generate the sitemap file but persist it for later calls on Heroku.
It works great for small\medium size projects, if you have a big\huge project and thousand of pages that changes endlessly , please consider using S3 to store the sitemap file.
Those are the steps:
use the sitemap_generator gem as instructed here https://github.com/kjvarga/sitemap_generator
after bundle, run rake sitemap:install, it will create a config/sitemap.rb file for you
edit the config/sitemap.rb file to look like this
SitemapGenerator::Sitemap.default_host = [your host name goes here]
SitemapGenerator::Sitemap.public_path = File.join(Rails.root, 'tmp').to_s
SitemapGenerator::Sitemap.compress = false
SitemapGenerator::Sitemap.create do
[all your site pages add commands goes here]
end
regarding the host name, I suggest it to be "#{ENV['HOST_PROTOCOL']}://#{ENV['HOST_NAME']}" (and of course add the appropriate environment variables) so you could change it on different environments.
regarding compress, start with false, make sure all is working great for you and change it later if it is a big file.
create your sitemap controller file - app/controllers/sitemap_controller.rb
Edit the sitemap controller file to look like this
require 'rake'
class SitemapController < ApplicationController
def index
file_name = File.join(Rails.root, 'tmp', 'sitemap.xml').to_s
unless File.exist?(file_name)
Rails.application.load_tasks
Rake::Task['sitemap:refresh:no_ping'].invoke
end
# it's better to be safe than sorry
if File.exist?(file_name)
respond_to do |format|
format.xml { render file: file_name }
end
else
render file: 'public/404.html', status: :not_found, layout: false
end
end
end
Add the index action to your routes.rb file
resources :sitemap, only: %i[index], constraints: ->(req) { req.format == :xml }
restart/deploy you server and go to /sitemap.xml
Enjoy 😊
Have taken a look to Dynamic Site Maps Gem this is really simple to set up just read the read me in the Github for more features you can also look at Site Map Generator gem
Wish you the best of luck
try like this
Controller
class SitemapController < ApplicationController
layout nil
def index
#static_pages = [jobs_url, advertising_url, join_url]
#offers = Offer.all
respond_to do |format|
format.xml
end
end
end
View
xml.instruct!
xml.urlset(
) do
#static_pages.each do |page|
xml.url do
xml.loc "#{page}"
xml.changefreq("monthly")
end
end
#offers.each do |offer|
xml.url do
xml.loc
xml.changefreq("daily")
end
end
end
Route
get 'sitemap.xml', :to => 'sitemap#index', :defaults => { :format => 'xml' }

Rails 3.0 Mobile Site

I was wondering how I go about creating a mobile version of a Rails 3.0 application.
I saw this post: Mobile version of views for Ruby on Rails
But I am confused on the respond_to method. How does the method know which format to render?
Would I create a method in my application controller to render a mobile layout and then for each view use the respond_to method?
Thank you,
Brian
Ryan Bates has done a great tutorial
http://railscasts.com/episodes/199-mobile-devices
The respond_to method will choose according to the current request's mime type.
This works out of the box for common mime types, but you'll need to tell your application about your custom ones. In your application controller, you'll want to define a method that will adjust the format of Rails' internal reprensentation of the request. Then, call that method as a before filter. Here's an example:
class ApplicationController < ActionController::Base
before_filter :adjust_for_mobile
def adjust_for_mobile
request.format = :mobile if mobile_request
end
# You'll also need to define the mobile_request method
# using whatever strategy you want to tell if a request
# is from a mobile client or not
def mobile_request
true
end
end
Make sure you've defined this new type in config/initializers/mime_types.rb:
Mime::Type.register "text/html", :mobile
Then, in your controllers, you'll be able to use the 'mobile' format:
class FoosController < ApplicationController
def index
#foos = Foo.all
respond_to do |format|
format.html # index.html.erb
format.mobile # index.mobile.erb
end
end
end
This sure looks elegant and all but in practice, I find that I rarely use it for mobile sites. The mobile sites I've been working on are generally quite different from the 'complete' sites. In those cases it makes sense to just define another bunch of controllers under a 'mobile' namespace.
Have a look at Rails Mobile
I have developed that plugin a while back. The idea behind that plugin is you can redirect to different controllers or views based on your mobile device capabilities through your router config file.
At the end of the routing.rb add these lines:
MobileDispatch::Categories.add do
def mobile_classifier(device)
"_mobile"
end
end
These lines define a new substring for all mobile devices which will be stored in $ variable for each request in the rouging.rb file.
That way you can play with your routing rules. For instance this line in routing.rb:
match '/photo/:id', :to => "photo#index$", :classifier => :mobile_classifier
for a normal user would be interpreted as:
match '/photo/:id', :to => "photo#index", :classifier => :mobile_classifier
while for a mobile user as:
match '/photo/:id', :to => "photo#index_mobile", :classifier => :mobile_classifier
The power here is in mobile_classifier(device) method where you can return different classification based on device object.
so let say we modify the method to return "_iphone" for all iphone devices and "_android" for all android mobiles, then the above routing line would be interpreted as:
match '/photo/:id', :to => "photo#index_iphone", :classifier => :mobile_classifier
match '/photo/:id', :to => "photo#index_android", :classifier => :mobile_classifier
If you add the $ to the end of view part of each route (similar to what we did here) you will get different methods in your controller for each category of devices and different view names for each method (index_iphone.htm.erb and index_android.ht.erb) This way you have seperate views/layers for each device category that you defined in your mobile_classifier method.

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

I'm using this code (taken from here) in ApplicationController to detect iPhone, iPod Touch and iPad requests:
before_filter :detect_mobile_request, :detect_tablet_request
protected
def detect_mobile_request
request.format = :mobile if mobile_request?
end
def mobile_request?
#request.subdomains.first == 'm'
request.user_agent =~ /iPhone/ || request.user_agent =~ /iPod/
end
def detect_tablet_request
request.format = :tablet if tablet_request?
end
def tablet_request?
#request.subdomains.first == 't'
request.user_agent =~ /iPad/
end
This allows me to have templates like show.html.erb, show.mobile.erb, and show.tablet.erb, which is great, but there's a problem: It seems I must define every template for each mime type. For example, requesting the "show" action from an iPhone without defining show.mobile.erb will throw an error even if show.html.erb is defined. If a mobile or tablet template is missing, I'd like to simply fall back on the html one. It doesn't seem too far fetched since "mobile" is defined as an alias to "text/html" in mime_types.rb.
So, a few questions:
Am I doing this wrong? Or, is there a better way to do this?
If not, can I get the mobile and tablet mime types to fall back on html if a mobile or tablet file is not present?
If it matters, I'm using Rails 3.0.1. Thanks in advance for any pointers.
EDIT: Something I forgot to mention: I'll eventually be moving to separate sub-domains (as you can see commented out in my example) so the template loading really needs to happen automatically regardless of which before_filter has run.
Possible Duplicate of Changing view formats in rails 3.1 (delivering mobile html formats, fallback on normal html)
However, I struggled with this exact same problem and came up with a fairly elegant solution that met my needs perfectly. Here is my answer from the other post.
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.
You need to do several things to wire this up, but the good news is that Rails 3 actually makes this a lot simpler than it used to be, and you can let the router do most of the hard work for you.
First off, you need to make a special route that sets up the correct mime type for you:
# In routes.rb:
resources :things, :user_agent => /iPhone/, :format => :iphone
resources :things
Now you have things accessed by an iphone user agent being marked with the iphone mime type. Rails will explode at you for a missing mime type though, so head over to config/initializers/mime_types.rb and uncomment the iphone one:
Mime::Type.register_alias "text/html", :iphone
Now you're mime type is ready for use, but your controller probably doesn't yet know about your new mime type, and as such you'll see 406 responses. To solve this, just add a mime-type allowance at the top of the controller, using repsond_to:
class ThingsController < ApplicationController
respond_to :html, :xml, :iphone
Now you can just use respond_to blocks or respond_with as normal.
There currently is no API to easily perform the automatic fallback other than the monkeypatch or non-mime template approaches already discussed. You might be able to wire up an override more cleanly using a specialized responder class.
Other recommended reading includes:
https://github.com/plataformatec/responders
http://www.railsdispatch.com/posts/rails-3-makes-life-better
Trying removing the .html from the .html.erb and both iPhone and browser will fallback to the common file.
I have added a new answer for version 3.2.X. This answer is valid for <~ 3.0.1.
I came to this question while looking to be able to have multiple fallbacks on the view. For example if my product can be white-labeled and in turn if my white-label partner is able to sell sponsorship, then I need a cascade of views on every page like this:
Sponsor View: .sponsor_html
Partner View: .partner_html
Default View: .html
The answer by Joe, of just removing .html works (really well) if you only have one level above the default, but in actual application I needed 5 levels in some cases.
There did not seem to be anyway to implement this short of some monkey patching in the same vein as Jeremy.
The Rails core makes some fairly wide ranging assumptions that you only want one format and that it maps to a single extension (with the default of NO extension).
I needed a single solution that would work for all view elements -- layouts, templates, and partials.
Attempting to make this more along the lines of convention I came up with the following.
# app/config/initializers/resolver.rb
module ActionView
class Base
cattr_accessor :extension_fallbacks
##extension_fallbacks = nil
end
class PathResolver < Resolver
private
def find_templates_with_fallbacks(name, prefix, partial, details)
fallbacks = Rails.application.config.action_view.extension_fallbacks
format = details[:formats].first
unless fallbacks && fallbacks[format]
return find_templates_without_fallbacks(name, prefix, partial, details)
end
deets = details.dup
deets[:formats] = fallbacks[format]
path = build_path(name, prefix, partial, deets)
query(path, EXTENSION_ORDER.map {|ext| deets[ext] }, details[:formats])
end
alias_method_chain :find_templates, :fallbacks
end
end
# config/application.rb
config.after_initialize do
config.action_view.extension_fallbacks = {
html: [:sponsor_html, :partner_html, :html],
mobile: [:sponsor_mobile, :partner_mobile, :sponsor_html, :partner_html, :html]
}
# config/initializers/mime_types.rb
register_alias 'text/html', :mobile
# app/controllers/examples_controller.rb
class ExamplesController
respond_to :html, :mobile
def index
#examples = Examples.all
respond_with(#examples)
end
end
Note: I did see the comments around alias_method_chain, and initially did make a call to super at the appropriate spot. This actually called ActionView::Resolver#find_templates (which raises a NotImplemented exception) rather than the ActionView::PathResolver#find_templates in some cases. I wasn't patient enough to track down why. I suspect its because of being a private method.
Plus, Rails, at this time, does not report alias_method_chain as deprecated. Just that post does.
I do not like this answer as it involves some very brittle implementation around that find_templates call. In particular the assumption that you only have ONE format, but this is an assumption made all over the place in the template request.
After 4 days of trying to solve this and combing through the whole of the template request stack its the best I can come up with.
The way that I'm handling this is to simply skip_before_filter on those requests that I know I want to render the HTML views for. Obviously, that will work with partials.
If your site has a lot of mobile and/or tablet views, you probably want to set your filter in ApplicationController and skip them in subclasses, but if only a few actions have mobile specific views, you should only call the before filter on those actions/controllers you want.
If your OS has symlinks you could use those.
$ ln -s show.html.erb show.mobile.erb
I am adding another answer now that we have updated to 3.2.X. Leaving the old answer as it was in case someone needs that one. But, I will edit it to direct people to this one for current versions.
The significant difference here is to make use of the "new" (since 3.1) availability of adding in custom path resolvers. Which does make the code shorter, as Jeroen suggested. But taken a little bit further. In particular the #find_templates is no longer private and it is expected that you will write a custom one.
# lib/fallback_resolver.rb
class FallbackResolver < ::ActionView::FileSystemResolver
def initialize(path, fallbacks = nil)
#fallback_list = fallbacks
super(path)
end
def find_templates(name, prefix, partial, details)
format = details[:formats].first
return super unless #fallback_list && #fallback_list[format]
formats = Array.wrap(#fallback_list[format])
details_copy = details.dup
details_copy[:formats] = formats
path = Path.build(name, prefix, partial)
query(path, details_copy, formats)
end
end
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
append_view_path 'app/views', {
mobile: [:sponsor_mobile, :mobile, :sponsor_html, :html],
html: [:sponsor_html, :html]
}
respond_to :html, :mobile
# config/initializers/mime_types.rb
register_alias 'text/html', :mobile
Here's a simpler solution:
class ApplicationController
...
def formats=(values)
values << :html if values == [:mobile]
super(values)
end
...
end
It turns out Rails (3.2.11) adds an :html fallback for requests with the :js format. Here's how it works:
ActionController::Rendering#process_action assigns the formats array from the request (see action_controller/metal/rendering.rb)
ActionView::LookupContext#formats= gets called with the result
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
This solution is gross but I don't know a better way to get Rails to interpret a request MIME type of "mobile" as formatters [:mobile, :html] - and Rails already does it this way.
Yes, I'm pretty sure this is the right way to do this in rails. I've defined iphone formats this way before. That's a good question about getting the format to default back to :html if a template for iphone doesn't exist. It sounds simple enough, but I think you'll have to add in a monkeypath to either rescue the missing template error, or to check if the template exists before rendering. Take a look a the type of patches shown in this question. Something like this would probably do the trick (writing this code in my browser, so more pseudo code) but throw this in an initializer
# config/initializers/default_html_view.rb
module ActionView
class PathSet
def find_template_with_exception_handling(original_template_path, format = nil, html_fallback = true)
begin
find_template_without_exception_handling(original_template_path, format, html_fallback)
rescue ActionView::MissingTemplate => e
# Template wasn't found
template_path = original_template_path.sub(/^\//, '')
# Check to see if the html version exists
if template = load_path["#{template_path}.#{I18n.locale}.html"]
# Return html version
return template
else
# The html format doesn't exist either
raise e
end
end
end
alias_method_chain :find_template, :exception_handling
end
end
Here is another example of how to do it, inspired by Simon's code, but a bit shorter and a bit less hacky:
# application_controller.rb
class ApplicationController < ActionController::Base
# ...
# When the format is iphone have it also fallback on :html
append_view_path ExtensionFallbackResolver.new("app/views", :iphone => :html)
# ...
end
and somewhere in an autoload_path or explicitly required:
# extension_fallback_resolver.rb
class ExtensionFallbackResolver < ActionView::FileSystemResolver
attr_reader :format_fallbacks
# In controller do append_view_path ExtensionFallbackResolver.new("app/views", :iphone => :html)
def initialize(path, format_fallbacks = {})
super(path)
#format_fallbacks = format_fallbacks
end
private
def find_templates(name, prefix, partial, details)
fallback_details = details.dup
fallback_details[:formats] = Array(format_fallbacks[details[:formats].first])
path = build_path(name, prefix, partial, details)
query(path, EXTENSION_ORDER.map { |ext| fallback_details[ext] }, details[:formats])
end
end
The above is still a hack because it is using a private API, but possibly less fragile as Simon's original proposal.
Note that you need to take care of the layout seperately. You will need to implement a method that chooses the layout based on the user agent or something similar. The will only take care of the fallback for the normal templates.
Rails 4.1 includes Variants, this is a great feature that allow you to set different views for the same mime. You can now simply add a before_action and let the variant to do the magic:
before_action :detect_device_variant
def detect_device_variant
case request.user_agent
when /iPad/i
request.variant = :tablet
when /iPhone/i
request.variant = :phone
end
end
Then, in your action:
respond_to do |format|
format.json
format.html # /app/views/the_controller/the_action.html.erb
format.html.phone # /app/views/the_controller/the_action.html+phone.erb
format.html.tablet do
#some_tablet_specific_variable = "foo"
end
end
More info here.
You can in this case for the format to html. By example you want always use the html in user show method
class UserController
def show
..your_code..
render :show, :format => :html
end
end
In this case, if you request show on User controller you render all the time the html version.
If you want render JSON too by example you can made some test about your type like :
class UserController
def show
..your_code..
if [:mobile, :tablet, :html].include?(request.format)
render :show, :format => :html
else
respond_with(#user)
end
end
end
I made a monkey patch for that, but now, I use a better solution :
In application_controller.rb :
layout :which_layout
def which_layout
mobile? ? 'mobile' : 'application'
end
With the mobile? method you can write.
So I have a different layout but all the same views, and in the mobile.html.erb layout, I use a different CSS file.
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).
I solved this problem by using this before_filter in my ApplicationController:
def set_mobile_format
request.formats.unshift(Mime::MOBILE) if mobile_client?
end
This puts the mobile format to the front of the list of acceptable formats. So, the Resolver prefers .mobile.erb templates, but will fall back to .html.erb if no mobile version is found.
Of course, for this to work you need to implement some kind of #mobile_client? function and put Mime::Type.register_alias "text/html", :mobile into your config/initializers/mime_types.rb

Resources