Route generation and clash prevention

Let’s say you’re building a movie rental website. You have customers,
movies, and rentals to manage. REST style urls might look like this:

/customers/4
/movies/15
/rentals

You’d probably also want administrators to be able to view all of the
rentals done by a particular customer, and other similar details:

/customers/4/rentals
/movies/15/rentals
/rentals/529/customers

Except you can’t, at least not as far as I understand route generation.
Looking at routes.rb for the example:

map.resources :movies do |movies|
movies.resources :rentals
end

map.resources :rentals do | rentals|
rentals.resources :customers
end

map.resources :customers do |customers|
customers.resources :rentals
end

These map blocks introduce ambiguity about what rental_url(1,5) should
generate - /movies/1/rentals/5 or /customers/1/movies/5. The error
message pointing this out sucks (rentals_url failed to generate from
{:controller=>“rentals”, :action=>“index”}, expected:
{:controller=>“rentals”, :action=>“index”}, diff: {}), but I digress.

Instead of throwing an error in this kind of situation (which I imagine
would be relatively common), why not take a named route like
customer_rentals_url(1,5) or new_movie_rental_url(1) when there is
ambiguity?

There may be a very good reason why not, and I’m new here, so bear with
me. :slight_smile:

Hi –

On Sun, 17 Dec 2006, D. Scott B. wrote:

map.resources :rentals do | rentals|
rentals.resources :customers
end

map.resources :customers do |customers|
customers.resources :rentals
end

Do you really want both rentals/customers and customers/rentals?

These map blocks introduce ambiguity about what rental_url(1,5) should
generate - /movies/1/rentals/5 or /customers/1/movies/5. The error
message pointing this out sucks (rentals_url failed to generate from
{:controller=>“rentals”, :action=>“index”}, expected:
{:controller=>“rentals”, :action=>“index”}, diff: {}), but I digress.

Yes, that error message is a source of much frustration as well as
amusement :slight_smile:

Instead of throwing an error in this kind of situation (which I imagine
would be relatively common), why not take a named route like
customer_rentals_url(1,5) or new_movie_rental_url(1) when there is
ambiguity?

There may be a very good reason why not, and I’m new here, so bear with
me. :slight_smile:

I’m a little uncertain what you want the end result to be (a couple of
things in the examples aren’t clear to me), but it sounds like you
could make use of name prefixes – for example:

map.resources :rentals do |rentals|
rentals.resources :movies, :name_prefix => “movies_”
rentals.resources :customers, :name_prefix => “customers_”
end

which (if I’ve got [untested] syntax right) should give you
rentals_movies_url, rentals_customers_url, and so forth. To do it the
other way around, you would do:

map.resources :movies do |movies|
movies.resources :rentals, :name_prefix => “movies_”
end

and the same for :customers, to get movies_rentals and
customers_rentals.

David


Q. What’s a good holiday present for the serious Rails developer?
A. RUBY FOR RAILS by David A. Black (Ruby for Rails)
aka The Ruby book for Rails developers!
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)

I’m a little uncertain what you want the end result to be (a couple of
things in the examples aren’t clear to me), but it sounds like you
could make use of name prefixes – for example:

map.resources :rentals do |rentals|
rentals.resources :movies, :name_prefix => “movies_”
rentals.resources :customers, :name_prefix => “customers_”
end

which (if I’ve got [untested] syntax right) should give you
rentals_movies_url, rentals_customers_url, and so forth. To do it the
other way around, you would do:

map.resources :movies do |movies|
movies.resources :rentals, :name_prefix => “movies_”
end

and the same for :customers, to get movies_rentals and
customers_rentals.

Eureka, that did the trick!

Thanks David, you rock. The only catch is that the prefix is truly a
prefix - you must do movie_new_rental_path instead of the catchier
new_movie_rental_path.