Routes in Engine mounted on subdomain do not inherit the constraints - ruby-on-rails

Setup
Our current Rails app is made out of sub-apps that are mounted as engines. Typically these engines are mounted on a subdomain in the main routes.rb file as follows
mount MySubApp::Engine => '/', as: :sub_app, constraints: {subdomain: 'sub_app'}
The Problem
Routes within MySubApp's routes.rb file do not get the subdomain when using the named _url helpers. For example the following in apps/my_sub_app/config/routes.rb
MySubApp::Engine.routes.draw do
resources :foos
end
gives us sub_app.foo_url(5) but it results in
http://www.example.com/foos/5
when we want
http://sub_app.example.com/foos/5
tl;dr
How can I get the engine's mounting constraints passed to its named routes?
EDIT: A Workaround
While I'd still prefer a better solution, the following will work. You can wrap all the routes in each of the sub apps routes.rb files that could be mounted on a subdomain like so
MySubApp::Engine.routes.draw do
constraints Rails.application.routes.named_routes[:sub_app].constraints do
resources :foos
end
end
EDIT 2: A much less desirable workaround
A commenter (since deleted?) pointed out you can pass a subdomain option to the helpers but we'd like to avoid having to use sub_app.foo_url(5, {subdomain: 'sub_app'}) for every cross subdomain link. Even if we moved the subdomain name into an ENV var and made a wrapper, this is not DRY.

check out the guide it says you can do it by
namespace :Engine do
resources :controller, :methods
end
the Engine is just name spacing your code

#Aaron not sure if you ever got this fixed, but look into the
config.action_dispatch.tld_length
setting (on the engine's config). I'm not sure how it'll react with engines, but in our case it lets us handle the case of sub-subdomains for our staging server (so when we use the _url helpers with the staging server it correctly does subdomain.staging.domain.com, rather than subdomain.domain.com).

Related

Rails - Add a route prefix to a specific directory

I have a messy rails 3 application that's come from a different developer and I need to refactor it.
What I want to do is move the contents of "app" into a subfolder called "classic".
app/classic
And then have all URL's with a classic prefix such as
localhost:3000/classic/wills/new
Route to controllers inside of the "app/classic" folder.
And then every regular url that does not contain the classic prefix - route in the standard way to app/
Is it possible to do this? The only thing I've discovered so far is that I can add a scope inside of my routes file.
scope(:path => '/classic')
But all that does is require a prefix for every URL. I'm really not sure how to go about this!
This is a route namespace. Take a look at this section in Rails Routing from the Outside In: http://guides.rubyonrails.org/routing.html#controller-namespaces-and-routing
namespace :classic do
# your routes here
end
This will do 3 things -
the path to the controller files need to be under /app/controllers/classic/
the name of the controllers need to change to Classic::ControllerName
the url is now /classic/controller/action
This sounds like what you want, but you can modify this to get just the parts you want if you don't want all 3.
In route.rb file:
#Of course, you have to configure the right http method.
get 'wills/new' => 'wills#new', as: 'to_classic_wills_new'
Hope this helps!

Insert routes programmatically in Rails

I'm creating an engine that needs to insert some routes into the application's router. For this particular gem, I'd rather not application's routes.rb if possible. Is there a way to insert routes at a particular location in the router via code? I'm looking for an API that does something like:
Rails.application.routes.insert("resources :foos", :before => "some string in routes.rb")
If I create a config/routes.rb inside the engine and define some routes, this kind of works. Rails is smart enough to mix the engine's routes into the application's routes, but it tacks them on at the end of the route list. I need them to appear at the beginning so the engine's routes take priority.
I'm aware that I can namespace the routes by mounting the engine in the application's routes.rb, but this creates a routing structure that I don't really want. I want the engine's routes to look they are a part of the application by defining some routes in the actual application.
I have a workaround which is to add the following to the application's routes.rb.
Rails.application.routes.draw do
MyEngine.setup_routes(self)
#...other routes below
end
MyEngine.setup_routes looks like
def self.setup_routes(map)
map.get 'a_path', :to => 'a_controller#a_path'
end
This at least allows me to control the point where the routes get defined in the application's route list, but the user still has to manually update his routes.rb (or I have to build an installer that does it). It seems like there should be a way to tell rails to tack some routes onto the start of the route list...

How to run copy of the app running on '/' on '/someurl' as well?

Let's say I have a site like Digg running on bookmarks.com and want to create bookmarks.com/girls which shares all the routes, but all the links point to /girls/<something> instead of just /<something>.
Without changing all the links in views, I would like to modify routes.rb to do something like this:
all_routes = lambda {
...
}
scope '/girls', &all_routes, constraint: { |r| r.session[:subsite] == girls }
scope '/', &all_routes
However I am stuck with trial and error and haven't been able to come up with a solution.
Bonus points: how would I route this, if I had dynamic multiple sub-sites?
I think you can achieve this by subclassing your Rails app, and then mounting it using Rack.mount in your routes
mount AnotherRailsApp, :at => "/something"
Alternatively, you could use a rackup file, so in config.ru:
map "/" do
run RailsApp::Application
end
map "/something" do
run AnotherRailsApp::Application
end
However, with either approach, I'm not sure Rails' helper methods for URLs will be able to generate valid URLs when mounted at "/something", and you're probably going to get some interesting issues with your data too, unless you have one db per app.

Namespace in rails routing

First, why do we need to namespace controllers?
The example on rails guides shows
namespace :admin do
resources :post, :comments
end
In this case, we have paths such as GET /admin/posts. Is it identical to GET /posts? Or is GET /posts kept as original while another GET /admin/posts is added as extra?
Does rails create any other stuff for namespace?
You dont have to use namespaces if you dont want to, but it's there to make your life easier, specially in big applications with a lot of controllers. And no, routes arent duplicated if you namespace them, unless you specify the route again in your route file without the namespace, but that doesnt make much sense.

How to serve a controller only on a specific subdomain? -- Rails 3.1

I have a controller Partners that I want to server off of http://partners.example.com
Is there something built into Rails 3.1 that I can use for this without messing with httpd.conf or creating another virtualhost?
What would the routes.rb entry look like for this?
Any help would be greatly appreciated.
PS. I'm not doing wildcard subdomains here -- just that one single subdomain.
UPDATE: I've gotten the subdomain to work with Ahmish's solution below, but now all other routes also respond on that subdomain. Is there a way I can have the subdomain respond exclusively to the controller specified?
Indeed you can.
If the PartnersController is a REST based controller, it is as simple as adding a constraints option to the call to resources.
resources :partners, :constraints => { :subdomain => "partners" }
Alternatively, constraints can be called with a block, and all routing calls inside the block will be subject to the set subdomain constraint.
constraints :subdomain => "partners" do
resources :partners
...
end
Requests to the constrained URLs without the correct subdomain will throw a RoutingError.
You should be able to accomplish this with a routes.rb update. Check out request-based constraints in the rails routing guide.

Resources