File: relatable.rb

package info (click to toggle)
ruby-awesome-nested-set 3.8.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 172 kB
  • sloc: ruby: 1,044; makefile: 2
file content (125 lines) | stat: -rw-r--r-- 3,745 bytes parent folder | download
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