How to do wild card subdomains in rails 5 - ruby-on-rails

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

Related

Invalid route name, already in use: 'root'

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

Exclude all other resources from subdomain in Rails

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

How redirect subdomains to multiple controllers on Rails 4?

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' }

How to do route constraints in Rails 4

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'

How can I use Rails routes to redirect from one domain to another?

My app used to run on foo.tld but now it runs on bar.tld. Requests will still come in for foo.tld, I want to redirect them to bar.tld.
How can I do this in rails routes?
This works in Rails 3.2.3
constraints(:host => /foo.tld/) do
match "/(*path)" => redirect {|params, req| "http://bar.tld/#{params[:path]}"}
end
This works in Rails 4.0
constraints(:host => /foo.tld/) do
match "/(*path)" => redirect {|params, req| "http://bar.tld/#{params[:path]}"}, via: [:get, :post]
end
This does the job of the other answer. Though in addition, it preserves query strings as well. (Rails 4):
# http://foo.tld?x=y redirects to http://bar.tld?x=y
constraints(:host => /foo.tld/) do
match '/(*path)' => redirect { |params, req|
query_params = req.params.except(:path)
"http://bar.tld/#{params[:path]}#{query_params.keys.any? ? "?" + query_params.to_query : ""}"
}, via: [:get, :post]
end
Note: If you're dealing with full domains instead of just subdomains, use :domain instead of :host.
similar to other answers, this one worked for me:
# config/routes.rb
constraints(host: "foo.com", format: "html") do
get ":any", to: redirect(host: "bar.com", path: "/%{any}"), any: /.*/
end
The following solution redirects multiple domains on GET and HEAD requests while returning http 400 on all other requests (as per this comment in a similar question).
/lib/constraints/domain_redirect_constraint.rb:
module Constraints
class DomainRedirectConstraint
def matches?(request)
request_host = request.host.downcase
return request_host == "foo.tld1" || \
request_host == "foo.tld2" || \
request_host == "foo.tld3"
end
end
end
/config/routes.rb:
require 'constraints/domain_redirect_constraint'
Rails.application.routes.draw do
match "/(*path)", to: redirect {|p, req| "//bar.tld#{req.fullpath}"}, via: [:get, :head], constraints: Constraints::DomainRedirectConstraint.new
match "/(*path)", to: proc { [400, {}, ['']] }, via: :all, constraints: Constraints::DomainRedirectConstraint.new
...
end
For some reason constraints Constraints::DomainRedirectConstraint.new do didn't work for me on heroku but constraints: Constraints::DomainRedirectConstraint.new worked fine.
Bit more modern approach:
constraints(host: 'www.mydomain.com') do
get '/:param' => redirect('https://www.mynewurl.com/:param')
end
constraints(host: /subdomain\.domain\.com/) do
match '/(*path)' => redirect { |params, req|
"https://www.example.com#{req.fullpath}"
}, via: [:get, :head]
end
I use this when using custom domains on Heroku and I want to redirect from the myapp.herokuapp.com -> www.example.com.

Resources