Add PolymorphicReflection and constraints method

`#constraints` builds a flattened version of `scope_chain` to
allow it to be accessible without requiring an index when iterating
over the `scope_chain`
This commit is contained in:
eileencodes 2014-10-18 14:53:21 -04:00
parent 9b366b95f9
commit 08acb4bccb
2 changed files with 62 additions and 4 deletions

@ -131,8 +131,6 @@ def next_chain_scope(scope, table, reflection, connection, assoc_klass, foreign_
def add_constraints(scope, owner, assoc_klass, refl, tracker) def add_constraints(scope, owner, assoc_klass, refl, tracker)
chain = refl.chain chain = refl.chain
scope_chain = refl.scope_chain
connection = tracker.connection
tables = construct_tables(chain, assoc_klass, refl, tracker) tables = construct_tables(chain, assoc_klass, refl, tracker)
@ -140,6 +138,7 @@ def add_constraints(scope, owner, assoc_klass, refl, tracker)
table = tables.last table = tables.last
scope = last_chain_scope(scope, table, owner_reflection, owner, connection, assoc_klass) scope = last_chain_scope(scope, table, owner_reflection, owner, connection, assoc_klass)
# chain.first always == refl
chain.each_with_index do |reflection, i| chain.each_with_index do |reflection, i|
table, foreign_table = tables.shift, tables.first table, foreign_table = tables.shift, tables.first
@ -151,9 +150,11 @@ def add_constraints(scope, owner, assoc_klass, refl, tracker)
is_first_chain = i == 0 is_first_chain = i == 0
klass = is_first_chain ? assoc_klass : reflection.klass klass = is_first_chain ? assoc_klass : reflection.klass
items = reflection.constraints
# Exclude the scope of the association itself, because that # Exclude the scope of the association itself, because that
# was already merged in the #scope method. # was already merged in the #scope method.
scope_chain[i].each do |scope_chain_item| items.each do |scope_chain_item|
item = eval_scope(klass, scope_chain_item, owner) item = eval_scope(klass, scope_chain_item, owner)
if scope_chain_item == refl.scope if scope_chain_item == refl.scope

@ -161,7 +161,12 @@ def source_macro
macro macro
end end
def constraints
scope ? [scope] : []
end
end end
# Base class for AggregateReflection and AssociationReflection. Objects of # Base class for AggregateReflection and AssociationReflection. Objects of
# AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods. # AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
# #
@ -697,13 +702,59 @@ def through_reflection
def chain def chain
@chain ||= begin @chain ||= begin
a = source_reflection.chain a = source_reflection.chain
b = through_reflection.chain b = through_reflection.chain.map(&:dup)
if options[:source_type]
b[0] = PolymorphicReflection.new(b[0], self)
end
chain = a + b chain = a + b
chain[0] = self # Use self so we don't lose the information from :source_type chain[0] = self # Use self so we don't lose the information from :source_type
chain chain
end end
end end
class PolymorphicReflection
def initialize(reflection, prev_reflection)
@reflection = reflection
@prev_reflection = prev_reflection
end
def klass
@reflection.klass
end
def scope
@reflection.scope
end
def table_name
@reflection.table_name
end
def plural_name
@reflection.plural_name
end
def join_keys(assoc_klass)
@reflection.join_keys(assoc_klass)
end
def type
@reflection.type
end
def constraints
[source_type_info]
end
def source_type_info
type = @prev_reflection.foreign_type
source_type = @prev_reflection.options[:source_type]
lambda { |object| where(type => source_type) }
end
end
# Consider the following example: # Consider the following example:
# #
# class Person # class Person
@ -855,6 +906,12 @@ def check_validity!
check_validity_of_inverse! check_validity_of_inverse!
end end
def constraints
scope_chain = source_reflection.constraints
scope_chain << scope if scope
scope_chain
end
protected protected
def actual_source_reflection # FIXME: this is a horrible name def actual_source_reflection # FIXME: this is a horrible name