aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG.md2
-rw-r--r--activerecord/lib/active_record/dynamic_finder_match.rb26
-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, 80 insertions, 2 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 9a036bdd66..6fd86b1f32 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -155,6 +155,8 @@
## 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 38dbbef5fc..0473d6aafc 100644
--- a/activerecord/lib/active_record/dynamic_finder_match.rb
+++ b/activerecord/lib/active_record/dynamic_finder_match.rb
@@ -7,7 +7,7 @@ module ActiveRecord
class DynamicFinderMatch
def self.match(method)
method = method.to_s
- klass = [FindBy, FindByBang, FindOrInitializeCreateBy].find do |_klass|
+ klass = klasses.find do |_klass|
_klass.matches?(method)
end
klass.new(method) if klass
@@ -17,6 +17,10 @@ module ActiveRecord
method =~ self::METHOD_PATTERN
end
+ def self.klasses
+ [FindBy, FindByBang, FindOrInitializeCreateBy, FindOrCreateByBang]
+ end
+
def initialize(method)
@finder = :first
@instantiator = nil
@@ -47,6 +51,14 @@ module ActiveRecord
arguments.size >= @attribute_names.size
end
+ def save_record?
+ @instantiator == :create
+ end
+
+ def save_method
+ bang? ? :save! : :save
+ end
+
private
def initialize_from_match_data(match_data)
@@ -81,4 +93,16 @@ module ActiveRecord
arguments.size == 1 && arguments.first.is_a?(Hash) || super
end
end
+
+ class FindOrCreateByBang < DynamicFinderMatch
+ METHOD_PATTERN = /^find_or_create_by_([_a-zA-Z]\w*)\!$/
+
+ def initialize_from_match_data(match_data)
+ @instantiator = :create
+ end
+
+ def bang?
+ true
+ end
+ end
end
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 4cd703e0a5..adfacf37ee 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 8a7d208524..96c8eb6417 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 7b1d65c6db..acec230c72 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -462,6 +462,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