How to make RC load enclosing resources with custom find_resource?

1 view
Skip to first unread message

Inviz

unread,
Oct 6, 2007, 4:49:52 AM10/6/07
to resources_controller
So I have "establishments" & "places" controllers. I want to use slugs
instead of ids, so it's:

class EstablishmentsController < ApplicationController
resources_controller_for :establishments
def find_resource(id = params[:id])
resource_service.find_by_slug id
end
end

so "establishments/cafes" works just fine. But when i do the same for
Places controller, and try to go to "establishments/cafes/places/
myplace" it loads "Establishment" by id via
"Ardes::ResourcesController::Specification::find_resource". I tried
"find" key but no success still - works on usual resources, not on
enclosing ones.

What can i do to make RC respect the slugs?

Ian White

unread,
Oct 6, 2007, 5:34:23 AM10/6/07
to resources_controller
> So I have "establishments" & "places" controllers. I want to use slugs
> instead of ids, so it's:

> What can i do to make RC respect the slugs?

If your establishments is always at the 'top' level, you should be
able to this:

map_resource :establishment do
Establishment.find_by_slug(params[:establishment_id])
end

If establishments is nested, then do this

map_resource :establishment do

enclosing_resource.establishments.find_by_slug(params[:establishment_id])
end

Or, if either of these might be the case:

map_resource :establishment do
if enclosing_resource

enclosing_resource.establishments.find_by_slug(params[:establishment_id])
else
Establishment.find_by_slug(params[:establishment_id])
end
end

This map_resource should be put in any controller that has
establishments as an enclosing resource - you can repeat it for any
such controller, or put it in ApplicationController (or a mixin that
you include in the relevant controllers)

Let me know if this works for you.

Cheers,
Ian

Ian White

unread,
Oct 6, 2007, 5:52:01 AM10/6/07
to resources_controller
> map_resource :establishment do
> if enclosing_resource
>
> enclosing_resource.establishments.find_by_slug(params[:establishment_id])
> else
> Establishment.find_by_slug(params[:establishment_id])
> end
> end

Or, more succinctly (but maybe more golf like)

map_resource :establishment do
(enclosing_resource ? enclosing_resource.establishments :
Establishment).find_by_slug params[:establishment_id]
end

cheers,
Ian
--
Argument from Design--We build web applications
Western House 239 Western Road Sheffield S10 1LE UK
Mobile: +44 (0)797 4678409 | Office: +44 (0)114 2667712
<http://www.ardes.com/> | <http://blog.ardes.com/>

Inviz

unread,
Oct 6, 2007, 6:29:13 AM10/6/07
to resources_controller
> map_resource :establishment do
> (enclosing_resource ? enclosing_resource.establishments :
> Establishment).find_by_slug params[:establishment_id]
> end

Basically, it works for /establishments/cafes/places/myplace. but it
kills /establishments/cafes/places/. According to SQL Log it tries
to :

SELECT * FROM places WHERE (places.establishment_id = 2) AND
(places.`slug` IS NULL) LIMIT 1

which is obviously wrong.

What can be done in this case?

Sorry if this sounds a bit rude, but why is this stuff so hard to
implement? I mean, i thought this would work as is. Isn't it possible
just to call find_resource of enclosing controller?

Ian White

unread,
Oct 6, 2007, 7:20:00 AM10/6/07
to resources_...@googlegroups.com
> Basically, it works for /establishments/cafes/places/myplace. but it
> kills /establishments/cafes/places/. According to SQL Log it tries
> to :

> SELECT * FROM places WHERE (places.establishment_id = 2) AND
> (places.`slug` IS NULL) LIMIT 1

This suggests that params[:establishment_id] is nil. Can you
investigate whether something in your setup might be causing this?

Or, perhaps you've found a bug - could you send me some more details of
the code, log and stacktrace.

> Sorry if this sounds a bit rude, but why is this stuff so hard to
> implement?

find_by_slug is an edge case for RC, a lot of people find RC pretty
easy to use for the main problem it sets out to solve. But there's
always room for improvement (tested patches always welcome), and I
welcome the issue report.

Cheers,

Inviz

unread,
Oct 6, 2007, 8:59:17 AM10/6/07
to resources_controller
> This suggests that params[:establishment_id] is nil. Can you
> investigate whether something in your setup might be causing this?
Sorry, I am newbie to ruby and rails, but I think you're wrong.
It's :place_id is nil, because i access /establishments/cafes/places
and there's no :place_id in URL and :establishment_id is 2 (id for
entry with "cafes" slug). That's why such SQL is run.

> find_by_slug is an edge case for RC, a lot of people find RC pretty
> easy to use for the main problem it sets out to solve. But there's
> always room for improvement (tested patches always welcome), and I
> welcome the issue report.

I will try to do what I can, although as I said I am not very
experiences with R & RoR

Inviz

unread,
Oct 6, 2007, 9:00:40 AM10/6/07
to resources_controller
What i was trying to say is that it tries to acces "show" instead of
"index"

Inviz

unread,
Oct 6, 2007, 9:30:03 AM10/6/07
to resources_controller
Okay. I think i figured it out. This happened to me with ":find
=> :find_resource" defined in PlacesController. Looks like i forget to
remove it.
Anyways, is there a way to access some method of a class? Or there is
no way to access "find_resource" of EnclosingResourceController
without instantiation of the controller? Will it make any things
slower or something? Again sorry for lame questions, just curious.

Inviz

unread,
Oct 6, 2007, 1:48:08 PM10/6/07
to resources_controller
Uh oh, Sorry for overposting here today.
I came to what I call DRY Dirt.

I changed my "find_resource" definition in "resources_controller.rb"
to this:

def find_resource(id = params[:id])

resource_service.send resource_service.from_param || :find, id
end

and "find_resource" definition in "specification.rb" to this:

def find_resource
(enclosing_resource || klass).send(
(enclosing_resource || klass).from_param || :find,
controller.params[key]
)
end


OK, Not very cute, but atleast now I can do this in model:

def to_param
slug
end

def self.from_param
:find_by_slug
end

and it works just fine!
Hope you find a better way to do this, but if there're anyone who
wants slug their RC controllers, use these two hacks :)

Matt Mower

unread,
Oct 11, 2007, 6:54:01 AM10/11/07
to resources_controller
Hi Ian,

Just come to RC today, looks very useful:

On Oct 6, 10:34 am, Ian White <ian.w.wh...@gmail.com> wrote:
> > So I have "establishments" & "places" controllers. I want to use slugs
> > instead of ids, so it's:
> > What can i do to make RC respect the slugs?
>

I am trying to do this as well. I have

map.resources :users do |users|
users.resources :contents
end

I use User#to_param to return the login name so that my urls look
like:

/users/matt/contents/<id>

In ApplicationController I have:

map_resource :user do
User.find_by_login( params[:id] )
end

However I still get:

Couldn't find User with ID=matt

I went back and added a puts into that map_resources block but it
doesn't get output suggesting that something is going wrong there.

The complication is that I am using Rails 2.0PR. From the notes about
using version <410 with 1.2.3 I guess RC should be edge compatible but
maybe that was a bad assumption on my part?

Regards,

Matt

Matt Mower

unread,
Oct 11, 2007, 7:16:37 AM10/11/07
to resources_controller

On Oct 11, 11:54 am, Matt Mower <matt.mo...@gmail.com> wrote:
> map_resource :user do
> User.find_by_login( params[:id] )
> end
>

It's a wild stab in the dark but I am guessing this relates to
resources_controller.rb:614

return if to_spec && to_spec.segment == segment

which, according to my debugging, is true (since the segment inferred
from map_resouce is 'users') so that the custom
resource_specification_map entry is never referenced or load_into
called.

Regards,

Matt

Ian White

unread,
Oct 11, 2007, 1:59:17 PM10/11/07
to resources_...@googlegroups.com
Hi Matt,

Welcome to the RC group!

> map.resources :users do |users|
> users.resources :contents
> end

What you want should be possible - but maybe you've found a bug. SO
I'll spell out what's needed, so that you can help me verify if it is
a bug.

> map_resource :user do
> User.find_by_login( params[:id] )
> end

This looks right to me - it specifies the :user enclosing_resource, so
do this in the contents controller (or app)

The UsersController however needs something else - you'll need to
either do this:

resources_controller_for :users do


User.find_by_login( params[:id] )
end

or this:

def find_resource(id = params[:id])

resource_service.find_by_login(id)
end

Let me know how this goes.

Ian White

unread,
Oct 11, 2007, 2:00:53 PM10/11/07
to resources_...@googlegroups.com
Hi Matt,


> which, according to my debugging, is true (since the segment inferred
> from map_resouce is 'users') so that the custom
> resource_specification_map entry is never referenced or load_into
> called.

You;re right - but that's intentional (at the moment) - map resource
is for enclosing resources only.

But, maybe I should make the map_resource specifications available to
the main resource as well.

Matt Mower

unread,
Oct 11, 2007, 2:41:50 PM10/11/07
to resources_controller
Hi Ian.

On Oct 11, 7:00 pm, "Ian White" <ian.w.wh...@gmail.com> wrote:
> > which, according to my debugging, is true (since the segment inferred
> > from map_resouce is 'users') so that the custom
> > resource_specification_map entry is never referenced or load_into
> > called.
>

Just back at home so I'm addressing your posts in the inverse order
(since this one is easier to grok):

> You;re right - but that's intentional (at the moment) - map resource
> is for enclosing resources only.
>

Maybe it's a terminology thing but I would say user *is* the enclosing
resource, i.e.

users have content

user: enclosing
content: enclosed

Am I misunderstanding?

> But, maybe I should make the map_resource specifications available to
> the main resource as well.
>

Regards,

Matt

Ian White

unread,
Oct 11, 2007, 4:46:21 PM10/11/07
to resources_...@googlegroups.com
> Maybe it's a terminology thing but I would say user *is* the enclosing
> resource, i.e.
It is when we're talking about the content controller, but not when
we're talking about the user controller
(so I think we're speaking the same language here)

My other post was directed at trying to figure out whether you were
getting these problems in the user, or content controller.

So, let me know if the other post makes sense, and solves your probs or not.

Matt Mower

unread,
Oct 12, 2007, 4:33:10 AM10/12/07
to resources_controller
Hi Ian.

On Oct 11, 6:59 pm, "Ian White" <ian.w.wh...@gmail.com> wrote:
> The UsersController however needs something else - you'll need to
> either do this:
>
> resources_controller_for :users do
> User.find_by_login( params[:id] )
> end
>

I tried this.

> def find_resource(id = params[:id])
> resource_service.find_by_login(id)
> end
>

And then this. I still get the "Couldn't find User with ID=matt" error
when accessing:

/users/matt/contents

Regards,

Matt

p.s. Do you use IM or IRC?

Matt Mower

unread,
Oct 12, 2007, 5:57:23 AM10/12/07
to resources_controller

On Oct 12, 9:33 am, Matt Mower <matt.mo...@gmail.com> wrote:
> > resources_controller_for :users do
> > User.find_by_login( params[:id] )
> > end
>
> I tried this.

I also tried putting a debugging puts in that block and, again, there
was no output so it appears that this block never gets executed
either.

Regards,

Matt.

Ian White

unread,
Oct 12, 2007, 6:02:25 AM10/12/07
to resources_...@googlegroups.com
Hi Matt,

Since this case has come up a couple of times, I'm adding it to the
spec suite. Then we can make sure we're on the same page. Will post
when that's done.

Cheers,
Ian

Ian White

unread,
Oct 12, 2007, 7:22:30 AM10/12/07
to resources_controller
Ok, so I speced out this behaviour and indeed found a bug. thanks for
the report!

(from the CHANGELOG)

* added specs for when you want to find_by_(something other than id)
(users,
addresses, interests)

Fixed a bug where the resource mapping was using name instead of
segment to
match when a map should be used (this meant mapping didn't work for
non
singleton resources)

Thanks to Inviz <invi...@gmail.com> and Matt Mower
<matt.mo...@gmail.com>
in http://groups.google.com/group/resources_controller/browse_thread/thread/b71b2ce196a09d15
for the bug reports


>From the docs - (which will update online within the hour)

using non standard ids

Lets say you want to set to_param to login, and use find_by_login for
your users in your URLs, with routes as follows:

map.reosurces :users do |user|
user.resources :addresses
end

First, the users controller needs to find reosurces using
find_by_login

class UsersController < ApplicationController
resources_controller_for :users

protected


def find_resource(id = params[:id])
resource_service.find_by_login(id)
end

end

This controller will find users (for editing, showing, and destroying)
as directed. (this controller will work for any route where user is
the last resource, including the /users/dave route)

Now you need to specify that the user as enclosing resource needs to
be found with find_by_login. For the addresses case above, you would
do this:

class AddressesController < ApplicationController
resources_controller_for :addresses
nested_in :user do
User.find_by_login(params[:user_id])
end
end

If you wanted to open up more nested resources under user, you could
repeat this specification in all such controllers, alternatively, you
could map the resource in the ApplicationController, which would be
usable by any controller

If you know that user is never nested (i.e. /users/dave/addresses),
then do this:

class ApplicationController < ActionController::Base
map_resource :user do
User.find(params[:user_id])
end
end

or, if user is sometimes nested (i.e. /forums/1/users/dave/addresses),
do this:

map_resource :user do
((enclosing_resource && enclosing_resource.users) ||
User).find(params[:user_id])
end

Your Addresses controller will now be the very simple one, and the
resource map will load user as specified when it is hit by a route /
users/dave/addresses.

class AddressesController < ApplicationController
resources_controller_for :addresses
end


Reply all
Reply to author
Forward
0 new messages