From 05f1a9bcc3c9223768187e2379b508638dfa19b6 Mon Sep 17 00:00:00 2001
From: Jon Leighton <j@jonathanleighton.com>
Date: Wed, 27 Jul 2011 12:36:00 +0100
Subject: Add a proxy_association method to association proxies, which can be
 called by association extensions to access information about the association.
 This replaces proxy_owner etc with proxy_association.owner.

---
 activerecord/CHANGELOG                                   |  6 ++++++
 activerecord/lib/active_record/associations.rb           |  6 ++++++
 .../lib/active_record/associations/collection_proxy.rb   | 16 ++++++++++------
 activerecord/test/cases/associations_test.rb             |  5 +++++
 4 files changed, 27 insertions(+), 6 deletions(-)

diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index e448609cf4..e8d4b9c04e 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -24,6 +24,12 @@ a URI that specifies the connection configuration.  For example:
 
 *Rails 3.1.0 (unreleased)*
 
+* Add a proxy_association method to association proxies, which can be called by association
+  extensions to access information about the association. This replaces proxy_owner etc with
+  proxy_association.owner.
+
+  [Jon Leighton]
+
 * ActiveRecord::MacroReflection::AssociationReflection#build_record has a new method signature.
 
   Before: def build_association(*options)
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 029d7a9b15..2605a54cb6 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -460,6 +460,12 @@ module ActiveRecord
     # * <tt>record.association(:items).target</tt> - Returns the associated object for +belongs_to+ and +has_one+, or
     #   the collection of associated objects for +has_many+ and +has_and_belongs_to_many+.
     #
+    # However, inside the actual extension code, you will not have access to the <tt>record</tt> as
+    # above. In this case, you can access <tt>proxy_association</tt>. For example,
+    # <tt>record.association(:items)</tt> and <tt>record.items.proxy_association</tt> will return
+    # the same object, allowing you to make calls like <tt>proxy_association.owner</tt> inside
+    # association extensions.
+    #
     # === Association Join Models
     #
     # Has Many associations can be configured with the <tt>:through</tt> option to use an
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index 827dfb7ccb..6ba3d45aff 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -58,23 +58,27 @@ module ActiveRecord
 
       alias_method :new, :build
 
+      def proxy_association
+        @association
+      end
+
       def respond_to?(name, include_private = false)
         super ||
         (load_target && target.respond_to?(name, include_private)) ||
-        @association.klass.respond_to?(name, include_private)
+        proxy_association.klass.respond_to?(name, include_private)
       end
 
       def method_missing(method, *args, &block)
         match = DynamicFinderMatch.match(method)
         if match && match.instantiator?
           send(:find_or_instantiator_by_attributes, match, match.attribute_names, *args) do |r|
-            @association.send :set_owner_attributes, r
-            @association.send :add_to_target, r
+            proxy_association.send :set_owner_attributes, r
+            proxy_association.send :add_to_target, r
             yield(r) if block_given?
           end
         end
 
-        if target.respond_to?(method) || (!@association.klass.respond_to?(method) && Class.respond_to?(method))
+        if target.respond_to?(method) || (!proxy_association.klass.respond_to?(method) && Class.respond_to?(method))
           if load_target
             if target.respond_to?(method)
               target.send(method, *args, &block)
@@ -104,7 +108,7 @@ module ActiveRecord
       alias_method :to_a, :to_ary
 
       def <<(*records)
-        @association.concat(records) && self
+        proxy_association.concat(records) && self
       end
       alias_method :push, :<<
 
@@ -114,7 +118,7 @@ module ActiveRecord
       end
 
       def reload
-        @association.reload
+        proxy_association.reload
         self
       end
     end
diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb
index 49d82ba2df..ffe2993e0f 100644
--- a/activerecord/test/cases/associations_test.rb
+++ b/activerecord/test/cases/associations_test.rb
@@ -203,6 +203,11 @@ class AssociationProxyTest < ActiveRecord::TestCase
       assert_equal david.projects, david.projects.reload.reload
     end
   end
+
+  def test_proxy_association_accessor
+    david = developers(:david)
+    assert_equal david.association(:projects), david.projects.proxy_association
+  end
 end
 
 class OverridingAssociationsTest < ActiveRecord::TestCase
-- 
cgit v1.2.3