:path_prefix on resources not passing to actions?

I am experiencing some difficulties using :path_prefix on resources.

I do the following
$ rails pathtest
$ cd pathtest
$ script/generate scaffold thing title:string
$ rake db:migrate

Then in routes.rb I change:
  map.resources :things
to
  map.resources :things, :path_prefix => ':blah'

And doing a rake routes I get the following:
              things GET /:blah/things
{:action=>"index", :controller=>"things"}
    formatted_things GET /:blah/things.:format
{:action=>"index", :controller=>"things"}
                     POST /:blah/things
{:action=>"create", :controller=>"things"}
                     POST /:blah/things.:format
{:action=>"create", :controller=>"things"}
           new_thing GET /:blah/things/new
{:action=>"new", :controller=>"things"}
formatted_new_thing GET /:blah/things/new.:format
{:action=>"new", :controller=>"things"}
          edit_thing GET /:blah/things/:id/edit
{:action=>"edit", :controller=>"things"}
formatted_edit_thing GET /:blah/things/:id/edit.:format
{:action=>"edit", :controller=>"things"}
               thing GET /:blah/things/:id
{:action=>"show", :controller=>"things"}
     formatted_thing GET /:blah/things/:id.:format
{:action=>"show", :controller=>"things"}
                     PUT /:blah/things/:id
{:action=>"update", :controller=>"things"}
                     PUT /:blah/things/:id.:format
{:action=>"update", :controller=>"things"}
                     DELETE /:blah/things/:id
{:action=>"destroy", :controller=>"things"}
                     DELETE /:blah/things/:id.:format
{:action=>"destroy", :controller=>"things"}
                            /:controller/:action/:id
                            /:controller/:action/:id.:format

But...
got to localhost:3000/my/things works fine. I create a new thing.
Then I get this error:
thing_url failed to generate from {:action=>"show", :blah=>#<Thing id:
1, title: "test", created_at: "2008-07-28 07:55:27", updated_at:
"2008-07-28 07:55:27">, :controller=>"things"}, expected:
{:action=>"show", :controller=>"things"}, diff: {:blah=>#<Thing id: 1,
title: "test", created_at: "2008-07-28 07:55:27", updated_at:
"2008-07-28 07:55:27">}

And I can no longer go to /my/things because of that error.
It seems that when it is trying to create
(eval):17:in `thing_path'

the :blah value is not being set.
I have to edit all my views to do this:

    <td><%= link_to 'Show', thing_path(params[:blah], thing) %></td>
instead of
    <td><%= link_to 'Show', thing %></td>

I think this is a bug in rails... or am I misunderstanding how this
should work?

Hi --

I am experiencing some difficulties using :path_prefix on resources.

I do the following
$ rails pathtest
$ cd pathtest
$ script/generate scaffold thing title:string
$ rake db:migrate

Then in routes.rb I change:
map.resources :things
to
map.resources :things, :path_prefix => ':blah'

And doing a rake routes I get the following:

[snip]

But...
got to localhost:3000/my/things works fine. I create a new thing.
Then I get this error:
thing_url failed to generate from {:action=>"show", :blah=>#<Thing id:
1, title: "test", created_at: "2008-07-28 07:55:27", updated_at:
"2008-07-28 07:55:27">, :controller=>"things"}, expected:
{:action=>"show", :controller=>"things"}, diff: {:blah=>#<Thing id: 1,
title: "test", created_at: "2008-07-28 07:55:27", updated_at:
"2008-07-28 07:55:27">}

And I can no longer go to /my/things because of that error.
It seems that when it is trying to create
(eval):17:in `thing_path'

the :blah value is not being set.
I have to edit all my views to do this:

   <td><%= link_to 'Show', thing_path(params[:blah], thing) %></td>
instead of
   <td><%= link_to 'Show', thing %></td>

I think this is a bug in rails... or am I misunderstanding how this
should work?

You're misunderstanding how it should work. It's certainly not a bug
in Rails; the routing system may have bugs, but not at this basic
level.

The problem, as it seems to be so often these days, is that you're
using the scaffolding and then being surprised when it turns out you
actually have to write code. The scaffolding is just a demo.

I don't mean to pounce on you about this. There's a lot of scaffolding
around these days, it seems, and therefore a lot of questions about
how to solve the problems it creates. The best thing to do is to
forget the scaffolding and study the routing system, and the other
systems in Rails, directly.

Anyway -- if you've got a resource route that looks like this:

   /:blah/things/:id

then with the GET request method it will go to the show action. But it
needs some way to fill in everything in the route string. It tries to
do this from the arguments to the named route. The first thing it does
is fill in the :blah field. If the only argument to the named route is
a Thing object, then it assigns that object to :blah -- and then it's
run out of arguments, so it can't figure out what :id is supposed to
be.

(The error messages on this stuff are a little hard to decipher, since
they might lead you to believe that it doesn't want a :blah value. But
it does; it just doesn't have enough to do the whole path.)

That's why you need two arguments:

   thing_path("my", @thing)

or equivalent.

David

Ok...fair enough. I guess I didn't expect when I added a path_prefix
to have to change every link in my code!

I guess I am surprised because it does pass through the :blah value to
certain links, but not to the resource links. For instance on the
(yes, scaffold generated!) pages the links to /things will
automatically be updated to have my/things. It is this inconsistent
behaviour that I am questioning - the fact that the plurals calls DO
update and the singular calls don't.

For example:
<%= link_to 'Back', things_path %>

On the page /my/things/new
Will generate a link like this:
http://localhost:3000/my/things

and indeed the form action will be:
<form action="/my/things" class="new_thing" id="new_thing"
method="post">

So the problem seems to be a) in singular routes and b) with
path_prefixes with variables because if your routes looks like:
  map.resources :things, :path_prefix => "my"

Then everything works a treat.

Hi --

Hi --

I am experiencing some difficulties using :path_prefix on resources.

I do the following
$ rails pathtest
$ cd pathtest
$ script/generate scaffold thing title:string
$ rake db:migrate

Then in routes.rb I change:
�map.resources :things
to
�map.resources :things, :path_prefix => ':blah'

And doing a rake routes I get the following:

[snip]

But...
got to localhost:3000/my/things works fine. I create a new thing.
Then I get this error:
thing_url failed to generate from {:action=>"show", :blah=>#<Thing id:
1, title: "test", created_at: "2008-07-28 07:55:27", updated_at:
"2008-07-28 07:55:27">, :controller=>"things"}, expected:
{:action=>"show", :controller=>"things"}, diff: {:blah=>#<Thing id: 1,
title: "test", created_at: "2008-07-28 07:55:27", updated_at:
"2008-07-28 07:55:27">}

And I can no longer go to /my/things because of that error.
It seems that when it is trying to create
(eval):17:in `thing_path'

the :blah value is not being set.
I have to edit all my views to do this:

� �<td><%= link_to 'Show', thing_path(params[:blah], thing) %></td>
instead of
� �<td><%= link_to 'Show', thing %></td>

I think this is a bug in rails... or am I misunderstanding how this
should work?

You're misunderstanding how it should work. It's certainly not a bug
in Rails; the routing system may have bugs, but not at this basic
level.

The problem, as it seems to be so often these days, is that you're
using the scaffolding and then being surprised when it turns out you
actually have to write code. The scaffolding is just a demo.

I don't mean to pounce on you about this. There's a lot of scaffolding
around these days, it seems, and therefore a lot of questions about
how to solve the problems it creates. The best thing to do is to
forget the scaffolding and study the routing system, and the other
systems in Rails, directly.

Anyway -- if you've got a resource route that looks like this:

� �/:blah/things/:id

then with the GET request method it will go to the show action. But it
needs some way to fill in everything in the route string. It tries to
do this from the arguments to the named route. The first thing it does
is fill in the :blah field. If the only argument to the named route is
a Thing object, then it assigns that object to :blah -- and then it's
run out of arguments, so it can't figure out what :id is supposed to
be.

(The error messages on this stuff are a little hard to decipher, since
they might lead you to believe that it doesn't want a :blah value. But
it does; it just doesn't have enough to do the whole path.)

That's why you need two arguments:

� �thing_path("my", @thing)

or equivalent.

Ok...fair enough. I guess I didn't expect when I added a path_prefix
to have to change every link in my code!

I guess I am surprised because it does pass through the :blah value to
certain links, but not to the resource links. For instance on the
(yes, scaffold generated!) pages the links to /things will
automatically be updated to have my/things. It is this inconsistent
behaviour that I am questioning - the fact that the plurals calls DO
update and the singular calls don't.

For example:
<%= link_to 'Back', things_path %>

On the page /my/things/new
Will generate a link like this:
http://localhost:3000/my/things

and indeed the form action will be:
<form action="/my/things" class="new_thing" id="new_thing"
method="post">

So the problem seems to be a) in singular routes and b) with
path_prefixes with variables because if your routes looks like:
map.resources :things, :path_prefix => "my"

Then everything works a treat.

The behavior isn't inconsistent with the documented rules of the
routing system. The plural one -- the "Back" link that shows
"/my/things" -- uses the method things_path. That method also requires
that doesn't work with the other ones is because of the following
rule: Given a named route like this:

    something_path ':x/:y/:z'

x, y, and z will default to whatever they were on the last response,
*unless* you override any one of them. If you override any of them,
then that one *and all to the right of it* will not default.

things_path doesn't override anything, so it takes the :blah value
from the last response ("my"). However, thing_path(@thing) (or
equivalent) *does* override the :blah value, since it assumes that its
first argument is meant to populate the :blah field. So all bets are
off.

By the same token, if you do this in the index view:

   <td><%= link_to 'Show', thing_path(:id => thing) %></td>

you'll find that :blah defaults to "my" (or whatever it was coming
in). That's because you're explicitly using thing for the :id field
(which, by the way, it will populate by calling the method to_params
on thing). Since the :id field is to the right of the :blah field in
the route string (":blah/things/:id"), it fills in the :blah field
with the existing value.

As I said, the best thing is to actually study the routing system :slight_smile:
It all really does make sense, and at some point you're going to need
to know how it thinks in order both to solve problems and to get the
most out of it.

David

Uh, I don't think he was being condescending. I think he was being
really helpful. I'm not sure why you're being so defensive about
someone answering your rather entry level question.

It's entirely likely it is undocumented. But other people apparently
understand how it works, so it's apparently not so obtuse that it
can't be understood. Perhaps reading the code wouldn't cause you too
much pain? People do add inline comments sometimes. Or maybe reading
a book? According to O'Reilly Safari, it's documented in no less than
three books, including David's eBook on routing (of course, reading
that would be one protracted condescension, though, right?). And
those are just the ones that I could find by doing a simple search.

Even so, if you're "familiar with routing," path_prefix works EXACTLY
like any other wildcard value. I find it VERY hard to believe that
you're "familiar" routing and don't get that wildcard values require
value in route generation. If you had something like:

    map.thing ":a/:b/:c"

You couldn't expect to do "thing_path" and have it generate a URL.

Next time you decide to be a prick, please do it someone else other
than someone who's helping you (and has spent a TON of time helping
others).

--Jeremy

Hi --

Ok - you don't HAVE to be condescending you know!

I am familiar with routing, it is this particular area (path_prefix) I
find to be completely undocumented as to how it works (I sense another
condescending post from you telling me its not). I appreciate that you
are trying to post a lot to look knowledgeable so that people can pay
a lot of money to have you read Agile Web Development With Rails to
them.

Please don't publicly attack my classroom skills, professionalism, or
ethics, all of which are demonstrably quite sound.

I answer questions on mailing lists and other forums principally for
two reasons: to help people (and I was doing it regularly, fifteen
years before I started making my living from training and consulting),
and to educate myself. I've learned a ton by tracking down answers and
puzzling things out away from the list.

Just so you know: in order to help you with your question, I ran the
commands you ran (creating the 'pathtest' Rails app and the
scaffolding), migrated the database, opened the console and created a
record, started the server, checked to see that I was getting the same
results as you were, and went back in the console and did things like
this for a while:

app.thing_path(:blah => "abc")
app.thing_path("abc")
app.thing_path("abc", "def")
app.thing_path("my", Thing.find(:first))

to make sure I understood what was going on and what was not working
and why. Then I made manual changes to the scaffold views, so that I
could verify that what I expected to happen would actually happen, and
make sure that the advice and explanations I was planning to give you
would be correct.

Then I spent about 15 minutes composing an answer to you.

None of this guarantees that my response will have helped you (though
if you think these things are under-documented, I would have thought
that getting an explanation would in fact have been helpful). But
believe me, if I were going to condescend to you, I wouldn't set aside
a large chunk of my morning to do it.

Now let's get back to technical matters.

David

David,

I really admire your professionalism. That was quite an underhanded
character snipe but you handled it gracefully.

phil,

Shame on you. David is one of the nicest and most knowledgeable people
you will meet in the Ruby community. I think he was being helpful
above and beyond the call without even a hint of condescension, taking
his time to give you a careful and complete answer. How dare you
attack his professional ethics! Not only are you potentially liable
for slander for what you said, it's just plain childish and hateful.
You're lucky that David is a nice guy or you might very well have
gotten sued.

I hope that everyone who comes here to ask for help (which is
everyone) will keep in mind that the people that help you are doing so
for your benefit, and that how you treat them will determine the
amount of help you (and everyone else) will receive from them in the
future. There is never, ever any call for this sort of behavior.

I hope that everyone who comes here to offer help will keep in mind
that most people are not as rude and ungrateful as phil. We do
appreciate your help!

Remember: We Are Nice Because Matz Is Nice. Be nice!

Best,

Rein Henrichs
Hashrocket
reinh.com

You're both right, I apologize.
I personally don't think David's email was that helpful - or that the
question was 'entry level'. But c'est la vie. I should not have
reacted so, and you are right to call me on it.

FWIW, I have made an interesting change to the way polymorphic_path
works that actually lets rails behave as I thought it should (by
passing those path_prefix's down to the singular resources below).
I've seen a few postings on here and other places about people
suffering the same problems as I.

So - it all seems to have worked out in the end.

Again, sorry for the bruised ego's.