Permalink
Browse files

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.
  • Loading branch information...
kamipo committed Dec 11, 2017
1 parent f5735ff commit d4007d5a54b60bd6924eeffb52c126ed32e9f31f
@@ -47,22 +47,21 @@ module ClassMethods
# 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)
end
end
if subclass && subclass != self
subclass.new(*args, &block)
subclass.new(attributes, &block)
else
super
end
@@ -52,8 +52,8 @@ def arel_attribute(name) # :nodoc:
#
# 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 @@ def new(*args, &block)
#
# 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 @@ def create(*args, &block)
#
# 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 @@ def where_values_hash(relation_table_name = klass.table_name)
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.
@@ -280,6 +280,21 @@ def test_inheritance_new_with_subclass
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 @@ def test_new_with_unrelated_type
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

0 comments on commit d4007d5

Please sign in to comment.