Nesting and make_resourceful
One of the many cool things about make_resourceful is that all the accessors and helper methods that make up the default actions and are used by the user are so concise. Most of them are one line. The rest are more, but just because they check properties of the controller to see how they should behave. If you implemented them for each controller individually, they’d also be one line.
Except for one: parent_objects.
def parent_objects return [] if parents.empty? return @parent_objects if @parent_objects first = parent_models[0].find(parent_params[0]) @parent_objects = [first] parent_params.zip(parents)[1..-1].inject(first) do |object, arr| id, name = arr @parent_objects << object.send(name.pluralize).find(id) @parent_objects[-1] end @parent_objects end
This is the method that goes through each of the parent models for the current model,
looking up that parent’s object,
and ensuring that it’s properly nested beneath the previous parent’s object.
For /grandparent/457/parent/12/child/90/grandchild/12,
it looks up GrandParent.find(457).parents.find(12).children.find(90).grandchildren.find(12),
assigning all the proper variables along the way.
The problem is that it seems to be pretty much as simple as it’s going to get. We can’t even move much of it into another method, because it relies so heavily on instance variables.
However, a while back it was pointed out on the make_resourceful Google Group that deeply nested resources aren’t actually very useful, and what’s more are considered bad style.
Also, assuming that there’s going to be one consistent deep hierarchy of parents actually limits make_resourceful’s flexibility. You have to struggle to get both /posts/16/comments and /comments/14 to work. Polymorphic nesting – using both /posts/16/comments and /tumbles/189/comments - is a royal pain.
For a long time, there was a patch floating around to fix this. Jonathan Linowes came up with a set of modifications that would break normal deep nesting but allow polymorphic nesting to work.
As of revision 161, this patch has been merged with make_resourceful trunk… in a sense. Actually, none of Jonathan’s code ended up in m_r, but all its ideas and capabilities are there.
One big difference between the patch and the polymorphism support in make_resourceful
is that while Jonathan tried to maintain the normal m_r parent API,
we’ve felt no such need.
All the old parent methods – parents, parent_objects, etc. have all been removed.
In their place are methods that assume there’s only one parent at a given time -
parent_name, parent_object, and so forth.
The nesting of a controller is declared in the same way it always was:
using the belong_to declaration.
However, the semantics are different than they used to be.
Instead of declaring a hierarchy of nesting,
it declares a list of parent models that are each valid.
For example:
class BeansController make_resourceful do actions :all belongs_to :can, :burrito end end
Then the routes /cans/15/beans, /burritos/22/beans, and /beans/76 will all work. You also get a bunch of handy accessors. What they return depends on what parameters are available.
If the current path is /cans/15/beans, params[:can_id] is defined,
which indicates to make_resourceful that “can” is the current parent.
If it’s /burritos/22/beans, params[:burrito_id] is defined,
which means that the parent is “burrito.”
If it’s just /beans/76, neither parameter is there,
so m_r knows there’s no parent.
The first new accessor is parent?.
It returns whether or not there’s a parent.
Make sure you check it before dealing with the other parent accessors -
they’ll die noisily if a parent doesn’t exist.
# /cans/15/beans parent? #=> true # /burritos/22/beans parent? #=> true # /beans/76 parent? #=> false
parent_name returns the name of the current parent.
# /cans/15/beans parent_name #=> "can" # /burritos/22/beans parent_name #=> "burrito"
parent_model returns the actual model class.
# /cans/15/beans parent_model #=> Can # /burritos/22/beans parent_model #=> Burrito
parent_object is probably the most useful.
It returns the actual record referenced by the parent id.
# /cans/15/beans parent_object #=> Can.find(15) # /burritos/22/beans parent_object #=> Burrito.find(22)
parent_path and parent_url return the path and URL for parent objects, respectively.
By default, the path is for parent_object,
but they can be any object of the proper type.
# /cans/15/beans parent_url #=> "http://beanworld.com/cans/15" parent_path #=> "/cans/15" parent_path(Can.find(12)) #=> "/cans/12" # /burritos/22/beans parent_url #=> "http://beanworld.com/burritos/22" parent_path #=> "/burritos/2" parent_path(Burrito.find(12)) #=> "/burritos/12"
So I hope everyone enjoys this much-requested change. For those of you using polymorphism, this should make your lives easier. For those of you who are still doing deep nesting, hopefully this will encourage you to switch.
We plan to release a stable 0.3 release with this and a few more bits of polish In the coming week or so. Until then, if you want to give it a whirl, just grab trunk:
./script/plugin install http://svn.hamptoncatlin.com/make_resourceful/trunk mv vendor/plugins/trunk vendor/plugins/make_resourceful
About Me
Feed
make_resourceful 0.2.0



Mind if I fork a parallel version (make_nested? ;)) and keep the deep nested stuff in there? It seems that a lot of the projects I’ve worked on lately have really needed deeply nested resources (e.g., working with school grades, books for those grades, units, chapters, sections, and activities within those sections). In that case, it doesn’t make sense to de-nest it with URLs like chapter/44/sections/9 because that makes the URL essentially useless.
I think its a good move to take it out (I’d like to not have to do that…and polymorphic nesting++), but it makes more sense in some cases. Any advice on cases like that or am I just screwed?
This just keeps getting better!
That was a problem I was dealing with and now it seemed to just melt away. Awesome!
Helps keep my controllers light and I love the DRY-ness!
Jeremy: After thinking it through, I think it’s likely some support for deep nesting will come back, although possibly in a more limited form. Note that your example wouldn’t be handled by the default deep nesting code anyway, because (presumably) /chapters/44/sections/9 refers to section 9 of chapter 44, not the section with absolute id 9. The new modifications don’t prevent you from dealing with nesting, they just don’t do it automatically. You can still override
current_modelto deal with all the nesting you want.I’d much rather not see m_r forked, for obvious reasons. I don’t think it’s necessary, though; make_resourceful is quite extensible, so you should be able to re-add the methods on a case-by-case basis, either by copying them into your ApplicationControllers, or creating a module and including it. You can even add declarations to the
make_resourcefulblock by including modules in Resourceful::Builder.These new changes look fantastic!
Does it lead to scoped actions, so that create in BeansController will call Can.find(15).beans.create(params[:beans]) or Burrito.find(22).beans.create(params[:beans]) when appropriate?
Jared: Yes, it does. When a parent object is present, the
current_modelaccessor returns the association object (e.g.Can.find(15).beans), which duck-typically responds to the same methods as the actual model. Then all the other accessors –current_object,build_object, etc. work properly.It just keeps getting better! This is the one addition to m_r which I was getting ready to go about doing via my own modifications. It’s great to see the official source code taking care of the task instead.
I can’t imagine what else might make m_r any better, it seems it is quickly reaching an epitomous approach to its problem domain. I’m eager to see what else is in store for the next release. Keep up the great work!
Congratulations on the continued improvements you’re making available for the Ruby on Rails community. =)
Why not add
as a default when using belongs_to in m_r ?
Because make_resourceful promotes flat routes – i.e.
object_path-style – by default.nested_object_pathexists because sometimes it’s useful to have knowledge of the parent encoded in the url (e.g. for polymorphic routes), but this isn’t the case often enough to warrant making it the default.You can always make it the default for your app by adding the
response_forblock to application.rb.How about making nested_object_path == object_path by default, but change to be parent->object when there is a parent? It seems more natural that when you create/update an object from a given parent—your url includes that parent when the object is presented to you after the action.
I disagree. Unless you’re using polymorphic routes, all the information a URL with a parent path gives you is also implicit in the bit of the URL of the child. Thus is you have, say, /posts/55/comments/472, the /posts/55 is unnecessary – you already know that from
comment.post_id.Won’t this add extra complexity – like when you have comments 470-480 on post 55, and you access the comment via
There is this extra complexity now in the mechanics for showing the “post” and the “comment” at the same page after the submit of the “comment”.
And what about the comment’s rating for example – (ignore ajax for a sec) – someone clicks on a 1-5 star on the comment to rate it, and it’s also a resource. Now the rating has a comment.id, and the comment has a post.id but the mechanics to show the “post” and all the comments, and that new rating on that specific comment becomes extra complex.
On the other hand—
when you submit the comment from
flash[:notice]="Comment submitted"shown onSame with the rating for that comment—by sending a POST to
Or am I getting it all wrong somehow?
Feature Request
I have one improvement to this that I would assume to be fairly necessary. If you have a CommentsController that can handle a polymorphic commentable record, then wouldn’t you want to be able to handle any record that comes along with the appropriate association, and not just the ones specified in belongs_to?
For instance if you add another record somewhere down the road (Event we’ll say), who’s model also has:
You would, necessarily, have to update the routes.rb as well:
However, it would be tedious also have to go into CommentsController and add :event to the belongs_to. Not to mention the situation if the CommentsController is located inside a vendor plugin.
Instead wouldn’t it be nicer if the Controller was smart enough to read whatever parent record was passed in (by simply parsing the path and params). Then taking that record and making sure it has a :as => :commentable association. And then finally using that record as the parent as normal. Furthermore setting the @commentable instance variable to this record would also be nice.
I have some code I’ve been playing around with that parses the path and params pretty well, but I’m sure there are multiple solutions to this. I do think it would significantly improve the usability of this plugin in modern site development.
one thing I forgot to mention is that you’d need to allow the belongs_to method in the m_r Builder to handle the polymorphic name:
Or perhaps a new method all together.
I have a branch of the “nearest to original” version of make_resourceful from github.
I have implemented the above described feature. As well as a couple other useful methods described in the README (member_actions and collection_actions).
http://github.com/glennpow/make_resourceful/tree
Feel free to give feedback.
I’m no longer maintaining make_resourceful. Your best bet for this sort of thing is the mailing list.