diff options
Diffstat (limited to 'activerecord/lib/active_record/associations/association_scope.rb')
-rw-r--r-- | activerecord/lib/active_record/associations/association_scope.rb | 69 |
1 files changed, 38 insertions, 31 deletions
diff --git a/activerecord/lib/active_record/associations/association_scope.rb b/activerecord/lib/active_record/associations/association_scope.rb index 0209ce36df..1303822868 100644 --- a/activerecord/lib/active_record/associations/association_scope.rb +++ b/activerecord/lib/active_record/associations/association_scope.rb @@ -6,32 +6,37 @@ module ActiveRecord attr_reader :association, :alias_tracker delegate :klass, :owner, :reflection, :interpolate, :to => :association - delegate :chain, :conditions, :options, :source_options, :active_record, :to => :reflection + delegate :chain, :scope_chain, :options, :source_options, :active_record, :to => :reflection def initialize(association) @association = association - @alias_tracker = AliasTracker.new + @alias_tracker = AliasTracker.new klass.connection end def scope scope = klass.unscoped - scope = scope.extending(*Array(options[:extend])) - - # It's okay to just apply all these like this. The options will only be present if the - # association supports that option; this is enforced by the association builder. - scope = scope.apply_finder_options(options.slice( - :readonly, :include, :references, :order, :limit, :joins, :group, :having, :offset, :select)) + scope.merge! eval_scope(klass, reflection.scope) if reflection.scope + add_constraints(scope) + end - if options[:through] && !options[:include] - scope = scope.includes(source_options[:include]) - end + private - scope = scope.uniq if options[:uniq] + def column_for(table_name, column_name) + columns = alias_tracker.connection.schema_cache.columns_hash[table_name] + columns[column_name] + end - add_constraints(scope) + def bind_value(scope, column, value) + substitute = alias_tracker.connection.substitute_at( + column, scope.bind_values.length) + scope.bind_values += [[column, value]] + substitute end - private + def bind(scope, table_name, column_name, value) + column = column_for table_name, column_name + bind_value scope, column, value + end def add_constraints(scope) tables = construct_tables @@ -64,21 +69,14 @@ module ActiveRecord foreign_key = reflection.active_record_primary_key end - conditions = self.conditions[i] - if reflection == chain.last - scope = scope.where(table[key].eq(owner[foreign_key])) + bind_val = bind scope, table.table_name, key.to_s, owner[foreign_key] + scope = scope.where(table[key].eq(bind_val)) if reflection.type - scope = scope.where(table[reflection.type].eq(owner.class.base_class.name)) - end - - conditions.each do |condition| - if options[:through] && condition.is_a?(Hash) - condition = { table.name => condition } - end - - scope = scope.where(interpolate(condition)) + value = owner.class.base_class.name + bind_val = bind scope, table.table_name, reflection.type.to_s, value + scope = scope.where(table[reflection.type].eq(bind_val)) end else constraint = table[key].eq(foreign_table[foreign_key]) @@ -89,13 +87,15 @@ module ActiveRecord end scope = scope.joins(join(foreign_table, constraint)) + end - conditions.each do |condition| - condition = interpolate(condition) - condition = { (table.table_alias || table.name) => condition } unless i == 0 + # Exclude the scope of the association itself, because that + # was already merged in the #scope method. + (scope_chain[i] - [self.reflection.scope]).each do |scope_chain_item| + item = eval_scope(reflection.klass, scope_chain_item) - scope = scope.where(condition) - end + scope.includes! item.includes_values + scope.where_values += item.where_values end end @@ -117,6 +117,13 @@ module ActiveRecord end end + def eval_scope(klass, scope) + if scope.is_a?(Relation) + scope + else + klass.unscoped.instance_exec(owner, &scope) + end + end end end end |