max
max

Reputation: 102250

Mongoid equivalent of create_with

Is there an equivalent of Active Records Model.create_with to pass creation parameters separate of find parameters in Mongoid?

# Find the first user named "Scarlett" or create a new one with
# a particular last name.
User.create_with(last_name: 'Johansson').find_or_create_by(first_name: 'Scarlett')
# => #<User id: 2, first_name: "Scarlett", last_name: "Johansson">

I find myself using a clunky workaround:

user = User.find_or_initialze_by(first_name: 'Scarlett')
user.update(last_name: 'Johansson') if user.new_record?

Upvotes: 2

Views: 295

Answers (1)

mu is too short
mu is too short

Reputation: 434805

Mongoid's find_or_create_by takes an optional block which is only used when it needs to create something. The documentation isn't exactly explicit about this behavior but if you check the code you'll see that find_or_create_by ends up as a call to this find_or method:

def find_or(method, attrs = {}, &block)
  where(attrs).first || send(method, attrs, &block)
end

with method being :create and the block isn't used if the document you're looking for is found by where.

That means that you can say:

user = User.find_or_create_by(first_name: 'Scarlett') do |user|
  user.last_name = 'Johansson'
end

to get the effect you're after.

Presumably this "the create half uses the block" behavior is supposed to be obvious because create takes a block to initialize the object but find doesn't.

If you're paranoid about this undocumented behavior, you can include a check for it in your specs so you'll at least know when an upgrade breaks it.

Upvotes: 4

Related Questions