aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--activerecord/CHANGELOG.md2
-rw-r--r--activerecord/lib/active_record/dynamic_finder_match.rb12
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb2
-rw-r--r--activerecord/test/cases/dynamic_finder_match_test.rb8
-rw-r--r--activerecord/test/cases/finder_respond_to_test.rb10
-rw-r--r--activerecord/test/cases/finder_test.rb22
-rw-r--r--activerecord/test/cases/relations_test.rb12
7 files changed, 67 insertions, 1 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 5fe57712d4..16c525d211 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,5 +1,7 @@
## Rails 3.2.3 (unreleased) ##
+* Added find_or_create_by_{attribute}! dynamic method. *Andrew White*
+
* Whitelist all attribute assignment by default. Change the default for newly generated applications to whitelist all attribute assignment. Also update the generated model classes so users are reminded of the importance of attr_accessible. *NZKoz*
* Update ActiveRecord::AttributeMethods#attribute_present? to return false for empty strings. *Jacobkg*
diff --git a/activerecord/lib/active_record/dynamic_finder_match.rb b/activerecord/lib/active_record/dynamic_finder_match.rb
index b309df9b1b..80b17a27df 100644
--- a/activerecord/lib/active_record/dynamic_finder_match.rb
+++ b/activerecord/lib/active_record/dynamic_finder_match.rb
@@ -18,6 +18,10 @@ module ActiveRecord
when /^find_by_([_a-zA-Z]\w*)\!$/
bang = true
names = $1
+ when /^find_or_create_by_([_a-zA-Z]\w*)\!$/
+ bang = true
+ instantiator = :create
+ names = $1
when /^find_or_(initialize|create)_by_([_a-zA-Z]\w*)$/
instantiator = $1 == 'initialize' ? :new : :create
names = $2
@@ -52,5 +56,13 @@ module ActiveRecord
def bang?
@bang
end
+
+ def save_record?
+ @instantiator == :create
+ end
+
+ def save_method
+ bang? ? :save! : :save
+ end
end
end
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 0305e561c8..9a34927857 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -290,7 +290,7 @@ module ActiveRecord
r.assign_attributes(unprotected_attributes_for_create, :without_protection => true)
end
yield(record) if block_given?
- record.save if match.instantiator == :create
+ record.send(match.save_method) if match.save_record?
end
record
diff --git a/activerecord/test/cases/dynamic_finder_match_test.rb b/activerecord/test/cases/dynamic_finder_match_test.rb
index e576870317..db619faa83 100644
--- a/activerecord/test/cases/dynamic_finder_match_test.rb
+++ b/activerecord/test/cases/dynamic_finder_match_test.rb
@@ -83,6 +83,14 @@ module ActiveRecord
assert_equal :create, m.instantiator
end
+ def test_find_or_create!
+ m = DynamicFinderMatch.match(:find_or_create_by_foo!)
+ assert_equal :first, m.finder
+ assert m.bang?, 'should be banging'
+ assert_equal %w{ foo }, m.attribute_names
+ assert_equal :create, m.instantiator
+ end
+
def test_find_or_initialize
m = DynamicFinderMatch.match(:find_or_initialize_by_foo)
assert_equal :first, m.finder
diff --git a/activerecord/test/cases/finder_respond_to_test.rb b/activerecord/test/cases/finder_respond_to_test.rb
index 235805a67c..810c1500cc 100644
--- a/activerecord/test/cases/finder_respond_to_test.rb
+++ b/activerecord/test/cases/finder_respond_to_test.rb
@@ -56,6 +56,16 @@ class FinderRespondToTest < ActiveRecord::TestCase
assert_respond_to Topic, :find_or_create_by_title_and_author_name
end
+ def test_should_respond_to_find_or_create_from_one_attribute_bang
+ ensure_topic_method_is_not_cached(:find_or_create_by_title!)
+ assert_respond_to Topic, :find_or_create_by_title!
+ end
+
+ def test_should_respond_to_find_or_create_from_two_attributes_bang
+ ensure_topic_method_is_not_cached(:find_or_create_by_title_and_author_name!)
+ assert_respond_to Topic, :find_or_create_by_title_and_author_name!
+ end
+
def test_should_not_respond_to_find_by_one_missing_attribute
assert !Topic.respond_to?(:find_by_undertitle)
end
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index 5a67fcdc0a..173044fc94 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -862,6 +862,28 @@ class FinderTest < ActiveRecord::TestCase
assert another.persisted?
end
+ def test_find_or_create_from_one_attribute_bang
+ number_of_companies = Company.count
+ assert_raises(ActiveRecord::RecordInvalid) { Company.find_or_create_by_name!("") }
+ assert_equal number_of_companies, Company.count
+ sig38 = Company.find_or_create_by_name!("38signals")
+ assert_equal number_of_companies + 1, Company.count
+ assert_equal sig38, Company.find_or_create_by_name!("38signals")
+ assert sig38.persisted?
+ end
+
+ def test_find_or_create_from_two_attributes_bang
+ number_of_companies = Company.count
+ assert_raises(ActiveRecord::RecordInvalid) { Company.find_or_create_by_name_and_firm_id!("", 17) }
+ assert_equal number_of_companies, Company.count
+ sig38 = Company.find_or_create_by_name_and_firm_id!("38signals", 17)
+ assert_equal number_of_companies + 1, Company.count
+ assert_equal sig38, Company.find_or_create_by_name_and_firm_id!("38signals", 17)
+ assert sig38.persisted?
+ assert_equal "38signals", sig38.name
+ assert_equal 17, sig38.firm_id
+ end
+
def test_find_or_create_from_two_attributes_with_one_being_an_aggregate
number_of_customers = Customer.count
created_customer = Customer.find_or_create_by_balance_and_name(Money.new(123), "Elizabeth")
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index bf1eb6386a..5e0caf5fce 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -449,6 +449,18 @@ class RelationTest < ActiveRecord::TestCase
assert_equal authors(:david), authors.find_or_create_by_name(:name => 'David')
end
+ def test_dynamic_find_or_create_by_attributes_bang
+ authors = Author.scoped
+
+ assert_raises(ActiveRecord::RecordInvalid) { authors.find_or_create_by_name!('') }
+
+ lifo = authors.find_or_create_by_name!('Lifo')
+ assert_equal "Lifo", lifo.name
+ assert lifo.persisted?
+
+ assert_equal authors(:david), authors.find_or_create_by_name!(:name => 'David')
+ end
+
def test_find_id
authors = Author.scoped