(first posted: Jul 01, 2007)
(18147 Reads)
keywords: make_resourceful polymorphic
Permalink
make-resourceful and nested polymorphic associations
polymorphic resources
A polymorphic model can belong to more than one parent model class. For example, Comments can belong to both Articles and Documents. The Rails idiom for setting this up is like this:
comments table schema includes:
commentable_id:integer
commentable_type:string
models/comment.rb contains
belongs_to :commentable, :polymorphic => true
then both the models/article.rb and document.rb contains
has_many :comments, :as => :commentable
In this way, you can reference article.comments and document.comments for an Article and Document object, respectively. With RESTful resource routes, you can have url's and named routes like
article_comment_path(@article,@comment) # => /articles/1/comments/22
article_new_comment_path(@article) # => /articles/1/comments/new
make_resourceful
The make_resourceful ("mr") plugin (ver 0.1.0) automatically creates typical RESTful actions in controllers, but does not presently support these associations.
Although mr supports has_many nested resources (and multiple levels of them at that), it assumes that a child resource has just one kind of parent.
I've submitted this patch to the authors but its not tightly integrated. So if you use this, consider it a temporary patch until the plugin authors integrate it or come up with their own (better) solution.
Most of the code can be put in application.rb. There are a few other methods that go into the controller for the polymorphic resource.
multiple parents
Important: When using polymorphic resources, I've changed the meaning of parents from the standard mr convention. Normally, mr's parents is a list of models in a nested resource chain, such as /projects/3/articles/4/comments/5, then parents contains ["project","article"].
In the polymorphic case, we can have several alternative parents. Thus, I use parents to represent the list of possible parent resources, one level up.
This also means that in the polymorphic case, we only handle one level of nesting. That's not so bad because that's being strongly recommended by the Rails core team. See http://weblog.jamisbuck.org/2007/2/5/nesting-resource
URL helpers
As a sidebar, I want to mention an undocumented feature: mr has a number of very handy URL generator methods, only you need to expose them as helpers to use them in views. They are :object_path, :objects_path, :new_object_path, and :edit_object_path.
You can use these instead of the built-in RESTful named routes. If your resources are nested the nested paths will be generated. My patch now lets them support polymorphic nesting too.
I've also added a new helper, parent_path, that can be used for example in a "Back" link. Thus, for example:
object_path(item) # => /articles/1/comments/22
object_path # assumes current_object
edit_object_path # => /articles/1/comments/22/edit
parent_path # => /articles/1
application.rb
Add the following to your application.rb to extend the make_resourceful library:
# make_resourceful fixes for polymorphic models
# use this to override parent_objects method
# def parent_objects
# parent_objects_poly
# end
def parent_objects_poly
return [] if parents.empty?
return @parent_objects if @parent_objects
# find index of first (and only) non-zero parent_param
parent_i = parent_params.index( parent_params.find { |el| el != 0 })return @parent_objects = [] unless parent_i
model = parent_models[ parent_i ]
@parent_objects= [model.find(parent_params[parent_i])]
end
# parent object helper
def parent_object
parent_objects[0] unless parent_objects.empty?
end
helper_method(:parent_object)
def parent_path
send("#{namespace_prefix}path", *parent_objects)end
helper_method(:parent_path)
# expose the url methods as helpers
helper_method(:object_path, :objects_path, :new_object_path, :edit_object_path)
The first method, parent_objects_poly, is the body of the override of the parent_object method.
I introduce a new parent_object helper that returns the parent of the current_object. Related to this, I've also added a parent_path URL helper.
The last line exposes the built-in mr URL generators as helpers.
You can keep this code in application.rb. Normally these will not affect make_resourceful controllers, unless you add the following code to the controller to make it a polymorphic resource.
your_controller.rb
The second part of the implementation requires adding a few methods to your controller.
If this part looks a little kludgey, its because I'm trying to avoid editing the plugin code directly, to force the order of method overrides (internally, make_resourceful method calls load_parent_objects in base from kontroller.before_filter).
make_resourceful do
actions: all
end
# add the following to make this controller for a polymorphic resource
def parents
["article","document"] #this is the only line specific to this resource
end
def parent_objects
parent_objects_poly
end
def namespaces
parent_i = parent_params.index( parent_params.find { |p| p != 0 })[parents[parent_i]]
end
(Note, we do not use the belongs_to declaration, rather we override the parents method directly.)
As noted above, parents returns the list of possible parents, one level up. You need to insert your own parents array there.
parent_objects is overriden using the body we put in application.rb. It still returns an array, but it will have no more than one element.
Finally, the internal method for generating namespaces is also overridden for the URL helper methods (again, this didn't work when I put it in application.rb)
That it. It seems to be working fine for me. I hope it helps you too.
make-resourceful and nested polymorphic associations
Posted by: linoj on September 15, 2007 12:38 PMThomas, if you're using rails edge, the routes might contain:
map.resources :articles, :has_many => :comments map.resources :documents, :has_many => :comments
Otherwise, its a bit more verbose,
map.resources :articles do |article| article.resources :comments, :controller => "Comments", :name_prefix => "articles_" end map.resources :documents do |doc| doc.resources :comments, :controller => "Comments", :name_prefix => "documents_" end




make-resourceful and nested polymorphic associations
Posted by: Thomas on September 08, 2007 08:00 PM#