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


60
61
62
63
64
65
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 60

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)

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

def branch?
  !leaf?
end

#child?Boolean

Returns true is this is a child node

Returns:

  • (Boolean)

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

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)


94
95
96
97
98
99
100
101
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 94

def descendants
  records = fetch_self_and_descendants - [self]

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

#first?Boolean

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

Returns:

  • (Boolean)

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

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


159
160
161
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 159

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

#is_ancestor_of?(other) ⇒ Boolean

Returns:

  • (Boolean)

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

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

#is_descendant_of?(other) ⇒ Boolean

Returns:

  • (Boolean)

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

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

#is_or_is_ancestor_of?(other) ⇒ Boolean

Returns:

  • (Boolean)

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

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

#is_or_is_descendant_of?(other) ⇒ Boolean

Returns:

  • (Boolean)

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

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)

135
136
137
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 135

def last?
  !right_sibling
end

#leaf?Boolean

Returns true if this is the end of a branch.

Returns:

  • (Boolean)

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

def leaf?
  persisted? && if children_counter_cache_column
    self[children_counter_cache_column] == 0
  else
    !children.reorder(nil).exists?
  end
end

#left_siblingObject Also known as: higher_item

Returns a left (upper) sibling of the node


140
141
142
143
144
145
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 140

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

#levelObject


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

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.


164
165
166
167
168
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 164

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)

222
223
224
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 222

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.


172
173
174
175
176
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 172

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


192
193
194
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 192

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)

197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 197

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


180
181
182
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 180

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


186
187
188
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 186

def move_to_right_of(node)
  move_to node, :right
end

#move_to_rootObject

Move the node to root nodes


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

def move_to_root
  move_to nil, :root
end

#right_siblingObject Also known as: lower_item

Returns a right (lower) sibling of the node


149
150
151
152
153
154
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 149

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)


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

def root
  root? ? self : parent.root
end

#root?Boolean

Returns true if this is a root node.

Returns:

  • (Boolean)

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

def root?
  self[parent_column].nil?
end

#same_scope?(other) ⇒ Boolean

Check if other model is in the same scope

Returns:

  • (Boolean)

227
228
229
230
231
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 227

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


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

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).compact).
      extending(Arrangeable).
      records(nodes)
end

#self_and_descendantsObject

Returns a set of itself and all of its nested children


104
105
106
107
108
109
110
111
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 104

def self_and_descendants
  records = fetch_self_and_descendants

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

#self_and_siblingsObject

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


68
69
70
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 68

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


73
74
75
# File 'lib/acts_as_ordered_tree/instance_methods.rb', line 73

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