aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/associations/association_proxy.rb
diff options
context:
space:
mode:
authorJon Leighton <j@jonathanleighton.com>2011-01-06 18:04:32 +0000
committerAaron Patterson <aaron.patterson@gmail.com>2011-01-07 15:03:15 -0800
commit770e6893b9f2aaaebe3de10576931dc7194451bc (patch)
treea69337db0ed7743d302a9d54c142efa5447ab810 /activerecord/lib/active_record/associations/association_proxy.rb
parent441118458d57011ee1b1f1dcfea558de462c6da9 (diff)
downloadrails-770e6893b9f2aaaebe3de10576931dc7194451bc.tar.gz
rails-770e6893b9f2aaaebe3de10576931dc7194451bc.tar.bz2
rails-770e6893b9f2aaaebe3de10576931dc7194451bc.zip
Construct an actual ActiveRecord::Relation object for the association scope, rather than a hash which is passed to apply_finder_options. This allows more flexibility in how the scope is created, for example because scope.where(a, b) and scope.where(a).where(b) mean different things.
Diffstat (limited to 'activerecord/lib/active_record/associations/association_proxy.rb')
-rw-r--r--activerecord/lib/active_record/associations/association_proxy.rb58
1 files changed, 42 insertions, 16 deletions
diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb
index 8e64056a06..405a0307c1 100644
--- a/activerecord/lib/active_record/associations/association_proxy.rb
+++ b/activerecord/lib/active_record/associations/association_proxy.rb
@@ -165,9 +165,7 @@ module ActiveRecord
end
def scoped
- target_scope.
- apply_finder_options(@finder_options).
- create_with(@creation_attributes)
+ target_scope & @association_scope
end
protected
@@ -180,27 +178,32 @@ module ActiveRecord
@reflection.klass.send(:sanitize_sql, sql, table_name)
end
- # Construct the data used for the scope for this association
+ # Construct the scope for this association.
#
- # Note that we don't actually build the scope here, we just construct the options and
- # attributes. We must only build the scope when it's actually needed, because at that
- # point the call may be surrounded by scope.scoping { ... } or with_scope { ... } etc,
- # which affects the scope which actually gets built.
+ # 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
- if target_klass
- @finder_options = finder_options
- @creation_attributes = creation_attributes
- end
+ @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(:conditions, :readonly, :include))
+ scope = scope.where(construct_owner_conditions)
+ scope = scope.select(select_value) if select_value = self.select_value
+ scope
end
- # Implemented by subclasses
- def finder_options
- raise NotImplementedError
+ def select_value
+ @reflection.options[:select]
end
# Implemented by (some) subclasses
def creation_attributes
- {}
+ { }
end
def aliased_table
@@ -226,6 +229,29 @@ module ActiveRecord
target_klass.scoped
end
+ # Returns a hash linking the owner to the association represented by the reflection
+ def construct_owner_attributes(reflection = @reflection)
+ attributes = {}
+ if reflection.macro == :belongs_to
+ attributes[reflection.association_primary_key] = @owner[reflection.foreign_key]
+ else
+ attributes[reflection.foreign_key] = @owner[reflection.active_record_primary_key]
+
+ if reflection.options[:as]
+ attributes["#{reflection.options[:as]}_type"] = @owner.class.base_class.name
+ end
+ end
+ attributes
+ end
+
+ # Builds an array of arel nodes from the owner attributes hash
+ def construct_owner_conditions(table = aliased_table, reflection = @reflection)
+ conditions = construct_owner_attributes(reflection).map do |attr, value|
+ table[attr].eq(value)
+ end
+ table.create_and(conditions)
+ end
+
private
# Forwards any missing method call to the \target.
def method_missing(method, *args)