Rails Routing by mapping subdomain to path via database - ruby-on-rails

A seemlingly simple problem which I can't figure out how to deal with (in Rails 3.2): we would like to offer the possibility to
our users to define a subdomain and map incoming requests using that subdomain to a path partially retrieved from
the database. So for example, while www.example.com will go to our usual root path, a request to steve.example.com
would first look up "steve" in a datbase table that associates subdomains with ids, and if a match is found route
the request to, say, www.example.com/folders/36 (where 36 is the id associated with "steve"), and if no match is found
continue looking for other routes in routes.rb.
I have a working solution using redirect, which goes something like:
constraints :subdomain => /^(?!www).+/ do # a subdomain is used and different from www
match '*path', :to => redirect {|params, req|
req_protocol=req.env['rack.url_scheme'] # e.g. "http"
req_host=req.env['HTTP_HOST'] # e.g. "steve.example.local:3000"
...
code to pick up "steve", do the lookup and return a suitable URL
}
end
Now, I do NOT want to use redirects for two reasons: firstly the URL is then modified in the user's browser address bar,
and secondly browsers tend to cache the redirects (even with status set to 302 or 307) making such a solution less dynamic.
If I had had access to the request object in routes.rb, I could possibly had done something like
match '*path' => "folders##{index}", :constraints => {:subdomain => /^(?!www).+/ }
after having retrieved index from the database table using the subdomain, but the request object is not available.
(I could however probably manage the case when no association is found using the "Advanced Contstraints" as described in the
Rails guide, although I haven't tested that, knowing that it would only fix a tiny part of the problem).

Related

Specify Rails Controller Action Based on Domain

I have a single application that supports two domains, http://domain1.com and http://domain2.com. Models, controllers and views are shared between the domains.
In the past http://domain1.com/user_type_2/faqs and http://domain2.com/user_type_2/faqs were served up separately using the same controller action. Now, however, for SEO reasons, I want to completely remove the view associated with domain1 and route all user type 2 faq requests (from either domain) to http://domain2.com/user_type_2/faqs - continuing to use the existing controller and actions.
How can I specify a route in routes.rb that redirects all requests from http://domain1.com/user_type_2/faqs to http://domain2.com/user_type_2/faqs?
I've tried
match 'http://domain1.com/user_type_2/faqs', :to => redirect('http://domain2.com/user_type_2/faqs')
and
constraints(:host => /domain1.com/) do
match "/user_type_2/faqs" => redirect {|params, req| "//domain2.com/#{params[:path]}"}
end
Neither seems to work.
It turns out the answer is a redirect within a domain constraint. Pretty straightforward once I saw it.
constraints domain: 'domain1.com' do
match 'user_type_2/faqs', :to => redirect('http://domain2.com/user_type_2/faqs')
end

rails 3.0 routing - redirect globbed route

In my routes file we originally had this rout set up:
match '/search/*tag' => 'search#search'
We now want to remove the word 'search' from the url. So I added a new route:
match '/*tag' => 'search#search'
That all works beautifully. We wanted to update the old route to redirect to the new one to keep seo and bookmarks working.
match '/search/*tag' => redirect {|params| "/#{params[:tag]}"}
However this is pluralizing the term.
Input url: www.fubar.com/search/work
Becomes: www.fubar.com/works
What is causing this and how do I stop it from pluralizing the tag?
Might be relevant: We need to use /*tag instead of /:tag because we sometimes have a list of tags. I.e. www.fubar.com/work/web/video
Turns out everything worked once I cleared my cache. Browsers remember 301 redirects (and I forgot that and was apparently kept serving a previous broken redirect.
match '/search/*tag' => redirect { |params| "/#{params[:tag]}" }
match '/*tag' => 'search#search'
All I can recommend is stuffing a Rails.logger.debug{ params.inspect } inside the block to redirect. That'd at least give you a start on debugging it. Rails routing can be tricky and brittle when you stray from the happy path, and knowing whether the issue is in the recognition (i.e. that the match for *tags is being pluralized) or in routing (that the result from the redirection is being pluralized) would be the first place to look.

Rails routing: cannot differentiate static and dynamic routes

I'm trying to create custom URLs for my site's users:
www.mysite.com/user1
If I set the routing rule to be this:
match ':id' => "user#view", :as => :user
Then it works great! I can access www.mysite.com/user1 and it will pull up the page for user1.
However, that breaks my about page, which is at www.mysite.com/about . The error I get here is:
"Couldn't find User with id=about"
Any idea how I can fix this problem? Thanks! As an aside - I'm using friendlyid to generate the human readable slugs.
Ringo
Is the about page a static file located at public/about.html? If so, then Rails will match this first and fall back to the router if it can't find the file.
If it's a route instead, then your match :id route should be beneath the one for about, so that about is matched first.

Rails 3 routing and multiple domains

My app allows people to create portfolios. I would like for them to be able to connect their domain to their portfolio.
So somedomain.com would show /portfolio/12, someotherdomain.com would show /portfolio/13 and so on. But I don't want the redirect. I want the user to see somedomain.com in the browser url.
How do I do that?
Ok, I've found this solution:
match "/" => "portfolio#show",
:constraints => { :domain => "somedomain.com" },
:defaults => { :id => '1' }
As I don't have many custom domains, this is fine for now but the question is - how to make this dynamic, to read domain and id data from db?
Ok, let's assume you own yourdomain.com and use it as your home page for your application. And any other domain name like somedomain.net is mapped to a portfolio page.
First of all, in your routes.rb you need to catch yourdomain.com and map it to wherever your home page is, so that it stands out from the rest of the crowd.
root :to => "static#home", :constraints => { :domain => "yourdomain.com" }
Then you need to catch any other root on any domain and forward it to your PortfoliosController
root :to => "portfolios#show"
Keep in mind that this line will only be checked if the previous line fails to match.
Then in your PortfoliosController find the requested portfolio by its domain rather than id.
def show
#portfolio = Portfolio.find_by_domain(request.host)
…
end
Of course you may want to rescue from an ActiveRecord::RecordNotFound exception in case the domain is not in your database, but let's leave that for another discussion.
Hope this helps.
First, you should add a field to the portfolio model to hold the user's domain. Make sure this field is unique. Adding an index to the field in your database would also be wise.
Second, set your root to route to the portfolios#show action, as you already did, but without the constraints.
Then, in the PortfoliosController#show method, do the following check:
if params[:id]
#portfolio = Portfolio.find(params[:id])
else
#portfolio = Portfolio.find_by_domain(request.host)
end
After this, the only thing left to do is to make sure your own domain does not trigger the portfolio#show action. This can be done with the constraint you used before, but now with your own domain. Be sure to put this line in routes.rb above the line for the portfolio#show action, since the priority is based upon order of creation.
The request object seems not to be available to the routes.rb file w/o some patching.
There are some plugins that make it available, but most of them seem to be outdated. This one here request_routing seems to be with the latest commit dates so it would be most up to date. Though I doubt it will work with Rails 3.0 out of the box, it is a start and might not be that hard to port.
Your users can set DNS CNAME redirects so that requests for theirdomain.com land on your_app.com/portfolio/12.

How to redirect (301) when changed routing translation?

I am looking for an easy way to make redirects in my application.
SITUATION:
I have routes like this:
http://myapp.com/authors/5-hemingway/books/1-moby-dick
The routes are translated this way (using gem 'i18n_routing'):
http://myapp.com/acutores/5-hemingway/libros/1-moby-dick
Now, I changed translation of acutores to scriptores. Easy step but I'd like to redirect all routes that contained an old "acutores" resource name to routes with "scriptores" instead.
My guess is, I should play in routes.rb with:
match "/acutores" => redirect("/scriptores")
But how to do it efficiently for all cases where 'acutores' appear? (especially with nested routes)
This redirects /acutores/something to /scriptores/something but fails with plain /acutores:
match "/acutores/*path" => redirect("/scriptores/%{path}")
This seems to handle both:
match "/acutores(/*path)" => redirect {|params| "/scriptores/#{params[:path]}"}
http://guides.rubyonrails.org/routing.html#redirection
http://guides.rubyonrails.org/routing.html#route-globbing
--edit
This will get rid of all the trailing slashes:
match "/acutores(/*path)" => redirect{ |params| "/scriptores/#{params[:path]}".chomp("/") }
I had issues with browser caching redirects, so empty the cache after modifications.

Resources