Skip to content

Instantly share code, notes, and snippets.

@madwork
Created Mar 7, 2019
Embed
What would you like to do?
Tree with apples
module Tree
# Returns list of ancestors, starting from parent until root.
#
# subchild1.ancestors # => [child1, root]
def ancestors
node, nodes = self, []
nodes << node = node.parent while node.parent
nodes
end
# Returns list of descendants, starting from current node, not including current node.
#
# root.descendants # => [child1, child2, subchild1, subchild2, subchild3, subchild4]
def descendants
children.each_with_object(children.to_a) {|child, arr|
arr.concat child.descendants
}.uniq
end
# Returns list of descendants, starting from current node, including current node.
#
# root.self_and_descendants # => [root, child1, child2, subchild1, subchild2, subchild3, subchild4]
def self_and_descendants
[self] + descendants
end
# Returns the root node of the tree.
def root
node = self
node = node.parent while node.parent
node
end
# Returns all siblings of the current node.
#
# subchild1.siblings # => [subchild2]
def siblings
self_and_siblings - [self]
end
# Returns all siblings and a reference to the current node.
#
# subchild1.self_and_siblings # => [subchild1, subchild2]
def self_and_siblings
parent ? parent.children : self.class.roots
end
# Returns all the nodes at the same level in the tree as the current node.
#
# root1child1.generation # => [root1child2, root2child1, root2child2]
def generation
self_and_generation - [self]
end
# Returns a reference to the current node and all the nodes at the same level as it in the tree.
#
# root1child1.self_and_generation # => [root1child1, root1child2, root2child1, root2child2]
def self_and_generation
self.class.select {|node| node.tree_level == self.tree_level }
end
# Returns the level (depth) of the current node
#
# root1child1.tree_level # => 1
def tree_level
self.ancestors.size
end
# Returns the level (depth) of the current node unless level is a column on the node.
# Allows backwards compatibility with older versions of the gem.
# Allows integration with apps using level as a column name.
#
# root1child1.level # => 1
def level
if self.class.column_names.include?('level')
super
else
tree_level
end
end
# Returns children (without subchildren) and current node itself.
#
# root.self_and_children # => [root, child1]
def self_and_children
[self] + self.children
end
# Returns ancestors and current node itself.
#
# subchild1.self_and_ancestors # => [subchild1, child1, root]
def self_and_ancestors
[self] + self.ancestors
end
# Returns true if node has no parent, false otherwise
#
# subchild1.root? # => false
# root.root? # => true
def root?
parent.nil?
end
# Returns true if node has no children, false otherwise
#
# subchild1.leaf? # => true
# child1.leaf? # => false
def leaf?
children.size.zero?
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment