An Introduction to Association Proxy
@post.comments << Comment.new(:title => 'something')
Comment.find(:all, :conditions => ['post_id = ? AND title like ?', @post.id, 'something'])
Comment.count(:all, :conditions => ['post_id = ?', @post.id])
There is an easier way to do all of that. So, if you're still manipulating ActiveRecord associations by hand, stop; there is a better way.
AssociationProxy
When you request a collection of associated models, ActiveRecord extends that array with several helpful methods. You can treat that array like a model class, except all of the methods are automatically scoped to the association's parent. It responds to methods like find, create, new, count, etc, and, it's called AssociationProxy.
So, what if we wanted to create a comment, scoped to our post model?
@post.comments.create(params[:comment])
Making use of AssociationProxy, the code reads better, and requires fewer keystrokes. We can also use AssociationProxy to find elements in our collection.
@post.comments.find(:all, :conditions => {:title => 'title we are looking for'})
We can even use dynamic, attribute-based finders through AssociationProxy.
@post.comments.find_by_title 'title we are looking for'
It Gets Better
Since assoc proxy responds to many of the same methods as a model class, the two can be interchanged for many operations. In resource_controller, for example, nested controllers depend heavily on association proxies. Let me show you what I mean.
If we had, for example, a typical show action.
def show
@comment = Comment.find(params[:id])
end
With very little effort, we can allow comments to be shown nested, or unnested.
def show
@comment = model.find(params[:id])
end
private
def model
params[:post_id] ? Post.find(params[:post_id]).comments : Comment
end
Since we know that AssociationProxy responds to other model class methods, we can base our create method on this technique, too.
def show
@comment = model.find(params[:id])
end
def create
@comment = model.new(params[:comment])
if @comment.save
redirect_to @comment
else
render :action => 'new'
end
end
private
def model
params[:post_id] ? Post.find(params[:post_id]).comments : Comment
end
In fact, this is nearly exactly how resource_controller is built.
So, association proxy is useful in simple and complex situations alike. It can save you a lot of code, and increase readability. It's a win-win really.