diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index e03c86f48c539..df795df52e426 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -344,6 +344,28 @@ def init_with(coder) self end + ## + # Initializer used for instantiating objects that have been read from the + # database. +attributes+ should be an attributes object, and unlike the + # `initialize` method, no assignment calls are made per attribute. + # + # :nodoc: + def init_from_db(attributes) + init_internals + + @new_record = false + @attributes = attributes + + self.class.define_attribute_methods + + yield self if block_given? + + _run_find_callbacks + _run_initialize_callbacks + + self + end + ## # :method: clone # Identical to Ruby's clone method. This is a "shallow" copy. Be warned that your attributes are not copied. diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index a0d5f1ee9fd2b..155d67fd8fb9e 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -67,8 +67,18 @@ def create!(attributes = nil, &block) # how this "single-table" inheritance mapping is implemented. def instantiate(attributes, column_types = {}, &block) klass = discriminate_class_for_record(attributes) + instantiate_instance_of(klass, attributes, column_types, &block) + end + + # Given a class, an attributes hash, +instantiate_instance_of+ returns a + # new instance of the class. Accepts only keys as strings. + # + # This is private, don't call it. :) + # + # :nodoc: + def instantiate_instance_of(klass, attributes, column_types = {}, &block) attributes = klass.attributes_builder.build_from_database(attributes, column_types) - klass.allocate.init_with("attributes" => attributes, "new_record" => false, &block) + klass.allocate.init_from_db(attributes, &block) end # Updates an object (or multiple objects) and saves it to the database, if validations pass. diff --git a/activerecord/lib/active_record/querying.rb b/activerecord/lib/active_record/querying.rb index d33d36ac02cdc..c84f3d0fbb51f 100644 --- a/activerecord/lib/active_record/querying.rb +++ b/activerecord/lib/active_record/querying.rb @@ -49,7 +49,12 @@ def find_by_sql(sql, binds = [], preparable: nil, &block) } message_bus.instrument("instantiation.active_record", payload) do - result_set.map { |record| instantiate(record, column_types, &block) } + if result_set.includes_column?(inheritance_column) + result_set.map { |record| instantiate(record, column_types, &block) } + else + # Instantiate a homogeneous set + result_set.map { |record| instantiate_instance_of(self, record, column_types, &block) } + end end end diff --git a/activerecord/lib/active_record/result.rb b/activerecord/lib/active_record/result.rb index ffef229be2af1..7f1c2fd7eb9d1 100644 --- a/activerecord/lib/active_record/result.rb +++ b/activerecord/lib/active_record/result.rb @@ -43,6 +43,11 @@ def initialize(columns, rows, column_types = {}) @column_types = column_types end + # Returns true if this result set includes the column named +name+ + def includes_column?(name) + @columns.include? name + end + # Returns the number of elements in the rows array. def length @rows.length diff --git a/activerecord/test/cases/result_test.rb b/activerecord/test/cases/result_test.rb index db52c108acbc0..68fcafb682ae8 100644 --- a/activerecord/test/cases/result_test.rb +++ b/activerecord/test/cases/result_test.rb @@ -12,6 +12,11 @@ def result ]) end + test "includes_column?" do + assert result.includes_column?("col_1") + assert_not result.includes_column?("foo") + end + test "length" do assert_equal 3, result.length end