1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Add dynamic find_or_create_by_{attribute}! method.

This commit is contained in:
Andrew White 2012-03-12 11:39:19 +00:00
parent 1e2de2cb74
commit eee32af45e
7 changed files with 80 additions and 2 deletions

View file

@ -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*

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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")

View file

@ -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