Module: ActiveRecord::Acts::HappyTree::InstanceMethods

Defined in:
lib/active_record/acts/happy_tree.rb

Instance Method Summary collapse

Instance Method Details

#ancestor_idsObject

Returns list of ancestor ids, starting from parent until root.

subchild1.ancestor_ids # => [child1.id, root.id]

1 DB SELECT per ancestor, only selects “parent_id” AR < 3.2 = 1 AR object per ancestor AR >= 3.2 = 0 AR objects



247
248
249
250
251
252
253
254
# File 'lib/active_record/acts/happy_tree.rb', line 247

def ancestor_ids
  key, node_ids = tree_parent_key, []
  until key.nil? do
    node_ids << key
    key = self.class.parent_id_of(key)
  end
  return node_ids
end

#ancestor_of?(node) ⇒ Boolean

Returns true if this instance is an ancestor of another instance

root.ancestor_of?(child1) # => true child1.ancestor_of?(root) # => false

1 DB SELECT per level examined, only selects “parent_id” AR < 3.2 = 1 AR object per level examined AR >= 3.2 = no AR objects

equivalent of node.descendant_of?(self) node1.ancestor_of(node2) == node2.descendant_of(node1)

Returns:

  • (Boolean)


211
212
213
214
215
216
217
218
219
# File 'lib/active_record/acts/happy_tree.rb', line 211

def ancestor_of?(node)
  return false if (node.nil? || !node.is_a?(self.class))
  key = node.tree_parent_key
  until key.nil? do
    return true if key == self.id
    key = self.class.parent_id_of(key)
  end
  return false
end

#ancestorsObject

Returns list of ancestors, starting from parent until root.

subchild1.ancestors # => [child1, root]


117
118
119
120
# File 'lib/active_record/acts/happy_tree.rb', line 117

def ancestors
  node, nodes = self, []
  nodes << node = node.parent until node.parent.nil? and return nodes
end

#ancestors_countObject

Returns a count of the number of ancestors

subchild1.ancestors_count # => 2

1 DB SELECT per ancestor, only selects “parent_id” AR < 3.2 = 1 AR object per ancestor AR >= 3.2 = 0 AR objects



263
264
265
266
267
268
269
270
# File 'lib/active_record/acts/happy_tree.rb', line 263

def ancestors_count
  key, count = tree_parent_key, 0
  until key.nil? do
    count += 1
    key = self.class.parent_id_of(key)
  end
  return count
end

#child?Boolean

Returns true if this instance has a parent (aka child node)

root.child? # => false child1.child? # => true

no DB access

Returns:

  • (Boolean)


176
177
178
# File 'lib/active_record/acts/happy_tree.rb', line 176

def child?
  !tree_parent_key.nil?
end

#childlessObject



156
157
158
# File 'lib/active_record/acts/happy_tree.rb', line 156

def childless
  self.descendants.collect{|d| d.children.empty? ? d : nil}.compact
end

#descendant_ids(options = {}) ⇒ Object

return an array of descendant ids uses iterative method in DFS order by default options are finder options



335
336
337
# File 'lib/active_record/acts/happy_tree.rb', line 335

def descendant_ids(options={})
  descendants_call(:descendant_ids, :dfs, options)
end

#descendant_of?(node) ⇒ Boolean

Returns true if this instance is a descendant of another instance

root.descendant_of?(child1) # => false child1.descendant_of?(root) # => true

same performance as ancestor_of?

equivalent of node.ancestor_of?(self) node1.descendant_of(node2) == node2.ancestor_of(node1)

Returns:

  • (Boolean)


230
231
232
233
234
235
236
237
238
# File 'lib/active_record/acts/happy_tree.rb', line 230

def descendant_of?(node)
  return false if (node.nil? || !node.is_a?(self.class))
  key = self.tree_parent_key
  until key.nil? do
    return true if key == node.id
    key = self.class.parent_id_of(key)
  end
  return false
end

#descendants(options = {}) ⇒ Object

returns all of the descendants uses iterative method in DFS order by default options are finder options



320
321
322
# File 'lib/active_record/acts/happy_tree.rb', line 320

def descendants(options={})
  descendants_call(:descendants, :dfs, options)
end

#descendants_call(method, default, options = {}) ⇒ Object

helper method to allow the choosing of the descendants traversal method by setting :traversal option as follows:

:classic - depth-first search, recursive

- only for descendants, ignores finder options

:dfs - depth-first search, iterative :dfs_rec - depth-first search, recursive :bfs - breadth-first search, interative :bfs_rec - breadth-first search, recursive



305
306
307
308
309
310
311
312
313
314
315
# File 'lib/active_record/acts/happy_tree.rb', line 305

def descendants_call(method, default, options={})
  traversal = options.delete(:traversal)
  case traversal
  when :classic
    send("#{method}_classic")
  when :bfs, :dfs, :bfs_rec, :dfs_rec
    send("#{method}_#{traversal}", options)
  else
    send("#{method}_#{default}", options)
  end
end

#descendants_classic(node = self) ⇒ Object

Returns a flat list of the descendants of the current node.

root.descendants # => [child1, subchild1, subchild2]


145
146
147
148
149
150
151
152
153
154
# File 'lib/active_record/acts/happy_tree.rb', line 145

def descendants_classic(node=self)
  nodes = []
  nodes << node unless node == self

  node.children.each do |child|
    nodes += descendants_classic(child)
  end

  nodes.compact
end

#descendants_count(options = {}) ⇒ Object

return a count of the number of descendants Use BFS for descendants_count because it should use fewer SQL queries and thus be faster options are finder options



343
344
345
# File 'lib/active_record/acts/happy_tree.rb', line 343

def descendants_count(options={})
  descendants_call(:descendants_count, :bfs, options)
end

#leaf?Boolean

returns true if this instance has no children (aka leaf node)

root.leaf? # => false subchild1.leaf? # => true

1 DB SELECT, no fields selected

Returns:

  • (Boolean)


196
197
198
# File 'lib/active_record/acts/happy_tree.rb', line 196

def leaf?
  !children.exists?
end

#parent?Boolean

returns true if this instance has any children (aka parent node)

root.parent? # => true subchild1.parent? # => false

1 DB SELECT, no fields selected

Returns:

  • (Boolean)


186
187
188
# File 'lib/active_record/acts/happy_tree.rb', line 186

def parent?
  children.exists?
end

#parent_key_must_be_validObject

method for validating parent_key to make sure it is not 1) the same as id 2) already a descendant of the current node



350
351
352
353
354
355
356
357
# File 'lib/active_record/acts/happy_tree.rb', line 350

def parent_key_must_be_valid
  return if id.nil?
  if (tree_parent_key==id)
    errors.add(tree_parent_key_name, "#{tree_parent_key_name} cannot be the same as id")
  elsif ancestor_of?(parent)
    errors.add(tree_parent_key_name, "#{tree_parent_key_name} cannot be a descendant")
  end
end

#rootObject

Returns the root node of the current node no DB access if node is root otherwise use root_id function which is optimized performance = root_id + 1 DB SELECT and 1 AR object if not root other acts_as_tree variants select all fields and instantiate all objects



292
293
294
# File 'lib/active_record/acts/happy_tree.rb', line 292

def root
  root? ? self : self.class.find(root_id)
end

#root?Boolean

Returns true if this instance has no parent (aka root node)

root.root? # => true child1.root? # => false

no DB access

Returns:

  • (Boolean)


166
167
168
# File 'lib/active_record/acts/happy_tree.rb', line 166

def root?
  tree_parent_key.nil?
end

#root_classicObject

Returns the root node of the tree.



123
124
125
126
# File 'lib/active_record/acts/happy_tree.rb', line 123

def root_classic
  node = self
  node = node.parent until node.parent.nil? and return node
end

#root_idObject

Returns the root id of the current node no DB access if node is root otherwise use root_id function which is optimized 1 DB SELECT per ancestor, only selects “parent_id” AR < 3.2 = 1 AR object per ancestor AR >= 3.2 = 0 AR objects



278
279
280
281
282
283
284
285
# File 'lib/active_record/acts/happy_tree.rb', line 278

def root_id
  key, node_id = tree_parent_key, id
  until key.nil? do
    node_id = key
    key = self.class.parent_id_of(key)
  end
  return node_id
end

#self_and_descendants(options = {}) ⇒ Object

return self and descendants provided for compatibility with other tree implementations uses iterative method in DFS order by default options are finder options



328
329
330
# File 'lib/active_record/acts/happy_tree.rb', line 328

def self_and_descendants(options={})
  descendants_call(:self_and_descendants, :dfs, options)
end

#self_and_siblingsObject

Returns all siblings and a reference to the current node.

subchild1.self_and_siblings # => [subchild1, subchild2]


138
139
140
# File 'lib/active_record/acts/happy_tree.rb', line 138

def self_and_siblings
  parent ? parent.children : self.class.roots
end

#siblingsObject

Returns all siblings of the current node.

subchild1.siblings # => [subchild2]


131
132
133
# File 'lib/active_record/acts/happy_tree.rb', line 131

def siblings
  self_and_siblings - [self]
end