polymorphic_url and namespaces

So, this has been discussed in great length before, see [1], but to me the solution [2] feels quite incomplete, so I thought I'd bring this up again.

Currently polymorphic_url needs to be passed a namespace explicitely to have it delegate to a namespaced url generation method:

   map.resources :articles
   map.namespace :admin do |admin|
     admin.resources :articles
   end

Now, no matter where link_to(@article) is called it always will delegate to article_path.

polymorphic_url to

- delegate to article_path when we're outside of the namespace but
- delegate to admin_article_path when we're inside of the namespace

It has been argued in [1] that polymorphic_url just does its "best guess" and thus the current behaviour was correct at that time.

But I find it hard to subscribe to that. With resources controllers, views, routes etc. obviously are grouped and conceptually belong together, so the "best guess" for link_to(@article) from within (e.g.) views/admin/show would IMO to delegate to admin_article_path, not article_path. Put differently I think that these url_helpers & co should default to the current resource (including the namespace) we're working with, not an more or less arbitrary resource like it's the current behaviour.

Now, there currently doesn't seem to be way to determine the current namespace in a controller (and thus in polymorphic_url). From looking at routes building/recognition etc. it might not be too hard to pass the :namespace option from the route definition to the route (while building it) and then to the controller instance (after recognition).

Am I missing something obvious?

[1] http://www.ruby-forum.com/topic/111136
[2] http://dev.rubyonrails.org/ticket/8640

From my perspective it would make a lot of sense to have
polymorphic_url to

- delegate to article_path when we're outside of the namespace but
- delegate to admin_article_path when we're inside of the namespace

It has been argued in [1] that polymorphic_url just does its "best
guess" and thus the current behaviour was correct at that time.

It definitely does it's 'best guess', but that's not to say it's best
is good enough. The all blacks did their best in the recent rugby
world cup, the french still kicked their ass :).

Now, there currently doesn't seem to be way to determine the current
namespace in a controller (and thus in polymorphic_url). From looking
at routes building/recognition etc. it might not be too hard to pass
the :namespace option from the route definition to the route (while
building it) and then to the controller instance (after recognition).

Am I missing something obvious?

We could pass the namespace option in the routes, however what about
just regular controllers in modules. Are they in a namespace? Also,
things like render :partial and friends need to be aware of these
namespaces.

Definitely sounds like something that'd be good to clean up and if
you're interested in hacking on it, let us know. Hopefully we'll have
a 2.1 branch out of the way soon and can get to work on the backlog.

It definitely does it's 'best guess', but that's not to say it's best
is good enough.

Sure! I hope my phrasing didn't sound harsh.

The all blacks did their best in the recent rugby
world cup, the french still kicked their ass :).

Hehe. I have no idea what you are talking about ;D

We could pass the namespace option in the routes, however what about
just regular controllers in modules. Are they in a namespace?

When we pass the namespace option to the route we can also pass it back from #recognize and set it to the current controller instance (like params). So the controller would always know if it has been instantiated for a request through a "namespaced" route or not.

Thus the user would have full control: if he maps a route to a controller inside of a module but doesn't use the namespace option in the route definition the controller would end up with no namespace set for that request. If the same controller is also mapped with a namespaced route and that route recognizes a request path the controller would have a namespace set.

When the controller can access the current namespace it can use it in #polymorphic_url to do a "better guess".

Also,
things like render :partial and friends need to be aware of these
namespaces.

By that you mean calls to polymorphic_url from within partials? Definitely. I believe that should work the same way as above.

Definitely sounds like something that'd be good to clean up and if
you're interested in hacking on it, let us know. Hopefully we'll have
a 2.1 branch out of the way soon and can get to work on the backlog.

Ok, fine. I'll have a stab at that.

I guess it makes more sense to discus implementation specific questions on #rails-contrib.

Sven,

+1 for namespace awareness. I’ve been making my own helpers to wrap route calls and inject the current namespace and I can tell it’s a pain (it’s certainly bug-prone). If the framework handled this, it would be much better.

Sven,

+1 for namespace awareness. I've been making my own helpers to wrap route
calls and inject the current namespace and I can tell it's a pain (it's
certainly bug-prone). If the framework handled this, it would be much
better.

Sounds like we have a few motivated contributors here, looking forward
to watching it take shape in irc :slight_smile:

Sounds like we have a few motivated contributors here, looking forward
to watching it take shape in irc :slight_smile:

Ok, I've implemented what I believe is a sane option here:

http://github.com/svenfuchs/rails/commits/master/

There are a couple of things to discuss though and none of you (koz, mislav) is on #rails-contrib right now so I thought I'd ask here instead.

Most importantly, after storing the namespace option in the route and then passing it back from Route#recognize_path where should it be stored in the controller layer?

I first thought that the best option is to set it on the controller instance. After studying the code more thoroughly I now think that another sane option is to just store it in the params hash.

Storing it in the controller instance would require to

- either temporarily store it in the params hash in Route#recognize_path and then delete if from there in Dispatcher#handle_request or even ActionController::Base#process (ugly, but doesn't require to change the involved method apis that pass the params hash)
- or change the return value apis of at least Route#recognize_path and RouteSet#recognize to return something like [params, route] or [params, namespace] (also noisy, but less ugly, also requires to change lots of tests)

Storing it in the params hash would require the fewest changes allover. We'd just stick the namespace to params in Route#recognize_path and can then access that in polymorphic_url (and everywhere else in the controller layer).

(Another option would be to store it in the Request object, but there's no similar information stored so far.)

I've opted for the last option for now, but of course that exposes another key in the params hash. What do you think about that? Looking at the existing keys like :controller and :action the additional key :namespace would IMO make a lot sense here anyways because that's all stuff that's defined in the routes.

Hope to catch any of you on IRC, please ping me :slight_smile: