Class: ActionBlocks::SelectionsViaWhereEngine

Inherits:
Object
  • Object
show all
Defined in:
lib/action_blocks/data_engine/selections_via_where_engine.rb

Overview

Data Engine

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(root_klass, user: nil, table_alias_prefix: nil, type: :many_to_many, selection_filter_reqs: [], selection_match_reqs: [], additional_where: nil) ⇒ SelectionsViaWhereEngine

Returns a new instance of SelectionsViaWhereEngine.



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# File 'lib/action_blocks/data_engine/selections_via_where_engine.rb', line 6

def initialize(root_klass, user: nil, table_alias_prefix: nil, type: :many_to_many, selection_filter_reqs: [], selection_match_reqs: [], additional_where: nil)
  @root_klass = root_klass
  @table_alias_prefix = table_alias_prefix
  @tables = {}
  @joins = {}
  @wheres = []
  @froms = []
  @type = type

  @additional_where = additional_where

  # I named them selection_match_reqs because
  # the DataEngine may work with match_reqs in
  # different contexts.  It may use them for
  # summary fields or it may use them for
  # a filtering a query to the children of some
  # base models 'children'
  @selection_match_reqs = selection_match_reqs
  @selection_filter_reqs = selection_filter_reqs
end

Instance Attribute Details

#joins ⇒ Object

Returns the value of attribute joins.



4
5
6
# File 'lib/action_blocks/data_engine/selections_via_where_engine.rb', line 4

def joins
  @joins
end

#root_klass ⇒ Object

Returns the value of attribute root_klass.



4
5
6
# File 'lib/action_blocks/data_engine/selections_via_where_engine.rb', line 4

def root_klass
  @root_klass
end

#selection_filter_reqs ⇒ Object

Returns the value of attribute selection_filter_reqs.



4
5
6
# File 'lib/action_blocks/data_engine/selections_via_where_engine.rb', line 4

def selection_filter_reqs
  @selection_filter_reqs
end

#selection_match_reqs ⇒ Object

Returns the value of attribute selection_match_reqs.



4
5
6
# File 'lib/action_blocks/data_engine/selections_via_where_engine.rb', line 4

def selection_match_reqs
  @selection_match_reqs
end

#selects ⇒ Object

Returns the value of attribute selects.



4
5
6
# File 'lib/action_blocks/data_engine/selections_via_where_engine.rb', line 4

def selects
  @selects
end

#tables ⇒ Object

Returns the value of attribute tables.



4
5
6
# File 'lib/action_blocks/data_engine/selections_via_where_engine.rb', line 4

def tables
  @tables
end

Instance Method Details

#froms ⇒ Object



92
93
94
95
96
97
98
# File 'lib/action_blocks/data_engine/selections_via_where_engine.rb', line 92

def froms
  if @type == :many_to_many
    [@root_table]
  else
    @froms
  end
end

#ordered_joins ⇒ Object



100
101
102
103
104
105
106
# File 'lib/action_blocks/data_engine/selections_via_where_engine.rb', line 100

def ordered_joins
  if @type == :many_to_many
    []
  else
    @joins.values
  end
end

#process ⇒ Object



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/action_blocks/data_engine/selections_via_where_engine.rb', line 27

def process
  @root_table = @root_klass.arel_table.alias([@table_alias_prefix, @root_klass.to_s.underscore.pluralize].compact.join('_'))
  root_key = [@table_alias_prefix, @root_klass.to_s.underscore.pluralize].compact.join('_').to_sym

  @froms << @root_table
  # Add base table to tables
  @tables[root_key.to_sym] = @root_table

  [@selection_match_reqs, @selection_filter_reqs].flatten.compact.each do |matchreq|
    node, *rest = matchreq[:base_path]
    # puts "base node: #{node} rest #{rest}"
    base_expression = walk_selection_match_path(@root_klass, node, root_key, rest)

    node, *rest = matchreq[:related_path]
    # puts "related node: #{node} rest #{rest}"
    related_expression = walk_selection_match_path(@root_klass, node, root_key, rest)

    where = if base_expression.class.ancestors.include?(Arel::Attributes::Attribute)
              base_expression.send(matchreq[:predicate], related_expression)
            else
              related_expression.send(matchreq[:predicate], base_expression)
            end
    @wheres << where
  end
end

#query ⇒ Object



128
129
130
131
132
# File 'lib/action_blocks/data_engine/selections_via_where_engine.rb', line 128

def query
  @root_klass
   .joins(ordered_joins)
    .where(wheres)
end

#subquery_for_many_to_many_selections ⇒ Object



119
120
121
122
123
124
125
126
# File 'lib/action_blocks/data_engine/selections_via_where_engine.rb', line 119

def subquery_for_many_to_many_selections
  # Arel::Distinct.new(@rook_klasas[:id])
  @root_klass
    .from([@froms].flatten)
    .select(@root_table[:id])
    .joins(@joins.values)
    .where(@wheres.reduce(&:and))
end

#walk_selection_match_path(klass, node, parent_key, col_path) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/action_blocks/data_engine/selections_via_where_engine.rb', line 53

def walk_selection_match_path(klass, node, parent_key, col_path)
  # puts "klass: #{klass} node: #{node} parent_key #{parent_key.inspect} col_path #{col_path.inspect}"
  # pp [key, rest_col_path]
  key = if node.class == Class
          [@table_alias_prefix, node.to_s.underscore.pluralize].compact.join('_').to_sym
        else
          [@table_alias_prefix, parent_key, node].compact.join('_').to_sym
        end
  return node if node.class != Symbol && node.class != Class
  if !col_path.empty?
    # Create Arel Table Alias
    if node.class != Class
      relation = klass.reflections[node.to_s]
      klass = relation.klass
      @tables[key] = klass.arel_table.alias(key) unless @tables[key]
      # Create Join
      fk = relation.join_foreign_key
      pk = relation.join_primary_key
      join_on = @tables[key].create_on(@tables[parent_key][fk].eq(@tables[key][pk]))
      @joins[key] = @tables[parent_key].create_join(@tables[key], join_on, Arel::Nodes::OuterJoin)
    else
      klass = node
      unless @tables[key]
        @tables[key] = klass.arel_table.alias(key) unless @tables[key]
        @froms << @tables[key]
      end
    end
    # Recurse
    next_node, *rest = col_path
    return walk_selection_match_path(klass, next_node, key, rest)
  else
    # Return expression
    # puts "parent_key: #{node.to_sym}"
    # puts "node: #{node.to_sym}"
    return @tables[parent_key][node.to_sym]
  end
end

#wheres ⇒ Object



108
109
110
111
112
113
114
115
116
117
# File 'lib/action_blocks/data_engine/selections_via_where_engine.rb', line 108

def wheres
  @wheres << @additional_where if @additional_where
  if @type == :many_to_many && (!@selection_match_reqs.empty? || !@selection_filter_reqs.empty?)
    subquery_arel = subquery_for_many_to_many_selections.arel
    # w = Arel::Nodes::In.new(@root_table[:id], subquery.ast)
    @root_table[:id].in(subquery_arel)
  else
    @wheres.reduce(&:and)
  end
end