19

The ActiveRecord find_or_create_by dynamic finder method allows me to specify a block. The documentation isn't clear on this, but it seems that the block only runs in the create case, and not in the find case. In other words, if the record is found, the block doesn't run. I tested it with this console code:

User.find_or_create_by_name("An Existing Name") do |u|
  puts "I'M IN THE BLOCK"
end

(nothing was printed). Is there any way to have the block run in both cases?

Simon Woodside
  • 7,175
  • 5
  • 50
  • 66
  • I've been bitten by this behavior too many times, so I usually opt out of using the block. Instead I would assign to a variable the result of `find_or_create_by_...` and then subsequently operate on that variable. – chemturion Sep 02 '20 at 14:46

2 Answers2

29

As far as I understand block will be executed if nothing found. Usecase of it looks like this:

User.find_or_create_by_name("Pedro") do |u|
  u.money = 0
  u.country = "Mexico"
  puts "User is created"
end

If user is not found the it will initialized new User with name "Pedro" and all this stuff inside block and will return new created user. If user exists it will just return this user without executing the block.

Also you can use "block style" other methods like:

User.create do |u|
  u.name = "Pedro"
  u.money = 1000
end

It will do the same as User.create( :name => "Pedro", :money => 1000 ) but looks little nicer

and

User.find(19) do |u|
  ..
end

etc

fl00r
  • 82,987
  • 33
  • 217
  • 237
13

It doesn't seem to me that this question is actually answered so I will. This is the simplest way, I think, you can achieve that:

User.find_or_create_by_name("An Existing Name or Non Existing Name").tap do |u|
  puts "I'M IN THE BLOCK REGARDLESS OF THE NAME'S EXISTENCE"
end

Cheers!

deivid
  • 4,808
  • 2
  • 33
  • 38
  • 1
    You've used `tap` which will return the result of the `find_or...`, so is not actually passing the block into the find, but into tap! – Ian Vaughan Jun 22 '16 at 13:32
  • Note that in case your table has a not null constraint which is not satisfied by the fields you are searching by, calling tap will raise ActiveRecord::StatementInvalid – Marcus Mansur Jan 19 '17 at 22:43