New on Edge: dynamic finders with hash attributes for creation

— March 14, 2007 at 03:19 PDT

During the Hackfest in January I put together a patch for an enhancement to Rails dynamic finders. It was just committed today (thanks, Jeremy!), so now's a good time to do a little explanation of it. It's just a little thing, so this should be brief.

Here's a typical situation:

tag = Tag.find_by_name(name)
if tag.nil?
  tag = Tag.create(:name => name, :creator => current_user)

You'd think the way to do that would be with the find_or_create_by_name dynamic finder, but that doesn't work since there's no way to search by name only, but create by name and creator. Well, that's just not right. So here's what we'll do:

Tag.find_or_create_by_name(:name => name, :creator => current_user)

The new feature is that if you pass a hash to the finder, it will still use only the values named in the finder to find the object, but it will create a new object using all the values.

It's not a big deal or something I use every day, but I like it anyway.

17 commentspatch, rails

  1. Josh Peek2007-03-14 03:52:09

    Just need to be tagged as "verified" to get noticed, :)

  2. Josh Susser2007-03-14 04:03:36

    Hey Josh (are we confused yet?) It's funny, a few of us (including Jeremy) were discussing that patch online just a few minutes before you tagged it verified. I guess between the two of us Jeremy couldn't ignore it anymore!

  3. Dr Nic2007-03-14 07:30:21

    Good patch. This is a better API for finc_or_create...

  4. Devin ben-Hur2007-03-14 07:40:31

    Nice fix, Josh. I've always found find_or_create_by essentially useless because it lacked this behavior.

  5. Ben2007-03-14 08:17:26

    Excellent work Josh, I've come across the need for this exact functionality a number of times. It'll be great to have it actually supported in Rails now.

  6. RSL2007-03-14 12:41:11

    Congrats on the patch, Josh. Kinda funny that I was emailing you yesterday about find_or_create_by_x, huh? Heh

  7. Phil2007-03-14 13:17:00

    Good job Josh! I've a need to use find_or_create like that before and now I can. Thanks!

  8. Aslak Hellesøy2007-03-14 13:46:53

    Wouldn't it be DRYer to do:

    Tag.find_or_create_by(:name => name, :creator => current_user)

    (note that there is only one use of 'name' here)

  9. Josh Susser2007-03-14 15:15:13

    Aslak: the use of "name" in the finder method name is how you tell it which of the values should be used to find an existing record. If you don't have that, then the method doesn't know which subset to use. The alternative would be to pass a list as another parameter. I decided to go with the way that was most similar to the existing one.

  10. Sandro2007-03-18 22:40:59

    Hi, josh !

    Excellent work! Thank you for this amazing piece of code ;)

  11. Sean2007-03-21 17:58:17

    Create little add Josh. Thanks.

    I've been thinking something like:

    Tag.find_by_name_and_creator_or_create(:name => name, :creator => current_user)

    would be great to have as well. I haven't looked into implementing this. However, you have any thoughts on it?


  12. Josh Susser2007-03-23 07:27:38

    Sean, that's basically the same as the current find_or_create_by_x dynamic finder that already exists, but with a hash instead of positional arguments. I thought about doing that (even commented on it in the ticket), but I've never had a need for it myself. If you have a need, go for it. Or as they say on rails-core, Please Do Investigate.

  13. Tim2007-03-24 01:01:52

    Did this make it into 123? No, right?

  14. Josh Susser2007-03-25 23:47:51

    Tim, no new features in 1.2.x, so we'll have to wait for 2.0 (or 1.3 maybe...). Or just live on the edge...

  15. Michael2007-05-03 20:43:29

    Was just remarking today that this would be a nice feature to have. Very cool!

  16. Paul2007-06-04 17:59:48

    Josh, very helpful post. Although, I like to use exceptions when a create fails. Do you know if there is a way to bang a findor_createby finder?

  17. Josh Susser2007-06-05 18:16:07

    Paul, not that I know of. And I don't know that it would be that useful. How would you get at the validation errors or the model you couldn't create?

Sorry, comments for this article are closed.