rubytree 0.9.7 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/tree.rb CHANGED
@@ -9,9 +9,7 @@
9
9
  # Author:: Anupam Sengupta (anupamsg@gmail.com)
10
10
  #
11
11
 
12
- # Copyright (c) 2006-2015 Anupam Sengupta
13
- #
14
- # All rights reserved.
12
+ # Copyright (c) 2006-2022 Anupam Sengupta. All rights reserved.
15
13
  #
16
14
  # Redistribution and use in source and binary forms, with or without
17
15
  # modification, are permitted provided that the following conditions are met:
@@ -38,6 +36,7 @@
38
36
  # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39
37
  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40
38
  #
39
+ # frozen_string_literal: true
41
40
 
42
41
  require 'tree/tree_deps'
43
42
 
@@ -47,7 +46,6 @@ require 'tree/tree_deps'
47
46
  # This module also acts as the namespace for all classes in the *RubyTree*
48
47
  # package.
49
48
  module Tree
50
-
51
49
  # == TreeNode Class Description
52
50
  #
53
51
  # This class models the nodes for an *N-ary* tree data structure. The
@@ -83,12 +81,12 @@ module Tree
83
81
  # {include:file:examples/example_basic.rb}
84
82
  #
85
83
  # @author Anupam Sengupta
84
+ # noinspection RubyTooManyMethodsInspection
86
85
  class TreeNode
87
86
  include Enumerable
88
87
  include Comparable
89
88
  include Tree::Utils::TreeMetricsHandler
90
89
  include Tree::Utils::TreePathHandler
91
- include Tree::Utils::CamelCaseMethodHandler
92
90
  include Tree::Utils::JSONConverter
93
91
  include Tree::Utils::TreeMergeHandler
94
92
  include Tree::Utils::HashConverter
@@ -108,11 +106,11 @@ module Tree
108
106
  # +content+ attribute for any non-unique node requirements.
109
107
  #
110
108
  # If you want to change the name, you probably want to call +rename+
111
- # instead.
109
+ # instead. Note that +name=+ is a protected method.
112
110
  #
113
111
  # @see content
114
112
  # @see rename
115
- attr_reader :name
113
+ attr_accessor :name
116
114
 
117
115
  # @!attribute [rw] content
118
116
  # Content of this node. Can be +nil+. Note that there is no
@@ -132,38 +130,44 @@ module Tree
132
130
  # @return [Tree::TreeNode] Root of the (sub)tree.
133
131
  def root
134
132
  root = self
135
- root = root.parent while !root.is_root?
133
+ root = root.parent until root.root?
136
134
  root
137
135
  end
138
136
 
139
- # @!attribute [r] is_root?
137
+ # @!attribute [r] root?
140
138
  # Returns +true+ if this is a root node. Note that
141
139
  # orphaned children will also be reported as root nodes.
142
140
  #
143
141
  # @return [Boolean] +true+ if this is a root node.
144
- def is_root?
142
+ def root?
145
143
  @parent.nil?
146
144
  end
147
145
 
148
- # @!attribute [r] has_content?
146
+ alias is_root? root? # @todo: Aliased for eventual replacement
147
+
148
+ # @!attribute [r] content?
149
149
  # +true+ if this node has content.
150
150
  #
151
151
  # @return [Boolean] +true+ if the node has content.
152
- def has_content?
152
+ def content?
153
153
  @content != nil
154
154
  end
155
155
 
156
- # @!attribute [r] is_leaf?
156
+ alias has_content? content? # @todo: Aliased for eventual replacement
157
+
158
+ # @!attribute [r] leaf?
157
159
  # +true+ if this node is a _leaf_ - i.e., one without
158
160
  # any children.
159
161
  #
160
162
  # @return [Boolean] +true+ if this is a leaf node.
161
163
  #
162
- # @see #has_children?
163
- def is_leaf?
164
- !has_children?
164
+ # @see #children?
165
+ def leaf?
166
+ !children?
165
167
  end
166
168
 
169
+ alias is_leaf? leaf? # @todo: Aliased for eventual replacement
170
+
167
171
  # @!attribute [r] parentage
168
172
  # An array of ancestors of this node in reversed order
169
173
  # (the first element is the immediate parent of this node).
@@ -173,27 +177,29 @@ module Tree
173
177
  # @return [Array<Tree::TreeNode>] An array of ancestors of this node
174
178
  # @return [nil] if this is a root node.
175
179
  def parentage
176
- return nil if is_root?
180
+ return nil if root?
177
181
 
178
182
  parentage_array = []
179
- prev_parent = self.parent
180
- while (prev_parent)
183
+ prev_parent = parent
184
+ while prev_parent
181
185
  parentage_array << prev_parent
182
186
  prev_parent = prev_parent.parent
183
187
  end
184
188
  parentage_array
185
189
  end
186
190
 
187
- # @!attribute [r] has_children?
191
+ # @!attribute [r] children?
188
192
  # +true+ if the this node has any child node.
189
193
  #
190
194
  # @return [Boolean] +true+ if child nodes exist.
191
195
  #
192
- # @see #is_leaf?
193
- def has_children?
194
- @children.length != 0
196
+ # @see #leaf?
197
+ def children?
198
+ !@children.empty?
195
199
  end
196
200
 
201
+ alias has_children? children? # @todo: Aliased for eventual replacement
202
+
197
203
  # @!group Node Creation
198
204
 
199
205
  # Creates a new node with a name and optional content.
@@ -215,18 +221,14 @@ module Tree
215
221
  #
216
222
  # @see #[]
217
223
  def initialize(name, content = nil)
218
- raise ArgumentError, "Node name HAS to be provided!" if name == nil
219
- @name, @content = name, content
220
-
221
- if name.kind_of?(Integer)
222
- warn StandardWarning,
223
- "Using integer as node name."\
224
- " Semantics of TreeNode[] may not be what you expect!"\
225
- " #{name} #{content}"
226
- end
224
+ raise ArgumentError, 'Node name HAS to be provided!' if name.nil?
225
+
226
+ name = name.to_s if name.is_a?(Integer)
227
+ @name = name
228
+ @content = content
227
229
 
228
- self.set_as_root!
229
- @children_hash = Hash.new
230
+ set_as_root!
231
+ @children_hash = {}
230
232
  @children = []
231
233
  end
232
234
 
@@ -235,7 +237,13 @@ module Tree
235
237
  #
236
238
  # @return [Tree::TreeNode] A copy of this node.
237
239
  def detached_copy
238
- self.class.new(@name, @content ? @content.clone : nil)
240
+ cloned_content =
241
+ begin
242
+ @content&.clone
243
+ rescue TypeError
244
+ @content
245
+ end
246
+ self.class.new(@name, cloned_content)
239
247
  end
240
248
 
241
249
  # Returns a copy of entire (sub-)tree from this node.
@@ -253,50 +261,52 @@ module Tree
253
261
  # Alias for {Tree::TreeNode#detached_subtree_copy}
254
262
  #
255
263
  # @see Tree::TreeNode#detached_subtree_copy
256
- alias :dup :detached_subtree_copy
264
+ alias dup detached_subtree_copy
257
265
 
258
266
  # Returns a {marshal-dump}[http://ruby-doc.org/core-1.8.7/Marshal.html]
259
- # represention of the (sub)tree rooted at this node.
267
+ # representation of the (sub)tree rooted at this node.
260
268
  #
261
269
  def marshal_dump
262
- self.collect { |node| node.create_dump_rep }
270
+ collect(&:create_dump_rep)
263
271
  end
264
272
 
265
273
  # Creates a dump representation of this node and returns the same as
266
274
  # a hash.
267
- def create_dump_rep # :nodoc:
268
- { :name => @name,
269
- :parent => (is_root? ? nil : @parent.name),
270
- :content => Marshal.dump(@content)
271
- }
275
+ def create_dump_rep # :nodoc:
276
+ { name: @name,
277
+ parent: (root? ? nil : @parent.name),
278
+ content: Marshal.dump(@content) }
272
279
  end
273
280
 
274
281
  protected :create_dump_rep
275
282
 
276
- # Loads a marshalled dump of a tree and returns the root node of the
283
+ # Loads a marshaled dump of a tree and returns the root node of the
277
284
  # reconstructed tree. See the
278
285
  # {Marshal}[http://ruby-doc.org/core-1.8.7/Marshal.html] class for
279
286
  # additional details.
280
287
  #
288
+ # NOTE: This is a potentially *unsafe* method with similar concerns as with
289
+ # the Marshal#load method, and should *not* be used with untrusted user
290
+ # provided data.
281
291
  #
282
292
  # @todo This method probably should be a class method. It currently clobbers
283
293
  # self and makes itself the root.
284
294
  #
285
295
  def marshal_load(dumped_tree_array)
286
- nodes = { }
296
+ nodes = {}
287
297
  dumped_tree_array.each do |node_hash|
288
298
  name = node_hash[:name]
289
299
  parent_name = node_hash[:parent]
290
300
  content = Marshal.load(node_hash[:content])
291
301
 
292
- if parent_name then
293
- nodes[name] = current_node = Tree::TreeNode.new(name, content)
302
+ if parent_name
303
+ nodes[name] = current_node = self.class.new(name, content)
294
304
  nodes[parent_name].add current_node
295
305
  else
296
306
  # This is the root node, hence initialize self.
297
307
  initialize(name, content)
298
308
 
299
- nodes[name] = self # Add self to the list of nodes
309
+ nodes[name] = self # Add self to the list of nodes
300
310
  end
301
311
  end
302
312
  end
@@ -308,11 +318,9 @@ module Tree
308
318
  #
309
319
  # @return [String] A string representation of the node.
310
320
  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()}"
321
+ "Node Name: #{@name} Content: #{@content.to_s || '<Empty>'} " \
322
+ "Parent: #{root? ? '<None>' : @parent.name.to_s} " \
323
+ "Children: #{@children.length} Total Nodes: #{size}"
316
324
  end
317
325
 
318
326
  # @!group Structure Modification
@@ -354,7 +362,7 @@ module Tree
354
362
  #
355
363
  # -children.size..children.size
356
364
  #
357
- # This is to prevent +nil+ nodes being created as children if a non-existant
365
+ # This is to prevent +nil+ nodes being created as children if a non-existent
358
366
  # position is used.
359
367
  #
360
368
  # If the new node being added has an existing parent node, then it will be
@@ -380,38 +388,37 @@ module Tree
380
388
  # @see #<<
381
389
  def add(child, at_index = -1)
382
390
  # Only handles the immediate child scenario
383
- raise ArgumentError,
384
- "Attempting to add a nil node" unless child
385
- raise ArgumentError,
386
- "Attempting add node to itself" if self.equal?(child)
387
- raise ArgumentError,
388
- "Attempting add root as a child" if child.equal?(root)
389
-
390
- # Lazy mans unique test, won't test if children of child are unique in
391
+ raise ArgumentError, 'Attempting to add a nil node' unless child
392
+
393
+ raise ArgumentError, 'Attempting add node to itself' if equal?(child)
394
+
395
+ raise ArgumentError, 'Attempting add root as a child' if child.equal?(root)
396
+
397
+ # Lazy man's unique test, won't test if children of child are unique in
391
398
  # this tree too.
392
399
  raise "Child #{child.name} already added!"\
393
400
  if @children_hash.include?(child.name)
394
401
 
395
- child.parent.remove! child if child.parent # Detach from the old parent
402
+ child.parent&.remove! child # Detach from the old parent
396
403
 
397
404
  if insertion_range.include?(at_index)
398
405
  @children.insert(at_index, child)
399
406
  else
400
- raise "Attempting to insert a child at a non-existent location"\
407
+ raise 'Attempting to insert a child at a non-existent location'\
401
408
  " (#{at_index}) "\
402
- "when only positions from "\
409
+ 'when only positions from '\
403
410
  "#{insertion_range.min} to #{insertion_range.max} exist."
404
411
  end
405
412
 
406
- @children_hash[child.name] = child
413
+ @children_hash[child.name] = child
407
414
  child.parent = self
408
- return child
415
+ child
409
416
  end
410
417
 
411
418
  # Return a range of valid insertion positions. Used in the #add method.
412
419
  def insertion_range
413
420
  max = @children.size
414
- min = -(max+1)
421
+ min = -(max + 1)
415
422
  min..max
416
423
  end
417
424
 
@@ -426,8 +433,8 @@ module Tree
426
433
  def rename(new_name)
427
434
  old_name = @name
428
435
 
429
- if is_root?
430
- self.name=(new_name)
436
+ if root?
437
+ self.name = new_name
431
438
  else
432
439
  @parent.rename_child old_name, new_name
433
440
  end
@@ -444,20 +451,10 @@ module Tree
444
451
  # pass a String (Integer names may cause *surprises*)
445
452
  def rename_child(old_name, new_name)
446
453
  raise ArgumentError, "Invalid child name specified: #{old_name}"\
447
- unless @children_hash.has_key?(old_name)
454
+ unless @children_hash.key?(old_name)
448
455
 
449
456
  @children_hash[new_name] = @children_hash.delete(old_name)
450
- @children_hash[new_name].name=(new_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
457
+ @children_hash[new_name].name = new_name
461
458
  end
462
459
 
463
460
  # Replaces the specified child node with another child node on this node.
@@ -472,7 +469,7 @@ module Tree
472
469
  old_child = remove! old_child
473
470
  add new_child, child_index
474
471
 
475
- return old_child
472
+ old_child
476
473
  end
477
474
 
478
475
  # Replaces the node with another node
@@ -513,7 +510,7 @@ module Tree
513
510
  # @param [Tree::TreeNode] parent The parent node.
514
511
  #
515
512
  # @return [Tree::TreeNode] The parent node.
516
- def parent=(parent) # :nodoc:
513
+ def parent=(parent) # :nodoc:
517
514
  @parent = parent
518
515
  @node_depth = nil
519
516
  end
@@ -530,7 +527,7 @@ module Tree
530
527
  #
531
528
  # @see #remove_all!
532
529
  def remove_from_parent!
533
- @parent.remove!(self) unless is_root?
530
+ @parent.remove!(self) unless root?
534
531
  end
535
532
 
536
533
  # Removes all children from this node. If an independent reference exists to
@@ -542,7 +539,7 @@ module Tree
542
539
  # @see #remove!
543
540
  # @see #remove_from_parent!
544
541
  def remove_all!
545
- @children.each { |child| child.set_as_root! }
542
+ @children.each(&:remove_all!)
546
543
 
547
544
  @children_hash.clear
548
545
  @children.clear
@@ -552,7 +549,7 @@ module Tree
552
549
  # Protected method which sets this node as a root node.
553
550
  #
554
551
  # @return +nil+.
555
- def set_as_root! # :nodoc:
552
+ def set_as_root! # :nodoc:
556
553
  self.parent = nil
557
554
  end
558
555
 
@@ -563,7 +560,7 @@ module Tree
563
560
  # The nodes become immutable after this operation. In effect, the entire tree's
564
561
  # structure and contents become _read-only_ and cannot be changed.
565
562
  def freeze_tree!
566
- each {|node| node.freeze}
563
+ each(&:freeze)
567
564
  end
568
565
 
569
566
  # @!endgroup
@@ -574,46 +571,31 @@ module Tree
574
571
  #
575
572
  # - If the +name+ argument is an _Integer_, then the in-sequence
576
573
  # array of children is accessed using the argument as the
577
- # *index* (zero-based). However, if the second _optional_
578
- # +num_as_name+ argument is +true+, then the +name+ is used
579
- # literally as a name, and *NOT* as an *index*
574
+ # *index* (zero-based).
580
575
  #
581
576
  # - If the +name+ argument is *NOT* an _Integer_, then it is taken to
582
577
  # be the *name* of the child node to be returned.
583
578
  #
584
- # If a non-+Integer+ +name+ is passed, and the +num_as_name+
585
- # parameter is also +true+, then a warning is thrown (as this is a
586
- # redundant use of the +num_as_name+ flag.)
579
+ # - To use an _Integer_ as the name, convert it to a _String_ first using
580
+ # +<integer>.to_s+.
587
581
  #
588
582
  # @param [String|Number] name_or_index Name of the child, or its
589
583
  # positional index in the array of child nodes.
590
584
  #
591
- # @param [Boolean] num_as_name Whether to treat the +Integer+
592
- # +name+ argument as an actual name, and *NOT* as an _index_ to
593
- # the children array.
594
- #
595
585
  # @return [Tree::TreeNode] the requested child node. If the index
596
586
  # in not in range, or the name is not present, then a +nil+
597
587
  # is returned.
598
588
  #
599
- # @note The use of +Integer+ names is allowed by using the optional
600
- # +num_as_name+ flag.
601
- #
602
589
  # @raise [ArgumentError] Raised if the +name_or_index+ argument is +nil+.
603
590
  #
604
591
  # @see #add
605
592
  # @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
593
+ def [](name_or_index)
594
+ raise ArgumentError, 'Name_or_index needs to be provided!' if name_or_index.nil?
609
595
 
610
- if name_or_index.kind_of?(Integer) and not num_as_name
596
+ if name_or_index.is_a?(Integer)
611
597
  @children[name_or_index]
612
598
  else
613
- if num_as_name and not name_or_index.kind_of?(Integer)
614
- warn StandardWarning,
615
- "Redundant use of the `num_as_name` flag for non-integer node name"
616
- end
617
599
  @children_hash[name_or_index]
618
600
  end
619
601
  end
@@ -631,22 +613,22 @@ module Tree
631
613
  #
632
614
  # @return [Tree::TreeNode] this node, if a block if given
633
615
  # @return [Enumerator] an enumerator on this tree, if a block is *not* given
634
- def each(&block) # :yields: node
635
-
636
- return self.to_enum unless block_given?
616
+ # noinspection RubyUnusedLocalVariable
617
+ def each # :yields: node
618
+ return to_enum unless block_given?
637
619
 
638
- node_stack = [self] # Start with this node
620
+ node_stack = [self] # Start with this node
639
621
 
640
622
  until node_stack.empty?
641
- current = node_stack.shift # Pop the top-most node
642
- if current # Might be 'nil' (esp. for binary trees)
643
- yield current # and process it
644
- # Stack children of the current node at top of the stack
645
- node_stack = current.children.concat(node_stack)
646
- end
623
+ current = node_stack.shift # Pop the top-most node
624
+ next unless current # Might be 'nil' (esp. for binary trees)
625
+
626
+ yield current # and process it
627
+ # Stack children of the current node at top of the stack
628
+ node_stack = current.children.concat(node_stack)
647
629
  end
648
630
 
649
- return self if block_given?
631
+ self if block_given?
650
632
  end
651
633
 
652
634
  # Traverses the (sub)tree rooted at this node in pre-ordered sequence.
@@ -659,7 +641,7 @@ module Tree
659
641
  #
660
642
  # @return [Tree::TreeNode] this node, if a block if given
661
643
  # @return [Enumerator] an enumerator on this tree, if a block is *not* given
662
- def preordered_each(&block) # :yields: node
644
+ def preordered_each(&block) # :yields: node
663
645
  each(&block)
664
646
  end
665
647
 
@@ -671,30 +653,31 @@ module Tree
671
653
  # @see #breadth_each
672
654
  # @return [Tree::TreeNode] this node, if a block if given
673
655
  # @return [Enumerator] an enumerator on this tree, if a block is *not* given
674
- def postordered_each(&block)
675
- return self.to_enum(:postordered_each) unless block_given?
656
+ # noinspection RubyUnusedLocalVariable
657
+ def postordered_each
658
+ return to_enum(:postordered_each) unless block_given?
676
659
 
677
660
  # Using a marked node in order to skip adding the children of nodes that
678
661
  # have already been visited. This allows the stack depth to be controlled,
679
662
  # and also allows stateful backtracking.
680
- markednode = Struct.new(:node, :visited)
681
- node_stack = [markednode.new(self, false)] # Start with self
663
+ marked_node = Struct.new(:node, :visited)
664
+ node_stack = [marked_node.new(self, false)] # Start with self
682
665
 
683
666
  until node_stack.empty?
684
667
  peek_node = node_stack[0]
685
- if peek_node.node.has_children? and not peek_node.visited
668
+ if peek_node.node.children? && !peek_node.visited
686
669
  peek_node.visited = true
687
670
  # Add the children to the stack. Use the marking structure.
688
671
  marked_children =
689
- peek_node.node.children.map {|node| markednode.new(node, false)}
672
+ peek_node.node.children.map { |node| marked_node.new(node, false) }
690
673
  node_stack = marked_children.concat(node_stack)
691
674
  next
692
675
  else
693
- yield node_stack.shift.node # Pop and yield the current node
676
+ yield node_stack.shift.node # Pop and yield the current node
694
677
  end
695
678
  end
696
679
 
697
- return self if block_given?
680
+ self if block_given?
698
681
  end
699
682
 
700
683
  # Performs breadth-first traversal of the (sub)tree rooted at this node. The
@@ -708,10 +691,11 @@ module Tree
708
691
  #
709
692
  # @return [Tree::TreeNode] this node, if a block if given
710
693
  # @return [Enumerator] an enumerator on this tree, if a block is *not* given
711
- def breadth_each(&block)
712
- return self.to_enum(:breadth_each) unless block_given?
694
+ # noinspection RubyUnusedLocalVariable
695
+ def breadth_each
696
+ return to_enum(:breadth_each) unless block_given?
713
697
 
714
- node_queue = [self] # Create a queue with self as the initial entry
698
+ node_queue = [self] # Create a queue with self as the initial entry
715
699
 
716
700
  # Use a queue to do breadth traversal
717
701
  until node_queue.empty?
@@ -721,7 +705,7 @@ module Tree
721
705
  node_to_traverse.children { |child| node_queue.push child }
722
706
  end
723
707
 
724
- return self if block_given?
708
+ self if block_given?
725
709
  end
726
710
 
727
711
  # An array of all the immediate children of this node. The child
@@ -736,12 +720,12 @@ module Tree
736
720
  #
737
721
  # @return [Array<Tree::TreeNode>] An array of the child nodes, if no block
738
722
  # is given.
739
- def children
723
+ def children(&block)
740
724
  if block_given?
741
- @children.each {|child| yield child}
742
- return self
725
+ @children.each(&block)
726
+ self
743
727
  else
744
- return @children.clone
728
+ @children.clone
745
729
  end
746
730
  end
747
731
 
@@ -758,12 +742,35 @@ module Tree
758
742
  #
759
743
  # @return [Tree::TreeNode] this node, if a block if given
760
744
  # @return [Array<Tree::TreeNode>] An array of the leaf nodes
761
- def each_leaf &block
745
+ # noinspection RubyUnusedLocalVariable
746
+ def each_leaf
747
+ if block_given?
748
+ each { |node| yield(node) if node.leaf? }
749
+ self
750
+ else
751
+ self.select(&:leaf?)
752
+ end
753
+ end
754
+
755
+ # Yields every level of the (sub)tree rooted at this node to the
756
+ # specified block.
757
+ #
758
+ # Will yield this node as well since it is considered the first level.
759
+ #
760
+ # @yieldparam level [Array<Tree::TreeNode>] All nodes in the level
761
+ #
762
+ # @return [Tree::TreeNode] this node, if a block if given
763
+ # @return [Enumerator] an enumerator on this tree, if a block is *not* given
764
+ def each_level
762
765
  if block_given?
763
- self.each { |node| yield(node) if node.is_leaf? }
764
- return self
766
+ level = [self]
767
+ until level.empty?
768
+ yield level
769
+ level = level.map(&:children).flatten
770
+ end
771
+ self
765
772
  else
766
- self.select { |node| node.is_leaf?}
773
+ each
767
774
  end
768
775
  end
769
776
 
@@ -776,7 +783,7 @@ module Tree
776
783
  #
777
784
  # @return [Tree::TreeNode] The first child, or +nil+ if none is present.
778
785
  def first_child
779
- children.first
786
+ @children.first
780
787
  end
781
788
 
782
789
  # Last child of this node.
@@ -784,7 +791,7 @@ module Tree
784
791
  #
785
792
  # @return [Tree::TreeNode] The last child, or +nil+ if none is present.
786
793
  def last_child
787
- children.last
794
+ @children.last
788
795
  end
789
796
 
790
797
  # @!group Navigating the Sibling Nodes
@@ -799,22 +806,24 @@ module Tree
799
806
  #
800
807
  # @return [Tree::TreeNode] The first sibling node.
801
808
  #
802
- # @see #is_first_sibling?
809
+ # @see #first_sibling?
803
810
  # @see #last_sibling
804
811
  def first_sibling
805
- is_root? ? self : parent.children.first
812
+ root? ? self : parent.children.first
806
813
  end
807
814
 
808
815
  # Returns +true+ if this node is the first sibling at its level.
809
816
  #
810
817
  # @return [Boolean] +true+ if this is the first sibling.
811
818
  #
812
- # @see #is_last_sibling?
819
+ # @see #last_sibling?
813
820
  # @see #first_sibling
814
- def is_first_sibling?
821
+ def first_sibling?
815
822
  first_sibling == self
816
823
  end
817
824
 
825
+ alias is_first_sibling? first_sibling? # @todo: Aliased for eventual replacement
826
+
818
827
  # Last sibling of this node. If this is the root node, then returns
819
828
  # itself.
820
829
  #
@@ -825,22 +834,24 @@ module Tree
825
834
  #
826
835
  # @return [Tree::TreeNode] The last sibling node.
827
836
  #
828
- # @see #is_last_sibling?
837
+ # @see #last_sibling?
829
838
  # @see #first_sibling
830
839
  def last_sibling
831
- is_root? ? self : parent.children.last
840
+ root? ? self : parent.children.last
832
841
  end
833
842
 
834
843
  # Returns +true+ if this node is the last sibling at its level.
835
844
  #
836
845
  # @return [Boolean] +true+ if this is the last sibling.
837
846
  #
838
- # @see #is_first_sibling?
847
+ # @see #first_sibling?
839
848
  # @see #last_sibling
840
- def is_last_sibling?
849
+ def last_sibling?
841
850
  last_sibling == self
842
851
  end
843
852
 
853
+ alias is_last_sibling? last_sibling? # @todo: Aliased for eventual replacement
854
+
844
855
  # An array of siblings for this node. This node is excluded.
845
856
  #
846
857
  # If a block is provided, yields each of the sibling nodes to the block.
@@ -858,12 +869,14 @@ module Tree
858
869
  def siblings
859
870
  if block_given?
860
871
  parent.children.each { |sibling| yield sibling if sibling != self }
861
- return self
872
+ self
862
873
  else
863
- return [] if is_root?
874
+ return [] if root?
875
+
864
876
  siblings = []
865
- parent.children {|my_sibling|
866
- siblings << my_sibling if my_sibling != self}
877
+ parent.children do |my_sibling|
878
+ siblings << my_sibling if my_sibling != self
879
+ end
867
880
  siblings
868
881
  end
869
882
  end
@@ -875,10 +888,12 @@ module Tree
875
888
  # @return [Boolean] +true+ if this is the only child of its parent.
876
889
  #
877
890
  # @see #siblings
878
- def is_only_child?
879
- is_root? ? true : parent.children.size == 1
891
+ def only_child?
892
+ root? ? true : parent.children.size == 1
880
893
  end
881
894
 
895
+ alias is_only_child? only_child? # @todo: Aliased for eventual replacement
896
+
882
897
  # Next sibling for this node.
883
898
  # The _next_ node is defined as the node to right of this node.
884
899
  #
@@ -890,10 +905,10 @@ module Tree
890
905
  # @see #previous_sibling
891
906
  # @see #siblings
892
907
  def next_sibling
893
- return nil if is_root?
908
+ return nil if root?
894
909
 
895
- myidx = parent.children.index(self)
896
- parent.children.at(myidx + 1) if myidx
910
+ idx = parent.children.index(self)
911
+ parent.children.at(idx + 1) if idx
897
912
  end
898
913
 
899
914
  # Previous sibling of this node.
@@ -907,17 +922,17 @@ module Tree
907
922
  # @see #next_sibling
908
923
  # @see #siblings
909
924
  def previous_sibling
910
- return nil if is_root?
925
+ return nil if root?
911
926
 
912
- myidx = parent.children.index(self)
913
- parent.children.at(myidx - 1) if myidx && myidx > 0
927
+ idx = parent.children.index(self)
928
+ parent.children.at(idx - 1) if idx&.positive?
914
929
  end
915
930
 
916
931
  # @!endgroup
917
932
 
918
- # Provides a comparision operation for the nodes.
933
+ # Provides a comparison operation for the nodes.
919
934
  #
920
- # Comparision is based on the natural ordering of the node name objects.
935
+ # Comparison is based on the natural ordering of the node name objects.
921
936
  #
922
937
  # @param [Tree::TreeNode] other The other node to compare against.
923
938
  #
@@ -925,8 +940,9 @@ module Tree
925
940
  # this node is a 'predecessor'. Returns 'nil' if the other
926
941
  # object is not a 'Tree::TreeNode'.
927
942
  def <=>(other)
928
- return nil if other == nil || other.class != Tree::TreeNode
929
- self.name <=> other.name
943
+ return nil if other.nil? || !other.is_a?(Tree::TreeNode)
944
+
945
+ name <=> other.name
930
946
  end
931
947
 
932
948
  # Pretty prints the (sub)tree rooted at this node.
@@ -937,17 +953,18 @@ module Tree
937
953
  # @param [Proc] block optional block to use for rendering
938
954
  def print_tree(level = node_depth, max_depth = nil,
939
955
  block = lambda { |node, prefix|
940
- puts "#{prefix} #{node.name}" })
941
- prefix = ''
956
+ puts "#{prefix} #{node.name}"
957
+ })
958
+ prefix = ''.dup # dup NEEDs to be invoked to make this mutable.
942
959
 
943
- if is_root?
960
+ if root?
944
961
  prefix << '*'
945
962
  else
946
- prefix << '|' unless parent.is_last_sibling?
963
+ prefix << '|' unless parent.last_sibling?
947
964
  prefix << (' ' * (level - 1) * 4)
948
- prefix << (is_last_sibling? ? '+' : '|')
965
+ prefix << (last_sibling? ? '+' : '|')
949
966
  prefix << '---'
950
- prefix << (has_children? ? '+' : '>')
967
+ prefix << (children? ? '+' : '>')
951
968
  end
952
969
 
953
970
  block.call(self, prefix)
@@ -955,10 +972,10 @@ module Tree
955
972
  # Exit if the max level is defined, and reached.
956
973
  return unless max_depth.nil? || level < max_depth
957
974
 
958
- children { |child|
959
- child.print_tree(level + 1,
960
- max_depth, block) if child } # Child might be 'nil'
975
+ # Child might be 'nil'
976
+ children do |child|
977
+ child&.print_tree(level + 1, max_depth, block)
978
+ end
961
979
  end
962
-
963
980
  end
964
981
  end