Specify Rails Controller Action Based on Domain - ruby-on-rails

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

Related

Rails Routing by mapping subdomain to path via database

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).

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.

Remove controller_name from URL in Rails 3 and use custom string instead of id

Since the beginning I always hat this one problem with rails, short urls without the controller name.
For example, I have a blog and I don't want any dates or controller names in the url, I already have a Page model with a unique field url in my database. Rails works great with such urls:
jeena.net/pages/1
And when I modify the model I even can get it to use
jeena.net/pages/foo
But it seems not to matter what I do I can not get it to work with just:
jeena.net/foo
Of course I want the index page still to work with
jeena.net/pages
And I want creating new pages and updating old pages to work too in some was as well as the link_to()-method. All suggestions are appreciated.
To define that route, try adding the following to your routes.rb:
match '/:id' => 'your_controller#your_action'
This will pretty much match everything to the id of your model. And that's not very nice... You don't want to route youe_host/pages to the pages controller, with an id equal to 'pages'... To prevent that from happening, make sure to put that line on the end of the routes.rb file. The router uses the first route that matches the path received, so putting that line on the end of it will make sure that it will only match your route after it ran out of other meaningful options.
A better practice would be to pass regexp constraints to the router, so that it will only match ids with a specific format, like that:
match '/:id' => 'your_controller#your_action', :constraints => { :id => /your_regexp/ }
Refer to the guides if you have doubts about the rails rounting system. It is pretty well written and covers lots of important things.
Rails rounting - Official Guides
edit: to create a named route, one that you can call in your controllers and override the normal routes that you are probably creating with resource, you have to provide the :as => parameter in your routes.rb
match '/:id' => 'your_controller#your_action', :as => some_name
Then you'll be able to call it in your controller/views like this:
link_to some_name_path(#my_string_id)
Hope this helps. And take a time to read the guides, it has really lots of useful info, including more details about creating named routes.

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