From 4a040e664dc5f85cc7cf78d2161d7bdfc56d90ec Mon Sep 17 00:00:00 2001 From: Brian Finney Date: Tue, 8 Mar 2011 01:59:24 +0000 Subject: [PATCH] fix #6147 find_or_create via has_many fails for hash parameters Incorporates tests from James Badger Add tests for edge cases and match behavior of ActiveRecord::Base#find_or_create_by_* Made extraction of implicit and explicit attributes more apparent --- .../associations/association_collection.rb | 9 +++- .../associations/has_many_associations_test.rb | 48 ++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletions(-) diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 3a602e4..7dc0203 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -384,7 +384,14 @@ module ActiveRecord return send("find_by_#{rest}", find_args) || method_missing("create_by_#{rest}", *args, &block) when /^create_by_(.*)$/ - return create($1.split('_and_').zip(args).inject({}) { |h,kv| k,v=kv ; h[k] = v ; h }, &block) + explicit_args = args.last.is_a?(Hash) ? args.pop : {} + implicit_args = $1.split('_and_') + if args.count > implicit_args.count + raise ActiveRecord::UnknownAttributeError + end + implicit_args = implicit_args.zip(args).inject({}) { |h,kv| k,v=kv ; h[k.to_sym] = v ; h } + + return create(implicit_args.merge(explicit_args), &block) end if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method)) diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 3996b84..6cf2956 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -82,6 +82,54 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal 4, post.comments.length end + def test_find_or_create_by_with_hash_sets_proper_attributes + author = Author.create!(:name => "Sir Vayor") + post = author.posts.create!(:title => "A fine post", :body => "It is fine, is it not?") + assert_equal 1, author.posts.count + assert_equal 1, author.posts.length + assert_equal post, author.posts.find_or_create_by_title("A fine post") + + post = author.posts.find_or_create_by_title(:title => "A wood post", :body => "It is made of wood.") + assert_equal 2, author.posts.count + assert_equal 2, author.posts.length + assert_equal "A wood post", post.title + assert_equal "It is made of wood.", post.body + + post = author.posts.find_or_create_by_title("An iron post", :body => "A heavy post made of iron.") + assert_equal 3, author.posts.count + assert_equal 3, author.posts.length + assert_equal "An iron post", post.title + assert_equal "A heavy post made of iron.", post.body + + post = author.posts.find_or_create_by_title_and_body("A witness post", "The other post was near here.") + assert_equal 4, author.posts.count + assert_equal 4, author.posts.length + assert_equal "A witness post", post.title + assert_equal "The other post was near here.", post.body + + post = author.posts.find_or_create_by_title_and_body("An iron post", :body => "A heavy post made of iron.") + assert_equal 5, author.posts.count + assert_equal 5, author.posts.length + assert_equal "An iron post", post.title + assert_equal "A heavy post made of iron.", post.body + + post = author.posts.find_or_create_by_title_and_body("The last post", "If only they were this easy to find.", {:title => "Maybe not the last post", :body => "Another post."}) + assert_equal 6, author.posts.count + assert_equal 6, author.posts.length + assert_equal "Maybe not the last post", post.title + assert_equal "Another post.", post.body + + post = author.posts.find_or_create_by_title_and_type("I have no type", :body => "test") + assert_equal 7, author.posts.count + assert_equal 7, author.posts.length + assert_equal "I have no type", post.title + assert_equal nil, post.type + + assert_raise ActiveRecord::UnknownAttributeError do + post = author.posts.find_or_create_by_title("I should throw ActiveRecord::UnknownAttributeError", "This is an unspecified body") + end + end + def test_find_or_create_by_with_block post = Post.create! :title => 'test_find_or_create_by_with_additional_parameters', :body => 'this is the body' comment = post.comments.find_or_create_by_body('other test comment body') { |comment| comment.type = 'test' } -- 1.6.3.3