diff options
author | Ryuta Kamizono <kamipo@gmail.com> | 2017-12-11 14:53:35 +0900 |
---|---|---|
committer | Ryuta Kamizono <kamipo@gmail.com> | 2017-12-13 14:13:52 +0900 |
commit | d4007d5a54b60bd6924eeffb52c126ed32e9f31f (patch) | |
tree | af37d31ea93a287efe7cb49b8aaef3bf8d001d57 | |
parent | f5735ff7d470c2174f06407008e9809d8d9c1574 (diff) | |
download | rails-d4007d5a54b60bd6924eeffb52c126ed32e9f31f.tar.gz rails-d4007d5a54b60bd6924eeffb52c126ed32e9f31f.tar.bz2 rails-d4007d5a54b60bd6924eeffb52c126ed32e9f31f.zip |
Fix inheritance object creation from relation
We need to pass scope attributes to `klass.new` to detect subclass.
Otherwise `subclass_from_attributes` can't detect subclass which is had
in scope attributes.
Fixes #18062.
Closes #18227.
Closes #30720.
-rw-r--r-- | activerecord/lib/active_record/inheritance.rb | 7 | ||||
-rw-r--r-- | activerecord/lib/active_record/relation.rb | 26 | ||||
-rw-r--r-- | activerecord/test/cases/inheritance_test.rb | 39 |
3 files changed, 60 insertions, 12 deletions
diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb index 0715c11473..f3fe610c09 100644 --- a/activerecord/lib/active_record/inheritance.rb +++ b/activerecord/lib/active_record/inheritance.rb @@ -47,14 +47,13 @@ module ActiveRecord # Determines if one of the attributes passed in is the inheritance column, # and if the inheritance column is attr accessible, it initializes an # instance of the given subclass instead of the base class. - def new(*args, &block) + def new(attributes = nil, &block) if abstract_class? || self == Base raise NotImplementedError, "#{self} is an abstract class and cannot be instantiated." end - attrs = args.first if has_attribute?(inheritance_column) - subclass = subclass_from_attributes(attrs) + subclass = subclass_from_attributes(attributes) if subclass.nil? && base_class == self subclass = subclass_from_attributes(column_defaults) @@ -62,7 +61,7 @@ module ActiveRecord end if subclass && subclass != self - subclass.new(*args, &block) + subclass.new(attributes, &block) else super end diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 9ae40e4f4d..dd0d52ad1c 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -52,8 +52,8 @@ module ActiveRecord # # user = users.new { |user| user.name = 'Oscar' } # user.name # => Oscar - def new(*args, &block) - scoping { @klass.new(*args, &block) } + def new(attributes = nil, &block) + scoping { klass.new(scope_for_create(attributes), &block) } end alias build new @@ -77,8 +77,12 @@ module ActiveRecord # # users.create(name: nil) # validation on name # # => #<User id: nil, name: nil, ...> - def create(*args, &block) - scoping { @klass.create(*args, &block) } + def create(attributes = nil, &block) + if attributes.is_a?(Array) + attributes.collect { |attr| create(attr, &block) } + else + scoping { klass.create(scope_for_create(attributes), &block) } + end end # Similar to #create, but calls @@ -87,8 +91,12 @@ module ActiveRecord # # Expects arguments in the same format as # {ActiveRecord::Base.create!}[rdoc-ref:Persistence::ClassMethods#create!]. - def create!(*args, &block) - scoping { @klass.create!(*args, &block) } + def create!(attributes = nil, &block) + if attributes.is_a?(Array) + attributes.collect { |attr| create!(attr, &block) } + else + scoping { klass.create!(scope_for_create(attributes), &block) } + end end def first_or_create(attributes = nil, &block) # :nodoc: @@ -440,8 +448,10 @@ module ActiveRecord where_clause.to_h(relation_table_name) end - def scope_for_create - where_values_hash.merge!(create_with_value.stringify_keys) + def scope_for_create(attributes = nil) + scope = where_values_hash.merge!(create_with_value.stringify_keys) + scope.merge!(attributes) if attributes + scope end # Returns true if relation needs eager loading. diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb index c931f7d21c..ff4385c8b4 100644 --- a/activerecord/test/cases/inheritance_test.rb +++ b/activerecord/test/cases/inheritance_test.rb @@ -280,6 +280,21 @@ class InheritanceTest < ActiveRecord::TestCase assert_equal Firm, firm.class end + def test_where_new_with_subclass + firm = Company.where(type: "Firm").new + assert_equal Firm, firm.class + end + + def test_where_create_with_subclass + firm = Company.where(type: "Firm").create(name: "Basecamp") + assert_equal Firm, firm.class + end + + def test_where_create_bang_with_subclass + firm = Company.where(type: "Firm").create!(name: "Basecamp") + assert_equal Firm, firm.class + end + def test_new_with_abstract_class e = assert_raises(NotImplementedError) do AbstractCompany.new @@ -302,6 +317,30 @@ class InheritanceTest < ActiveRecord::TestCase assert_raise(ActiveRecord::SubclassNotFound) { Company.new(type: "Account") } end + def test_where_new_with_invalid_type + assert_raise(ActiveRecord::SubclassNotFound) { Company.where(type: "InvalidType").new } + end + + def test_where_new_with_unrelated_type + assert_raise(ActiveRecord::SubclassNotFound) { Company.where(type: "Account").new } + end + + def test_where_create_with_invalid_type + assert_raise(ActiveRecord::SubclassNotFound) { Company.where(type: "InvalidType").create } + end + + def test_where_create_with_unrelated_type + assert_raise(ActiveRecord::SubclassNotFound) { Company.where(type: "Account").create } + end + + def test_where_create_bang_with_invalid_type + assert_raise(ActiveRecord::SubclassNotFound) { Company.where(type: "InvalidType").create! } + end + + def test_where_create_bang_with_unrelated_type + assert_raise(ActiveRecord::SubclassNotFound) { Company.where(type: "Account").create! } + end + def test_new_with_unrelated_namespaced_type without_store_full_sti_class do e = assert_raises ActiveRecord::SubclassNotFound do |