Module: ActsAsOrderedTree::InstanceMethods

Includes:
TenaciousTransaction
Defined in:
lib/acts_as_ordered_tree/instance_methods.rb

Constant Summary

Constants included from TenaciousTransaction

TenaciousTransaction::DEADLOCK_MESSAGES, TenaciousTransaction::RETRY_COUNT

Instance Method Summary collapse

Methods included from TenaciousTransaction

#tenacious_transaction

Instance Method Details

#ancestorsObject

Returns the array of all parents starting from root



58
59
60
61
62
63
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 58

def ancestors
  records = self_and_ancestors - [self]

  scope = self_and_ancestors.where(arel[:id].not_eq(id))
  scope.records(records)
end

#branch?Boolean

Returns:

  • (Boolean)


23
24
25
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 23

def branch?
  !leaf?
end

#child?Boolean

Returns true is this is a child node

Returns:

  • (Boolean)


28
29
30
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 28

def child?
  !root?
end

#descendantsObject

Returns a set of all of its children and nested children. A little bit tricky. use RDBMS with recursive queries support (PostgreSQL)



92
93
94
95
96
97
98
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 92

def descendants
  records = fetch_self_and_descendants - [self]

  ActsAsOrderedTree::Relation::Preloaded.new(self.class).
      where(:id => records.map(&:id)).
      records(records)
end

#first?Boolean

Return true if this object is the first in the list.

Returns:

  • (Boolean)


126
127
128
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 126

def first?
  self[position_column] <= 1
end

#insert_at(position = 1) ⇒ Object

Insert the item at the given position (defaults to the top position of 1). acts_as_list compatability



155
156
157
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 155

def insert_at(position = 1)
  move_to_child_with_index(parent, position - 1)
end

#is_ancestor_of?(other) ⇒ Boolean

Returns:

  • (Boolean)


117
118
119
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 117

def is_ancestor_of?(other)
  other.is_descendant_of? self
end

#is_descendant_of?(other) ⇒ Boolean

Returns:

  • (Boolean)


109
110
111
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 109

def is_descendant_of?(other)
  ancestors.include? other
end

#is_or_is_ancestor_of?(other) ⇒ Boolean

Returns:

  • (Boolean)


121
122
123
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 121

def is_or_is_ancestor_of?(other)
  other.is_or_is_descendant_of? self
end

#is_or_is_descendant_of?(other) ⇒ Boolean

Returns:

  • (Boolean)


113
114
115
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 113

def is_or_is_descendant_of?(other)
  self == other || is_descendant_of?(other)
end

#last?Boolean

Return true if this object is the last in the list.

Returns:

  • (Boolean)


131
132
133
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 131

def last?
  !right_sibling
end

#leaf?Boolean

Returns true if this is the end of a branch.

Returns:

  • (Boolean)


15
16
17
18
19
20
21
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 15

def leaf?
  persisted? && if children_counter_cache_column
    self[children_counter_cache_column] == 0
  else
    children.count == 0
  end
end

#left_siblingObject Also known as: higher_item

Returns a left (upper) sibling of the node



136
137
138
139
140
141
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 136

def left_sibling
  siblings.
      where( arel[position_column].lt(self[position_column]) ).
      reorder( arel[position_column].desc ).
      first
end

#levelObject



75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 75

def level
  if depth_column
    # cached result becomes invalid when parent is changed
    if new_record? ||
        changed_attributes.include?(parent_column.to_s) ||
        self[depth_column].blank?
      self[depth_column] = compute_level
    else
      self[depth_column]
    end
  else
    compute_level
  end
end

#move_leftObject Also known as: move_higher

Shorthand method for finding the left sibling and moving to the left of it.



160
161
162
163
164
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 160

def move_left
  tenacious_transaction do
    move_to_left_of left_sibling.try(:lock!)
  end
end

#move_possible?(target) ⇒ Boolean

Returns true it is possible to move node to left/right/child of target

Returns:

  • (Boolean)


218
219
220
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 218

def move_possible?(target)
  same_scope?(target) && !is_or_is_ancestor_of?(target)
end

#move_rightObject Also known as: move_lower

Shorthand method for finding the right sibling and moving to the right of it.



168
169
170
171
172
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 168

def move_right
  tenacious_transaction do
    move_to_right_of right_sibling.try(:lock!)
  end
end

#move_to_child_of(node) ⇒ Object

Move the node to the child of another node



188
189
190
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 188

def move_to_child_of(node)
  move_to node, :child
end

#move_to_child_with_index(node, index) ⇒ Object

Move the node to the child of another node with specify index

Raises:

  • (ActiveRecord::ActiveRecordError)


193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 193

def move_to_child_with_index(node, index)
  raise ActiveRecord::ActiveRecordError, "index can't be nil" unless index

  tenacious_transaction do
    new_siblings = (node.try(:children) || ordered_tree_scope.roots).
        reload.
        lock(true).
        reject { |root_node| root_node == self }

    if new_siblings.empty?
      node ? move_to_child_of(node) : move_to_root
    elsif new_siblings.count <= index
      move_to_right_of(new_siblings.last)
    elsif
      index >= 0 ? move_to_left_of(new_siblings[index]) : move_to_right_of(new_siblings[index])
    end
  end
end

#move_to_left_of(node) ⇒ Object Also known as: move_to_above_of

Move the node to the left of another node



176
177
178
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 176

def move_to_left_of(node)
  move_to node, :left
end

#move_to_right_of(node) ⇒ Object Also known as: move_to_bottom_of

Move the node to the left of another node



182
183
184
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 182

def move_to_right_of(node)
  move_to node, :right
end

#move_to_rootObject

Move the node to root nodes



213
214
215
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 213

def move_to_root
  move_to nil, :root
end

#right_siblingObject Also known as: lower_item

Returns a right (lower) sibling of the node



145
146
147
148
149
150
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 145

def right_sibling
  siblings.
      where( arel[position_column].gt(self[position_column]) ).
      reorder( arel[position_column].asc ).
      first
end

#rootObject

Returns root (not really fast operation)



33
34
35
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 33

def root
  root? ? self : parent.root
end

#root?Boolean

Returns true if this is a root node.

Returns:

  • (Boolean)


10
11
12
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 10

def root?
  self[parent_column].nil?
end

#same_scope?(other) ⇒ Boolean

Check if other model is in the same scope

Returns:

  • (Boolean)


223
224
225
226
227
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 223

def same_scope?(other)
  scope_column_names.empty? || scope_column_names.all? do |attr|
    self[attr] == other[attr]
  end
end

#self_and_ancestorsObject

Returns the array of all parents and self starting from root



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 38

def self_and_ancestors
  # 1. recursively load ancestors
  nodes = []
  node = self

  while node
    nodes << node
    node = node.parent
  end

  # 2. first ancestor is a root
  nodes.reverse!

  # 3. create fake scope
  ActsAsOrderedTree::Relation::Preloaded.new(self.class).
      where(:id => nodes.map(&:id)).
      records(nodes)
end

#self_and_descendantsObject

Returns a set of itself and all of its nested children



101
102
103
104
105
106
107
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 101

def self_and_descendants
  records = fetch_self_and_descendants

  ActsAsOrderedTree::Relation::Preloaded.new(self.class).
      where(:id => records.map(&:id)).
      records(records)
end

#self_and_siblingsObject

Returns the array of all children of the parent, including self



66
67
68
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 66

def self_and_siblings
  ordered_tree_scope.where(parent_column => self[parent_column])
end

#siblingsObject

Returns the array of all children of the parent, except self



71
72
73
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 71

def siblings
  self_and_siblings.where(arel[:id].not_eq(id))
end