Add first_or_create family of methods to Active Record #2757

Merged
merged 4 commits into from Sep 8, 2011

Projects

None yet

5 participants

@andmej
Contributor
andmej commented Aug 30, 2011

This pull request let's you write:

User.where(:first_name => "Scarlett").first_or_create!(:last_name => "Johansson", :hot => true)

Take a look at #2420 for the discussion about this.

andmej and others added some commits Aug 28, 2011
@andmej andmej Adding first_or_create, first_or_create!, first_or_new and first_or_b…
…uild to Active Record.

This let's you write things like:

    User.where(:first_name => "Scarlett").first_or_create!(:last_name => "Johansson", :hot => true)

Related to #2420.
84dad44
@nhocki @andmej nhocki Adding `first_or_new` documentation to the AR Querying guide. ee822f2
@jonleighton
Member

Thanks, I will review this in due course but it might be a few days.

@vijaydev vijaydev commented on the diff Aug 31, 2011
railties/guides/source/active_record_querying.textile
<sql>
-SELECT * FROM clients WHERE (clients.first_name = 'Ryan') LIMIT 1
@vijaydev
vijaydev Aug 31, 2011 Member

poor Ryan :D

@andmej
andmej Aug 31, 2011 Contributor

Haha

@jonleighton jonleighton was assigned Aug 31, 2011
@jonleighton jonleighton commented on the diff Sep 4, 2011
activerecord/lib/active_record/relation.rb
@@ -94,6 +94,49 @@ module ActiveRecord
scoping { @klass.create!(*args, &block) }
end
+ # Tries to load the first record; if it fails, then <tt>create</tt> is called with the same arguments as this method.
+ #
+ # Expects arguments in the same format as <tt>Base.create</tt>.
+ #
+ # ==== Examples
@jonleighton
jonleighton Sep 4, 2011 Member

Can you add a first example with no arguments to first_or_create? (As I think this will be the most common usage.)

E.g.

User.where(:first_name => 'Scarlett').first_or_create
# => <User id: 1, first_name: 'Scarlett'>

Also, I would remove the examples of creating several users at the same time, as I think this is a kind of odd use case and only works because create happens to be implemented that way.

@andmej
andmej Sep 6, 2011 Contributor

Both things done in d03aff7.

@jonleighton jonleighton and 1 other commented on an outdated diff Sep 4, 2011
activerecord/lib/active_record/relation.rb
+ # # Find the first user named Scarlett or create one with a different last name.
+ # # We already have one Scarlett, so she'll be returned.
+ # User.where(:first_name => 'Scarlett').first_or_create do |user|
+ # user.last_name = "O'Hara"
+ # end
+ # # => <User id: 1, first_name: 'Scarlett', last_name: 'Johansson'>
+ #
+ # # Find the first user named Andy or create several at a time.
+ # User.where(:first_name => 'Andy').first_or_create([{:last_name => 'García'}, {:last_name => 'Mejía'}])
+ # # => [#<User id: 2, first_name: 'Andy', last_name: 'García'>,
+ # #<User id: 3, first_name: 'Andy', last_name: 'Mejía'>]
+ #
+ # # Find the first user with last name García or create several at a time.
+ # User.where(:last_name => 'García').first_or_create([{:first_name => 'Jorge'}, {:first_name => 'Andy'}])
+ # # => <User id: 2, first_name: 'Andy', last_name: 'García'>
+ def first_or_create(*args, &block)
@jonleighton
jonleighton Sep 4, 2011 Member

Please write the exact method signature, e.g.

def first_or_create(attributes = nil, options = {}, &block) 
@andmej
andmej Sep 6, 2011 Contributor

Done in 7231788.

@jonleighton jonleighton and 1 other commented on an outdated diff Sep 4, 2011
activerecord/lib/active_record/relation.rb
+ end
+
+ # Like <tt>first_or_create</tt> but calls <tt>create!</tt> so an exception is raised if the created record is invalid.
+ #
+ # Expects arguments in the same format as <tt>Base.create</tt>.
+ def first_or_create!(*args, &block)
+ first || create!(*args, &block)
+ end
+
+ # Like <tt>first_or_create</tt> but calls <tt>new</tt> instead of <tt>create</tt>.
+ #
+ # Expects arguments in the same format as <tt>Base.new</tt>.
+ def first_or_new(*args, &block)
+ first || new(*args, &block)
+ end
+ alias :first_or_build :first_or_new
@jonleighton
jonleighton Sep 4, 2011 Member

@tenderlove what do you think of this? do you think we should standardise on one of build or new, or continue to permit both? (I am undecided, but both seems ok.)

@tenderlove
tenderlove Sep 8, 2011 Member

IMO we should standardize on build. But let's support both for now.

andmej added some commits Sep 6, 2011
@andmej andmej Adding first example with no arguments to AR::Relation#first_or_creat…
…e and removing examples that create several users at the same time (this is confusing and not really helpful).
d03aff7
@andmej andmej Using more precise method signatures for AR::Relation#first_or_create…
… family of methods.
7231788
@andmej
Contributor
andmej commented Sep 8, 2011

@jonleighton @tenderlove I know you are busy with lots of stuff but can you take a quick look at this pull request? I think there's not much left to do before merging.

@jonleighton jonleighton merged commit cbf1dc7 into rails:master Sep 8, 2011
@jonleighton
Member

Merged, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment