aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/associations
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record/associations')
-rw-r--r--activerecord/lib/active_record/associations/association.rb (renamed from activerecord/lib/active_record/associations/association_proxy.rb)217
-rw-r--r--activerecord/lib/active_record/associations/class_methods/join_dependency.rb14
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb (renamed from activerecord/lib/active_record/associations/association_collection.rb)131
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb127
-rw-r--r--activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb36
-rw-r--r--activerecord/lib/active_record/associations/has_many_association.rb12
-rw-r--r--activerecord/lib/active_record/associations/has_many_through_association.rb20
-rw-r--r--activerecord/lib/active_record/associations/has_one_through_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/singular_association.rb4
9 files changed, 287 insertions, 276 deletions
diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association.rb
index fc03a4b619..2264631584 100644
--- a/activerecord/lib/active_record/associations/association_proxy.rb
+++ b/activerecord/lib/active_record/associations/association.rb
@@ -4,102 +4,33 @@ module ActiveRecord
module Associations
# = Active Record Associations
#
- # This is the root class of all association proxies ('+ Foo' signifies an included module Foo):
+ # This is the root class of all associations ('+ Foo' signifies an included module Foo):
#
- # AssociationProxy
+ # Association
# SingularAssociaton
# HasOneAssociation
# HasOneThroughAssociation + ThroughAssociation
# BelongsToAssociation
# BelongsToPolymorphicAssociation
- # AssociationCollection
+ # CollectionAssociation
# HasAndBelongsToManyAssociation
# HasManyAssociation
# HasManyThroughAssociation + ThroughAssociation
- #
- # Association proxies in Active Record are middlemen between the object that
- # holds the association, known as the <tt>@owner</tt>, and the actual associated
- # object, known as the <tt>@target</tt>. The kind of association any proxy is
- # about is available in <tt>@reflection</tt>. That's an instance of the class
- # ActiveRecord::Reflection::AssociationReflection.
- #
- # For example, given
- #
- # class Blog < ActiveRecord::Base
- # has_many :posts
- # end
- #
- # blog = Blog.find(:first)
- #
- # the association proxy in <tt>blog.posts</tt> has the object in +blog+ as
- # <tt>@owner</tt>, the collection of its posts as <tt>@target</tt>, and
- # the <tt>@reflection</tt> object represents a <tt>:has_many</tt> macro.
- #
- # This class has most of the basic instance methods removed, and delegates
- # unknown methods to <tt>@target</tt> via <tt>method_missing</tt>. As a
- # corner case, it even removes the +class+ method and that's why you get
- #
- # blog.posts.class # => Array
- #
- # though the object behind <tt>blog.posts</tt> is not an Array, but an
- # ActiveRecord::Associations::HasManyAssociation.
- #
- # The <tt>@target</tt> object is not \loaded until needed. For example,
- #
- # blog.posts.count
- #
- # is computed directly through SQL and does not trigger by itself the
- # instantiation of the actual post records.
- class AssociationProxy #:nodoc:
- alias_method :proxy_extend, :extend
+ class Association #:nodoc:
+ attr_reader :owner, :target, :reflection
- instance_methods.each { |m| undef_method m unless m.to_s =~ /^(?:nil\?|send|object_id|to_a)$|^__|^respond_to|proxy_/ }
+ delegate :options, :klass, :to => :reflection
def initialize(owner, reflection)
+ reflection.check_validity!
+
@owner, @reflection = owner, reflection
@updated = false
- reflection.check_validity!
- Array.wrap(reflection.options[:extend]).each { |ext| proxy_extend(ext) }
+
reset
construct_scope
end
- def to_param
- proxy_target.to_param
- end
-
- # Returns the owner of the proxy.
- def proxy_owner
- @owner
- end
-
- # Returns the reflection object that represents the association handled
- # by the proxy.
- def proxy_reflection
- @reflection
- end
-
- # Does the proxy or its \target respond to +symbol+?
- def respond_to?(*args)
- super || (load_target && @target.respond_to?(*args))
- end
-
- # 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
-
- # Forwards <tt>===</tt> explicitly to the \target because the instance method
- # removal above doesn't catch it. Loads the \target if needed.
- def ===(other)
- other === load_target
- end
-
# Returns the name of the table of the related class:
#
# post.comments.aliased_table_name # => "comments"
@@ -143,97 +74,79 @@ module ActiveRecord
loaded? && @stale_state != stale_state
end
- # Returns the target of this proxy, same as +proxy_target+.
- attr_reader :target
-
- # Returns the \target of the proxy, same as +target+.
- alias :proxy_target :target
-
- # Sets the target of this proxy to <tt>\target</tt>, and the \loaded flag to +true+.
+ # Sets the target of this association to <tt>\target</tt>, and the \loaded flag to +true+.
def target=(target)
@target = target
loaded!
end
- # Forwards the call to the target. Loads the \target if needed.
- def inspect
- load_target.inspect
- end
-
- def send(method, *args)
- return super if respond_to?(method)
- load_target.send(method, *args)
- end
-
def scoped
target_scope.merge(@association_scope)
end
- protected
-
- # Construct the scope for this association.
- #
- # Note that the association_scope is merged into the targed_scope only when the
- # scoped method is called. This is because at that point the call may be surrounded
- # by scope.scoping { ... } or with_scope { ... } etc, which affects the scope which
- # actually gets built.
- def construct_scope
- @association_scope = association_scope if target_klass
- end
+ # Construct the scope for this association.
+ #
+ # Note that the association_scope is merged into the targed_scope only when the
+ # scoped method is called. This is because at that point the call may be surrounded
+ # by scope.scoping { ... } or with_scope { ... } etc, which affects the scope which
+ # actually gets built.
+ def construct_scope
+ @association_scope = association_scope if target_klass
+ end
- def association_scope
- scope = target_klass.unscoped
- scope = scope.create_with(creation_attributes)
- scope = scope.apply_finder_options(@reflection.options.slice(:readonly, :include))
- scope = scope.where(interpolate(@reflection.options[:conditions]))
- if select = select_value
- scope = scope.select(select)
- end
- scope = scope.extending(*Array.wrap(@reflection.options[:extend]))
- scope.where(construct_owner_conditions)
+ def association_scope
+ scope = target_klass.unscoped
+ scope = scope.create_with(creation_attributes)
+ scope = scope.apply_finder_options(@reflection.options.slice(:readonly, :include))
+ scope = scope.where(interpolate(@reflection.options[:conditions]))
+ if select = select_value
+ scope = scope.select(select)
end
+ scope = scope.extending(*Array.wrap(@reflection.options[:extend]))
+ scope.where(construct_owner_conditions)
+ end
- def aliased_table
- target_klass.arel_table
- end
+ def aliased_table
+ target_klass.arel_table
+ end
- # Set the inverse association, if possible
- def set_inverse_instance(record)
- if record && invertible_for?(record)
- inverse = record.send(:association_proxy, inverse_reflection_for(record).name)
- inverse.target = @owner
- end
+ # Set the inverse association, if possible
+ def set_inverse_instance(record)
+ if record && invertible_for?(record)
+ inverse = record.association(inverse_reflection_for(record).name)
+ inverse.target = @owner
end
+ end
- # This class of the target. belongs_to polymorphic overrides this to look at the
- # polymorphic_type field on the owner.
- def target_klass
- @reflection.klass
- end
+ # This class of the target. belongs_to polymorphic overrides this to look at the
+ # polymorphic_type field on the owner.
+ def target_klass
+ @reflection.klass
+ end
- # Can be overridden (i.e. in ThroughAssociation) to merge in other scopes (i.e. the
- # through association's scope)
- def target_scope
- target_klass.scoped
- end
+ # Can be overridden (i.e. in ThroughAssociation) to merge in other scopes (i.e. the
+ # through association's scope)
+ def target_scope
+ target_klass.scoped
+ end
- # Loads the \target if needed and returns it.
- #
- # This method is abstract in the sense that it relies on +find_target+,
- # which is expected to be provided by descendants.
- #
- # If the \target is already \loaded it is just returned. Thus, you can call
- # +load_target+ unconditionally to get the \target.
- #
- # ActiveRecord::RecordNotFound is rescued within the method, and it is
- # not reraised. The proxy is \reset and +nil+ is the return value.
- def load_target
- @target = find_target if find_target?
- loaded!
- target
- rescue ActiveRecord::RecordNotFound
- reset
- end
+ # Loads the \target if needed and returns it.
+ #
+ # This method is abstract in the sense that it relies on +find_target+,
+ # which is expected to be provided by descendants.
+ #
+ # If the \target is already \loaded it is just returned. Thus, you can call
+ # +load_target+ unconditionally to get the \target.
+ #
+ # ActiveRecord::RecordNotFound is rescued within the method, and it is
+ # not reraised. The proxy is \reset and +nil+ is the return value.
+ def load_target
+ @target = find_target if find_target?
+ loaded!
+ target
+ rescue ActiveRecord::RecordNotFound
+ reset
+ end
private
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 fdd4fe8946..89503ccafa 100644
--- a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb
+++ b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb
@@ -209,10 +209,10 @@ module ActiveRecord
association = join_part.instantiate(row)
case macro
when :has_many, :has_and_belongs_to_many
- collection = record.send(join_part.reflection.name)
- collection.loaded!
- collection.target.push(association)
- collection.send(:set_inverse_instance, association)
+ other = record.association(join_part.reflection.name)
+ other.loaded!
+ other.target.push(association)
+ other.set_inverse_instance(association)
when :belongs_to
set_target_and_inverse(join_part, association, record)
else
@@ -223,9 +223,9 @@ module ActiveRecord
end
def set_target_and_inverse(join_part, association, record)
- association_proxy = record.send(:association_proxy, join_part.reflection.name)
- association_proxy.target = association
- association_proxy.send(:set_inverse_instance, association)
+ other = record.association(join_part.reflection.name)
+ other.target = association
+ other.set_inverse_instance(association)
end
end
end
diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/collection_association.rb
index ca350f51c9..68631681e4 100644
--- a/activerecord/lib/active_record/associations/association_collection.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -17,8 +17,26 @@ module ActiveRecord
#
# If you need to work on all current children, new and existing records,
# +load_target+ and the +loaded+ flag are your friends.
- class AssociationCollection < AssociationProxy #:nodoc:
- delegate :group, :order, :limit, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :to => :scoped
+ class CollectionAssociation < Association #:nodoc:
+ attr_reader :proxy
+
+ def initialize(owner, reflection)
+ # When scopes are created via method_missing on the proxy, they are stored so that
+ # any records fetched from the database are kept around for future use.
+ @scopes_cache = Hash.new do |hash, method|
+ hash[method] = { }
+ end
+
+ super
+
+ @proxy = CollectionProxy.new(self)
+ end
+
+ def reset
+ @loaded = false
+ @target = []
+ @scopes_cache.clear
+ end
def select(select = nil)
if block_given?
@@ -44,17 +62,6 @@ module ActiveRecord
first_or_last(:last, *args)
end
- def to_ary
- load_target.dup
- end
- alias_method :to_a, :to_ary
-
- def reset
- @_scopes_cache = {}
- @loaded = false
- @target = []
- end
-
def build(attributes = {}, &block)
build_or_create(attributes, :build, &block)
end
@@ -75,7 +82,7 @@ module ActiveRecord
# Add +records+ to this association. Returns +self+ so method calls may be chained.
# Since << flattens its argument list and inserts each record, +push+ and +concat+ behave identically.
- def <<(*records)
+ def concat(*records)
result = true
load_target if @owner.new_record?
@@ -88,12 +95,9 @@ module ActiveRecord
end
end
- result && self
+ result && records
end
- alias_method :push, :<<
- alias_method :concat, :<<
-
# Starts a transaction in the association class's database connection.
#
# class Author < ActiveRecord::Base
@@ -119,13 +123,6 @@ module ActiveRecord
end
end
- # Identical to delete_all, except that the return value is the association (for chaining)
- # rather than the records which have been removed.
- def clear
- delete_all
- self
- end
-
# Destroy all the records from this association.
#
# See destroy for more info.
@@ -254,7 +251,7 @@ module ActiveRecord
end
end
- def uniq(collection = self)
+ def uniq(collection = load_target)
seen = {}
collection.find_all do |record|
seen[record.id] = true unless seen.key?(record.id)
@@ -291,70 +288,50 @@ module ActiveRecord
end
end
- def respond_to?(method, include_private = false)
- super || @reflection.klass.respond_to?(method, include_private)
+ def cached_scope(method, args)
+ @scopes_cache[method][args] ||= scoped.readonly(nil).send(method, *args)
end
- def method_missing(method, *args, &block)
- match = DynamicFinderMatch.match(method)
- if match && match.creator?
- attributes = match.attribute_names
- return send(:"find_by_#{attributes.join('_and_')}", *args) || create(Hash[attributes.zip(args)])
- end
-
- if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method))
- super
- elsif @reflection.klass.scopes[method]
- @_scopes_cache ||= {}
- @_scopes_cache[method] ||= {}
- @_scopes_cache[method][args] ||= scoped.readonly(nil).send(method, *args)
- else
- scoped.readonly(nil).send(method, *args, &block)
- end
+ def association_scope
+ options = @reflection.options.slice(:order, :limit, :joins, :group, :having, :offset)
+ super.apply_finder_options(options)
end
- protected
-
- def association_scope
- options = @reflection.options.slice(:order, :limit, :joins, :group, :having, :offset)
- super.apply_finder_options(options)
- end
-
- def load_target
- if find_target?
- targets = []
+ def load_target
+ if find_target?
+ targets = []
- begin
- targets = find_target
- rescue ActiveRecord::RecordNotFound
- reset
- end
-
- @target = merge_target_lists(targets, @target)
+ begin
+ targets = find_target
+ rescue ActiveRecord::RecordNotFound
+ reset
end
- loaded!
- target
+ @target = merge_target_lists(targets, @target)
end
- def add_to_target(record)
- transaction do
- callback(:before_add, record)
- yield(record) if block_given?
+ loaded!
+ target
+ end
- if @reflection.options[:uniq] && index = @target.index(record)
- @target[index] = record
- else
- @target << record
- end
+ def add_to_target(record)
+ transaction do
+ callback(:before_add, record)
+ yield(record) if block_given?
- callback(:after_add, record)
- set_inverse_instance(record)
+ if @reflection.options[:uniq] && index = @target.index(record)
+ @target[index] = record
+ else
+ @target << record
end
- record
+ callback(:after_add, record)
+ set_inverse_instance(record)
end
+ record
+ end
+
private
def select_value
@@ -498,8 +475,8 @@ module ActiveRecord
def include_in_memory?(record)
if @reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
- @owner.send(proxy_reflection.through_reflection.name).any? { |source|
- target = source.send(proxy_reflection.source_reflection.name)
+ @owner.send(@reflection.through_reflection.name).any? { |source|
+ target = source.send(@reflection.source_reflection.name)
target.respond_to?(:include?) ? target.include?(record) : target == record
} || @target.include?(record)
else
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
new file mode 100644
index 0000000000..cf77d770c9
--- /dev/null
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -0,0 +1,127 @@
+module ActiveRecord
+ module Associations
+ # Association proxies in Active Record are middlemen between the object that
+ # holds the association, known as the <tt>@owner</tt>, and the actual associated
+ # object, known as the <tt>@target</tt>. The kind of association any proxy is
+ # about is available in <tt>@reflection</tt>. That's an instance of the class
+ # ActiveRecord::Reflection::AssociationReflection.
+ #
+ # For example, given
+ #
+ # class Blog < ActiveRecord::Base
+ # has_many :posts
+ # end
+ #
+ # blog = Blog.find(:first)
+ #
+ # the association proxy in <tt>blog.posts</tt> has the object in +blog+ as
+ # <tt>@owner</tt>, the collection of its posts as <tt>@target</tt>, and
+ # the <tt>@reflection</tt> object represents a <tt>:has_many</tt> macro.
+ #
+ # This class has most of the basic instance methods removed, and delegates
+ # unknown methods to <tt>@target</tt> via <tt>method_missing</tt>. As a
+ # corner case, it even removes the +class+ method and that's why you get
+ #
+ # blog.posts.class # => Array
+ #
+ # though the object behind <tt>blog.posts</tt> is not an Array, but an
+ # ActiveRecord::Associations::HasManyAssociation.
+ #
+ # The <tt>@target</tt> object is not \loaded until needed. For example,
+ #
+ # blog.posts.count
+ #
+ # is computed directly through SQL and does not trigger by itself the
+ # instantiation of the actual post records.
+ class CollectionProxy # :nodoc:
+ alias :proxy_extend :extend
+
+ instance_methods.each { |m| undef_method m unless m.to_s =~ /^(?:nil\?|send|object_id|to_a)$|^__|^respond_to|proxy_/ }
+
+ delegate :group, :order, :limit, :joins, :where, :preload, :eager_load, :includes, :from,
+ :lock, :readonly, :having, :to => :scoped
+
+ delegate :target, :load_target, :loaded?, :scoped,
+ :to => :@association
+
+ delegate :select, :find, :first, :last,
+ :build, :create, :create!,
+ :concat, :delete_all, :destroy_all, :delete, :destroy, :uniq,
+ :sum, :count, :size, :length, :empty?,
+ :any?, :many?, :include?,
+ :to => :@association
+
+ def initialize(association)
+ @association = association
+ Array.wrap(association.options[:extend]).each { |ext| proxy_extend(ext) }
+ end
+
+ def respond_to?(*args)
+ super ||
+ (load_target && target.respond_to?(*args)) ||
+ @association.klass.respond_to?(*args)
+ end
+
+ def method_missing(method, *args, &block)
+ match = DynamicFinderMatch.match(method)
+ if match && match.creator?
+ attributes = match.attribute_names
+ return send(:"find_by_#{attributes.join('_and_')}", *args) || create(Hash[attributes.zip(args)])
+ end
+
+ if target.respond_to?(method) || (!@association.klass.respond_to?(method) && Class.respond_to?(method))
+ if load_target
+ if target.respond_to?(method)
+ target.send(method, *args, &block)
+ else
+ begin
+ super
+ rescue NoMethodError => e
+ raise e, e.message.sub(/ for #<.*$/, " via proxy for #{target}")
+ end
+ end
+ end
+
+ elsif @association.klass.scopes[method]
+ @association.cached_scope(method, args)
+ else
+ scoped.readonly(nil).send(method, *args, &block)
+ end
+ end
+
+ # Forwards <tt>===</tt> explicitly to the \target because the instance method
+ # removal above doesn't catch it. Loads the \target if needed.
+ def ===(other)
+ other === load_target
+ end
+
+ def to_ary
+ load_target.dup
+ end
+ alias_method :to_a, :to_ary
+
+ def <<(*records)
+ @association.concat(records) && self
+ end
+ alias_method :push, :<<
+
+ def clear
+ delete_all
+ self
+ end
+
+ def reload
+ @association.reload
+ self
+ end
+
+ def new(*args, &block)
+ if @association.is_a?(HasManyThroughAssociation)
+ @association.build(*args, &block)
+ else
+ method_missing(:new, *args, &block)
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
index b9c9919e7a..bcaea5ded4 100644
--- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
@@ -1,7 +1,7 @@
module ActiveRecord
# = Active Record Has And Belongs To Many Association
module Associations
- class HasAndBelongsToManyAssociation < AssociationCollection #:nodoc:
+ class HasAndBelongsToManyAssociation < CollectionAssociation #:nodoc:
attr_reader :join_table
def initialize(owner, reflection)
@@ -9,28 +9,26 @@ module ActiveRecord
super
end
- protected
+ def insert_record(record, validate = true)
+ return if record.new_record? && !record.save(:validate => validate)
- def insert_record(record, validate = true)
- return if record.new_record? && !record.save(:validate => validate)
+ if @reflection.options[:insert_sql]
+ @owner.connection.insert(interpolate(@reflection.options[:insert_sql], record))
+ else
+ stmt = join_table.compile_insert(
+ join_table[@reflection.foreign_key] => @owner.id,
+ join_table[@reflection.association_foreign_key] => record.id
+ )
- if @reflection.options[:insert_sql]
- @owner.connection.insert(interpolate(@reflection.options[:insert_sql], record))
- else
- stmt = join_table.compile_insert(
- join_table[@reflection.foreign_key] => @owner.id,
- join_table[@reflection.association_foreign_key] => record.id
- )
-
- @owner.connection.insert stmt.to_sql
- end
-
- record
+ @owner.connection.insert stmt.to_sql
end
- def association_scope
- super.joins(construct_joins)
- end
+ record
+ end
+
+ def association_scope
+ super.joins(construct_joins)
+ end
private
diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb
index 543b073393..91565b247a 100644
--- a/activerecord/lib/active_record/associations/has_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_association.rb
@@ -5,13 +5,11 @@ module ActiveRecord
#
# If the association has a <tt>:through</tt> option further specialization
# is provided by its child HasManyThroughAssociation.
- class HasManyAssociation < AssociationCollection #:nodoc:
- protected
-
- def insert_record(record, validate = true)
- set_owner_attributes(record)
- record.save(:validate => validate)
- end
+ class HasManyAssociation < CollectionAssociation #:nodoc:
+ def insert_record(record, validate = true)
+ set_owner_attributes(record)
+ record.save(:validate => validate)
+ end
private
diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb
index c4d3ef8fef..664c284d45 100644
--- a/activerecord/lib/active_record/associations/has_many_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_through_association.rb
@@ -22,7 +22,7 @@ module ActiveRecord
end
end
- def <<(*records)
+ def concat(*records)
unless @owner.new_record?
records.flatten.each do |record|
raise_on_type_mismatch(record)
@@ -33,19 +33,17 @@ module ActiveRecord
super
end
- protected
-
- def insert_record(record, validate = true)
- return if record.new_record? && !record.save(:validate => validate)
- through_record(record).save!
- update_counter(1)
- record
- end
+ def insert_record(record, validate = true)
+ return if record.new_record? && !record.save(:validate => validate)
+ through_record(record).save!
+ update_counter(1)
+ record
+ end
private
def through_record(record)
- through_association = @owner.send(:association_proxy, @reflection.through_reflection.name)
+ through_association = @owner.association(@reflection.through_reflection.name)
attributes = construct_join_attributes(record)
through_record = Array.wrap(through_association.target).find { |candidate|
@@ -95,7 +93,7 @@ module ActiveRecord
end
def delete_records(records, method)
- through = @owner.send(:association_proxy, @reflection.through_reflection.name)
+ through = @owner.association(@reflection.through_reflection.name)
scope = through.scoped.where(construct_join_attributes(*records))
case method
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 149daf69f7..112b773ec4 100644
--- a/activerecord/lib/active_record/associations/has_one_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_one_through_association.rb
@@ -12,7 +12,7 @@ module ActiveRecord
private
def create_through_record(record)
- through_proxy = @owner.send(:association_proxy, @reflection.through_reflection.name)
+ through_proxy = @owner.association(@reflection.through_reflection.name)
through_record = through_proxy.send(:load_target)
if through_record && !record
diff --git a/activerecord/lib/active_record/associations/singular_association.rb b/activerecord/lib/active_record/associations/singular_association.rb
index 7f92d9712a..0aa647c63d 100644
--- a/activerecord/lib/active_record/associations/singular_association.rb
+++ b/activerecord/lib/active_record/associations/singular_association.rb
@@ -1,6 +1,6 @@
module ActiveRecord
module Associations
- class SingularAssociation < AssociationProxy #:nodoc:
+ class SingularAssociation < Association #:nodoc:
def create(attributes = {})
new_record(:create, attributes)
end
@@ -29,7 +29,7 @@ module ActiveRecord
end
def check_record(record)
- record = record.target if AssociationProxy === record
+ record = record.target if Association === record
raise_on_type_mismatch(record) if record
record
end