bug 1108: fix a bug with find_or_create_by and additional values

There was a bug with find_or_create_by_x introduced in 2.3.9 - if you
included extra parameters for the create() then those parameters would
confuse the find() so you'd never get to the create().  This patch
filters the parameters so we only pass to find() the subset that it's
interested in.  The code for the filtering was modelled on the code in
base.rb's method_missing().
This commit is contained in:
toby cabot
2010-09-29 16:05:26 -04:00
committed by Aaron Patterson
parent f5ed5c317e
commit fdfc8e3b9c
2 changed files with 38 additions and 1 deletions

View File

@@ -381,7 +381,8 @@ module ActiveRecord
return find(:first, :conditions => args.first) || create(args.first)
when /^find_or_create_by_(.*)$/
rest = $1
return send("find_by_#{rest}", *args) ||
find_args = pull_finder_args_from(DynamicFinderMatch.match(method).attribute_names, *args)
return send("find_by_#{rest}", find_args) ||
method_missing("create_by_#{rest}", *args)
when /^create_by_(.*)$/
return create($1.split('_and_').zip(args).inject({}) { |h,kv| k,v=kv ; h[k] = v ; h })
@@ -448,6 +449,25 @@ module ActiveRecord
end
private
# Separate the "finder" args from the "create" args given to a
# find_or_create_by_ call. Returns an array with the
# parameter values in the same order as the keys in the
# "names" array. This code was based on code in base.rb's
# method_missing method.
def pull_finder_args_from(names, *args)
attributes = names.collect { |name| name.intern }
attribute_hash = {}
args.each_with_index do |arg, i|
if arg.is_a?(Hash)
attribute_hash.merge! arg
else
attribute_hash[attributes[i]] = arg
end
end
attribute_hash = attribute_hash.with_indifferent_access
attributes.collect { |attr| attribute_hash[attr] }
end
def create_record(attrs)
attrs.update(@reflection.options[:conditions]) if @reflection.options[:conditions].is_a?(Hash)
ensure_owner_is_not_new

View File

@@ -65,6 +65,23 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal person, person.readers.first.person
end
def test_find_or_create_by_with_additional_parameters
post = Post.create! :title => 'test_find_or_create_by_with_additional_parameters', :body => 'this is the body'
comment = post.comments.create! :body => 'test comment body', :type => 'test'
assert_equal comment, post.comments.find_or_create_by_body('test comment body')
post.comments.find_or_create_by_body(:body => 'other test comment body', :type => 'test')
assert_equal 2, post.comments.count
assert_equal 2, post.comments.length
post.comments.find_or_create_by_body('other other test comment body', :type => 'test')
assert_equal 3, post.comments.count
assert_equal 3, post.comments.length
post.comments.find_or_create_by_body_and_type('3rd test comment body', 'test')
assert_equal 4, post.comments.count
assert_equal 4, post.comments.length
end
def test_find_or_create
person = Person.create! :first_name => 'tenderlove'
post = Post.find :first