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:

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

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