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
- #value_reader readonly private
Instance Method Summary
- #add_constraints(scope, owner, chain)
- #apply_scope(scope, table, key, value, create_default: true)
- #eval_scope(reflection, scope, owner)
- #get_chain(reflection, association, tracker, routes)
- #query_constraint(predicate)
- #redundant_join?(item, chain, join) ⇒ Boolean
- #scope(association, routes = nil)
- #join(table, constraint) private
- #last_chain_scope(scope, reflection, owner) private
- #next_chain_scope(scope, reflection, next_reflection) private
Constructor Details
.new(value_reader) ⇒ AssociationScope
# File 'activerecord/lib/active_record/associations/association_scope.rb', line 15
def initialize(value_reader) @value_reader = value_reader end
Class Method Details
.create(&block)
[ GitHub ]# File 'activerecord/lib/active_record/associations/association_scope.rb', line 10
def self.create(&block) block ||= lambda { |owner, column| owner.read_attr(column) } new(block) end
.scope(association, routes = nil)
[ GitHub ]# File 'activerecord/lib/active_record/associations/association_scope.rb', line 6
def self.scope(association, routes = nil) INSTANCE.scope(association, routes) end
Instance Attribute Details
#value_reader (readonly, private)
[ GitHub ]# File 'activerecord/lib/active_record/associations/association_scope.rb', line 39
attr_reader :value_reader
Instance Method Details
#add_constraints(scope, owner, chain)
[ GitHub ]# File 'activerecord/lib/active_record/associations/association_scope.rb', line 118
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, table, key, value, create_default: true)
[ GitHub ]# File 'activerecord/lib/active_record/associations/association_scope.rb', line 157
def apply_scope(scope, table, key, value, create_default: true) if scope.table == table && create_default scope.where!(key => value) else if scope.table != table scope.references_values |= [Arel.sql(table.name, retryable: true)] predicate_builder = TableMetadata.new(table.klass, table).predicate_builder else predicate_builder = scope.predicate_builder end predicate = predicate_builder[key, value] predicate = query_constraint(predicate) unless create_default scope.where!(predicate) end end
#eval_scope(reflection, scope, owner)
[ GitHub ]#get_chain(reflection, association, tracker, routes)
[ GitHub ]# File 'activerecord/lib/active_record/associations/association_scope.rb', line 102
def get_chain(reflection, association, tracker, routes) name = reflection.name chain = [] reflection.chain.zip(routes) do |refl, route| if chain.empty? chain << Reflection::RuntimeReflection.new(reflection, association, route) else aliased_table = tracker.aliased_table_for(refl.klass.arel_table) do refl.alias_candidate(name) end chain << ReflectionProxy.new(refl, aliased_table, route) end end chain end
#join(table, constraint) (private)
[ GitHub ]#last_chain_scope(scope, reflection, owner) (private)
[ GitHub ]# File 'activerecord/lib/active_record/associations/association_scope.rb', line 45
def last_chain_scope(scope, reflection, owner) table = reflection.aliased_table route = reflection.association_route route.each_constraint do |origin_column, destination_column| value = value_reader.call(owner, origin_column) scope = apply_scope(scope, table, destination_column, value, create_default: false) end route.each_reference do |origin_column, destination_column| value = value_reader.call(owner, origin_column) scope = apply_scope(scope, table, destination_column, value) end route.destination_fixed_values.each do |column, value| scope = apply_scope(scope, table, column, value) end scope end
#next_chain_scope(scope, reflection, next_reflection) (private)
[ GitHub ]# File 'activerecord/lib/active_record/associations/association_scope.rb', line 66
def next_chain_scope(scope, reflection, next_reflection) table = reflection.aliased_table foreign_table = next_reflection.aliased_table predicate_builder = scope.predicate_builder route = reflection.association_route constraints = route.each_match.map do |origin_column, destination_column| destination_attribute = predicate_builder.predicate_attribute(table[destination_column]) origin_attribute = predicate_builder.predicate_attribute(foreign_table[origin_column]) destination_attribute.eq(origin_attribute) end.inject(&:and) route.destination_fixed_values.each do |column, value| scope = apply_scope(scope, table, column, value) end scope.joins!(join(foreign_table, constraints)) end
#query_constraint(predicate)
[ GitHub ]# File 'activerecord/lib/active_record/associations/association_scope.rb', line 174
def query_constraint(predicate) # WhereClause#to_h extracts ungrouped equalities for scope_for_create, so by # grouping here, we hide query-only constraints from that extraction and avoid # assigning them to new records. Arel::Nodes::Grouping.new(predicate) end
#redundant_join?(item, chain, join) ⇒ Boolean
# File 'activerecord/lib/active_record/associations/association_scope.rb', line 181
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, routes = nil)
[ GitHub ]# File 'activerecord/lib/active_record/associations/association_scope.rb', line 21
def scope(association, routes = nil) klass = association.klass reflection = association.reflection scope = klass.unscoped owner = association.owner routes ||= reflection.association_route_chain(origin_class: owner.class, destination_class: klass) chain = get_chain(reflection, association, scope.alias_tracker, routes) extensions = reflection.extensions scope.extending!(extensions) unless extensions.empty? scope = add_constraints(scope, owner, chain) scope.default_order!(reflection.[:default_order]) if reflection.[:default_order].present? scope.limit!(1) unless reflection.collection? scope end