class ThroughHierarchy::Hierarchicals::Hierarchical

Attributes

source[R]

Public Class Methods

new(source, target, hierarchy, as:, parent: nil) click to toggle source

source should be an Arel::Table or Arel::TableAlias TODO: parent only on derived tables. Make that a separate class or module.

# File lib/through_hierarchy/hierarchicals/hierarchical.rb, line 8
def initialize(source, target, hierarchy, as:, parent: nil)
  @source = source
  set_target(target)
  @hierarchy = hierarchy
  @polymorphic_name = as.to_s
  @parent = parent
end

Public Instance Methods

and_conditions(conditions) click to toggle source
# File lib/through_hierarchy/hierarchicals/hierarchical.rb, line 41
def and_conditions(conditions)
  conditions.reduce{|q, cond| q.and(cond)}
end
filter(model) click to toggle source
# File lib/through_hierarchy/hierarchicals/hierarchical.rb, line 53
def filter(model)
  foreign_type_column.eq(model_type(model)).
    and(foreign_key_column.eq(model_key(model)))
end
filters() click to toggle source
# File lib/through_hierarchy/hierarchicals/hierarchical.rb, line 49
def filters
  or_conditions(hierarchy_models.map{|model| filter(model)})
end
foreign_key_column() click to toggle source
# File lib/through_hierarchy/hierarchicals/hierarchical.rb, line 77
def foreign_key_column
  @source[foreign_key_name]
end
foreign_key_name() click to toggle source
# File lib/through_hierarchy/hierarchicals/hierarchical.rb, line 69
def foreign_key_name
  @polymorphic_name.foreign_key
end
foreign_type_column() click to toggle source
# File lib/through_hierarchy/hierarchicals/hierarchical.rb, line 81
def foreign_type_column
  @source[foreign_type_name]
end
foreign_type_name() click to toggle source
# File lib/through_hierarchy/hierarchicals/hierarchical.rb, line 73
def foreign_type_name
  @polymorphic_name + "_type"
end
hierarchy_joins() click to toggle source

TODO: some of these may be :through others, so this may generate redundant joins

# File lib/through_hierarchy/hierarchicals/hierarchical.rb, line 37
def hierarchy_joins
  @hierarchy
end
hierarchy_models() click to toggle source
# File lib/through_hierarchy/hierarchicals/hierarchical.rb, line 32
def hierarchy_models
  [@model] + @hierarchy.map{|m| @model.reflect_on_association(m).klass}
end
hierarchy_rank() click to toggle source

Sort order for hierarchy shadowing queries

# File lib/through_hierarchy/hierarchicals/hierarchical.rb, line 59
def hierarchy_rank
  Arel.sql(
    "CASE `#{@source.name}`.`#{foreign_type_name}` " +
    hierarchy_models.map.with_index do |model, ii|
      "WHEN #{ThroughHierarchy::RailsUtils.sanitize_sql(model.base_class.to_s)} THEN #{ii} "
    end.join +
    "END"
  )
end
join_best_rank(group_by: nil) click to toggle source

Join @model to @source only on best hierarchy matches

FASTER METHOD: join source to source alias on source.rank < alias.rank where alias does not exist

This performs OK. TODO: return arel once we know how to use the binds properly

# File lib/through_hierarchy/hierarchicals/hierarchical.rb, line 97
def join_best_rank(group_by: nil)
  better_rank = spawn(@source.alias("better_hierarchy"))
  join_condition_array = [
    better_rank.filters,
    better_rank.hierarchy_rank.lt(hierarchy_rank)
  ]
  join_condition_array << better_rank.source[group_by].eq(@source[group_by]) if group_by.present?
  arel = @model.arel_table.
    join(@source).on(filters).
    join(better_rank.source, Arel::Nodes::OuterJoin).
    on(and_conditions(join_condition_array)).
    where(better_rank.source[:id].eq(nil))
  result = @model.joins(@hierarchy).joins(arel.join_sources).order(arel.orders)
    arel.constraints.each{|cc| result = result.where(cc)}
    return result
end
model_key(model) click to toggle source
# File lib/through_hierarchy/hierarchicals/hierarchical.rb, line 89
def model_key(model)
  model.arel_table[model.primary_key]
end
model_type(model) click to toggle source
# File lib/through_hierarchy/hierarchicals/hierarchical.rb, line 85
def model_type(model)
  model.base_class.to_s
end
or_conditions(conditions) click to toggle source
# File lib/through_hierarchy/hierarchicals/hierarchical.rb, line 45
def or_conditions(conditions)
  conditions.reduce{|q, cond| q.or(cond)}
end
set_target(target) click to toggle source
# File lib/through_hierarchy/hierarchicals/hierarchical.rb, line 16
def set_target(target)
  @target = target
  @model = @target
end
spawn(source) click to toggle source

Intialize a copy of self with a new / derived source table

# File lib/through_hierarchy/hierarchicals/hierarchical.rb, line 28
def spawn(source)
  return self.class.new(source, @target, @hierarchy, as: @polymorphic_name, parent: self)
end
with_instance(instance) click to toggle source

Initialize a new copy of self bound to a specific instance

# File lib/through_hierarchy/hierarchicals/hierarchical.rb, line 22
def with_instance(instance)
  instance.is_a?(@model) or raise ThroughHierarchyInstanceError, "#{instance} is not an instance of #{@model}"
  Instance.new(@source, instance, @hierarchy, as: @polymorphic_name)
end