123456789_123456789_123456789_123456789_123456789_

Class: ActiveRecord::Associations::AssociationScope

Do not use. This class is for internal use only.
Relationships & Source Files
Namespace Children
Classes:
Extension / Inclusion / Inheritance Descendants
Subclasses:
Inherits: Object
Defined in: activerecord/lib/active_record/associations/association_scope.rb

Constant Summary

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.new(value_transformation) ⇒ AssociationScope

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/association_scope.rb', line 15

def initialize(value_transformation)
  @value_transformation = value_transformation
end

Class Method Details

.create(&block)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/association_scope.rb', line 10

def self.create(&block)
  block ||= lambda { |val| val }
  new(block)
end

.get_bind_values(owner, chain)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/association_scope.rb', line 37

def self.get_bind_values(owner, chain)
  binds = []
  last_reflection = chain.last

  binds.push(*last_reflection.join_id_for(owner))
  if last_reflection.type
    binds << owner.class.polymorphic_name
  end

  chain.each_cons(2).each do |reflection, next_reflection|
    if reflection.type
      binds << next_reflection.klass.polymorphic_name
    end
  end
  binds
end

.scope(association)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/association_scope.rb', line 6

def self.scope(association)
  INSTANCE.scope(association)
end

Instance Attribute Details

#value_transformation (readonly, private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/association_scope.rb', line 55

attr_reader :value_transformation

Instance Method Details

#add_constraints(scope, owner, chain)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/association_scope.rb', line 131

def add_constraints(scope, owner, chain)
  scope = last_chain_scope(scope, chain.last, owner)

  chain.each_cons(2) do |reflection, next_reflection|
    scope = next_chain_scope(scope, reflection, next_reflection)
  end

  chain_head = chain.first
  chain.reverse_each do |reflection|
    reflection.constraints.each do |scope_chain_item|
      item = eval_scope(reflection, scope_chain_item, owner)

      if scope_chain_item == chain_head.scope
        scope.merge! item.except(:where, :includes, :unscope, :order)
      elsif !item.references_values.empty?
        item.joins_values = item.joins_values.reject { |join| redundant_join?(item, chain, join) }
        scope.merge! item.only(:joins, :left_outer_joins)

        associations = item.eager_load_values | item.includes_values

        unless associations.empty?
          scope.joins! item.construct_join_dependency(associations, Arel::Nodes::OuterJoin)
        end
      end

      reflection.all_includes do
        scope.includes_values |= item.includes_values
      end

      scope.unscope!(*item.table_name_qualified_unscope_values)
      scope.where_clause += item.where_clause
      scope.order_values = item.order_values | scope.order_values
      scope.default_order_values = item.default_order_values | scope.default_order_values
    end
  end

  scope
end

#apply_scope(scope, reflection, table, key, value)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/association_scope.rb', line 170

def apply_scope(scope, reflection, table, key, value)
  if scope.table == table
    scope.where!(key => value)
  else
    scope.references_values |= [Arel.sql(table.name, retryable: true)]
    predicate_builder = reflection.klass.predicate_builder.with(TableMetadata.new(reflection.klass, table))
    scope.where!(predicate_builder[key, value])
  end
end

#eval_scope(reflection, scope, owner)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/association_scope.rb', line 192

def eval_scope(reflection, scope, owner)
  relation = reflection.build_scope(reflection.aliased_table)
  relation.instance_exec(owner, &scope) || relation
end

#get_chain(reflection, association, tracker)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/association_scope.rb', line 119

def get_chain(reflection, association, tracker)
  name = reflection.name
  chain = [Reflection::RuntimeReflection.new(reflection, association)]
  reflection.chain.drop(1).each do |refl|
    aliased_table = tracker.aliased_table_for(refl.klass.arel_table) do
      refl.alias_candidate(name)
    end
    chain << ReflectionProxy.new(refl, aliased_table)
  end
  chain
end

#join(table, constraint) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/association_scope.rb', line 57

def join(table, constraint)
  Arel::Nodes::LeadingJoin.new(table, Arel::Nodes::On.new(constraint))
end

#last_chain_scope(scope, reflection, owner) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/association_scope.rb', line 61

def last_chain_scope(scope, reflection, owner)
  primary_key = ActiveRecord::Key.for(reflection.join_primary_key)
  foreign_key = ActiveRecord::Key.for(reflection.join_foreign_key)

  table = reflection.aliased_table
  primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
  primary_key_foreign_key_pairs.each do |join_key, foreign_key|
    value = transform_value(owner.read_attribute(foreign_key))
    scope = apply_scope(scope, reflection, table, join_key, value)
  end

  if reflection.type
    polymorphic_type = transform_value(owner.class.polymorphic_name)
    scope = apply_scope(scope, reflection, table, reflection.type, polymorphic_type)
  end

  scope
end

#next_chain_scope(scope, reflection, next_reflection) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/association_scope.rb', line 84

def next_chain_scope(scope, reflection, next_reflection)
  primary_key = ActiveRecord::Key.for(reflection.join_primary_key)
  foreign_key = ActiveRecord::Key.for(reflection.join_foreign_key)

  table = reflection.aliased_table
  foreign_table = next_reflection.aliased_table

  predicate_builder = scope.predicate_builder
  primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
  constraints = primary_key_foreign_key_pairs.map do |join_primary_key, foreign_key|
    join_primary_key_attribute = predicate_builder.predicate_attribute(table[join_primary_key])
    foreign_key_attribute = predicate_builder.predicate_attribute(foreign_table[foreign_key])

    join_primary_key_attribute.eq(foreign_key_attribute)
  end.inject(&:and)

  if reflection.type
    value = transform_value(next_reflection.klass.polymorphic_name)
    scope = apply_scope(scope, reflection, table, reflection.type, value)
  end

  scope.joins!(join(foreign_table, constraints))
end

#redundant_join?(item, chain, join) ⇒ Boolean

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/association_scope.rb', line 180

def redundant_join?(item, chain, join)
  return false unless join.is_a?(Symbol)

  reflection = item.model._reflect_on_association(join)

  # Dropping a collection join would change how many rows the query
  # returns, so only singular ones are considered redundant here.
  return false unless reflection && !reflection.collection? && !reflection.through_reflection?

  chain.drop(1).any? { |chain_reflection| chain_reflection == reflection }
end

#scope(association)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/association_scope.rb', line 21

def scope(association)
  klass = association.klass
  reflection = association.reflection
  scope = klass.unscoped
  owner = association.owner
  chain = get_chain(reflection, association, scope.alias_tracker)

  extensions = reflection.extensions
  scope.extending!(extensions) unless extensions.empty?

  scope = add_constraints(scope, owner, chain)
  scope.default_order!(reflection.options[:default_order]) if reflection.options[:default_order].present?
  scope.limit!(1) unless reflection.collection?
  scope
end

#transform_value(value) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/association_scope.rb', line 80

def transform_value(value)
  value_transformation.call(value)
end