aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record
diff options
context:
space:
mode:
authorJon Leighton <j@jonathanleighton.com>2011-09-08 15:07:23 -0700
committerJon Leighton <j@jonathanleighton.com>2011-09-08 15:07:23 -0700
commitcbf1dc78429bcceb9e90a3ff3159eafd8af5c711 (patch)
tree765e0bc392d77a62118dfe2fead7dbdd07b06467 /activerecord/lib/active_record
parent04baa4b2cad105718ac6acda3d82b6521c4536ea (diff)
parent72317883ed3fd035a94ca1536def364d2d00a6c6 (diff)
downloadrails-cbf1dc78429bcceb9e90a3ff3159eafd8af5c711.tar.gz
rails-cbf1dc78429bcceb9e90a3ff3159eafd8af5c711.tar.bz2
rails-cbf1dc78429bcceb9e90a3ff3159eafd8af5c711.zip
Merge pull request #2757 from andmej/first_or_create_pull_request
Add first_or_create family of methods to Active Record
Diffstat (limited to 'activerecord/lib/active_record')
-rw-r--r--activerecord/lib/active_record/base.rb1
-rw-r--r--activerecord/lib/active_record/relation.rb43
2 files changed, 44 insertions, 0 deletions
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 374791deb1..2979ad1cb3 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -442,6 +442,7 @@ module ActiveRecord #:nodoc:
class << self # Class methods
delegate :find, :first, :first!, :last, :last!, :all, :exists?, :any?, :many?, :to => :scoped
+ delegate :first_or_create, :first_or_create!, :first_or_new, :first_or_build, :to => :scoped
delegate :destroy, :destroy_all, :delete, :delete_all, :update, :update_all, :to => :scoped
delegate :find_each, :find_in_batches, :to => :scoped
delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :create_with, :to => :scoped
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 15fd1a58c8..d3f1347e03 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/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
+ # # Find the first user named Penélope or create a new one.
+ # User.where(:first_name => 'Penélope').first_or_create
+ # # => <User id: 1, first_name: 'Penélope', last_name: nil>
+ #
+ # # Find the first user named Penélope or create a new one.
+ # # We already have one so the existing record will be returned.
+ # User.where(:first_name => 'Penélope').first_or_create
+ # # => <User id: 1, first_name: 'Penélope', last_name: nil>
+ #
+ # # Find the first user named Scarlett or create a new one with a particular last name.
+ # User.where(:first_name => 'Scarlett').first_or_create(:last_name => 'Johansson')
+ # # => <User id: 2, first_name: 'Scarlett', last_name: 'Johansson'>
+ #
+ # # Find the first user named Scarlett or create a new one with a different last name.
+ # # We already have one so the existing record will be returned.
+ # User.where(:first_name => 'Scarlett').first_or_create do |user|
+ # user.last_name = "O'Hara"
+ # end
+ # # => <User id: 2, first_name: 'Scarlett', last_name: 'Johansson'>
+ def first_or_create(attributes = nil, options = {}, &block)
+ first || create(attributes, options, &block)
+ 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!(attributes = nil, options = {}, &block)
+ first || create!(attributes, options, &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(attributes = nil, options = {}, &block)
+ first || new(attributes, options, &block)
+ end
+ alias :first_or_build :first_or_new
+
def respond_to?(method, include_private = false)
arel.respond_to?(method, include_private) ||
Array.method_defined?(method) ||