Class: ActsAsRecursiveTree::Builders::RelationBuilder

Inherits:
Object
  • Object
show all
Defined in:
lib/acts_as_recursive_tree/builders/relation_builder.rb

Overview

Constructs the Arel necessary for recursion.

Direct Known Subclasses

Ancestors, Descendants

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(klass, ids, exclude_ids: false, &block) ⇒ RelationBuilder

Returns a new instance of RelationBuilder.



23
24
25
26
27
28
29
30
31
32
# File 'lib/acts_as_recursive_tree/builders/relation_builder.rb', line 23

def initialize(klass, ids, exclude_ids: false, &block)
  @klass       = klass
  @ids         = ActsAsRecursiveTree::Options::Values.create(ids, klass._recursive_tree_config)
  @without_ids = exclude_ids

  @query_opts = get_query_options(&block)

  # random seed for the temp tables
  @rand_int = SecureRandom.rand(1_000_000)
end

Instance Attribute Details

#idsObject (readonly)

Returns the value of attribute ids.



17
18
19
# File 'lib/acts_as_recursive_tree/builders/relation_builder.rb', line 17

def ids
  @ids
end

#klassObject (readonly)

Returns the value of attribute klass.



17
18
19
# File 'lib/acts_as_recursive_tree/builders/relation_builder.rb', line 17

def klass
  @klass
end

#without_idsObject (readonly)

Returns the value of attribute without_ids.



17
18
19
# File 'lib/acts_as_recursive_tree/builders/relation_builder.rb', line 17

def without_ids
  @without_ids
end

Class Method Details

.build(klass, ids, exclude_ids: false, &block) ⇒ Object



11
12
13
# File 'lib/acts_as_recursive_tree/builders/relation_builder.rb', line 11

def self.build(klass, ids, exclude_ids: false, &block)
  new(klass, ids, exclude_ids:, &block).build
end

Instance Method Details

#add_pg_cycle_detection(union_query) ⇒ Object



94
95
96
97
98
99
100
101
102
# File 'lib/acts_as_recursive_tree/builders/relation_builder.rb', line 94

def add_pg_cycle_detection(union_query)
  return union_query unless config.cycle_detection?

  Arel::Nodes::InfixOperation.new(
    '',
    union_query,
    Arel.sql("CYCLE #{primary_key} SET is_cycle USING path")
  )
end

#apply_depth(select_manager) ⇒ Object



71
72
73
74
75
# File 'lib/acts_as_recursive_tree/builders/relation_builder.rb', line 71

def apply_depth(select_manager)
  return select_manager unless depth_present?

  select_manager.where(depth.apply_to(travers_loc_table[depth_column]))
end

#apply_except_id(relation) ⇒ Object



65
66
67
68
69
# File 'lib/acts_as_recursive_tree/builders/relation_builder.rb', line 65

def apply_except_id(relation)
  return relation unless without_ids

  relation.where(ids.apply_negated_to(base_table[primary_key]))
end

#apply_parent_type_column(arel_condition) ⇒ Object



132
133
134
135
136
# File 'lib/acts_as_recursive_tree/builders/relation_builder.rb', line 132

def apply_parent_type_column(arel_condition)
  return arel_condition if parent_type_column.blank?

  arel_condition.and(base_table[parent_type_column].eq(klass.base_class))
end

#apply_query_opts_condition(relation) ⇒ Object



148
149
150
151
152
153
# File 'lib/acts_as_recursive_tree/builders/relation_builder.rb', line 148

def apply_query_opts_condition(relation)
  # check with nil? and not #present?/#blank? which will execute the query
  return relation if condition.nil?

  relation.merge(condition)
end

#base_tableObject



55
56
57
# File 'lib/acts_as_recursive_tree/builders/relation_builder.rb', line 55

def base_table
  klass.arel_table
end

#buildObject



59
60
61
62
63
# File 'lib/acts_as_recursive_tree/builders/relation_builder.rb', line 59

def build
  relation = Strategies.for_query_options(@query_opts).build(self)

  apply_except_id(relation)
end

#build_base_join_select(select_manager) ⇒ Object



138
139
140
141
142
143
144
145
146
# File 'lib/acts_as_recursive_tree/builders/relation_builder.rb', line 138

def build_base_join_select(select_manager)
  klass.select(
    base_table[primary_key],
    base_table[parent_key],
    Arel.sql(
      (travers_loc_table[depth_column] + 1).to_sql
    ).as(depth_column.to_s)
  ).unscope(where: :type).joins(select_manager.join_sources)
end

#build_base_selectObject

Builds SQL: SELECT id, parent_id, 0 AS depth FROM base_table WHERE id = 123



106
107
108
109
110
111
112
113
114
115
116
# File 'lib/acts_as_recursive_tree/builders/relation_builder.rb', line 106

def build_base_select
  id_node = base_table[primary_key]

  base_table.where(
    ids.apply_to(id_node)
  ).project(
    id_node,
    base_table[parent_key],
    Arel.sql('0').as(depth_column.to_s)
  )
end

#build_cte_tableObject



85
86
87
88
89
90
91
92
# File 'lib/acts_as_recursive_tree/builders/relation_builder.rb', line 85

def build_cte_table
  Arel::Nodes::As.new(
    travers_loc_table,
    add_pg_cycle_detection(
      build_base_select.union(build_union_select)
    )
  )
end

#build_union_selectObject



118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/acts_as_recursive_tree/builders/relation_builder.rb', line 118

def build_union_select
  join_condition = apply_parent_type_column(
    traversal_strategy.build(self)
  )

  select_manager = base_table.join(travers_loc_table).on(join_condition)

  # need to use ActiveRecord here for merging relation
  relation = build_base_join_select(select_manager)

  relation = apply_query_opts_condition(relation)
  relation.arel
end

#configObject



42
43
44
# File 'lib/acts_as_recursive_tree/builders/relation_builder.rb', line 42

def config
  klass._recursive_tree_config
end

#create_select_manger(column = nil) ⇒ Object



77
78
79
80
81
82
83
# File 'lib/acts_as_recursive_tree/builders/relation_builder.rb', line 77

def create_select_manger(column = nil)
  projections = column ? travers_loc_table[column] : Arel.star

  select_mgr = travers_loc_table.project(projections).with(:recursive, build_cte_table)

  apply_depth(select_mgr)
end

#get_query_optionsActsAsRecursiveTree::Options::QueryOptions

Constructs a new QueryOptions and yield it to the proc if one is present. Subclasses may override this method to provide sane defaults.

Returns:



51
52
53
# File 'lib/acts_as_recursive_tree/builders/relation_builder.rb', line 51

def get_query_options(&)
  ActsAsRecursiveTree::Options::QueryOptions.from(&)
end

#recursive_temp_tableObject



34
35
36
# File 'lib/acts_as_recursive_tree/builders/relation_builder.rb', line 34

def recursive_temp_table
  @recursive_temp_table ||= Arel::Table.new("recursive_#{klass.table_name}_#{@rand_int}_temp")
end

#travers_loc_tableObject



38
39
40
# File 'lib/acts_as_recursive_tree/builders/relation_builder.rb', line 38

def travers_loc_table
  @travers_loc_table ||= Arel::Table.new("traverse_#{@rand_int}_loc")
end