rubytree 0.7.0 → 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +1 -0
- data/API-CHANGES +9 -0
- data/History.txt +37 -0
- data/README +2 -1
- data/Rakefile +26 -3
- data/TODO +12 -7
- data/lib/tree.rb +196 -114
- data/lib/tree/binarytree.rb +40 -21
- data/test/test_binarytree.rb +84 -55
- data/test/test_tree.rb +346 -190
- metadata +48 -11
- metadata.gz.sig +2 -0
data.tar.gz.sig
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ܥ**�]ڏ�)�5"����qƵtܣ�g��Fl]t�߉���8?��)��������Bl"3mj(˳~�"U<_�M="�J�>�ݚ�K�1�h�Hgaw(�,���)T9�N��ⶎ�ڙ���P-q���U������)��u��T�e��>-��l�H�{�`,���Cn6� ow�x��q�(k'g`��{pɆ^u�4��&xGQ��E�DP*�.`���F?�m��m>+��W��C���U�f%u��2z\0�
|
data/API-CHANGES
CHANGED
@@ -5,6 +5,15 @@ This file documents various API level changes that have been made to the RubyTre
|
|
5
5
|
Note: API level changes are expected to reduce dramatically after the 1.x release. In most cases, an alternative will
|
6
6
|
be provided to ensure relatively smooth transition to the new APIs.
|
7
7
|
|
8
|
+
== Release 0.8.0 Changes
|
9
|
+
|
10
|
+
- Added the ability to specify an optional insertion position in the {Tree::TreeNode#add} method. Idea and original
|
11
|
+
code contributed by Dirk.
|
12
|
+
- Added a new method {Tree::TreeNode#detached_subtree_copy} to allow cloning the entire tree. This method is also
|
13
|
+
aliased to {Tree::TreeNode#dup}. Idea and original code contributed by Vincenzo Farruggia.
|
14
|
+
- Converted all CamelCase method names to the canonical ruby_method_names (underscore separated). The CamelCase methods
|
15
|
+
can still be invoked, but will throw a Deprecated warning.
|
16
|
+
|
8
17
|
== Release 0.7.0 Changes
|
9
18
|
|
10
19
|
- Converted all exceptions thrown on invalid method arguments to from 'RuntimeError' to 'ArgumentError'. This impacts the
|
data/History.txt
CHANGED
@@ -1,3 +1,40 @@
|
|
1
|
+
=== 0.8.1 / 2010-10-02
|
2
|
+
|
3
|
+
* This is the public release of R0.8.0, with additional bug-fixes.
|
4
|
+
Note that R0.8.0 will not be released separately as a publicly
|
5
|
+
available Rubygem. All changes as listed for R0.8.0 are available in
|
6
|
+
this release.
|
7
|
+
|
8
|
+
* The main change in R0.8.0/R0.8.1 is conversion of all CamelCase
|
9
|
+
method names to snake_case. The old CamelCase method names will
|
10
|
+
still work (to ensure backwards compatibility), but will also
|
11
|
+
display a warning.
|
12
|
+
|
13
|
+
* The TreeNode#add method now accepts an optional child insertion
|
14
|
+
point.
|
15
|
+
|
16
|
+
* The subtree from the current node can now be cloned in its entirety
|
17
|
+
using the `TreeNode#detached_subtree_copy' method.
|
18
|
+
|
19
|
+
* A major bug-fix for bug #28613 which impacted the Binarytree
|
20
|
+
implementation. See
|
21
|
+
http://rubyforge.org/tracker/index.php?func=detail&aid=28613&group_id=1215&atid=4793
|
22
|
+
for details.
|
23
|
+
|
24
|
+
* Minor code re-factoring driven by the code-smell checks using reek.
|
25
|
+
|
26
|
+
* Inclusion of the `reek' code-smell detection tool in the Rakefile.
|
27
|
+
|
28
|
+
=== 0.8.0 / 2010-05-04
|
29
|
+
|
30
|
+
* Updated the 'add' method to allow the optional specification of an insertion position in the child array.
|
31
|
+
|
32
|
+
* Added a new method 'detached_subtree_copy' to allow cloning the entire tree (this method is also aliased as 'dup').
|
33
|
+
|
34
|
+
* Converted all CamelCase method names to the canonical ruby_method_names (underscore separated). The CamelCase methods
|
35
|
+
can still be invoked, but will throw a Deprecated warning. The support for old CamelCase methods will go away some
|
36
|
+
time in the future, so the user is advised to convert all current method invocations to the new names.
|
37
|
+
|
1
38
|
=== 0.7.0 / 2010-05-03
|
2
39
|
|
3
40
|
* Added new methods to report the degree statistics of a node.
|
data/README
CHANGED
@@ -47,7 +47,7 @@ As an example, the following code-snippet implements this tree structure:
|
|
47
47
|
root_node << Tree::TreeNode.new("CHILD2", "Child2 Content")
|
48
48
|
|
49
49
|
# ..... Lets print the representation to stdout. This is primarily used for debugging purposes.
|
50
|
-
root_node.
|
50
|
+
root_node.print_tree
|
51
51
|
|
52
52
|
# ..... Lets directly access children and grandchildren of the root. The can be "chained" for a given path to any depth.
|
53
53
|
child1 = root_node["CHILD1"]
|
@@ -183,6 +183,7 @@ For generating the documentation, it is strongly suggested that the Yard[http://
|
|
183
183
|
I would like to acknowledge the following contributors for helping improve RubyTree:
|
184
184
|
|
185
185
|
1. Dirk Breuer (http://github.com/railsbros-dirk) for contributing the JSON conversion code.
|
186
|
+
2. Vincenzo Farruggia for contributing the (sub)tree cloning code.
|
186
187
|
|
187
188
|
== LICENSE:
|
188
189
|
|
data/Rakefile
CHANGED
@@ -71,10 +71,15 @@ begin
|
|
71
71
|
|
72
72
|
Thank you for installing #{PKG_NAME}.
|
73
73
|
|
74
|
-
|
74
|
+
WARNING: SIGNIFICANT API CHANGE in 0.8.0 !
|
75
|
+
------------------------------------------
|
75
76
|
|
76
|
-
|
77
|
-
|
77
|
+
Please note that as of 0.8.0 the CamelCase method names are DEPRECATED.
|
78
|
+
|
79
|
+
The new method names follow the ruby_convention (separated by '_').
|
80
|
+
|
81
|
+
The old CamelCase methods still work (a warning will be displayed),
|
82
|
+
but may go away in the future.
|
78
83
|
|
79
84
|
Details of the API changes are documented in the API-CHANGES file.
|
80
85
|
|
@@ -122,3 +127,21 @@ rescue LoadError
|
|
122
127
|
|
123
128
|
END
|
124
129
|
end
|
130
|
+
|
131
|
+
begin
|
132
|
+
require 'reek/rake/task'
|
133
|
+
Reek::Rake::Task.new do |t|
|
134
|
+
t.verbose = false
|
135
|
+
t.source_files = 'lib/**/*.rb'
|
136
|
+
end
|
137
|
+
rescue LoadError
|
138
|
+
$stderr.puts <<-END
|
139
|
+
ERROR!!! You need to have reek (http://github.com/kevinrutherford/reek) for detecing the code smell.
|
140
|
+
|
141
|
+
You can install the reek gem by running the following command as root (or sudo):
|
142
|
+
|
143
|
+
$ gem install reek
|
144
|
+
|
145
|
+
END
|
146
|
+
|
147
|
+
end
|
data/TODO
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
# -*- mode: org; coding: utf-8-unix; -*-
|
2
2
|
|
3
|
-
|
4
3
|
* R0.7.0
|
5
|
-
***
|
4
|
+
*** DONE Start using signed tags from R0.7.0
|
6
5
|
*** DONE Add a check in the Tree::TreeNode.add method to prevent addition of nil child nodes
|
7
6
|
CLOSED: [2010-02-23 Tue 23:07]
|
8
7
|
*** DONE Fix the edge condition for Tree::TreeNode.isOnlyChild? when the root node is the receiver.
|
@@ -16,9 +15,19 @@
|
|
16
15
|
*** DONE Add new methods to return the degree counts of the receiver node (in-degree and out-degree)
|
17
16
|
CLOSED: [2010-01-30 Sat 23:56]
|
18
17
|
|
18
|
+
|
19
|
+
|
19
20
|
* R0.8.0
|
20
|
-
***
|
21
|
+
*** DONE Convert all method names to the canonical /^[_a-z<>=\[|+-\/\*`]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/ pattern
|
21
22
|
See Roodi report at http://getcaliper.com/caliper/tool?tool=roodi&repo=git://github.com/evolve75/RubyTree.git
|
23
|
+
*** DONE Integrate the subtree cloning patch submitted by Vincenzo Farrugia.
|
24
|
+
|
25
|
+
|
26
|
+
* R0.8.1
|
27
|
+
*** DONE Fix [[http://rubyforge.org/tracker/index.php?func%3Ddetail&aid%3D28613&group_id%3D1215&atid%3D4793][bug #28613]] which was affecting the `leftChild=' and `rightChild=' methods for binary trees.
|
28
|
+
|
29
|
+
|
30
|
+
* R0.9.0
|
22
31
|
*** TODO Fix the inconsistency of returning root as its first sibling, and returning a nil instead. Ditto for last sibling.
|
23
32
|
*** TODO fix the inconsistency of returning nil for the root, and an empty array for nodes which have no siblings.
|
24
33
|
*** TODO We should perhaps return nil as root's root.
|
@@ -46,7 +55,3 @@
|
|
46
55
|
*** TODO Fix the inconsistency of returning root as its last sibling, and returning a nil array for siblings of the node
|
47
56
|
*** TODO marshal_load method probably should be a class method. It currently clobbers self.
|
48
57
|
*** TODO Fix the inconsistency of returning root as its first/last sibling, and returning a nil array for siblings of the node
|
49
|
-
|
50
|
-
$Id$
|
51
|
-
|
52
|
-
|
data/lib/tree.rb
CHANGED
@@ -48,7 +48,7 @@
|
|
48
48
|
module Tree
|
49
49
|
|
50
50
|
# Rubytree Package Version
|
51
|
-
VERSION = '0.
|
51
|
+
VERSION = '0.8.1'
|
52
52
|
|
53
53
|
# == TreeNode Class Description
|
54
54
|
#
|
@@ -97,13 +97,14 @@ module Tree
|
|
97
97
|
#
|
98
98
|
# # ..... Create the root node first. Note that every node has a name and an optional content payload.
|
99
99
|
# root_node = Tree::TreeNode.new("ROOT", "Root Content")
|
100
|
+
# root_node.print_tree
|
100
101
|
#
|
101
102
|
# # ..... Now insert the child nodes. Note that you can "chain" the child insertions for a given path to any depth.
|
102
103
|
# root_node << Tree::TreeNode.new("CHILD1", "Child1 Content") << Tree::TreeNode.new("GRANDCHILD1", "GrandChild1 Content")
|
103
104
|
# root_node << Tree::TreeNode.new("CHILD2", "Child2 Content")
|
104
105
|
#
|
105
106
|
# # ..... Lets print the representation to stdout. This is primarily used for debugging purposes.
|
106
|
-
# root_node.
|
107
|
+
# root_node.print_tree
|
107
108
|
#
|
108
109
|
# # ..... Lets directly access children and grandchildren of the root. The can be "chained" for a given path to any depth.
|
109
110
|
# child1 = root_node["CHILD1"]
|
@@ -134,7 +135,6 @@ module Tree
|
|
134
135
|
# Parent of this node. Will be +nil+ for a root node.
|
135
136
|
attr_reader :parent
|
136
137
|
|
137
|
-
|
138
138
|
# Creates a new node with a name and optional content.
|
139
139
|
# The node name is expected to be unique within the tree.
|
140
140
|
#
|
@@ -148,8 +148,8 @@ module Tree
|
|
148
148
|
raise ArgumentError, "Node name HAS to be provided!" if name == nil
|
149
149
|
@name, @content = name, content
|
150
150
|
|
151
|
-
self.
|
152
|
-
@
|
151
|
+
self.set_as_root!
|
152
|
+
@children_hash = Hash.new
|
153
153
|
@children = []
|
154
154
|
end
|
155
155
|
|
@@ -161,6 +161,23 @@ module Tree
|
|
161
161
|
Tree::TreeNode.new(@name, @content ? @content.clone : nil)
|
162
162
|
end
|
163
163
|
|
164
|
+
# Returns a copy of entire (sub-)tree from receiver node.
|
165
|
+
#
|
166
|
+
# @author Vincenzo Farruggia
|
167
|
+
# @since 0.8.0
|
168
|
+
#
|
169
|
+
# @return [Tree::TreeNode] A copy of (sub-)tree from receiver node.
|
170
|
+
def detached_subtree_copy
|
171
|
+
new_node = detached_copy
|
172
|
+
children { |child| new_node << child.detached_subtree_copy }
|
173
|
+
new_node
|
174
|
+
end
|
175
|
+
|
176
|
+
# Alias for {Tree::TreeNode#detached_subtree_copy}
|
177
|
+
#
|
178
|
+
# @see Tree::TreeNode#detached_subtree_copy
|
179
|
+
alias :dup :detached_subtree_copy
|
180
|
+
|
164
181
|
# Returns string representation of the receiver node.
|
165
182
|
# This method is primarily meant for debugging purposes.
|
166
183
|
#
|
@@ -168,7 +185,7 @@ module Tree
|
|
168
185
|
def to_s
|
169
186
|
"Node Name: #{@name}" +
|
170
187
|
" Content: " + (@content || "<Empty>") +
|
171
|
-
" Parent: " + (
|
188
|
+
" Parent: " + (is_root?() ? "<None>" : @parent.name) +
|
172
189
|
" Children: #{@children.length}" +
|
173
190
|
" Total Nodes: #{size()}"
|
174
191
|
end
|
@@ -180,16 +197,16 @@ module Tree
|
|
180
197
|
#
|
181
198
|
# @return [Array, nil] An array of ancestors of the receiver node, or +nil+ if this is a root node.
|
182
199
|
def parentage
|
183
|
-
return nil if
|
200
|
+
return nil if is_root?
|
184
201
|
|
185
|
-
|
186
|
-
|
187
|
-
while (
|
188
|
-
|
189
|
-
|
202
|
+
parentage_array = []
|
203
|
+
prev_parent = self.parent
|
204
|
+
while (prev_parent)
|
205
|
+
parentage_array << prev_parent
|
206
|
+
prev_parent = prev_parent.parent
|
190
207
|
end
|
191
208
|
|
192
|
-
|
209
|
+
parentage_array
|
193
210
|
end
|
194
211
|
|
195
212
|
# Protected method to set the parent node for the receiver node.
|
@@ -228,25 +245,44 @@ module Tree
|
|
228
245
|
# the child is added as the last child ("right most") in the current set of
|
229
246
|
# children of the receiver node.
|
230
247
|
#
|
248
|
+
# Additionally you can specify a insert position. The new node will be inserted
|
249
|
+
# BEFORE that position. If you don't specify any position the node will be
|
250
|
+
# just appended. This feature is provided to make implementation of node
|
251
|
+
# movement within the tree very simple.
|
252
|
+
#
|
253
|
+
# If an insertion position is provided, it needs to be within the valid range of:
|
254
|
+
#
|
255
|
+
# -children.size..children.size
|
256
|
+
#
|
257
|
+
# This is to prevent +nil+ nodes being created as children if a non-existant position is used.
|
258
|
+
#
|
231
259
|
# @param [Tree::TreeNode] child The child node to add.
|
260
|
+
# @param [optional, Number] at_index The optional position where the node is to be inserted.
|
232
261
|
#
|
233
262
|
# @return [Tree::TreeNode] The added child node.
|
234
263
|
#
|
235
264
|
# @raise [RuntimeError] This exception is raised if another child node with the same
|
236
|
-
# name exists.
|
265
|
+
# name exists, or if an invalid insertion position is specified.
|
237
266
|
# @raise [ArgumentError] This exception is raised if a +nil+ node is passed as the argument.
|
238
267
|
#
|
239
268
|
# @see #<<
|
240
|
-
def add(child)
|
269
|
+
def add(child, at_index = -1)
|
241
270
|
raise ArgumentError, "Attempting to add a nil node" unless child
|
242
|
-
raise "Child #{child.name} already added!" if @
|
271
|
+
raise "Child #{child.name} already added!" if @children_hash.has_key?(child.name)
|
243
272
|
|
244
|
-
|
245
|
-
|
273
|
+
if insertion_range.include?(at_index)
|
274
|
+
@children.insert(at_index, child)
|
275
|
+
else
|
276
|
+
raise "Attempting to insert a child at a non-existent location (#{at_index}) when only positions from #{insertion_range.min} to #{insertion_range.max} exist."
|
277
|
+
end
|
278
|
+
|
279
|
+
@children_hash[child.name] = child
|
246
280
|
child.parent = self
|
247
281
|
return child
|
248
282
|
end
|
249
283
|
|
284
|
+
|
285
|
+
|
250
286
|
# Removes the specified child node from the receiver node.
|
251
287
|
#
|
252
288
|
# This method can also be used for *pruning* a sub-tree, in cases where the removed child node is
|
@@ -259,14 +295,14 @@ module Tree
|
|
259
295
|
#
|
260
296
|
# @return [Tree::TreeNode] The removed child node, or +nil+ if a +nil+ was passed in as argument.
|
261
297
|
#
|
262
|
-
# @see #
|
263
|
-
# @see #
|
298
|
+
# @see #remove_from_parent!
|
299
|
+
# @see #remove_all!
|
264
300
|
def remove!(child)
|
265
301
|
return nil unless child
|
266
302
|
|
267
|
-
@
|
303
|
+
@children_hash.delete(child.name)
|
268
304
|
@children.delete(child)
|
269
|
-
child.
|
305
|
+
child.set_as_root!
|
270
306
|
child
|
271
307
|
end
|
272
308
|
|
@@ -276,9 +312,9 @@ module Tree
|
|
276
312
|
#
|
277
313
|
# @return [Tree:TreeNode] +self+ (the removed receiver node) if the operation is successful, +nil+ otherwise.
|
278
314
|
#
|
279
|
-
# @see #
|
280
|
-
def
|
281
|
-
@parent.remove!(self) unless
|
315
|
+
# @see #remove_all!
|
316
|
+
def remove_from_parent!
|
317
|
+
@parent.remove!(self) unless is_root?
|
282
318
|
end
|
283
319
|
|
284
320
|
# Removes all children from the receiver node. If an indepedent reference exists to the child
|
@@ -287,12 +323,11 @@ module Tree
|
|
287
323
|
# @return [Tree::TreeNode] The receiver node (+self+)
|
288
324
|
#
|
289
325
|
# @see #remove!
|
290
|
-
# @see #
|
291
|
-
def
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
@childrenHash.clear
|
326
|
+
# @see #remove_from_parent!
|
327
|
+
def remove_all!
|
328
|
+
@children.each { |child| child.set_as_root! }
|
329
|
+
|
330
|
+
@children_hash.clear
|
296
331
|
@children.clear
|
297
332
|
self
|
298
333
|
end
|
@@ -300,14 +335,14 @@ module Tree
|
|
300
335
|
# Returns +true+ if the receiver node has content.
|
301
336
|
#
|
302
337
|
# @return [Boolean] +true+ if the node has content.
|
303
|
-
def
|
338
|
+
def has_content?
|
304
339
|
@content != nil
|
305
340
|
end
|
306
341
|
|
307
342
|
# Protected method which sets the receiver node as a root node.
|
308
343
|
#
|
309
344
|
# @return +nil+.
|
310
|
-
def
|
345
|
+
def set_as_root! # :nodoc:
|
311
346
|
@parent = nil
|
312
347
|
end
|
313
348
|
|
@@ -315,7 +350,7 @@ module Tree
|
|
315
350
|
# orphaned children will also be reported as root nodes.
|
316
351
|
#
|
317
352
|
# @return [Boolean] +true+ if this is a root node.
|
318
|
-
def
|
353
|
+
def is_root?
|
319
354
|
@parent == nil
|
320
355
|
end
|
321
356
|
|
@@ -323,8 +358,8 @@ module Tree
|
|
323
358
|
#
|
324
359
|
# @return [Boolean] +true+ if child nodes exist.
|
325
360
|
#
|
326
|
-
# @see #
|
327
|
-
def
|
361
|
+
# @see #is_leaf?
|
362
|
+
def has_children?
|
328
363
|
@children.length != 0
|
329
364
|
end
|
330
365
|
|
@@ -333,9 +368,9 @@ module Tree
|
|
333
368
|
#
|
334
369
|
# @return [Boolean] +true+ if this is a leaf node.
|
335
370
|
#
|
336
|
-
# @see #
|
337
|
-
def
|
338
|
-
!
|
371
|
+
# @see #has_children?
|
372
|
+
def is_leaf?
|
373
|
+
!has_children?
|
339
374
|
end
|
340
375
|
|
341
376
|
# Returns an array of all the immediate children of the receiver node. The child nodes are ordered
|
@@ -360,7 +395,7 @@ module Tree
|
|
360
395
|
# Will return +nil+ if no children are present.
|
361
396
|
#
|
362
397
|
# @return [Tree::TreeNode] The first child, or +nil+ if none is present.
|
363
|
-
def
|
398
|
+
def first_child
|
364
399
|
children.first
|
365
400
|
end
|
366
401
|
|
@@ -369,7 +404,7 @@ module Tree
|
|
369
404
|
# Will return +nil+ if no children are present.
|
370
405
|
#
|
371
406
|
# @return [Tree::TreeNode] The last child, or +nil+ if none is present.
|
372
|
-
def
|
407
|
+
def last_child
|
373
408
|
children.last
|
374
409
|
end
|
375
410
|
|
@@ -432,7 +467,7 @@ module Tree
|
|
432
467
|
# @see #each
|
433
468
|
# @see #breadth_each
|
434
469
|
def each_leaf &block
|
435
|
-
self.each { |node| yield(node) if node.
|
470
|
+
self.each { |node| yield(node) if node.is_leaf? }
|
436
471
|
end
|
437
472
|
|
438
473
|
# Returns the requested node from the set of immediate children.
|
@@ -458,7 +493,7 @@ module Tree
|
|
458
493
|
if name_or_index.kind_of?(Integer)
|
459
494
|
@children[name_or_index]
|
460
495
|
else
|
461
|
-
@
|
496
|
+
@children_hash[name_or_index]
|
462
497
|
end
|
463
498
|
end
|
464
499
|
|
@@ -489,21 +524,20 @@ module Tree
|
|
489
524
|
# Pretty prints the (sub)tree rooted at the receiver node.
|
490
525
|
#
|
491
526
|
# @param [Number] level The indentation level (4 spaces) to start with.
|
492
|
-
def
|
493
|
-
|
494
|
-
if isRoot?
|
527
|
+
def print_tree(level = 0)
|
528
|
+
if is_root?
|
495
529
|
print "*"
|
496
530
|
else
|
497
|
-
print "|" unless parent.
|
531
|
+
print "|" unless parent.is_last_sibling?
|
498
532
|
print(' ' * (level - 1) * 4)
|
499
|
-
print(
|
533
|
+
print(is_last_sibling? ? "+" : "|")
|
500
534
|
print "---"
|
501
|
-
print(
|
535
|
+
print(has_children? ? "+" : ">")
|
502
536
|
end
|
503
537
|
|
504
538
|
puts " #{name}"
|
505
539
|
|
506
|
-
children { |child| child.
|
540
|
+
children { |child| child.print_tree(level + 1)}
|
507
541
|
end
|
508
542
|
|
509
543
|
# Returns root node for the (sub)tree to which the receiver node belongs.
|
@@ -515,7 +549,7 @@ module Tree
|
|
515
549
|
# @return [Tree::TreeNode] Root of the (sub)tree.
|
516
550
|
def root
|
517
551
|
root = self
|
518
|
-
root = root.parent while !root.
|
552
|
+
root = root.parent while !root.is_root?
|
519
553
|
root
|
520
554
|
end
|
521
555
|
|
@@ -530,20 +564,20 @@ module Tree
|
|
530
564
|
#
|
531
565
|
# @return [Tree::TreeNode] The first sibling node.
|
532
566
|
#
|
533
|
-
# @see #
|
534
|
-
# @see #
|
535
|
-
def
|
536
|
-
|
567
|
+
# @see #is_first_sibling?
|
568
|
+
# @see #last_sibling
|
569
|
+
def first_sibling
|
570
|
+
is_root? ? self : parent.children.first
|
537
571
|
end
|
538
572
|
|
539
573
|
# Returns +true+ if the receiver node is the first sibling at its level.
|
540
574
|
#
|
541
575
|
# @return [Boolean] +true+ if this is the first sibling.
|
542
576
|
#
|
543
|
-
# @see #
|
544
|
-
# @see #
|
545
|
-
def
|
546
|
-
|
577
|
+
# @see #is_last_sibling?
|
578
|
+
# @see #first_sibling
|
579
|
+
def is_first_sibling?
|
580
|
+
first_sibling == self
|
547
581
|
end
|
548
582
|
|
549
583
|
# Returns the last sibling of the receiver node. If this is the root node, then returns
|
@@ -557,20 +591,20 @@ module Tree
|
|
557
591
|
#
|
558
592
|
# @return [Tree::TreeNode] The last sibling node.
|
559
593
|
#
|
560
|
-
# @see #
|
561
|
-
# @see #
|
562
|
-
def
|
563
|
-
|
594
|
+
# @see #is_last_sibling?
|
595
|
+
# @see #first_sibling
|
596
|
+
def last_sibling
|
597
|
+
is_root? ? self : parent.children.last
|
564
598
|
end
|
565
599
|
|
566
600
|
# Returns +true+ if the receiver node is the last sibling at its level.
|
567
601
|
#
|
568
602
|
# @return [Boolean] +true+ if this is the last sibling.
|
569
603
|
#
|
570
|
-
# @see #
|
571
|
-
# @see #
|
572
|
-
def
|
573
|
-
|
604
|
+
# @see #is_first_sibling?
|
605
|
+
# @see #last_sibling
|
606
|
+
def is_last_sibling?
|
607
|
+
last_sibling == self
|
574
608
|
end
|
575
609
|
|
576
610
|
# Returns an array of siblings for the receiver node. The receiver node is excluded.
|
@@ -588,15 +622,13 @@ module Tree
|
|
588
622
|
#
|
589
623
|
# @return [Array<Tree::TreeNode>] Array of siblings of this node.
|
590
624
|
#
|
591
|
-
# @see #
|
592
|
-
# @see #
|
625
|
+
# @see #first_sibling
|
626
|
+
# @see #last_sibling
|
593
627
|
def siblings
|
594
|
-
return nil if
|
628
|
+
return nil if is_root?
|
595
629
|
|
596
630
|
if block_given?
|
597
|
-
|
598
|
-
yield sibling if sibling != self
|
599
|
-
end
|
631
|
+
parent.children.each { |sibling| yield sibling if sibling != self }
|
600
632
|
else
|
601
633
|
siblings = []
|
602
634
|
parent.children {|my_sibling| siblings << my_sibling if my_sibling != self}
|
@@ -611,8 +643,8 @@ module Tree
|
|
611
643
|
# @return [Boolean] +true+ if this is the only child of its parent.
|
612
644
|
#
|
613
645
|
# @see #siblings
|
614
|
-
def
|
615
|
-
|
646
|
+
def is_only_child?
|
647
|
+
is_root? ? true : parent.children.size == 1
|
616
648
|
end
|
617
649
|
|
618
650
|
# Returns the next sibling for the receiver node.
|
@@ -622,13 +654,13 @@ module Tree
|
|
622
654
|
#
|
623
655
|
# @return [Tree::treeNode] the next sibling node, if present.
|
624
656
|
#
|
625
|
-
# @see #
|
657
|
+
# @see #previous_sibling
|
626
658
|
# @see #siblings
|
627
|
-
def
|
628
|
-
return nil if
|
629
|
-
|
630
|
-
|
631
|
-
|
659
|
+
def next_sibling
|
660
|
+
return nil if is_root?
|
661
|
+
|
662
|
+
myidx = parent.children.index(self)
|
663
|
+
parent.children.at(myidx + 1) if myidx
|
632
664
|
end
|
633
665
|
|
634
666
|
# Returns the previous sibling of the receiver node.
|
@@ -638,13 +670,13 @@ module Tree
|
|
638
670
|
#
|
639
671
|
# @return [Tree::treeNode] the previous sibling node, if present.
|
640
672
|
#
|
641
|
-
# @see #
|
673
|
+
# @see #next_sibling
|
642
674
|
# @see #siblings
|
643
|
-
def
|
644
|
-
return nil if
|
645
|
-
|
646
|
-
|
647
|
-
|
675
|
+
def previous_sibling
|
676
|
+
return nil if is_root?
|
677
|
+
|
678
|
+
myidx = parent.children.index(self)
|
679
|
+
parent.children.at(myidx - 1) if myidx && myidx > 0
|
648
680
|
end
|
649
681
|
|
650
682
|
# Provides a comparision operation for the nodes.
|
@@ -663,18 +695,18 @@ module Tree
|
|
663
695
|
#
|
664
696
|
# The nodes become immutable after this operation. In effect, the entire tree's
|
665
697
|
# structure and contents become _read-only_ and cannot be changed.
|
666
|
-
def
|
698
|
+
def freeze_tree!
|
667
699
|
each {|node| node.freeze}
|
668
700
|
end
|
669
701
|
|
670
702
|
# Returns a marshal-dump represention of the (sub)tree rooted at the receiver node.
|
671
703
|
def marshal_dump
|
672
|
-
self.collect { |node| node.
|
704
|
+
self.collect { |node| node.create_dump_rep }
|
673
705
|
end
|
674
706
|
|
675
707
|
# Creates a dump representation of the reciever node and returns the same as a hash.
|
676
|
-
def
|
677
|
-
{ :name => @name, :parent => (
|
708
|
+
def create_dump_rep # :nodoc:
|
709
|
+
{ :name => @name, :parent => (is_root? ? nil : @parent.name), :content => Marshal.dump(@content)}
|
678
710
|
end
|
679
711
|
|
680
712
|
# Loads a marshalled dump of a tree and returns the root node of the
|
@@ -686,7 +718,7 @@ module Tree
|
|
686
718
|
#
|
687
719
|
def marshal_load(dumped_tree_array)
|
688
720
|
nodes = { }
|
689
|
-
|
721
|
+
dumped_tree_array.each do |node_hash|
|
690
722
|
name = node_hash[:name]
|
691
723
|
parent_name = node_hash[:parent]
|
692
724
|
content = Marshal.load(node_hash[:content])
|
@@ -723,19 +755,23 @@ module Tree
|
|
723
755
|
JSON.create_id => self.class.name
|
724
756
|
}
|
725
757
|
|
726
|
-
if
|
758
|
+
if has_children?
|
727
759
|
json_hash["children"] = children
|
728
760
|
end
|
729
761
|
|
730
762
|
return json_hash.to_json
|
731
763
|
|
732
|
-
rescue LoadError
|
764
|
+
rescue LoadError
|
733
765
|
warn "The JSON gem couldn't be loaded. Due to this we cannot serialize the tree to a JSON representation"
|
734
766
|
end
|
735
767
|
end
|
736
768
|
|
737
|
-
#
|
738
|
-
#
|
769
|
+
# Helper method to create a Tree::TreeNode instance from the JSON hash representation. Note that this method should
|
770
|
+
# *NOT* be called directly. Instead, to convert the JSON hash back to a tree, do:
|
771
|
+
#
|
772
|
+
# tree = JSON.parse (the_json_hash)
|
773
|
+
#
|
774
|
+
# This operation requires the JSON gem to be available, or else the operation fails with a warning message.
|
739
775
|
#
|
740
776
|
# @author Dirk Breuer (http://github.com/railsbros-dirk)
|
741
777
|
# @since 0.7.0
|
@@ -770,24 +806,27 @@ module Tree
|
|
770
806
|
# - The height of a leaf node is zero.
|
771
807
|
#
|
772
808
|
# @return [Number] Height of the node.
|
773
|
-
def
|
774
|
-
return 0 if
|
775
|
-
1 + @children.collect { |child| child.
|
809
|
+
def node_height
|
810
|
+
return 0 if is_leaf?
|
811
|
+
1 + @children.collect { |child| child.node_height }.max
|
776
812
|
end
|
777
813
|
|
778
814
|
# Returns depth of the receiver node in its tree. Depth of a node is defined as:
|
779
815
|
#
|
780
816
|
# Depth:: Length of the node's path to its root. Depth of a root node is zero.
|
781
817
|
#
|
818
|
+
# *Note* that the deprecated method Tree::TreeNode#depth was incorrectly computing this value.
|
819
|
+
# Please replace all calls to the old method with Tree::TreeNode#node_depth instead.
|
820
|
+
#
|
782
821
|
# 'level' is an alias for this method.
|
783
822
|
#
|
784
823
|
# @return [Number] Depth of this node.
|
785
|
-
def
|
786
|
-
return 0 if
|
787
|
-
1 + parent.
|
824
|
+
def node_depth
|
825
|
+
return 0 if is_root?
|
826
|
+
1 + parent.node_depth
|
788
827
|
end
|
789
828
|
|
790
|
-
alias level
|
829
|
+
alias level node_depth # Aliased level() method to the node_depth().
|
791
830
|
|
792
831
|
# Returns depth of the tree from the receiver node. A single leaf node has a depth of 1.
|
793
832
|
#
|
@@ -796,26 +835,46 @@ module Tree
|
|
796
835
|
#
|
797
836
|
# _height_ + 1 of the node, *NOT* the _depth_.
|
798
837
|
#
|
799
|
-
# For correct and conventional behavior, please use {Tree::TreeNode#
|
800
|
-
# {Tree::TreeNode#
|
838
|
+
# For correct and conventional behavior, please use {Tree::TreeNode#node_depth} and
|
839
|
+
# {Tree::TreeNode#node_height} methods instead.
|
801
840
|
#
|
802
841
|
# @return [Number] depth of the node.
|
803
|
-
# @deprecated This method returns an incorrect value. Use the '
|
842
|
+
# @deprecated This method returns an incorrect value. Use the 'node_depth' method instead.
|
804
843
|
#
|
805
|
-
# @see #
|
844
|
+
# @see #node_depth
|
806
845
|
def depth
|
807
846
|
begin
|
808
847
|
require 'structured_warnings' # To enable a nice way of deprecating of the depth method.
|
809
|
-
warn DeprecatedMethodWarning, 'This method is deprecated. Please use
|
848
|
+
warn DeprecatedMethodWarning, 'This method is deprecated. Please use node_depth() or node_height() instead (bug # 22535)'
|
810
849
|
rescue LoadError
|
811
850
|
# Oh well. Will use the standard Kernel#warn. Behavior will be identical.
|
812
|
-
warn 'Tree::TreeNode#depth() method is deprecated. Please use
|
851
|
+
warn 'Tree::TreeNode#depth() method is deprecated. Please use node_depth() or node_height() instead (bug # 22535)'
|
813
852
|
end
|
814
853
|
|
815
|
-
return 1 if
|
854
|
+
return 1 if is_leaf?
|
816
855
|
1 + @children.collect { |child| child.depth }.max
|
817
856
|
end
|
818
857
|
|
858
|
+
# Allow the deprecated CamelCase method names. Display a warning.
|
859
|
+
def method_missing(meth, *args, &blk)
|
860
|
+
if self.respond_to?(new_method_name = underscore(meth))
|
861
|
+
begin
|
862
|
+
require 'structured_warnings' # To enable a nice way of deprecating of the invoked CamelCase method.
|
863
|
+
warn DeprecatedMethodWarning, "The camelCased methods are deprecated. Please use #{new_method_name} instead of #{meth}"
|
864
|
+
|
865
|
+
rescue LoadError
|
866
|
+
# Oh well. Will use the standard Kernel#warn. Behavior will be identical.
|
867
|
+
warn "Tree::TreeNode##{meth}() method is deprecated. Please use #{new_method_name} instead."
|
868
|
+
|
869
|
+
ensure # Invoke the method now.
|
870
|
+
return send(new_method_name, *args, &blk)
|
871
|
+
end
|
872
|
+
|
873
|
+
else
|
874
|
+
super
|
875
|
+
end
|
876
|
+
end
|
877
|
+
|
819
878
|
# Returns breadth of the tree at the receiver node's level.
|
820
879
|
# A single node without siblings has a breadth of 1.
|
821
880
|
#
|
@@ -825,7 +884,7 @@ module Tree
|
|
825
884
|
#
|
826
885
|
# @return [Number] breadth of the node's level.
|
827
886
|
def breadth
|
828
|
-
|
887
|
+
is_root? ? 1 : parent.children.size
|
829
888
|
end
|
830
889
|
|
831
890
|
# Returns the incoming edge-count of the receiver node.
|
@@ -838,7 +897,7 @@ module Tree
|
|
838
897
|
#
|
839
898
|
# @return [Number] The in-degree of this node.
|
840
899
|
def in_degree
|
841
|
-
|
900
|
+
is_root? ? 0 : 1
|
842
901
|
end
|
843
902
|
|
844
903
|
# Returns the outgoing edge-count of the receiver node.
|
@@ -848,10 +907,33 @@ module Tree
|
|
848
907
|
#
|
849
908
|
# @return [Number] The out-degree of this node.
|
850
909
|
def out_degree
|
851
|
-
|
910
|
+
is_leaf? ? 0 : children.size
|
852
911
|
end
|
853
912
|
|
854
|
-
protected :parent=, :
|
913
|
+
protected :parent=, :set_as_root!, :create_dump_rep
|
914
|
+
|
915
|
+
private
|
916
|
+
|
917
|
+
# Convert a CamelCasedWord to a underscore separated camel_cased_word.
|
918
|
+
#
|
919
|
+
# Just copied from ActiveSupport::Inflector because it is only needed
|
920
|
+
# aliasing deprecated methods
|
921
|
+
def underscore(camel_cased_word)
|
922
|
+
word = camel_cased_word.to_s.dup
|
923
|
+
word.gsub!(/::/, '/')
|
924
|
+
word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
|
925
|
+
word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
926
|
+
word.tr!("-", "_")
|
927
|
+
word.downcase!
|
928
|
+
word
|
929
|
+
end
|
930
|
+
|
931
|
+
# Return a range of valid insertion positions. Used in the #add method.
|
932
|
+
def insertion_range
|
933
|
+
max = @children.size
|
934
|
+
min = -(max+1)
|
935
|
+
min..max
|
936
|
+
end
|
855
937
|
|
856
938
|
end
|
857
939
|
end
|