rubytree 0.7.0 → 0.8.1
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.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
|