I want to validate an IP address before I login in my Admin Controller.
before_filter :validate_ip
How should I create this IP validation? I just want to write the IP addresses that have access in the controller.
The first solution is dynamic, because you can populate the array in runtime:
def validate_ip
valid = %w[127.0.0.1 10.0.0.1]
redirect_to(login_path) unless valid.include?(request.remote_ip)
end
The other way to restrict IPs is, to use constraints in your routes.rb:
get '/admin' => 'admin#super_secure_action', :constraints => { :ip => %w[127.0.0.1 10.0.0.1] }
# or
constraints(:ip => %w[127.0.0.1 10.0.0.1]) do
# Every route defined in here will be restricted.
get '/admin' => 'admin#super_secure_action'
end
For more information about Rails and Routes, be sure to check out: Rails Dispatch - The Powerful New Rails Router.
Related
How can I have multi level sub domains in ruby on rails ?
Currently if I want to create a sub domain, I configure this is routes.rb.
constraints :subdomain => 'my' do
mount API => '/'
mount GrapeSwaggerRails::Engine => '/documentation'
end
This will create support for my.domain.com
However, if I wish to have another level api.my.domain.com, what can i do to have one more level of subdomain in the routes ? Thanks.
You could nest your subdomain definitions. The subdomain constraints can be regular expressions so you could do something like
constraints subdomain: /.*my/ do
constraints subdomain: 'api.my' do
mount API => '/'
mount GrapeSwaggerRails::Engine => '/documentation'
end
# Non-API my subdomain routes
end
Currently if you wish to add a constraint there are many ways to do it but as I see currently you can only include one definitive method which is called. E.g.
Class Subdomain
# Possible other `def`s here, but it's self.matches? that gets called.
def self.matches?( request )
# Typical subdomain check here
request.subdomain.present? && request.subdomain != "www"
end
end
The problem with the above approach is it doesn't handle routes prefixed in www, that is admin and www.admin are indistuingishable. More logic can be added, but if this was required over a set of static subdomains like admin, support, and api you currently need to make SubdomainAdmin, SubdomainSupport etc....
This can be solved with regex as follows in routes.rb:
admin
:constraints => { :subdomain => /(www.)?admin/ }
api
:constraints => { :subdomain => /(www.)?api/ }
If requests were even more complex than this things get tricky. So is there a way to add individual methods inside a class used for constraints?
Essentially, how is the below achieved? Is it even possible? Whats the best method of white-listing subdomains to use?
E.g.
Class Subdomain
def self.admin_constraint( request )
# Some logic specifically for admin, possible calls to a shared method above.
# We could check splits `request.subdomain.split(".")[ 1 ].blank?` to see if things are prefixed with "www" etc....
end
def self.api_constraint( request )
# Some logic specifically for api, possibly calls to a shared method above.
# We could check splits `request.subdomain.split(".")[ 1 ].blank?` to see if things are prefixed with "www" etc....
end
def self.matches?( request )
# Catch for normal requests.
end
end
With which we can now call constraints specifically as follows:
:constraints => Subdomain.admin_constraints
And all generic constraints as follows:
:constraints => Subdomain
Is this possible in Rails 4.0.3?
The router will call the #matches?(request) method on whatever object you pass the route. In the case of
:constraints => Subdomain
you're giving the route the Subdomain Class object. However, you could also pass an instance, which you could configure via arguments. e.g.,
Class Subdomain
def initialize(pattern)
#pattern = pattern
end
def matches?(request)
request.subdomain.present? && #pattern ~= request.subdomain
end
end
# routes.rb
namespace :admin, constraints: Subdomain.new(/(www.)?admin/) do
# your admin routes here.
end
NOTE: I didn't verify that code works, I just wrote it off the top of my head, so consider it more of pseudo-code than implementation ready.
Also, you can see an example of this technique, with some more details, at: Use custom Routing Constraints to limit access to Rails Routes via Warden.
i have routes like this :
get "/:article_id" => "categories#show", as: :articles_category
get '/:account_id' => "accounts#show", as: :show_account
but why when i access show_account_url, i always entry to articles_category_url ??
why?
how to make my routes have twice "/:id" in url with different action?
But why when i access show_account_url, i always entry to
articles_category_url ??
The problem you have is you're trying to access the same URL -- domain.com/______. Because Rails cannot process the difference, it uses the first route - your category_url.
There are two ways to deal with this:
Have a "Routing" controller / use slugs
Split your routes up conventionally
Everyone wants app-wide slugs, but you can't do it unless you have a mechanism to calculate which URL is correct. The methods you have to achieve this are either to create a routing controller, or use slugs.
Using a routing controller is actually quite simple:
#config/routes.rb
get "/:id" => "router#direct", as: :slug
#app/controllers/routers_controller.rb
def direct
#routing code (lots of ifs etc)
end
A better way is to use a slug system, which allows you to route to your slugs directly. We use this with http://firststopcosmeticshop.co.uk & the slugalicious gem:
#Slugs
begin
Slug.all.each do |s|
begin
get "#{s.slug}" => "#{s.sluggable_type.downcase.pluralize}#show", :id => s.slug
rescue
end
end
rescue
end
This allows you to send specific slugs to specific controllers / actions. The reason? It creates /[slug] routes, which you can access across the site
Further to this, you could look at the friendly_id gem -- which helps you create resourceful routes using slugs. Highly recommended
I'm looking to route users' pages from their subdomains and also their custom domains. For example, consider three domains:
app.com
user1.app.com
user1.com
A visitor should be able to see the user's page at both the subdomain from the app's domain (user1.app.com) as well as the user's custom domain (user1.com). That is, a visitor will visit the user page when visiting any subdomain of "app.com" or a root domain that is NOT "app.com".
How would I set up routes to do so?
Maybe something along the lines of this pseudo-code:
match "/", :to => "user_page#show", :constraints => { :subdomain => /.+/ OR :domain => NOT(app.com) }
What do you think?
use a constraint utility class or module.
module DomainConstraint
def self.matches? request
request.subdomain.present? || request.domain != 'app.com'
end
end
constraints DomainConstraint do
# routing here
end
if your constraint only applies to one route, you can do :
resources :foo, constraints: DomainConstraint
note : your utility class can also be replaced by a simple lambda (see "Dynamic request matching")
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.