I get:
Invalid route name, already in use: 'root'
You may have defined two routes with the same name using the :as option, or you may be overriding a route already defined by a resource with the same naming. For the latter, you can restrict the routes created with resources as explained here:
https://guides.rubyonrails.org/routing.html#restricting-the-routes-created
routes.rb
AlectricaSite::Application.routes.draw do
constraints lambda{ |req| ( req.subdomain == 'shop' || req.subdomain == 'servicios' ) } do
root to: 'comercio/servicios#index'
end
constraints lambda{ |req| req.subdomain == 'www' } do
root to: 'electrico/album#hola'
end
end
when trying to set a root_path for each subdomain (www, shop, servicios).
This solution works for rails 5, but rails 6.1 is complaining about that.
I think it could be you're encountering a conflict in your routes because you're using the same name root for two different routes.
Perhaps you can try:
AlectricaSite::Application.routes.draw do
constraints lambda { |req| req.subdomain == 'shop' || req.subdomain == 'servicios' } do
get '/', to: 'comercio/servicios#index', as: 'comercio_root'
end
constraints lambda { |req| req.subdomain == 'www' } do
get '/', to: 'electrico/album#hola', as: 'electrico_root'
end
end
The helper methods for those routes should be comercio_root_path and electrico_root_path
A found a solution by doing this:
AlectricaSite::Application.routes.draw do
get '/', to: 'comercio/servicios#index', constraints: { subdomain: 'shop' }
get '/', to: 'comercio/servicios#index', constraints: { subdomain: 'servicios' }
root to: 'electrico/album#hola'
....
end
Related
In Ryan Bates railscast on subdomains, he gives the following way to match subdomains
# config/routes.rb
match '', to: 'pro_users#show', contraints: lambda { |r| r.subdomain.present? && r.subdomain != 'www' }
but match without the HTTP verb was deprecated in rails 5, so this gives an exception
You should not use the `match` method in your router without specifying an HTTP method.
So how do you do this in rails 5 and above?
In rails 5, you have to specify which HTTP verb that you want to use. So in if you want to use that route for get and post, you would write
# config/routes.rb
match '', to: 'pro_users#show', via: [:get,:post], contraints: lambda { |r| r.subdomain.present? && r.subdomain != 'www' }
You might also like to clean up the constraint by using a contraint class, this blog has some suggestions.
So you could write the route matcher as
# config/routes.rb
match '', to: 'pro_users#show', via: [:get,:post], contraints: SubdomainConstraint }
and
class SubdomainConstraint
def self.matches?(request)
request.subdomain.present? && request.subdomain != 'www'
end
end
I am looking for a simple way to replace all my routes matching mydomain.com/resources/xx by xx.mydomain.com with Rails 4.
Would anyone have an idea to do that easily and that would work with nested resources as well?
Thanks, Joris
Constraints
What you're looking for is constraints in your routes, specifically that you're looking to use one to determine whether you have a subdomain that you can access
There are a number of resources on how to achieve this:
Basecamp-style subdomains by DHH
Subdomains Railscast
The bottom line is that you'll probably have to create a custom subdomai constraint, which you can then use the standard routing structure for:
#lib/subdomain.rb
class Subdomain
def self.matches?(request)
if request.subdomain.present? && request.subdomain != 'www'
account = Account.find_by username: request.subdomain
return true if account # -> if account is not found, return false (IE no route)
end
end
end
#config/routes.rb
constraints(Subdomain) do
get "/", to: "controller#action"
resources :posts #-> subdomain.domain.com/posts
...
end
The above is untested - I also found the following with Rails' documentation:
#lib/subdomain.rb
class Subdomain
def initialize
#accounts = Account.all
end
def matches?(request)
if request.subdomain.present? && request.subdomain != 'www'
#accounts.include?(request.subdomain)
end
end
end
#config/routes.rb
constraints: Subdomain.new do
get "/", to: "controller#action"
resources :posts #-> subdomain.domain.com/posts
...
end
Here how I have done it before in a Rails 3 app:
constraints :subdomain => /ambassador/ do
namespace(:influencer, :path => '/') do
root :to => 'home#index'
match 'home' => 'sweepstakes#index', :as => :influencer_home
resources :sweepstakes
resources :associates
resources :widgets
resources :sessions
resources :reports do
resource :member
end
match 'faq' => 'info#faq'
end
end
Be sure to put this block towards the top of the routes.rb file so it takes precedence.
You can of course nest your resources in here like normal.
I have a Rails 4.0 app with that allows users to access blogs through subdomains. My routes currently look like this:
match '', to: 'blogs#show', via: [:get, :post], constraints: lambda { |r| r.subdomain.present? && r.subdomain != 'www' }
resources :foobars
Now, when I navigate to somesubdomain.example.com I am indeed taken to the showaction of the blogs controller action, as expected.
When I navigate to example.com/foobars I can access the index action of the foobars controller, as expected.
However, I only get a behavior I do not desire:
When I navigate to somesubdomain.example.com/foobars, I can still access the the index action of foobars controller.
Is there a way to limit or exclude all resources that I do not specifically allow for a particular subdomain (i.e. somesubdomain.example.com/foobars will not work unless otherwise specified).
Thanks!
If you need to define a specific subdomain to exclude from a set of routes you can simply do this (uses negative lookahead regex):
# exclude all subdomains with 'www'
constrain :subdomain => /^(?!www)(\w+)/ do
root to: 'session#new'
resources :foobars
end
Or similarly, to define a specific subdomain to include a set of routes you could do this:
# only for subdomain matching 'somesubdomain'
constrain :subdomain => /^somesubdomain/ do
root to: 'blog#show'
resources :foobars
end
Another approach would be to define the constraint match in a class (or module) and then wrap all routes within constraints block:
class WorldWideWebSubdomainConstraint
def self.matches?(request)
request.subdomain.present? && request.subdomain != 'www'
end
end
App::Application.routes.draw do
# All "www" requests handled here
constraints(WorldWideWebSubdomainConstraint.new) do
root to: 'session#new'
resources :foobars
end
# All non "www" requests handled here
root to: 'blogs#show', via: [:get, :post]
end
I have Agent and Brand in my Rails 4 app (an Agent has many :brands), and each one have a :subdomain field.
So I want to have:
- agentsubdomain.domain.com (should redirect to 'agents#show')
- brandsubdomain.domain.com (should redirect to 'agents/brands#show')
I'm trying to do this in routes.rb:
resources :agents do
resources :brands, module: 'agents'
end
get '/', to: 'agents/brands#show',
constraints: lambda { |r| r.subdomain.present? && r.subdomain != 'www' }
get '/', to: 'agents#show',
constraints: lambda { |r| r.subdomain.present? && r.subdomain != 'www' }
root 'home#index'
That obviously doesn't work because only the first '/' is matched, always going to brands#show.
How can I redirect to agents#show OR agents/brands#show, depending on which one exists?
Thank you!
Create a routes_helper.rb module. In it create 2 methods one called "agents_brands_present?" and one called "agents_present?" then have them receive the request object as a parameter and check if the appropriate subdomains exist.
require 'RoutesHelper'
get '/', to: 'agents/brands#show',
constraints: lambda { |r| r.subdomain.present? && RoutesHelper.agents_brand_present?(r) && r.subdomain != 'www' }
get '/', to: 'agents#show',
constraints: lambda { |r| r.subdomain.agents_present? && RoutesHelper.agents_present?(r) && r.subdomain != 'www' }
This is not working:
get '/' => 'addresses#show', :constraints => Subdomain
lib/subdomain.rb:
class Subdomain
def self.matches?(request)
request.subdomain.present? && request.subdomain != 'www'
end
end
this route is just ignored...
thank you
Solution:
the specified route has to go before
root to: 'home#index'
This worked perfectly only you need to add subdomain.rb in you routes manually
get '/' => 'addresses#show', :constraints => Subdomain
lib/subdomain.rb:
class Subdomain
def self.matches?(request)
request.subdomain.present? && request.subdomain != 'www'
end
end
Add in routes.rb
require 'subdomain'
And yes the specified route has to go before
root to: 'home#index'