1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
|
module CollectiveIdea
module Acts
module NestedSet
module Model
module Relatable
# Returns an collection of all parents
def ancestors
without_self self_and_ancestors
end
# Returns the collection of all parents and self
def self_and_ancestors
nested_set_scope.
where(arel_table[left_column_name].lteq(left)).
where(arel_table[right_column_name].gteq(right))
end
# Returns the collection of all children of the parent, except self
def siblings
without_self self_and_siblings
end
# Returns the collection of all children of the parent, including self
def self_and_siblings
nested_set_scope.children_of parent_id
end
# Returns a set of all of its nested children which do not have children
def leaves
descendants.where(
"#{quoted_right_column_full_name} - #{quoted_left_column_full_name} = 1"
)
end
# Returns the level of this object in the tree
# root level is 0
def level
parent_id.nil? ? 0 : compute_level
end
# Returns a collection including all of its children and nested children
def descendants
without_self self_and_descendants
end
# Returns a collection including itself and all of its nested children
def self_and_descendants
# using _left_ for both sides here lets us benefit from an index on that column if one exists
nested_set_scope.right_of(left).left_of(right)
end
def is_descendant_of?(other)
within_node?(other, self) && same_scope?(other)
end
def is_or_is_descendant_of?(other)
(other == self || within_node?(other, self)) && same_scope?(other)
end
def is_ancestor_of?(other)
within_node?(self, other) && same_scope?(other)
end
def is_or_is_ancestor_of?(other)
(self == other || within_node?(self, other)) && same_scope?(other)
end
# Check if other model is in the same scope
def same_scope?(other)
Array(acts_as_nested_set_options[:scope]).all? do |attr|
self.send(attr) == other.send(attr)
end
end
# Find the first sibling to the left
def left_sibling
siblings.left_of(left).last
end
# Find the first sibling to the right
def right_sibling
siblings.right_of(left).first
end
def root
return self_and_ancestors.children_of(nil).first if persisted?
if parent_id && current_parent = nested_set_scope.where(primary_column_name => parent_id).first!
current_parent.root
else
self
end
end
def roots
nested_set_scope.where(parent_id: nil)
end
protected
def compute_level
node, nesting = determine_depth
node == self ? ancestors.count : node.level + nesting
end
def determine_depth(node = self, nesting = 0)
while (association = node.association(:parent)).loaded? && association.target
nesting += 1
node = node.parent
end if node.respond_to?(:association)
[node, nesting]
end
def within_node?(node, within)
node.left < within.left && within.left < node.right
end
end
end
end
end
end
|