/ Rails

TIL: the power of Ruby's find(ifnone) {}

Ever wanted to perform something similar to a first_or_create with a bunch of associations, but keep everything in memory? You can with Ruby's Enumerable#find!

Say you have an array of objects you'd like to pluck from, perhaps a collection of ActiveRecord instances. find returns the first element where &block returns true:

tags = [ #<Tag id: 1, word: "foo">,
         #<Tag id: 2, word: "bar"> ]

tags.find {|t| t.word == "foo" }
=> #<Tag id: 1, word: "foo">

But what if it doesn't match?

tags.find {|t| t.word == "baz" }
=> nil

Well that doesn't help much, we were looking for a Tag that matched 'baz', returning nil is just frustrating.

We could use first_or_create and add the result to the collection, but why code so much when we can just pass a function for find to call when it doesn't find anything?

tags.find(-> { tags << Tag.new(word: "baz") }) { |t| t.word == "baz" }
=> #<Tag id: nil, word: "baz">

=> [ #<Tag id: 1, word: "foo">,
     #<Tag id: 2, word: "bar">,
     #<Tag id: nil, word: "bar"> ]

And viola! Our tag is found, returned, and in memory to be saved later!

Of coarse, if you then assign the tag to multiple associations, then save one of them, ActiveRecord is not smart enough to update the other. Instead it will raise a dirty record error. Resolving that is a whole other blog post.

TIL: the power of Ruby's find(ifnone) {}
Share this