module Ancestry::MaterializedPath

store ancestry as grandparent_id/parent_id root a=nil,id=1 children=id,id/% == 1, 1/% 3: a=1/2,id=3 children=a/id,a/id/% == 1/2/3, 1/2/3/%

Public Class Methods

extended(base) click to toggle source
# File lib/ancestry/materialized_path.rb, line 6
def self.extended(base)
  base.send(:include, InstanceMethods)
end

Public Instance Methods

ancestors_of(object) click to toggle source
# File lib/ancestry/materialized_path.rb, line 18
def ancestors_of(object)
  t = arel_table
  node = to_node(object)
  where(t[primary_key].in(node.ancestor_ids))
end
ancestry_root() click to toggle source
# File lib/ancestry/materialized_path.rb, line 91
def ancestry_root
  nil
end
children_of(object) click to toggle source
# File lib/ancestry/materialized_path.rb, line 30
def children_of(object)
  t = arel_table
  node = to_node(object)
  where(t[ancestry_column].eq(node.child_ancestry))
end
descendant_before_save_conditions(object) click to toggle source
# File lib/ancestry/materialized_path.rb, line 57
def descendant_before_save_conditions(object)
  node = to_node(object)
  descendants_by_ancestry( node.child_ancestry_before_save )
end
descendant_conditions(object) click to toggle source
# File lib/ancestry/materialized_path.rb, line 52
def descendant_conditions(object)
  node = to_node(object)
  descendants_by_ancestry( node.child_ancestry )
end
descendants_by_ancestry(ancestry) click to toggle source
# File lib/ancestry/materialized_path.rb, line 47
def descendants_by_ancestry(ancestry)
  t = arel_table
  t[ancestry_column].matches("#{ancestry}#{ancestry_delimiter}%", nil, true).or(t[ancestry_column].eq(ancestry))
end
descendants_of(object) click to toggle source
# File lib/ancestry/materialized_path.rb, line 43
def descendants_of(object)
  where(descendant_conditions(object))
end
indirects_of(object) click to toggle source

indirect = anyone who is a descendant, but not a child

# File lib/ancestry/materialized_path.rb, line 37
def indirects_of(object)
  t = arel_table
  node = to_node(object)
  where(t[ancestry_column].matches("#{node.child_ancestry}#{ancestry_delimiter}%", nil, true))
end
inpath_of(object) click to toggle source
# File lib/ancestry/materialized_path.rb, line 24
def inpath_of(object)
  t = arel_table
  node = to_node(object)
  where(t[primary_key].in(node.path_ids))
end
ordered_by_ancestry(order = nil) click to toggle source
# File lib/ancestry/materialized_path.rb, line 74
def ordered_by_ancestry(order = nil)
  if %w(mysql mysql2 sqlite sqlite3).include?(connection.adapter_name.downcase)
    reorder(arel_table[ancestry_column], order)
  elsif %w(postgresql oracleenhanced).include?(connection.adapter_name.downcase) && ActiveRecord::VERSION::STRING >= "6.1"
    reorder(Arel::Nodes::Ascending.new(arel_table[ancestry_column]).nulls_first, order)
  else
    reorder(
      Arel::Nodes::Ascending.new(Arel::Nodes::NamedFunction.new('COALESCE', [arel_table[ancestry_column], Arel.sql("''")])),
      order
    )
  end
end
ordered_by_ancestry_and(order) click to toggle source
# File lib/ancestry/materialized_path.rb, line 87
def ordered_by_ancestry_and(order)
  ordered_by_ancestry(order)
end
path_of(object) click to toggle source
# File lib/ancestry/materialized_path.rb, line 10
def path_of(object)
  to_node(object).path
end
roots() click to toggle source
# File lib/ancestry/materialized_path.rb, line 14
def roots
  where(arel_table[ancestry_column].eq(ancestry_root))
end
siblings_of(object) click to toggle source
# File lib/ancestry/materialized_path.rb, line 68
def siblings_of(object)
  t = arel_table
  node = to_node(object)
  where(t[ancestry_column].eq(node[ancestry_column].presence))
end
subtree_of(object) click to toggle source
# File lib/ancestry/materialized_path.rb, line 62
def subtree_of(object)
  t = arel_table
  node = to_node(object)
  descendants_of(node).or(where(t[primary_key].eq(node.id)))
end

Private Instance Methods

ancestry_format_regexp() click to toggle source
# File lib/ancestry/materialized_path.rb, line 108
def ancestry_format_regexp
  /\A#{ancestry_primary_key_format}(#{Regexp.escape(ancestry_delimiter)}#{ancestry_primary_key_format})*\z/.freeze
end
ancestry_nil_allowed?() click to toggle source
# File lib/ancestry/materialized_path.rb, line 104
def ancestry_nil_allowed?
  true
end
ancestry_validation_options() click to toggle source
# File lib/ancestry/materialized_path.rb, line 97
def ancestry_validation_options
  {
    format: { with: ancestry_format_regexp },
    allow_nil: ancestry_nil_allowed?
  }
end