rubytree 0.9.5 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/Gemfile +0 -6
- data/Gemfile.lock +75 -44
- data/History.rdoc +18 -2
- data/LICENSE.md +1 -2
- data/README.md +10 -8
- data/Rakefile +43 -35
- data/examples/example_basic.rb +11 -8
- data/lib/rubytree.rb +1 -1
- data/lib/tree/binarytree.rb +17 -18
- data/lib/tree/tree_deps.rb +8 -8
- data/lib/tree/utils/camel_case_method_handler.rb +12 -14
- data/lib/tree/utils/hash_converter.rb +62 -64
- data/lib/tree/utils/json_converter.rb +16 -17
- data/lib/tree/utils/metrics_methods.rb +12 -9
- data/lib/tree/utils/path_methods.rb +8 -10
- data/lib/tree/utils/tree_merge_handler.rb +9 -10
- data/lib/tree/utils/utils.rb +3 -2
- data/lib/tree/version.rb +2 -3
- data/lib/tree.rb +143 -123
- data/rubytree.gemspec +27 -25
- data/setup.rb +239 -259
- data/spec/spec_helper.rb +10 -0
- data/spec/tree_spec.rb +72 -0
- data/test/run_test.rb +6 -6
- data/test/test_binarytree.rb +93 -92
- data/test/test_rubytree_require.rb +2 -4
- data/test/test_subclassed_node.rb +13 -16
- data/test/test_thread_and_fiber.rb +10 -13
- data/test/test_tree.rb +565 -572
- metadata +98 -29
- data/TAGS +0 -248
- data/gem_graph.png +0 -0
data/lib/tree.rb
CHANGED
@@ -9,7 +9,7 @@
|
|
9
9
|
# Author:: Anupam Sengupta (anupamsg@gmail.com)
|
10
10
|
#
|
11
11
|
|
12
|
-
# Copyright (c) 2006-
|
12
|
+
# Copyright (c) 2006-2021 Anupam Sengupta
|
13
13
|
#
|
14
14
|
# All rights reserved.
|
15
15
|
#
|
@@ -47,7 +47,6 @@ require 'tree/tree_deps'
|
|
47
47
|
# This module also acts as the namespace for all classes in the *RubyTree*
|
48
48
|
# package.
|
49
49
|
module Tree
|
50
|
-
|
51
50
|
# == TreeNode Class Description
|
52
51
|
#
|
53
52
|
# This class models the nodes for an *N-ary* tree data structure. The
|
@@ -83,6 +82,7 @@ module Tree
|
|
83
82
|
# {include:file:examples/example_basic.rb}
|
84
83
|
#
|
85
84
|
# @author Anupam Sengupta
|
85
|
+
# noinspection RubyTooManyMethodsInspection
|
86
86
|
class TreeNode
|
87
87
|
include Enumerable
|
88
88
|
include Comparable
|
@@ -108,11 +108,11 @@ module Tree
|
|
108
108
|
# +content+ attribute for any non-unique node requirements.
|
109
109
|
#
|
110
110
|
# If you want to change the name, you probably want to call +rename+
|
111
|
-
# instead.
|
111
|
+
# instead. Note that +name=+ is a protected method.
|
112
112
|
#
|
113
113
|
# @see content
|
114
114
|
# @see rename
|
115
|
-
|
115
|
+
attr_accessor :name
|
116
116
|
|
117
117
|
# @!attribute [rw] content
|
118
118
|
# Content of this node. Can be +nil+. Note that there is no
|
@@ -132,7 +132,7 @@ module Tree
|
|
132
132
|
# @return [Tree::TreeNode] Root of the (sub)tree.
|
133
133
|
def root
|
134
134
|
root = self
|
135
|
-
root = root.parent
|
135
|
+
root = root.parent until root.is_root?
|
136
136
|
root
|
137
137
|
end
|
138
138
|
|
@@ -176,8 +176,8 @@ module Tree
|
|
176
176
|
return nil if is_root?
|
177
177
|
|
178
178
|
parentage_array = []
|
179
|
-
prev_parent =
|
180
|
-
while
|
179
|
+
prev_parent = parent
|
180
|
+
while prev_parent
|
181
181
|
parentage_array << prev_parent
|
182
182
|
prev_parent = prev_parent.parent
|
183
183
|
end
|
@@ -215,18 +215,20 @@ module Tree
|
|
215
215
|
#
|
216
216
|
# @see #[]
|
217
217
|
def initialize(name, content = nil)
|
218
|
-
raise ArgumentError,
|
219
|
-
|
218
|
+
raise ArgumentError, 'Node name HAS to be provided!' if name.nil?
|
219
|
+
|
220
|
+
@name = name
|
221
|
+
@content = content
|
220
222
|
|
221
|
-
if name.
|
222
|
-
warn StandardWarning,
|
223
|
-
|
224
|
-
|
223
|
+
if name.is_a?(Integer)
|
224
|
+
warn StructuredWarnings::StandardWarning,
|
225
|
+
'Using integer as node name.'\
|
226
|
+
' Semantics of TreeNode[] may not be what you expect!'\
|
225
227
|
" #{name} #{content}"
|
226
228
|
end
|
227
229
|
|
228
|
-
|
229
|
-
@children_hash =
|
230
|
+
set_as_root!
|
231
|
+
@children_hash = {}
|
230
232
|
@children = []
|
231
233
|
end
|
232
234
|
|
@@ -253,27 +255,26 @@ module Tree
|
|
253
255
|
# Alias for {Tree::TreeNode#detached_subtree_copy}
|
254
256
|
#
|
255
257
|
# @see Tree::TreeNode#detached_subtree_copy
|
256
|
-
alias
|
258
|
+
alias dup detached_subtree_copy
|
257
259
|
|
258
260
|
# Returns a {marshal-dump}[http://ruby-doc.org/core-1.8.7/Marshal.html]
|
259
|
-
#
|
261
|
+
# representation of the (sub)tree rooted at this node.
|
260
262
|
#
|
261
263
|
def marshal_dump
|
262
|
-
|
264
|
+
collect { |node| node.create_dump_rep }
|
263
265
|
end
|
264
266
|
|
265
267
|
# Creates a dump representation of this node and returns the same as
|
266
268
|
# a hash.
|
267
|
-
def create_dump_rep
|
268
|
-
{ :
|
269
|
-
:
|
270
|
-
:
|
271
|
-
}
|
269
|
+
def create_dump_rep # :nodoc:
|
270
|
+
{ name: @name,
|
271
|
+
parent: (is_root? ? nil : @parent.name),
|
272
|
+
content: Marshal.dump(@content) }
|
272
273
|
end
|
273
274
|
|
274
275
|
protected :create_dump_rep
|
275
276
|
|
276
|
-
# Loads a
|
277
|
+
# Loads a marshaled dump of a tree and returns the root node of the
|
277
278
|
# reconstructed tree. See the
|
278
279
|
# {Marshal}[http://ruby-doc.org/core-1.8.7/Marshal.html] class for
|
279
280
|
# additional details.
|
@@ -283,20 +284,20 @@ module Tree
|
|
283
284
|
# self and makes itself the root.
|
284
285
|
#
|
285
286
|
def marshal_load(dumped_tree_array)
|
286
|
-
nodes = {
|
287
|
+
nodes = {}
|
287
288
|
dumped_tree_array.each do |node_hash|
|
288
289
|
name = node_hash[:name]
|
289
290
|
parent_name = node_hash[:parent]
|
290
291
|
content = Marshal.load(node_hash[:content])
|
291
292
|
|
292
|
-
if parent_name
|
293
|
+
if parent_name
|
293
294
|
nodes[name] = current_node = Tree::TreeNode.new(name, content)
|
294
295
|
nodes[parent_name].add current_node
|
295
296
|
else
|
296
297
|
# This is the root node, hence initialize self.
|
297
298
|
initialize(name, content)
|
298
299
|
|
299
|
-
nodes[name] = self
|
300
|
+
nodes[name] = self # Add self to the list of nodes
|
300
301
|
end
|
301
302
|
end
|
302
303
|
end
|
@@ -308,11 +309,7 @@ module Tree
|
|
308
309
|
#
|
309
310
|
# @return [String] A string representation of the node.
|
310
311
|
def to_s
|
311
|
-
"Node Name: #{@name}"
|
312
|
-
" Content: " + (@content.to_s || "<Empty>") +
|
313
|
-
" Parent: " + (is_root?() ? "<None>" : @parent.name.to_s) +
|
314
|
-
" Children: #{@children.length}" +
|
315
|
-
" Total Nodes: #{size()}"
|
312
|
+
"Node Name: #{@name} Content: #{@content.to_s || '<Empty>'} Parent: #{is_root? ? '<None>' : @parent.name.to_s} Children: #{@children.length} Total Nodes: #{size}"
|
316
313
|
end
|
317
314
|
|
318
315
|
# @!group Structure Modification
|
@@ -354,7 +351,7 @@ module Tree
|
|
354
351
|
#
|
355
352
|
# -children.size..children.size
|
356
353
|
#
|
357
|
-
# This is to prevent +nil+ nodes being created as children if a non-
|
354
|
+
# This is to prevent +nil+ nodes being created as children if a non-existent
|
358
355
|
# position is used.
|
359
356
|
#
|
360
357
|
# If the new node being added has an existing parent node, then it will be
|
@@ -380,38 +377,37 @@ module Tree
|
|
380
377
|
# @see #<<
|
381
378
|
def add(child, at_index = -1)
|
382
379
|
# Only handles the immediate child scenario
|
383
|
-
raise ArgumentError,
|
384
|
-
|
385
|
-
raise ArgumentError,
|
386
|
-
|
387
|
-
raise ArgumentError,
|
388
|
-
|
389
|
-
|
390
|
-
# Lazy mans unique test, won't test if children of child are unique in
|
380
|
+
raise ArgumentError, 'Attempting to add a nil node' unless child
|
381
|
+
|
382
|
+
raise ArgumentError, 'Attempting add node to itself' if equal?(child)
|
383
|
+
|
384
|
+
raise ArgumentError, 'Attempting add root as a child' if child.equal?(root)
|
385
|
+
|
386
|
+
# Lazy man's unique test, won't test if children of child are unique in
|
391
387
|
# this tree too.
|
392
388
|
raise "Child #{child.name} already added!"\
|
393
389
|
if @children_hash.include?(child.name)
|
394
390
|
|
395
|
-
child.parent
|
391
|
+
child.parent&.remove! child # Detach from the old parent
|
396
392
|
|
397
393
|
if insertion_range.include?(at_index)
|
398
394
|
@children.insert(at_index, child)
|
399
395
|
else
|
400
|
-
raise
|
396
|
+
raise 'Attempting to insert a child at a non-existent location'\
|
401
397
|
" (#{at_index}) "\
|
402
|
-
|
398
|
+
'when only positions from '\
|
403
399
|
"#{insertion_range.min} to #{insertion_range.max} exist."
|
404
400
|
end
|
405
401
|
|
406
|
-
@children_hash[child.name]
|
402
|
+
@children_hash[child.name] = child
|
407
403
|
child.parent = self
|
408
|
-
|
404
|
+
child
|
409
405
|
end
|
410
406
|
|
411
407
|
# Return a range of valid insertion positions. Used in the #add method.
|
412
408
|
def insertion_range
|
413
409
|
max = @children.size
|
414
|
-
min = -(max+1)
|
410
|
+
min = -(max + 1)
|
415
411
|
min..max
|
416
412
|
end
|
417
413
|
|
@@ -427,7 +423,7 @@ module Tree
|
|
427
423
|
old_name = @name
|
428
424
|
|
429
425
|
if is_root?
|
430
|
-
self.name=
|
426
|
+
self.name = new_name
|
431
427
|
else
|
432
428
|
@parent.rename_child old_name, new_name
|
433
429
|
end
|
@@ -444,20 +440,10 @@ module Tree
|
|
444
440
|
# pass a String (Integer names may cause *surprises*)
|
445
441
|
def rename_child(old_name, new_name)
|
446
442
|
raise ArgumentError, "Invalid child name specified: #{old_name}"\
|
447
|
-
unless @children_hash.
|
443
|
+
unless @children_hash.key?(old_name)
|
448
444
|
|
449
445
|
@children_hash[new_name] = @children_hash.delete(old_name)
|
450
|
-
@children_hash[new_name].name=
|
451
|
-
end
|
452
|
-
|
453
|
-
# Protected method to set the name of this node.
|
454
|
-
# This method should *NOT* be invoked by client code.
|
455
|
-
#
|
456
|
-
# @param [Object] new_name The node Name to set.
|
457
|
-
#
|
458
|
-
# @return [Object] The new name.
|
459
|
-
def name=(new_name)
|
460
|
-
@name = new_name
|
446
|
+
@children_hash[new_name].name = new_name
|
461
447
|
end
|
462
448
|
|
463
449
|
# Replaces the specified child node with another child node on this node.
|
@@ -472,7 +458,7 @@ module Tree
|
|
472
458
|
old_child = remove! old_child
|
473
459
|
add new_child, child_index
|
474
460
|
|
475
|
-
|
461
|
+
old_child
|
476
462
|
end
|
477
463
|
|
478
464
|
# Replaces the node with another node
|
@@ -513,7 +499,7 @@ module Tree
|
|
513
499
|
# @param [Tree::TreeNode] parent The parent node.
|
514
500
|
#
|
515
501
|
# @return [Tree::TreeNode] The parent node.
|
516
|
-
def parent=(parent)
|
502
|
+
def parent=(parent) # :nodoc:
|
517
503
|
@parent = parent
|
518
504
|
@node_depth = nil
|
519
505
|
end
|
@@ -552,7 +538,7 @@ module Tree
|
|
552
538
|
# Protected method which sets this node as a root node.
|
553
539
|
#
|
554
540
|
# @return +nil+.
|
555
|
-
def set_as_root!
|
541
|
+
def set_as_root! # :nodoc:
|
556
542
|
self.parent = nil
|
557
543
|
end
|
558
544
|
|
@@ -563,7 +549,7 @@ module Tree
|
|
563
549
|
# The nodes become immutable after this operation. In effect, the entire tree's
|
564
550
|
# structure and contents become _read-only_ and cannot be changed.
|
565
551
|
def freeze_tree!
|
566
|
-
each {|node| node.freeze}
|
552
|
+
each { |node| node.freeze }
|
567
553
|
end
|
568
554
|
|
569
555
|
# @!endgroup
|
@@ -603,16 +589,15 @@ module Tree
|
|
603
589
|
#
|
604
590
|
# @see #add
|
605
591
|
# @see #initialize
|
606
|
-
def [](name_or_index, num_as_name=false)
|
607
|
-
raise ArgumentError,
|
608
|
-
"Name_or_index needs to be provided!" if name_or_index == nil
|
592
|
+
def [](name_or_index, num_as_name = false)
|
593
|
+
raise ArgumentError, 'Name_or_index needs to be provided!' if name_or_index.nil?
|
609
594
|
|
610
|
-
if name_or_index.
|
595
|
+
if name_or_index.is_a?(Integer) && !num_as_name
|
611
596
|
@children[name_or_index]
|
612
597
|
else
|
613
|
-
if num_as_name
|
614
|
-
warn StandardWarning,
|
615
|
-
|
598
|
+
if num_as_name && !name_or_index.is_a?(Integer)
|
599
|
+
warn StructuredWarnings::StandardWarning,
|
600
|
+
'Redundant use of the `num_as_name` flag for non-integer node name'
|
616
601
|
end
|
617
602
|
@children_hash[name_or_index]
|
618
603
|
end
|
@@ -624,6 +609,7 @@ module Tree
|
|
624
609
|
# The traversal is *depth-first* and from *left-to-right* in pre-ordered
|
625
610
|
# sequence.
|
626
611
|
#
|
612
|
+
# @param [Object] block
|
627
613
|
# @yieldparam node [Tree::TreeNode] Each node.
|
628
614
|
#
|
629
615
|
# @see #preordered_each
|
@@ -631,22 +617,22 @@ module Tree
|
|
631
617
|
#
|
632
618
|
# @return [Tree::TreeNode] this node, if a block if given
|
633
619
|
# @return [Enumerator] an enumerator on this tree, if a block is *not* given
|
634
|
-
|
635
|
-
|
636
|
-
|
620
|
+
# noinspection RubyUnusedLocalVariable
|
621
|
+
def each # :yields: node
|
622
|
+
return to_enum unless block_given?
|
637
623
|
|
638
|
-
node_stack = [self]
|
624
|
+
node_stack = [self] # Start with this node
|
639
625
|
|
640
626
|
until node_stack.empty?
|
641
|
-
current = node_stack.shift
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
627
|
+
current = node_stack.shift # Pop the top-most node
|
628
|
+
next unless current # Might be 'nil' (esp. for binary trees)
|
629
|
+
|
630
|
+
yield current # and process it
|
631
|
+
# Stack children of the current node at top of the stack
|
632
|
+
node_stack = current.children.concat(node_stack)
|
647
633
|
end
|
648
634
|
|
649
|
-
|
635
|
+
self if block_given?
|
650
636
|
end
|
651
637
|
|
652
638
|
# Traverses the (sub)tree rooted at this node in pre-ordered sequence.
|
@@ -659,48 +645,51 @@ module Tree
|
|
659
645
|
#
|
660
646
|
# @return [Tree::TreeNode] this node, if a block if given
|
661
647
|
# @return [Enumerator] an enumerator on this tree, if a block is *not* given
|
662
|
-
def preordered_each(&block)
|
648
|
+
def preordered_each(&block) # :yields: node
|
663
649
|
each(&block)
|
664
650
|
end
|
665
651
|
|
666
652
|
# Traverses the (sub)tree rooted at this node in post-ordered sequence.
|
667
653
|
#
|
654
|
+
# @param [Object] block
|
668
655
|
# @yieldparam node [Tree::TreeNode] Each node.
|
669
656
|
#
|
670
657
|
# @see #preordered_each
|
671
658
|
# @see #breadth_each
|
672
659
|
# @return [Tree::TreeNode] this node, if a block if given
|
673
660
|
# @return [Enumerator] an enumerator on this tree, if a block is *not* given
|
674
|
-
|
675
|
-
|
661
|
+
# noinspection RubyUnusedLocalVariable
|
662
|
+
def postordered_each
|
663
|
+
return to_enum(:postordered_each) unless block_given?
|
676
664
|
|
677
665
|
# Using a marked node in order to skip adding the children of nodes that
|
678
666
|
# have already been visited. This allows the stack depth to be controlled,
|
679
667
|
# and also allows stateful backtracking.
|
680
|
-
|
681
|
-
node_stack = [
|
668
|
+
marked_node = Struct.new(:node, :visited)
|
669
|
+
node_stack = [marked_node.new(self, false)] # Start with self
|
682
670
|
|
683
671
|
until node_stack.empty?
|
684
672
|
peek_node = node_stack[0]
|
685
|
-
if peek_node.node.has_children?
|
673
|
+
if peek_node.node.has_children? && !peek_node.visited
|
686
674
|
peek_node.visited = true
|
687
675
|
# Add the children to the stack. Use the marking structure.
|
688
676
|
marked_children =
|
689
|
-
peek_node.node.children.map {|node|
|
677
|
+
peek_node.node.children.map { |node| marked_node.new(node, false) }
|
690
678
|
node_stack = marked_children.concat(node_stack)
|
691
679
|
next
|
692
680
|
else
|
693
|
-
yield node_stack.shift.node
|
681
|
+
yield node_stack.shift.node # Pop and yield the current node
|
694
682
|
end
|
695
683
|
end
|
696
684
|
|
697
|
-
|
685
|
+
self if block_given?
|
698
686
|
end
|
699
687
|
|
700
688
|
# Performs breadth-first traversal of the (sub)tree rooted at this node. The
|
701
689
|
# traversal at a given level is from *left-to-right*. this node itself is
|
702
690
|
# the first node to be traversed.
|
703
691
|
#
|
692
|
+
# @param [Object] block
|
704
693
|
# @yieldparam node [Tree::TreeNode] Each node.
|
705
694
|
#
|
706
695
|
# @see #preordered_each
|
@@ -708,10 +697,11 @@ module Tree
|
|
708
697
|
#
|
709
698
|
# @return [Tree::TreeNode] this node, if a block if given
|
710
699
|
# @return [Enumerator] an enumerator on this tree, if a block is *not* given
|
711
|
-
|
712
|
-
|
700
|
+
# noinspection RubyUnusedLocalVariable
|
701
|
+
def breadth_each
|
702
|
+
return to_enum(:breadth_each) unless block_given?
|
713
703
|
|
714
|
-
node_queue = [self]
|
704
|
+
node_queue = [self] # Create a queue with self as the initial entry
|
715
705
|
|
716
706
|
# Use a queue to do breadth traversal
|
717
707
|
until node_queue.empty?
|
@@ -721,7 +711,7 @@ module Tree
|
|
721
711
|
node_to_traverse.children { |child| node_queue.push child }
|
722
712
|
end
|
723
713
|
|
724
|
-
|
714
|
+
self if block_given?
|
725
715
|
end
|
726
716
|
|
727
717
|
# An array of all the immediate children of this node. The child
|
@@ -736,12 +726,12 @@ module Tree
|
|
736
726
|
#
|
737
727
|
# @return [Array<Tree::TreeNode>] An array of the child nodes, if no block
|
738
728
|
# is given.
|
739
|
-
def children
|
729
|
+
def children(&block)
|
740
730
|
if block_given?
|
741
|
-
@children.each
|
742
|
-
|
731
|
+
@children.each(&block)
|
732
|
+
self
|
743
733
|
else
|
744
|
-
|
734
|
+
@children.clone
|
745
735
|
end
|
746
736
|
end
|
747
737
|
|
@@ -751,6 +741,7 @@ module Tree
|
|
751
741
|
# May yield this node as well if this is a leaf node.
|
752
742
|
# Leaf traversal is *depth-first* and *left-to-right*.
|
753
743
|
#
|
744
|
+
# @param [Object] block
|
754
745
|
# @yieldparam node [Tree::TreeNode] Each leaf node.
|
755
746
|
#
|
756
747
|
# @see #each
|
@@ -758,12 +749,35 @@ module Tree
|
|
758
749
|
#
|
759
750
|
# @return [Tree::TreeNode] this node, if a block if given
|
760
751
|
# @return [Array<Tree::TreeNode>] An array of the leaf nodes
|
761
|
-
|
752
|
+
# noinspection RubyUnusedLocalVariable
|
753
|
+
def each_leaf
|
762
754
|
if block_given?
|
763
|
-
|
764
|
-
|
755
|
+
each { |node| yield(node) if node.is_leaf? }
|
756
|
+
self
|
765
757
|
else
|
766
|
-
self.select { |node| node.is_leaf?}
|
758
|
+
self.select { |node| node.is_leaf? }
|
759
|
+
end
|
760
|
+
end
|
761
|
+
|
762
|
+
# Yields every level of the (sub)tree rooted at this node to the
|
763
|
+
# specified block.
|
764
|
+
#
|
765
|
+
# Will yield this node as well since it is considered the first level.
|
766
|
+
#
|
767
|
+
# @yieldparam level [Array<Tree::TreeNode>] All nodes in the level
|
768
|
+
#
|
769
|
+
# @return [Tree::TreeNode] this node, if a block if given
|
770
|
+
# @return [Enumerator] an enumerator on this tree, if a block is *not* given
|
771
|
+
def each_level
|
772
|
+
if block_given?
|
773
|
+
level = [self]
|
774
|
+
until level.empty?
|
775
|
+
yield level
|
776
|
+
level = level.map(&:children).flatten
|
777
|
+
end
|
778
|
+
self
|
779
|
+
else
|
780
|
+
each
|
767
781
|
end
|
768
782
|
end
|
769
783
|
|
@@ -776,7 +790,7 @@ module Tree
|
|
776
790
|
#
|
777
791
|
# @return [Tree::TreeNode] The first child, or +nil+ if none is present.
|
778
792
|
def first_child
|
779
|
-
children.first
|
793
|
+
@children.first
|
780
794
|
end
|
781
795
|
|
782
796
|
# Last child of this node.
|
@@ -784,7 +798,7 @@ module Tree
|
|
784
798
|
#
|
785
799
|
# @return [Tree::TreeNode] The last child, or +nil+ if none is present.
|
786
800
|
def last_child
|
787
|
-
children.last
|
801
|
+
@children.last
|
788
802
|
end
|
789
803
|
|
790
804
|
# @!group Navigating the Sibling Nodes
|
@@ -858,12 +872,14 @@ module Tree
|
|
858
872
|
def siblings
|
859
873
|
if block_given?
|
860
874
|
parent.children.each { |sibling| yield sibling if sibling != self }
|
861
|
-
|
875
|
+
self
|
862
876
|
else
|
863
877
|
return [] if is_root?
|
878
|
+
|
864
879
|
siblings = []
|
865
|
-
parent.children
|
866
|
-
|
880
|
+
parent.children do |my_sibling|
|
881
|
+
siblings << my_sibling if my_sibling != self
|
882
|
+
end
|
867
883
|
siblings
|
868
884
|
end
|
869
885
|
end
|
@@ -892,8 +908,8 @@ module Tree
|
|
892
908
|
def next_sibling
|
893
909
|
return nil if is_root?
|
894
910
|
|
895
|
-
|
896
|
-
parent.children.at(
|
911
|
+
idx = parent.children.index(self)
|
912
|
+
parent.children.at(idx + 1) if idx
|
897
913
|
end
|
898
914
|
|
899
915
|
# Previous sibling of this node.
|
@@ -909,23 +925,25 @@ module Tree
|
|
909
925
|
def previous_sibling
|
910
926
|
return nil if is_root?
|
911
927
|
|
912
|
-
|
913
|
-
parent.children.at(
|
928
|
+
idx = parent.children.index(self)
|
929
|
+
parent.children.at(idx - 1) if idx && idx > 0
|
914
930
|
end
|
915
931
|
|
916
932
|
# @!endgroup
|
917
933
|
|
918
|
-
# Provides a
|
934
|
+
# Provides a comparison operation for the nodes.
|
919
935
|
#
|
920
|
-
#
|
936
|
+
# Comparison is based on the natural ordering of the node name objects.
|
921
937
|
#
|
922
938
|
# @param [Tree::TreeNode] other The other node to compare against.
|
923
939
|
#
|
924
940
|
# @return [Integer] +1 if this node is a 'successor', 0 if equal and -1 if
|
925
|
-
# this node is a 'predecessor'.
|
941
|
+
# this node is a 'predecessor'. Returns 'nil' if the other
|
942
|
+
# object is not a 'Tree::TreeNode'.
|
926
943
|
def <=>(other)
|
927
|
-
return
|
928
|
-
|
944
|
+
return nil if other.nil? || other.class != Tree::TreeNode
|
945
|
+
|
946
|
+
name <=> other.name
|
929
947
|
end
|
930
948
|
|
931
949
|
# Pretty prints the (sub)tree rooted at this node.
|
@@ -934,9 +952,10 @@ module Tree
|
|
934
952
|
# @param [Integer] max_depth optional maximum depth at which the printing
|
935
953
|
# with stop.
|
936
954
|
# @param [Proc] block optional block to use for rendering
|
937
|
-
def print_tree(level =
|
955
|
+
def print_tree(level = node_depth, max_depth = nil,
|
938
956
|
block = lambda { |node, prefix|
|
939
|
-
|
957
|
+
puts "#{prefix} #{node.name}"
|
958
|
+
})
|
940
959
|
prefix = ''
|
941
960
|
|
942
961
|
if is_root?
|
@@ -954,10 +973,11 @@ module Tree
|
|
954
973
|
# Exit if the max level is defined, and reached.
|
955
974
|
return unless max_depth.nil? || level < max_depth
|
956
975
|
|
957
|
-
|
958
|
-
|
959
|
-
|
976
|
+
# Child might be 'nil'
|
977
|
+
children do |child|
|
978
|
+
child&.print_tree(level + 1,
|
979
|
+
max_depth, block)
|
980
|
+
end
|
960
981
|
end
|
961
|
-
|
962
982
|
end
|
963
983
|
end
|