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
Related
I'd like certain rails routes to only be accessible from localhost. In other words if you attempted to access that url from a non localhost connection you would be given a response equivalent to the route not existing.
Optimally some way of specifying routes as local in the routes.rb itself would be the cleanest solution but if there is some way to filter the request later at the controller level for example thats okay too.
If you want to specify that these urls exist only in a development environment, you can do just simple:
if Rails.env.development?
#your routes
end
But if your server in development mode is being accessed by others and you want to specify that these routes exist for localhost only then you can add constraint for domain:
if Rails.env.development?
resources :users, constraints: { domain: 'localhost' }
end
The file routes.rb contains special DSL for routes, but it's still ruby.
So, did you try put your routes in simple condition?
# routes.rb
if Rails.env.development?
# your special local routes definition
end
I have a Rails 4.2 application with a set of routes that are constrained to a subdomain.
constraints subdomain: 'admin' do
# ...
end
However, I'm not sure how to specify multiple subdomains (both admin and admin.staging). How can I specify multiple subdomains?
Even though it's not documented, you can also pass an array of subdomains:
constraints subdomain: ['admin', 'admin.staging'] do
# ...
end
You can use a regular expression, e.g.:
constraints subdomain: /^admin|admin\.staging$/ do
# ...
end
...or...
constraints subdomain: /^admin(\.staging)?$/ do
# ...
end
You can also use a lambda:
constraints subdomain: ->(req) { %w[ admin admin.staging ].include?(req.subdomain) } do
# ...
end
You can read the documentation for constraints here: http://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Scoping.html#method-i-constraints
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'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'm making a small rails engine which I mount like this:
mount BasicApp::Engine => "/app"
Using this answer I have verified that all the routes in the engine are as the should be:
However - when I (inside the engine) link to a named route (defined inside the engine) I get this error
undefined local variable or method `new_post_path' for #<#<Class:0x000000065e0c08>:0x000000065d71d0>
Running "rake route" clearly verifies that "new_post" should be a named path, so I have no idea why Rails (3.1.0) can't figure it out. Any help is welcome
my config/route.rb (for the engine) look like this
BasicApp::Engine.routes.draw do
resources :posts, :path => '' do
resources :post_comments
resources :post_images
end
end
I should add that it is and isolated engine. However paths like main_app.root_path works fine - while root_path does not
The right way
I believe the best solution is to call new_post_path on the Engine's routes proxy, which is available as a helper method. In your case, the helper method will default to basic_app_engine, so you can call basic_app_engine.new_post_path in your views or helpers.
If you want, you can set the name in one of two ways.
# in engine/lib/basic_app/engine.rb:
module BasicApp
class Engine < ::Rails::Engine
engine_name 'basic'
end
end
or
# in app/config/routes.rb:
mount BasicApp::Engine => '/app', :as => 'basic'
In either case, you could then call basic.new_posts_path in your views or helpers.
Another way
Another option is to not use a mounted engine and instead have the engine add the routes directly to the app. Thoughtbot's HighVoltage does this. I don't love this solution because it is likely to cause namespace conflicts when you add many engines, but it does work.
# in engine/config/routes.rb
Rails.application.routes.draw do
resources :posts, :path => '' do
resources :post_comments
resources :post_images
end
end
# in app/config/routes.rb:
# (no mention of the engine)
On Rails 4 the engine_name directive did not work for me.
To access a named route defined in engine's routes from engine's own view or controller, I ended up using the verbose
BasicApp::Engine.routes.url_helpers.new_post_path
I recommend defining a simple helper method to make this more usable
# in /helpers/basic_app/application_helper.rb
module BasicApp::ApplicationHelper
def basic_app_engine
##basic_app_engine_url_helpers ||= BasicApp::Engine.routes.url_helpers
end
end
With this in place you can now use
basic_app_engine.new_post_path
In case you need to access your main application helper from the engine you can just use main_app:
main_app.root_path
use the below in you app to access the engine routes
MyApp::Engine.routes.url_helpers.new_post_path