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:
parent
1e2de2cb74
commit
eee32af45e
7 changed files with 80 additions and 2 deletions
|
@ -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*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in a new issue