Class: ThroughHierarchy::Hierarchicals::Hierarchical

Inherits:
Object
  • Object
show all
Defined in:
lib/through_hierarchy/hierarchicals/hierarchical.rb

Direct Known Subclasses

Instance

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(source, target, hierarchy, as:, parent: nil) ⇒ Hierarchical

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


8
9
10
11
12
13
14
# 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

Instance Attribute Details

#sourceObject (readonly)

Returns the value of attribute source.


4
5
6
# File 'lib/through_hierarchy/hierarchicals/hierarchical.rb', line 4

def source
  @source
end

Instance Method Details

#and_conditions(conditions) ⇒ Object


41
42
43
# File 'lib/through_hierarchy/hierarchicals/hierarchical.rb', line 41

def and_conditions(conditions)
  conditions.reduce{|q, cond| q.and(cond)}
end

#filter(model) ⇒ Object


53
54
55
56
# 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

#filtersObject


49
50
51
# File 'lib/through_hierarchy/hierarchicals/hierarchical.rb', line 49

def filters
  or_conditions(hierarchy_models.map{|model| filter(model)})
end

#foreign_key_columnObject


77
78
79
# File 'lib/through_hierarchy/hierarchicals/hierarchical.rb', line 77

def foreign_key_column
  @source[foreign_key_name]
end

#foreign_key_nameObject


69
70
71
# File 'lib/through_hierarchy/hierarchicals/hierarchical.rb', line 69

def foreign_key_name
  @polymorphic_name.foreign_key
end

#foreign_type_columnObject


81
82
83
# File 'lib/through_hierarchy/hierarchicals/hierarchical.rb', line 81

def foreign_type_column
  @source[foreign_type_name]
end

#foreign_type_nameObject


73
74
75
# File 'lib/through_hierarchy/hierarchicals/hierarchical.rb', line 73

def foreign_type_name
  @polymorphic_name + "_type"
end

#hierarchy_joinsObject

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


37
38
39
# File 'lib/through_hierarchy/hierarchicals/hierarchical.rb', line 37

def hierarchy_joins
  @hierarchy
end

#hierarchy_modelsObject


32
33
34
# File 'lib/through_hierarchy/hierarchicals/hierarchical.rb', line 32

def hierarchy_models
  [@model] + @hierarchy.map{|m| @model.reflect_on_association(m).klass}
end

#hierarchy_rankObject

Sort order for hierarchy shadowing queries


59
60
61
62
63
64
65
66
67
# 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) ⇒ Object

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


97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# 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) ⇒ Object


89
90
91
# File 'lib/through_hierarchy/hierarchicals/hierarchical.rb', line 89

def model_key(model)
  model.arel_table[model.primary_key]
end

#model_type(model) ⇒ Object


85
86
87
# File 'lib/through_hierarchy/hierarchicals/hierarchical.rb', line 85

def model_type(model)
  model.base_class.to_s
end

#or_conditions(conditions) ⇒ Object


45
46
47
# File 'lib/through_hierarchy/hierarchicals/hierarchical.rb', line 45

def or_conditions(conditions)
  conditions.reduce{|q, cond| q.or(cond)}
end

#set_target(target) ⇒ Object


16
17
18
19
# File 'lib/through_hierarchy/hierarchicals/hierarchical.rb', line 16

def set_target(target)
  @target = target
  @model = @target
end

#spawn(source) ⇒ Object

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


28
29
30
# 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) ⇒ Object

Initialize a new copy of self bound to a specific instance


22
23
24
25
# 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