class ActsAsRecursiveTree::Builders::RelationBuilder

Constructs the Arel necessary for recursion.

Attributes

ids[R]
klass[R]
recursive_temp_table[R]
travers_loc_table[R]
without_ids[R]

Public Class Methods

build(klass, ids, exclude_ids: false, &block) click to toggle source
# File lib/acts_as_recursive_tree/builders/relation_builder.rb, line 9
def self.build(klass, ids, exclude_ids: false, &block)
  new(klass, ids, exclude_ids: exclude_ids, &block).build
end
new(klass, ids, exclude_ids: false, &block) click to toggle source
# File lib/acts_as_recursive_tree/builders/relation_builder.rb, line 23
def initialize(klass, ids, exclude_ids: false, &block)
  @klass       = klass
  @config      = klass._recursive_tree_config
  @ids         = ActsAsRecursiveTree::Options::Values.create(ids, @config)
  @without_ids = exclude_ids

  @query_opts = get_query_options(block)

  rand_int              = random.rand(1_000_000)
  @recursive_temp_table = Arel::Table.new("recursive_#{klass.table_name}_#{rand_int}_temp")
  @travers_loc_table    = Arel::Table.new("traverse_#{rand_int}_loc")
end

Public Instance Methods

apply_depth(select_manager) click to toggle source
# File lib/acts_as_recursive_tree/builders/relation_builder.rb, line 67
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) click to toggle source
# File lib/acts_as_recursive_tree/builders/relation_builder.rb, line 61
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) click to toggle source
# File lib/acts_as_recursive_tree/builders/relation_builder.rb, line 118
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) click to toggle source
# File lib/acts_as_recursive_tree/builders/relation_builder.rb, line 134
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_table() click to toggle source
# File lib/acts_as_recursive_tree/builders/relation_builder.rb, line 51
def base_table
  klass.arel_table
end
build() click to toggle source
# File lib/acts_as_recursive_tree/builders/relation_builder.rb, line 55
def build
  relation = Strategies.for_query_options(@query_opts).build(self)

  apply_except_id(relation)
end
build_base_join_select(select_manager) click to toggle source
# File lib/acts_as_recursive_tree/builders/relation_builder.rb, line 124
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_select() click to toggle source
# File lib/acts_as_recursive_tree/builders/relation_builder.rb, line 92
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_table() click to toggle source
# File lib/acts_as_recursive_tree/builders/relation_builder.rb, line 85
def build_cte_table
  Arel::Nodes::As.new(
    travers_loc_table,
    build_base_select.union(build_union_select)
  )
end
build_union_select() click to toggle source
# File lib/acts_as_recursive_tree/builders/relation_builder.rb, line 104
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
create_select_manger(column = nil) click to toggle source
# File lib/acts_as_recursive_tree/builders/relation_builder.rb, line 73
def create_select_manger(column = nil)
  projections = if column
                  travers_loc_table[column]
                else
                  Arel.star
                end

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

  apply_depth(select_mgr)
end
get_query_options(proc) click to toggle source

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

@param proc [Proc] a proc or nil

@return [ActsAsRecursiveTree::Options::QueryOptions] the new QueryOptions instance

# File lib/acts_as_recursive_tree/builders/relation_builder.rb, line 43
def get_query_options(proc)
  opts = ActsAsRecursiveTree::Options::QueryOptions.new

  proc&.call(opts)

  opts
end