find_or_create_ methods with a block don't always work

when you use a find_or_create_ automatic method with an initializer block, the block isn't called on the first run, but is called on subsequent runs:

u = User.find_or_create_by_name('bob') { |new_user| new_user.password = '1234' } u.name

"bob"

u.password

nil

u2 = User.find_or_create_by_name('billy') { |new_user| new_user.password = '1234' } u2.name

'billy'

u2.password

'1234'

This appears to have been around for a while.

Bug filed (with patch) here:

http://rails.lighthouseapp.com/projects/8994/tickets/1224-find_or_create_by_-methods-do-not-execute-their-block-on-first-call#ticket-1224-1

Is this not intended?

My understanding was always that if a record is found, no changes are
made, otherwise we create one with the prams passed in and set it up
with the block too.

i.e.   User.find_or_create_by_name(name){|new_user| new_user.password =
'1234' } should be equivalent to:   User.find_by_name(name) || User.create!{ |new_user| new_user.password
= '1234' }

Perhaps a documentation patch would be more appropriate?

-Tom

This seems to work in Rails 2.1.1, which version are you running?

Perhaps you can try using a returning block

user = returning User.find_or_create_by_name('bob') do |u|              u.password = '1234'           end

when you use a find_or_create_ automatic method with an initializer block, the block isn't called on the first run, but is called on subsequent runs:

u = User.find_or_create_by_name('bob') { |new_user| new_user.password = '1234' } u.name

"bob"

u.password

nil

u2 = User.find_or_create_by_name('billy') { |new_user| new_user.password = '1234' } u2.name

'billy'

u2.password

'1234'

This appears to have been around for a while.

Bug filed (with patch) here:

#1224 find_or_create_by_ methods do not execute their block on first call - Ruby on Rails - rails

Applied, nice catch.

If it needs backporting to any other branches, let us know.

We found this behavior in 2.1.0, so it would be great to have it backported to 2.1.x, but I haven't tried the patch against the that line. Looks like it might work.

K

Is this really the desired behavior? I've always considered that block equivalent to the AR::Base initializer:

Foo.new do |f|   f.thing = default_value end

If that's the case, I feel like it *should* be ignored when find_or_create_by_n returns a preexisting record.

~ j.

Sorry, I left that bit of context out of my original message. In *both* of my examples, the record does NOT exist. That is, the first time the method is called, if the record exists, there's no bug -- the block isn't called and shouldn't be. But if, on that first call, the record doesn't exist, the block is still not called, which is a bug. This is related to the way AR auto-defines these methods the first time they're invoked.

K