Rails wildcard route with database lookup & multiple controllers - ruby-on-rails

I have a Rails application setup where, after all of the other site routes are defined, I have a catch-all wildcard for my Users to display their Profiles on selected root-level "vanity" URLs of non-reserved paths/keywords:
get '*path' => 'profiles#show'
The Profiles controller then checks to make sure the path defines a valid Profile, otherwise redirects to root. This works fine.
What I need to do now is create a mechanism where the catch-all path could define either a Profile or a Blog, based on the database lookup of the path for the proper controller to route to.
I do not want to do a redirect ... I want to load either the Profile or Blog content on the original wildcard URL.
What are my options to go from wildcard route -> db lookup -> proper controller?
In other words, where might this logic properly go?
Thanks.

It seem like you want a route constraint that will match some pattern (regex) that defines if a path matches a route or if it tries the subsequent routes. Maybe something like this.
get '*path', :to => 'profiles#show', :constraints => { path: /blog\/.+/ }
The idea is that you must know something at the routing level, if it can be path based then the above thing will work otherwise if it needs to be more complex you can use a custom constraints class.
# lib/blog_constraint.rb
class BlogConstraint
def initialize
#slugs = Blog.pluck(:slug)
end
def matches?(request)
request.url =~ /blog\/(.+)/
#slugs.include?($1)
end
end
# config/routes.rb
YourApp::Application.routes.draw do
get '*path', :to => 'blogs#show', :constraints => BlogConstraint.new
get '*path', :to => 'profiles#show'
end

Related

How do you constraint a RAILS 4 route to exclude all paths with integers in it?

I am wanting to route the main site and exclude paths with digits in them (like the account id).
So for example, I want to constraint a url like domain.com/about_us or domain.com/signup/plan/1
BUT exclude all paths that start with a integer, like domain.com/1234573/user/3 or domain.com/123456
I have the following code but I get "No route matches [GET] '/signup'" when going to any of the constraint urls like domain.com/signup. I get the homepage just fine (domain.com).
class MainSite
# Match only non account pages
# urls like domain.com/signup
# BUT NOT like domain.com/2342342
def self.matches?(request)
not request.path =~ %r{\A\/\D}
end
end
Myapp::Application.routes.draw do
devise_for :app_admins
mount RailsAdmin::Engine => '/app_admin', :as => 'rails_admin'
# Routes for the public site
constraints MainSite do
# Homepage
get '/' => "content#index"
get '/signup' => 'accounts#plans', :as => 'plans'
....
end
root :to => "accounts#dashboard"
devise_for :users
...
Not sure what I am missing here.
Ok, I see what I did wrong... It was in my regex. I had \D instead of \d. I was excluding all numbers. Hah!
I have an actual answer:
Put the route that you don't want above the one you do in routes.rb, and send it to a handler that redirects to home or similar.

Prevent subdomain access for html requests

I have a subdomain widgets.mywebsite.com. The only thing the subdomain does is return json on a given route. How do I prevent people/crawlers accessing my site via the subdomain whilst still keeping the route available? I would be happy to simply redirect any requests that include this subdomain to mywebsite.com but can't work out how to do it.
constraints :subdomain => 'widgets' do
namespace :widgets, :path => nil, :format => 'json' do
match 'v1' => 'v1/widgets#index'
end
end
So it sounds like you want to reverse the logic in your routes - you want to constrain the bulk of your routes so that they only resolve when the subdomain isn't equal to widgets. That's opposed to the above example, where you're making a single route only available on the 'widgets' subdomain. This is pretty straightforward.
In your config/routes.rb you can define a class before the Application.routes.draw block
class NotWidgetsRequest
def matches?(request)
request.subdomain != 'widgets'
end
end
and then you can wrap all of your routes other than the v1/widgets#index route in a
constraints NotWidgetRequest.new do
...
end
block. This will prevent these routes from resolving on widgets.mywebsite.com

Ruby on Rails route for wildcard subdomains to a controller/action

I dynamically create URLs of the form username.users.example.com:
bob.users.example.com
tim.users.example.com
scott.users.example.com
All of *.users.example.com requests should go to a particular controller/action. How do I specify this in routes.rb?
All other requests to www.example.com go to the normal list of routes in my routes.rb file.
UPDATE: I watch the railscast about subdomains and it showed the following bit of code which would seem to be exactly what I need (changed the controller and subdomain):
match '', to: 'my_controller#show', constraints: {subdomain: /.+\.users/}
The problem is it only matches the root URL. I need this to match EVERY possible URL with a *.users subdomain. So obviously I would put it at the top of my routes.rb file. But how do I specify a catch-all route? Is it simply '*'? Or '/*'?
I think, you just need to do the following :
create a class Subdomain in lib :
class Subdomain
def self.matches?(request)
request.subdomain.present? && request.host.include?('.users')
end
end
and in your routes :
constraints Subdomain do
match '', to: 'my_controller#show'
end
You can constraint route dynamically based on some specific criteria by creating a matches? method
Lets say we have to filter sub domain of URL
constraints Subdomain do
get '*path', to: 'users#show'
end
class Subdomain
def self.matches?(request)
(request.subdomain.present? && request.subdomain.start_with?('.users')
end
end
What we are doing here is checking for URL if it start with sub domain users then only hit users#show action. Your class must have mathes? method either class method or instance method. If you want to make it a instance method then do
constraints Subdomain.new do
get '*path', to: 'proxy#index'
end
you can achieve same thing using lambda as well like below.
Instead of writing class we can also use lambdas
get '*path', to: 'users#show', constraints: lambda{|request|request.env['SERVER_NAME'].match('.users')}

Conflict routes and params name in Ruby on Rails

In my website, users can visit category via example.com/category1, example.com/category2, etc. So I write the route rule as below:
match '/:category' => 'home#category', :constraints => ShowCategory.new
ShowCategory is a Class that make sure the category user visited is exist. At the same time, users can specify their personal domain name, then his/her profile page can be visited via url like example.com/peter. So There is another route rule:
match '/:user_domain' => 'Profiles#show'
Because I use the :constraints for category route, the routes didn't conflict. However, in the Profiles#show action I always get the parameters {'category' => 'peter'}, not {'user_domain' => 'peter'}.
How can I correct the parameter name? I don't want a parameter named category in the profiles controller.
Thanks.
Well, the way you're naming your routes doesn't make much sense; it's making the routing engine confused. It should be categories/1 rather than category1.
As for the profiles, I guess the first rule and the to_param method should be enough.
# routes.rb
profile '/:user_domain', controller: 'profiles', action: 'show'
resources :profiles
resources :categories
# profile.rb
def to_param
self.user_domain
end

Ruby on Rails: subdomain too powerful? how do I set this up the right way

routes:
match '/' => 'site_admin/admin#index'
resources :link_pages
resources :services
resource :user_session
resource :account, :controller => "users"
resources :password_resets
resources :users
resources :addresses
resources :info
match "/home", :to => 'info#home'
match "/register", :to => 'users#new'
root :to => 'info#home'
match ':controller(/:action(/:id(.:format)))'
so when I got to admin.lvh.me:3000/ it goes to site_admin/admin#index... which is great...
but when I take off the subdomain, and just have lvh.me:3000/ it goes to the same route....
how do I get admin to stay where it is. and no subdomain to go to my root page, as in my routes file?
Routes are parsed in order, so when you request / from any domain it finds "match '/'..." first and sends you to the specified page. Your subdomain isn't coming into play at all. You can use Request-based constraints to route based on subdomain:
http://guides.rubyonrails.org/routing.html#request-based-constraints
Not sure how subdomain factors into this at all. Perhaps you're confusing subdomain with route namespacing (http://edgeguides.rubyonrails.org/routing.html#controller-namespaces-and-routing)?
match '/' => 'site_admin/admin#index'
Is being selected over
root :to => 'info#home'
Because it's defined first in the routes file. They're ostensibly the same thing.
Yes #Cory is right. Above both statements are similar and first defined route is considered every time. If you change admin route to
match '/admin' => 'site_admin/admin#index'
then it does make sense... What say??
or else, using the following code you can determine your URL conditionally:
request.subdomains(0).first will give you the subdomain value- either admin or blank. But it will go to any one controller action only which is defined first in route.rb file.
Then from that action using subdomain, you can decide where to re-direct it- either to admin panel or home page...

Resources