aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record')
-rw-r--r--activerecord/lib/active_record/association_preload.rb47
-rw-r--r--activerecord/lib/active_record/associations.rb141
-rw-r--r--activerecord/lib/active_record/associations/association_collection.rb6
-rw-r--r--activerecord/lib/active_record/associations/association_proxy.rb32
-rw-r--r--activerecord/lib/active_record/associations/class_methods/join_dependency.rb3
-rw-r--r--activerecord/lib/active_record/associations/has_one_association.rb49
-rw-r--r--activerecord/lib/active_record/associations/has_one_through_association.rb9
-rw-r--r--activerecord/lib/active_record/base.rb21
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb16
-rw-r--r--activerecord/lib/active_record/reflection.rb25
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb14
-rw-r--r--activerecord/lib/active_record/schema_dumper.rb2
13 files changed, 204 insertions, 165 deletions
diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb
index 5aad2b4558..68aaff175a 100644
--- a/activerecord/lib/active_record/association_preload.rb
+++ b/activerecord/lib/active_record/association_preload.rb
@@ -126,21 +126,21 @@ module ActiveRecord
parent_records.each do |parent_record|
association_proxy = parent_record.send(reflection_name)
association_proxy.loaded
- association_proxy.target.push(*Array.wrap(associated_record))
+ association_proxy.target.concat(Array.wrap(associated_record))
association_proxy.send(:set_inverse_instance, associated_record)
end
end
def add_preloaded_record_to_collection(parent_records, reflection_name, associated_record)
parent_records.each do |parent_record|
- parent_record.send("set_#{reflection_name}_target", associated_record)
+ parent_record.send(:association_proxy, reflection_name).target = associated_record
end
end
- def set_association_collection_records(id_to_record_map, reflection_name, associated_records, key)
+ def set_association_collection_records(id_to_parent_map, reflection_name, associated_records, key)
associated_records.each do |associated_record|
- mapped_records = id_to_record_map[associated_record[key].to_s]
- add_preloaded_records_to_collection(mapped_records, reflection_name, associated_record)
+ parent_records = id_to_parent_map[associated_record[key].to_s]
+ add_preloaded_records_to_collection(parent_records, reflection_name, associated_record)
end
end
@@ -158,14 +158,17 @@ module ActiveRecord
seen_keys[seen_key] = true
mapped_records = id_to_record_map[seen_key]
mapped_records.each do |mapped_record|
- association_proxy = mapped_record.send("set_#{reflection_name}_target", associated_record)
+ association_proxy = mapped_record.send(:association_proxy, reflection_name)
+ association_proxy.target = associated_record
association_proxy.send(:set_inverse_instance, associated_record)
end
end
id_to_record_map.each do |id, records|
next if seen_keys.include?(id.to_s)
- records.each {|record| record.send("set_#{reflection_name}_target", nil) }
+ records.each do |record|
+ record.send(:association_proxy, reflection_name).target = nil
+ end
end
end
@@ -196,7 +199,6 @@ module ActiveRecord
right = Arel::Table.new(options[:join_table]).alias('t0')
-
join_condition = left[reflection.klass.primary_key].eq(
right[reflection.association_foreign_key])
@@ -218,24 +220,35 @@ module ActiveRecord
custom_conditions = append_conditions(reflection, preload_options)
- all_associated_records = associated_records(ids) do |some_ids|
+ klass = associated_records_proxy.klass
+
+ associated_records(ids) { |some_ids|
method = in_or_equal(some_ids)
conditions = right[reflection.foreign_key].send(*method)
conditions = custom_conditions.inject(conditions) do |ast, cond|
ast.and cond
end
- associated_records_proxy.where(conditions).to_a
- end
-
- set_association_collection_records(id_to_record_map, reflection.name, all_associated_records, 'the_parent_record_id')
+ relation = associated_records_proxy.where(conditions)
+ klass.connection.select_all(relation.arel.to_sql, 'SQL', relation.bind_values)
+ }.map! { |row|
+ parent_records = id_to_record_map[row['the_parent_record_id'].to_s]
+ associated_record = klass.instantiate row
+ add_preloaded_records_to_collection(
+ parent_records, reflection.name, associated_record)
+ associated_record
+ }
end
def preload_has_one_association(records, reflection, preload_options={})
- return if records.first.send("loaded_#{reflection.name}?")
+ return if records.first.send(:association_proxy, reflection.name).loaded?
id_to_record_map, ids = construct_id_map(records, reflection.options[:primary_key])
options = reflection.options
- records.each {|record| record.send("set_#{reflection.name}_target", nil)}
+
+ records.each do |record|
+ record.send(:association_proxy, reflection.name).target = nil
+ end
+
if options[:through]
through_records = preload_through_records(records, reflection, options[:through])
@@ -317,7 +330,7 @@ module ActiveRecord
end
def preload_belongs_to_association(records, reflection, preload_options={})
- return if records.first.send("loaded_#{reflection.name}?")
+ return if records.first.send(:association_proxy, reflection.name).loaded?
options = reflection.options
klasses_and_ids = {}
@@ -415,7 +428,7 @@ module ActiveRecord
in_clause_length = connection.in_clause_length || ids.size
records = []
ids.each_slice(in_clause_length) do |some_ids|
- records += yield(some_ids)
+ records.concat yield(some_ids)
end
records
end
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 3e7c9a370d..a03d1bbb06 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -137,6 +137,22 @@ module ActiveRecord
# :nodoc:
attr_reader :association_cache
+ protected
+
+ # Returns the proxy for the given association name, instantiating it if it doesn't
+ # already exist
+ def association_proxy(name)
+ association = association_instance_get(name)
+
+ if association.nil?
+ reflection = self.class.reflect_on_association(name)
+ association = reflection.proxy_class.new(self, reflection)
+ association_instance_set(name, association)
+ end
+
+ association
+ end
+
private
# Returns the specified association instance if it responds to :loaded?, nil otherwise.
def association_instance_get(name)
@@ -332,26 +348,31 @@ module ActiveRecord
# === One-to-one associations
#
# * Assigning an object to a +has_one+ association automatically saves that object and
- # the object being replaced (if there is one), in order to update their primary
- # keys - except if the parent object is unsaved (<tt>new_record? == true</tt>).
- # * If either of these saves fail (due to one of the objects being invalid) the assignment
- # statement returns +false+ and the assignment is cancelled.
+ # the object being replaced (if there is one), in order to update their foreign
+ # keys - except if the parent object is unsaved (<tt>new_record? == true</tt>).
+ # * If either of these saves fail (due to one of the objects being invalid), an
+ # <tt>ActiveRecord::RecordNotSaved</tt> exception is raised and the assignment is
+ # cancelled.
# * If you wish to assign an object to a +has_one+ association without saving it,
- # use the <tt>association.build</tt> method (documented below).
+ # use the <tt>build_association</tt> method (documented below). The object being
+ # replaced will still be saved to update its foreign key.
# * Assigning an object to a +belongs_to+ association does not save the object, since
- # the foreign key field belongs on the parent. It does not save the parent either.
+ # the foreign key field belongs on the parent. It does not save the parent either.
#
# === Collections
#
# * Adding an object to a collection (+has_many+ or +has_and_belongs_to_many+) automatically
- # saves that object, except if the parent object (the owner of the collection) is not yet
- # stored in the database.
+ # saves that object, except if the parent object (the owner of the collection) is not yet
+ # stored in the database.
# * If saving any of the objects being added to a collection (via <tt>push</tt> or similar)
- # fails, then <tt>push</tt> returns +false+.
+ # fails, then <tt>push</tt> returns +false+.
+ # * If saving fails while replacing the collection (via <tt>association=</tt>), an
+ # <tt>ActiveRecord::RecordNotSaved</tt> exception is raised and the assignment is
+ # cancelled.
# * You can add an object to a collection without automatically saving it by using the
- # <tt>collection.build</tt> method (documented below).
+ # <tt>collection.build</tt> method (documented below).
# * All unsaved (<tt>new_record? == true</tt>) members of the collection are automatically
- # saved when the parent is saved.
+ # saved when the parent is saved.
#
# === Association callbacks
#
@@ -998,12 +1019,7 @@ module ActiveRecord
reflection = create_has_many_reflection(association_id, options, &extension)
configure_dependency_for_has_many(reflection)
add_association_callbacks(reflection.name, reflection.options)
-
- if options[:through]
- collection_accessor_methods(reflection, HasManyThroughAssociation)
- else
- collection_accessor_methods(reflection, HasManyAssociation)
- end
+ collection_accessor_methods(reflection)
end
# Specifies a one-to-one association with another class. This method should only be used
@@ -1021,8 +1037,7 @@ module ActiveRecord
# [build_association(attributes = {})]
# Returns a new object of the associated type that has been instantiated
# with +attributes+ and linked to this object through a foreign key, but has not
- # yet been saved. <b>Note:</b> This ONLY works if an association already exists.
- # It will NOT work if the association is +nil+.
+ # yet been saved.
# [create_association(attributes = {})]
# Returns a new object of the associated type that has been instantiated
# with +attributes+, linked to this object through a foreign key, and that
@@ -1115,14 +1130,12 @@ module ActiveRecord
def has_one(association_id, options = {})
if options[:through]
reflection = create_has_one_through_reflection(association_id, options)
- association_accessor_methods(reflection, ActiveRecord::Associations::HasOneThroughAssociation)
else
reflection = create_has_one_reflection(association_id, options)
- association_accessor_methods(reflection, HasOneAssociation)
- association_constructor_method(:build, reflection, HasOneAssociation)
- association_constructor_method(:create, reflection, HasOneAssociation)
+ association_constructor_methods(reflection)
configure_dependency_for_has_one(reflection)
end
+ association_accessor_methods(reflection)
end
# Specifies a one-to-one association with another class. This method should only be used
@@ -1239,12 +1252,10 @@ module ActiveRecord
def belongs_to(association_id, options = {})
reflection = create_belongs_to_reflection(association_id, options)
- if reflection.options[:polymorphic]
- association_accessor_methods(reflection, BelongsToPolymorphicAssociation)
- else
- association_accessor_methods(reflection, BelongsToAssociation)
- association_constructor_method(:build, reflection, BelongsToAssociation)
- association_constructor_method(:create, reflection, BelongsToAssociation)
+ association_accessor_methods(reflection)
+
+ unless reflection.options[:polymorphic]
+ association_constructor_methods(reflection)
end
add_counter_cache_callbacks(reflection) if options[:counter_cache]
@@ -1429,7 +1440,7 @@ module ActiveRecord
# 'DELETE FROM developers_projects WHERE active=1 AND developer_id = #{id} AND project_id = #{record.id}'
def has_and_belongs_to_many(association_id, options = {}, &extension)
reflection = create_has_and_belongs_to_many_reflection(association_id, options, &extension)
- collection_accessor_methods(reflection, HasAndBelongsToManyAssociation)
+ collection_accessor_methods(reflection)
# Don't use a before_destroy callback since users' before_destroy
# callbacks will be executed after the association is wiped out.
@@ -1461,15 +1472,10 @@ module ActiveRecord
table_name_prefix + join_table + table_name_suffix
end
- def association_accessor_methods(reflection, association_proxy_class)
+ def association_accessor_methods(reflection)
redefine_method(reflection.name) do |*params|
force_reload = params.first unless params.empty?
- association = association_instance_get(reflection.name)
-
- if association.nil?
- association = association_proxy_class.new(self, reflection)
- association_instance_set(reflection.name, association)
- end
+ association = association_proxy(reflection.name)
if force_reload
reflection.klass.uncached { association.reload }
@@ -1480,40 +1486,15 @@ module ActiveRecord
association.target.nil? ? nil : association
end
- redefine_method("loaded_#{reflection.name}?") do
- association = association_instance_get(reflection.name)
- association && association.loaded?
- end
-
redefine_method("#{reflection.name}=") do |record|
- association = association_instance_get(reflection.name)
-
- if association.nil?
- association = association_proxy_class.new(self, reflection)
- association_instance_set(reflection.name, association)
- end
-
- association.replace(record)
- association.target.nil? ? nil : association
- end
-
- redefine_method("set_#{reflection.name}_target") do |target|
- association = association_proxy_class.new(self, reflection)
- association.target = target
- association.loaded
- association_instance_set(reflection.name, association)
+ association_proxy(reflection.name).replace(record)
end
end
- def collection_reader_method(reflection, association_proxy_class)
+ def collection_reader_method(reflection)
redefine_method(reflection.name) do |*params|
force_reload = params.first unless params.empty?
- association = association_instance_get(reflection.name)
-
- unless association
- association = association_proxy_class.new(self, reflection)
- association_instance_set(reflection.name, association)
- end
+ association = association_proxy(reflection.name)
if force_reload
reflection.klass.uncached { association.reload }
@@ -1533,15 +1514,12 @@ module ActiveRecord
end
end
- def collection_accessor_methods(reflection, association_proxy_class, writer = true)
- collection_reader_method(reflection, association_proxy_class)
+ def collection_accessor_methods(reflection, writer = true)
+ collection_reader_method(reflection)
if writer
redefine_method("#{reflection.name}=") do |new_value|
- # Loads proxy class instance (defined in collection_reader_method) if not already loaded
- association = send(reflection.name)
- association.replace(new_value)
- association
+ association_proxy(reflection.name).replace(new_value)
end
redefine_method("#{reflection.name.to_s.singularize}_ids=") do |new_value|
@@ -1553,17 +1531,18 @@ module ActiveRecord
end
end
- def association_constructor_method(constructor, reflection, association_proxy_class)
- redefine_method("#{constructor}_#{reflection.name}") do |*params|
- attributes = params.first unless params.empty?
- association = association_instance_get(reflection.name)
-
- unless association
- association = association_proxy_class.new(self, reflection)
- association_instance_set(reflection.name, association)
+ def association_constructor_methods(reflection)
+ constructors = {
+ "build_#{reflection.name}" => "build",
+ "create_#{reflection.name}" => "create"
+ }
+ constructors["create_#{reflection.name}!"] = "create!" if reflection.macro == :has_one
+
+ constructors.each do |name, proxy_name|
+ redefine_method(name) do |*params|
+ attributes = params.first unless params.empty?
+ association_proxy(reflection.name).send(proxy_name, attributes)
end
-
- association.send(constructor, attributes)
end
end
diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb
index daec8493ac..3d8a23fdca 100644
--- a/activerecord/lib/active_record/associations/association_collection.rb
+++ b/activerecord/lib/active_record/associations/association_collection.rb
@@ -314,7 +314,11 @@ module ActiveRecord
transaction do
delete(@target - other_array)
- concat(other_array - @target)
+
+ unless concat(other_array - @target)
+ raise RecordNotSaved, "Failed to replace #{@reflection.name} because one or more of the "
+ "new records could not be saved."
+ end
end
end
diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb
index 294e1cab50..e4a449d4f4 100644
--- a/activerecord/lib/active_record/associations/association_proxy.rb
+++ b/activerecord/lib/active_record/associations/association_proxy.rb
@@ -213,7 +213,8 @@ module ActiveRecord
# Set the inverse association, if possible
def set_inverse_instance(record)
if record && invertible_for?(record)
- record.send("set_#{inverse_reflection_for(record).name}_target", @owner)
+ inverse = record.send(:association_proxy, inverse_reflection_for(record).name)
+ inverse.target = @owner
end
end
@@ -259,23 +260,6 @@ module ActiveRecord
end
end
- private
- # Forwards any missing method call to the \target.
- def method_missing(method, *args)
- if load_target
- unless @target.respond_to?(method)
- message = "undefined method `#{method.to_s}' for \"#{@target}\":#{@target.class.to_s}"
- raise NoMethodError, message
- end
-
- if block_given?
- @target.send(method, *args) { |*block_args| yield(*block_args) }
- else
- @target.send(method, *args)
- end
- end
- end
-
# Loads the \target if needed and returns it.
#
# This method is abstract in the sense that it relies on +find_target+,
@@ -299,6 +283,18 @@ module ActiveRecord
reset
end
+ private
+
+ # Forwards any missing method call to the \target.
+ def method_missing(method, *args, &block)
+ if load_target
+ return super unless @target.respond_to?(method)
+ @target.send(method, *args, &block)
+ end
+ rescue NoMethodError => e
+ raise e, e.message.sub(/ for #<.*$/, " via proxy for #{@target}")
+ end
+
# Should be true if there is a foreign key present on the @owner which
# references the target. This is used to determine whether we can load
# the target if the @owner is currently a new record (and therefore
diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb
index 6263c4e3b0..cb3edafab1 100644
--- a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb
+++ b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb
@@ -223,7 +223,8 @@ module ActiveRecord
end
def set_target_and_inverse(join_part, association, record)
- association_proxy = record.send("set_#{join_part.reflection.name}_target", association)
+ association_proxy = record.send(:association_proxy, join_part.reflection.name)
+ association_proxy.target = association
association_proxy.send(:set_inverse_instance, association)
end
end
diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb
index 739bb919c5..c29ab8dcec 100644
--- a/activerecord/lib/active_record/associations/has_one_association.rb
+++ b/activerecord/lib/active_record/associations/has_one_association.rb
@@ -7,7 +7,7 @@ module ActiveRecord
end
def create!(attributes = {})
- new_record(:create_association!, attributes)
+ build(attributes).tap { |record| record.save! }
end
def build(attributes = {})
@@ -19,23 +19,25 @@ module ActiveRecord
raise_on_type_mismatch(record) unless record.nil?
load_target
- if @target && @target != record
- remove_target(save && @reflection.options[:dependent])
- end
+ @reflection.klass.transaction do
+ if @target && @target != record
+ remove_target!(@reflection.options[:dependent])
+ end
+
+ if record
+ set_inverse_instance(record)
+ set_owner_attributes(record)
- if record
- set_owner_attributes(record)
- set_inverse_instance(record)
+ if @owner.persisted? && save && !record.save
+ nullify_owner_attributes(record)
+ set_owner_attributes(@target)
+ raise RecordNotSaved, "Failed to save the new associated #{@reflection.name}."
+ end
+ end
end
@target = record
loaded
-
- if @owner.persisted? && record && save
- record.save && self
- else
- record && self
- end
end
private
@@ -49,21 +51,32 @@ module ActiveRecord
alias creation_attributes construct_owner_attributes
+ # The reason that the save param for replace is false, if for create (not just build),
+ # is because the setting of the foreign keys is actually handled by the scoping, and
+ # so they are set straight away and do not need to be updated within replace.
def new_record(method, attributes)
record = scoped.scoping { @reflection.send(method, attributes) }
replace(record, false)
record
end
- def remove_target(method)
- case method
- when :delete, :destroy
+ def remove_target!(method)
+ if [:delete, :destroy].include?(method)
@target.send(method)
else
- @target[@reflection.foreign_key] = nil
- @target.save if @target.persisted? && @owner.persisted?
+ nullify_owner_attributes(@target)
+
+ if @target.persisted? && @owner.persisted? && !@target.save
+ set_owner_attributes(@target)
+ raise RecordNotSaved, "Failed to remove the existing associated #{@reflection.name}. " +
+ "The record failed to save when after its foreign key was set to nil."
+ end
end
end
+
+ def nullify_owner_attributes(record)
+ record[@reflection.foreign_key] = nil
+ end
end
end
end
diff --git a/activerecord/lib/active_record/associations/has_one_through_association.rb b/activerecord/lib/active_record/associations/has_one_through_association.rb
index 11fa40a5c4..59a704b7bf 100644
--- a/activerecord/lib/active_record/associations/has_one_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_one_through_association.rb
@@ -13,9 +13,8 @@ module ActiveRecord
private
def create_through_record(new_value)
- proxy = @owner.send(@reflection.through_reflection.name) ||
- @owner.send(:association_instance_get, @reflection.through_reflection.name)
- record = proxy.target
+ proxy = @owner.send(:association_proxy, @reflection.through_reflection.name)
+ record = proxy.send(:load_target)
if record && !new_value
record.destroy
@@ -31,10 +30,6 @@ module ActiveRecord
end
end
end
-
- def find_target
- scoped.first
- end
end
end
end
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 1079094bbf..dde52269d4 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -880,6 +880,16 @@ module ActiveRecord #:nodoc:
record
end
+
+ # Finder methods must instantiate through this method to work with the
+ # single-table inheritance model that makes it possible to create
+ # objects of different types from the same table.
+ def instantiate(record) # :nodoc:
+ model = find_sti_class(record[inheritance_column]).allocate
+ model.init_with('attributes' => record)
+ model
+ end
+
private
def relation #:nodoc:
@@ -892,15 +902,6 @@ module ActiveRecord #:nodoc:
end
end
- # Finder methods must instantiate through this method to work with the
- # single-table inheritance model that makes it possible to create
- # objects of different types from the same table.
- def instantiate(record)
- model = find_sti_class(record[inheritance_column]).allocate
- model.init_with('attributes' => record)
- model
- end
-
def find_sti_class(type_name)
if type_name.blank? || !columns_hash.include?(inheritance_column)
self
@@ -1577,7 +1578,7 @@ MSG
# Returns true if the specified +attribute+ has been set by the user or by a database load and is neither
# nil nor empty? (the latter only applies to objects that respond to empty?, most notably Strings).
def attribute_present?(attribute)
- !read_attribute(attribute).blank?
+ !_read_attribute(attribute).blank?
end
# Returns the column object for the named attribute.
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index 0282493219..5ff5813699 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -77,8 +77,8 @@ module ActiveRecord
false
end
- # Does this adapter support savepoints? PostgreSQL and MySQL do, SQLite
- # does not.
+ # Does this adapter support savepoints? PostgreSQL and MySQL do,
+ # SQLite < 3.6.8 does not.
def supports_savepoints?
false
end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
index bf599a95f7..b04383d5bf 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
@@ -62,6 +62,10 @@ module ActiveRecord
sqlite_version >= '2.0.0'
end
+ def supports_savepoints?
+ sqlite_version >= '3.6.8'
+ end
+
# Returns +true+ when the connection adapter supports prepared statement
# caching, otherwise returns +false+
def supports_statement_cache?
@@ -189,6 +193,18 @@ module ActiveRecord
exec_query(sql, name).rows
end
+ def create_savepoint
+ execute("SAVEPOINT #{current_savepoint_name}")
+ end
+
+ def rollback_to_savepoint
+ execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
+ end
+
+ def release_savepoint
+ execute("RELEASE SAVEPOINT #{current_savepoint_name}")
+ end
+
def begin_db_transaction #:nodoc:
@connection.transaction
end
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 937efe395f..ceeb0ec39d 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -313,6 +313,31 @@ module ActiveRecord
macro == :belongs_to
end
+ def proxy_class
+ case macro
+ when :belongs_to
+ if options[:polymorphic]
+ Associations::BelongsToPolymorphicAssociation
+ else
+ Associations::BelongsToAssociation
+ end
+ when :has_and_belongs_to_many
+ Associations::HasAndBelongsToManyAssociation
+ when :has_many
+ if options[:through]
+ Associations::HasManyThroughAssociation
+ else
+ Associations::HasManyAssociation
+ end
+ when :has_one
+ if options[:through]
+ Associations::HasOneThroughAssociation
+ else
+ Associations::HasOneAssociation
+ end
+ end
+ end
+
private
def derive_class_name
class_name = name.to_s.camelize
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index e9e451ec5c..b75a65e3ca 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -282,15 +282,11 @@ module ActiveRecord
end
def type_cast_calculated_value(value, column, operation = nil)
- if value.is_a?(String) || value.nil?
- case operation
- when 'count' then value.to_i
- when 'sum' then type_cast_using_column(value || '0', column)
- when 'average' then value.try(:to_d)
- else type_cast_using_column(value, column)
- end
- else
- type_cast_using_column(value, column)
+ case operation
+ when 'count' then value.to_i
+ when 'sum' then type_cast_using_column(value || '0', column)
+ when 'average' then value.try(:to_d)
+ else type_cast_using_column(value, column)
end
end
diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb
index e30b481fe1..a893c0ad85 100644
--- a/activerecord/lib/active_record/schema_dumper.rb
+++ b/activerecord/lib/active_record/schema_dumper.rb
@@ -83,7 +83,7 @@ HEADER
# first dump primary key column
if @connection.respond_to?(:pk_and_sequence_for)
- pk, pk_seq = @connection.pk_and_sequence_for(table)
+ pk, _ = @connection.pk_and_sequence_for(table)
elsif @connection.respond_to?(:primary_key)
pk = @connection.primary_key(table)
end