Dynamic finders are pretty neat, but I've grown tired of having to think in terms of foreign keys instead of the names of belongs_to associations. So I fixed it.
This patch enhances dynamic finders to allow use of a belongs_to association name instead of having to use the foreign key attribute directly. For example, these two lines are equivalent:
posts = Post.find_all_by_author_id(author.id)
posts = Post.find_all_by_author(author)
Of course, that makes more of a difference when the association name and foreign key name differ more. But I think that makes dynamic finders a bit more transparent and easier to use.
I'll be submitting this as a patch fairly soon, but first I wanted to toss it out into the wild to see how well it works. I've written unit tests, but for something like this I'd feel just slightly better knowing a few people have given it a try first. So I've packaged the patch as a plugin to make it easy for you to try it out.
dynamic_finders_with_belongs_to_names
Give it a shot, and either email me or leave a comment here to let me know if it works or if it breaks something.
UPDATE: I've fixed the plugin so that the monkey patch loads properly now. If it wasn't working for you before, update to the new version and try it again now. Thanks!
+1
Not sure I'll have time to test it much but it's something that's always bugged me and now you've fixed it :)
Hope the patch gets in.
Why not say:
Can you give an example where I would want a find method like this instead?
Sweet! I'll definitely give it a whirl the next time I start up RadRails :-) Sadly, I've not touched a line of Rails code in so long, predating RailsConf even. I miss it :-\
@Remco
Check out his previous post about Working with the relationship model.
This now makes a three line solution into a one liner. Very nifty.
This is wonderful! I wonder if it would be possible to do the same thing with this syntax:
posts = Post.find(:all, :by => {:author => author})
I tend to prefer this way, but it's probably just a preference. Also, not sure if this is out of the scope of this article.
Thanks for your work.
@fabio: Edge rails already supports half of what you want. You can now just pass a hash as as the value for the :conditions option, and the finder will turn it into the correct expressions for the WHERE clause. However I don't think it works with the belongs_to names. You should also check out the ez_where plugin for enhanced query conditions.
sorry for the double post, just an additional thought.
posts = Post.find(:all, :by => {:author => author})
could be refactored down using some fancy meta-programming footwork to:
assume you've already done dhh = author.find_by_fname("David")
and dhh == 12
posts = Post.find(:all, :by => dhh)
You could then do dhh.class to find out what type of object it is and use that to construct the sql:
SELECT * FROM posts WHERE author_id = 12
This did not work for me initially. As is, I got...
To get this to work I had to package the plugin a bit differently. I put the patch code in lib/dynamic_finders_with_belongs_to_names.rb and changed the contents of init.rb to just...
Then, it worked. Thanks for the patch (and for indirectly helping me sort out how to make my own work).
Brent: oh duh. I was wondering why some people were having trouble loading the patch. I'll put together a new version with your fix. Thanks.
Ah nice, it's the little things in life.