module Treeable

Public Class Methods

define_association() click to toggle source
# File lib/treeable.rb, line 14
def self.define_association
  belongs_to :parent, :class_name => self.to_s, :foreign_key => @treeable_parent 
  has_many :children, :class_name => self.to_s, :foreign_key=> @treeable_parent 
end
json_tree(nodes) click to toggle source
# File lib/treeable.rb, line 35
def self.json_tree(nodes)
  nodes.map do |node|
    {:id => node.id, :children => json_tree(node.children)}
  end
end
leaves() click to toggle source
# File lib/treeable.rb, line 45
def self.leaves
  array = []      
  roots.each do |k|
    array += k.leafs
  end  
  self.where(id: (array.uniq.compact))
end
roots() click to toggle source
# File lib/treeable.rb, line 41
def self.roots
  where(@treeable_parent => nil)
end
treeable_parent_column(column_name=nil) click to toggle source
# File lib/treeable.rb, line 27
def self.treeable_parent_column(column_name=nil)
  if column_name
    @treeable_parent = column_name.to_sym
    self.define_association
  end
  return @treeable_parent
end
treeable_path_column(column_name=nil) click to toggle source
# File lib/treeable.rb, line 19
def self.treeable_path_column(column_name=nil)
  if column_name
    @treeable_path = column_name.to_sym
  end
  return @treeable_path
end

Public Instance Methods

all_children() click to toggle source
# File lib/treeable.rb, line 102
def all_children
  self.class.where("#{self.class.treeable_path_column} LIKE '#{self.path}%'").where.not(:id => self.id)
end
all_children_ids() click to toggle source
# File lib/treeable.rb, line 106
def all_children_ids
    all_children.pluck(:id)
end
children_map(&block) click to toggle source
# File lib/treeable.rb, line 72
def children_map(&block)
  unless all_children.empty?
    children.each{|k| k.children_map(&block)}
  end  
end
is_leaf() click to toggle source
# File lib/treeable.rb, line 60
def is_leaf
  self.children.empty? 
end
is_root() click to toggle source
# File lib/treeable.rb, line 64
def is_root
  self.parent.blank?
end
leaf_nodes() click to toggle source
# File lib/treeable.rb, line 89
def leaf_nodes
  internal_nodes  = self.class.where(self.class.treeable_parent_column => self.all_children_ids).select(self.class.treeable_parent_column).group(self.class.treeable_parent_column).pluck(self.class.treeable_parent_column)
  self.all_children.map{|t| t unless internal_nodes.include?(t[:id])}.uniq.compact
end
leafs() click to toggle source
# File lib/treeable.rb, line 78
def leafs
  array = []
  if self.is_leaf
    array << self.id
  else 
    parent_ids = self.all_children.where(self.class.treeable_parent_column => (all_children_ids << self.id)).pluck(self.class.treeable_parent_column)
    array += self.all_children_ids - parent_ids
  end
  array
end
node_ids_from_root() click to toggle source
# File lib/treeable.rb, line 94
def node_ids_from_root
  self.path.split(".").map{|k| k.to_i}
end
nodes_from_root() click to toggle source
# File lib/treeable.rb, line 98
def nodes_from_root
  self.class.where(:id => node_ids_from_root).order("length(#{self.class.treeable_path_column.to_s}) ASC")
end
parent_map(&block) click to toggle source
# File lib/treeable.rb, line 68
def parent_map(&block)
  (nodes_from_root || []).each{|k| block.call k}
end
path() click to toggle source
# File lib/treeable.rb, line 54
def path
  if !self.attributes.keys.include?("path") && self.class.treeable_path_column !="path"
    send(self.class.treeable_path_column)
  end
end
repopulate_path() click to toggle source
# File lib/treeable.rb, line 110
def repopulate_path
  if self.parent_id_changed?
    child_nodes = all_children
    path = parent ? "#{parent.path}#{self.id}." : "#{self.id}." 
    self.update_column(self.class.treeable_path_column.to_s, path)
    child_nodes.each{|k| k.update_path}
  end
end
root() click to toggle source
# File lib/treeable.rb, line 124
def root
  self.path.split('.').first.to_i
end
update_path() click to toggle source
# File lib/treeable.rb, line 119
def update_path
  new_path = self.parent ? "#{self.parent.path}#{self.id}." : "#{self.id}." 
  self.update_column(self.class.treeable_path_column.to_s, new_path)
end