vcs 0.1 → 0.2.148

Sign up to get free protection for your applications and to get access to all the features.
Files changed (132) hide show
  1. data/Rakefile +17 -3
  2. data/bin/vcs +57 -34
  3. data/doc/jamis.rb +564 -0
  4. data/ruby_ex/abstract.rb +254 -0
  5. data/ruby_ex/abstract_node.rb +85 -0
  6. data/ruby_ex/algorithms/simulated_annealing.rb +140 -0
  7. data/ruby_ex/array_each_pair.rb +18 -0
  8. data/ruby_ex/ask.rb +101 -0
  9. data/ruby_ex/attributed_class.rb +302 -0
  10. data/ruby_ex/cache.rb +373 -0
  11. data/ruby_ex/checkout.rb +12 -0
  12. data/ruby_ex/choose.rb +271 -0
  13. data/ruby_ex/commands.rb +18 -0
  14. data/ruby_ex/commands/command.rb +401 -0
  15. data/ruby_ex/commands/datas.rb +16 -0
  16. data/ruby_ex/commands/datas/data.rb +33 -0
  17. data/ruby_ex/commands/datas/factory.rb +66 -0
  18. data/ruby_ex/commands/factory.rb +66 -0
  19. data/ruby_ex/commands/helpers.rb +67 -0
  20. data/ruby_ex/commands/pipe.rb +64 -0
  21. data/ruby_ex/commands/runners.rb +17 -0
  22. data/ruby_ex/commands/runners/exec.rb +49 -0
  23. data/ruby_ex/commands/runners/fork.rb +97 -0
  24. data/ruby_ex/commands/runners/runner.rb +107 -0
  25. data/ruby_ex/commands/seq.rb +27 -0
  26. data/ruby_ex/config_file.rb +96 -0
  27. data/ruby_ex/const_regexp.rb +59 -0
  28. data/ruby_ex/daemon.rb +134 -0
  29. data/ruby_ex/diff.rb +667 -0
  30. data/ruby_ex/dlogger.rb +62 -0
  31. data/ruby_ex/drb/dispatcher.rb +252 -0
  32. data/ruby_ex/drb/dispatcher_server_test.rb +29 -0
  33. data/ruby_ex/drb/drb_observable.rb +97 -0
  34. data/ruby_ex/drb/drb_observable_pool.rb +27 -0
  35. data/ruby_ex/drb/drb_service.rb +43 -0
  36. data/ruby_ex/drb/drb_undumped_attributes.rb +55 -0
  37. data/ruby_ex/drb/drb_undumped_indexed_object.rb +54 -0
  38. data/ruby_ex/drb/insecure_protected_methods.rb +103 -0
  39. data/ruby_ex/drb/session_client_test.rb +40 -0
  40. data/ruby_ex/drb/session_manager.rb +246 -0
  41. data/ruby_ex/drb/session_server.rb +53 -0
  42. data/ruby_ex/dtime.rb +143 -0
  43. data/ruby_ex/dumpable_proc.rb +63 -0
  44. data/ruby_ex/exception.rb +32 -0
  45. data/ruby_ex/filetype.rb +229 -0
  46. data/ruby_ex/fileutils_ex.rb +44 -0
  47. data/ruby_ex/fold.rb +58 -0
  48. data/ruby_ex/generate_id.rb +44 -0
  49. data/ruby_ex/hookable.rb +262 -0
  50. data/ruby_ex/hooker.rb +54 -0
  51. data/ruby_ex/inactive_timeout.rb +137 -0
  52. data/ruby_ex/indexed_node.rb +66 -0
  53. data/ruby_ex/io_marshal.rb +100 -0
  54. data/ruby_ex/ioo.rb +194 -0
  55. data/ruby_ex/labeled_node.rb +63 -0
  56. data/ruby_ex/logger_observer.rb +23 -0
  57. data/ruby_ex/md5sum.rb +66 -0
  58. data/ruby_ex/mktemp.rb +208 -0
  59. data/ruby_ex/module/attr_once.rb +36 -0
  60. data/ruby_ex/module/autoload_tree.rb +75 -0
  61. data/ruby_ex/module/hierarchy.rb +335 -0
  62. data/ruby_ex/module/instance_method_visibility.rb +73 -0
  63. data/ruby_ex/module_ex.rb +11 -0
  64. data/ruby_ex/node.rb +80 -0
  65. data/ruby_ex/object_monitor.rb +145 -0
  66. data/ruby_ex/object_monitor_activity.rb +33 -0
  67. data/ruby_ex/observable.rb +140 -0
  68. data/ruby_ex/observable_pool.rb +293 -0
  69. data/ruby_ex/orderedhash.rb +252 -0
  70. data/ruby_ex/pathname_ex.rb +152 -0
  71. data/ruby_ex/pp_hierarchy.rb +29 -0
  72. data/ruby_ex/pseudo_cache.rb +190 -0
  73. data/ruby_ex/queue.rb +56 -0
  74. data/ruby_ex/random_generators.rb +25 -0
  75. data/ruby_ex/random_generators/random_generator.rb +31 -0
  76. data/ruby_ex/random_generators/ruby.rb +23 -0
  77. data/ruby_ex/safe_eval.rb +348 -0
  78. data/ruby_ex/sendmail.rb +215 -0
  79. data/ruby_ex/service_manager.rb +121 -0
  80. data/ruby_ex/session/administrable.rb +120 -0
  81. data/ruby_ex/session/client.rb +153 -0
  82. data/ruby_ex/session/const.rb +18 -0
  83. data/ruby_ex/session/dispatcher.rb +184 -0
  84. data/ruby_ex/session/error.rb +21 -0
  85. data/ruby_ex/session/fetchable.rb +57 -0
  86. data/ruby_ex/session/fetcher.rb +62 -0
  87. data/ruby_ex/session/hookable.rb +26 -0
  88. data/ruby_ex/session/profile.rb +110 -0
  89. data/ruby_ex/session/server.rb +582 -0
  90. data/ruby_ex/session/test/administrable_test.rb +337 -0
  91. data/ruby_ex/session/test/basic_test.rb +523 -0
  92. data/ruby_ex/session/test/dispatcher_test.rb +409 -0
  93. data/ruby_ex/session/test/fetchable_test.rb +119 -0
  94. data/ruby_ex/session/test/sub_server_test.rb +188 -0
  95. data/ruby_ex/shuffle.rb +30 -0
  96. data/ruby_ex/spring.rb +136 -0
  97. data/ruby_ex/spring_set.rb +137 -0
  98. data/ruby_ex/string_ex.rb +28 -0
  99. data/ruby_ex/symtbl.rb +106 -0
  100. data/ruby_ex/synflow.rb +474 -0
  101. data/ruby_ex/test/unit/ui/yaml/testrunner.rb +164 -0
  102. data/ruby_ex/thread_mutex.rb +10 -0
  103. data/ruby_ex/timeout_ex.rb +81 -0
  104. data/ruby_ex/top_down.rb +73 -0
  105. data/ruby_ex/trace.rb +26 -0
  106. data/ruby_ex/uri/druby.rb +81 -0
  107. data/ruby_ex/uri/file.rb +65 -0
  108. data/ruby_ex/uri/ftp_ex.rb +37 -0
  109. data/ruby_ex/uri/http_ex.rb +43 -0
  110. data/ruby_ex/uri/ssh.rb +92 -0
  111. data/ruby_ex/uri/svn.rb +118 -0
  112. data/ruby_ex/uri_ex.rb +45 -0
  113. data/ruby_ex/verbose_object.rb +30 -0
  114. data/ruby_ex/version.rb +66 -0
  115. data/ruby_ex/yaml/basenode_ext.rb +63 -0
  116. data/ruby_ex/yaml/chop_header.rb +23 -0
  117. data/ruby_ex/yaml/transform.rb +449 -0
  118. data/ruby_ex/yaml/yregexpath.rb +76 -0
  119. data/src/changelog.rb +28 -18
  120. data/src/conflict.rb +20 -0
  121. data/src/diff.rb +18 -0
  122. data/src/diffstat.rb +9 -3
  123. data/src/last_changed_date.rb +18 -0
  124. data/src/mail.rb +33 -65
  125. data/src/message.rb +15 -9
  126. data/src/mycommit.rb +29 -14
  127. data/src/news.rb +24 -3
  128. data/src/status.rb +17 -0
  129. data/src/svn.rb +2 -2
  130. data/src/vcs.rb +24 -3
  131. metadata +124 -5
  132. data/lrdetools.rb +0 -12
@@ -0,0 +1,335 @@
1
+ # Copyright: Copyright (c) 2004 Nicolas Despres. All rights reserved.
2
+ # Author: Nicolas Despres <polrop@lrde.epita.fr>.
3
+ # License: Gnu General Public License.
4
+
5
+ # $LastChangedBy: polrop $
6
+ # $Id: hierarchy.rb 194 2005-04-11 10:03:22Z polrop $
7
+
8
+
9
+ # Extend the Module class with method to manipulate inheritance relation
10
+ # between constants of a module.
11
+ class Module
12
+
13
+ # Return the list of all sub classes of base_class (including itself).
14
+ # If force_autoload is false, not yet loaded constants will be ignored.
15
+ # If recursive is true sub modules will also be traversed.
16
+ def sub_classes(base_class, force_autoload=false, recursive=false)
17
+ check_const_is_class?(base_class)
18
+ result = []
19
+ constants.each do |const_name|
20
+ if autoload?(const_name).nil? or force_autoload
21
+ const = const_get(const_name)
22
+ if const.is_a?(Module)
23
+ if const.is_a?(Class)
24
+ result << const if const.ancestors.include?(base_class)
25
+ elsif recursive
26
+ result += const.sub_classes(base_class, force_autoload, recursive)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ result
32
+ end
33
+
34
+ # Remove a class from the hierarchy using remove_const. Sub classes of this
35
+ # class are also removed. It returns an array of the removed class name.
36
+ # See sub_classes for a description of the recursive argument.
37
+ def remove_class(base_class, recursive=false)
38
+ subs = sub_classes(base_class, true, recursive)
39
+ re = Regexp.new("^#{name}::")
40
+ result = []
41
+ subs.each do |sub|
42
+ sub_name = sub.to_s.sub!(re, '')
43
+ if sub_name =~ /^(.+)::(.+)$/
44
+ const_get($1).module_eval { remove_const($2.to_sym) }
45
+ else
46
+ remove_const(sub_name.to_sym)
47
+ end
48
+ result << sub_name
49
+ end
50
+ result
51
+ end
52
+
53
+ # Return the inheritance tree of the base_class.
54
+ # Example:
55
+ #
56
+ # module M
57
+ # class A; end
58
+ # class B < A; end
59
+ # class C < B; end
60
+ # class D < B; end
61
+ # class E < A; end
62
+ # module N
63
+ # class F < A; end
64
+ # class G < F; end
65
+ # class H < D; end
66
+ # class I < H; end
67
+ # class J < E; end
68
+ # class Y; end
69
+ # end
70
+ # class Z; end
71
+ # FOO = 42
72
+ # end
73
+ #
74
+ # M.sub_classes_tree(M::A)
75
+ #
76
+ # produces:
77
+ #
78
+ # {
79
+ # M::A => {
80
+ # M::B => {
81
+ # M::D => {},
82
+ # M::C => {}
83
+ # },
84
+ # M::E => {}
85
+ # }
86
+ # }
87
+ #
88
+ # and
89
+ #
90
+ # M.sub_classes_tree(M::A, false, true)
91
+ #
92
+ # produces:
93
+ #
94
+ # {
95
+ # M::A => {
96
+ # M::N::F => {
97
+ # M::N::G => {}
98
+ # },
99
+ # M::B => {
100
+ # M::C => {},
101
+ # M::D => {
102
+ # M::N::H => {
103
+ # M::N::I => {}
104
+ # }
105
+ # }
106
+ # },
107
+ # M::E => {
108
+ # M::N::J => {}
109
+ # }
110
+ # }
111
+ # }
112
+ def sub_classes_tree(base_class, force_autoload=false, recursive=false)
113
+ subs = sub_classes(base_class, force_autoload, recursive)
114
+ sub_classes_tree_rec([base_class], subs)
115
+ end
116
+
117
+ private
118
+ def sub_classes_tree_rec(base_classes, csts)
119
+ result = {}
120
+ next_csts = []
121
+ base_classes.each do |b|
122
+ next_base_classes = []
123
+ csts.each do |c|
124
+ c.superclass == b ? next_base_classes << c : next_csts << c;
125
+ end
126
+ result[b] = sub_classes_tree_rec(next_base_classes, next_csts)
127
+ end
128
+ result
129
+ end
130
+
131
+ private
132
+ def check_const_is_class?(*base_classes)
133
+ base_classes.each do |b|
134
+ raise(TypeError, "`#{b}' - not a class") unless b.is_a?(Class)
135
+ end
136
+ end
137
+
138
+ end # class Module
139
+
140
+
141
+ if (not defined? HIERARCHY_TESTED) and (defined? TEST_MODE or __FILE__ == $0)
142
+ HIERARCHY_TESTED = true
143
+
144
+ require 'test/unit/ui/console/testrunner'
145
+ require 'diff'
146
+
147
+
148
+ class HierarchyTest < Test::Unit::TestCase
149
+
150
+ module M
151
+
152
+ class A; end
153
+ class B < A; end
154
+ class C; end
155
+ FOO = 42
156
+
157
+ end
158
+
159
+ def test_sub_classes
160
+ sub_classes = M.sub_classes(HierarchyTest::M::A)
161
+ assert_equal(2, sub_classes.size)
162
+ [HierarchyTest::M::B, HierarchyTest::M::A].each do |x|
163
+ assert(sub_classes.include?(x))
164
+ end
165
+ assert_equal([HierarchyTest::M::C], M.sub_classes(M::C))
166
+ end
167
+
168
+ module Mautoload
169
+
170
+ class A; end
171
+ class B < A; end
172
+ autoload(:C, '/foo')
173
+ FOO = 42
174
+
175
+ end
176
+
177
+ def test_sub_classes_force_autoload
178
+ sub_classes = Mautoload.sub_classes(Mautoload::A)
179
+ assert_equal(2, sub_classes.size)
180
+ [Mautoload::B, Mautoload::A].each do |x|
181
+ assert(sub_classes.include?(x))
182
+ end
183
+ assert_equal(4, Mautoload.constants.size)
184
+ assert_not_nil(Mautoload.autoload?(:C))
185
+ assert_raises(LoadError) { Mautoload.sub_classes(Mautoload::A, true) }
186
+ assert_equal(3, Mautoload.constants.size)
187
+ assert_nil(Mautoload.autoload?(:C))
188
+ end
189
+
190
+ module Mrec
191
+ class A; end
192
+ class B < A; end
193
+ module M
194
+ class C < A; end
195
+ class D < B; end
196
+ class E < D; end
197
+ class Y; end
198
+ end
199
+ class Z; end
200
+ FOO = 42
201
+ end
202
+
203
+ def test_sub_classes_recursive
204
+ sub_classes = Mrec.sub_classes(Mrec::A)
205
+ assert_equal(2, sub_classes.size)
206
+ [Mrec::B, Mrec::A].each do |x|
207
+ assert(sub_classes.include?(x))
208
+ end
209
+ sub_classes = Mrec.sub_classes(Mrec::A, false, true)
210
+ assert_equal(5, sub_classes.size)
211
+ [Mrec::B, Mrec::A, Mrec::M::C, Mrec::M::D, Mrec::M::E].each do |x|
212
+ assert(sub_classes.include?(x))
213
+ end
214
+ r = Mrec.remove_class(Mrec::A, true)
215
+ assert_equal(5, r.size)
216
+ ['B', 'A', 'M::E', 'M::C', 'M::D'].each do |x|
217
+ assert(r.include?(x))
218
+ end
219
+ assert_equal(3, Mrec.constants.size)
220
+ ['FOO', 'M', 'Z'].each { |x| assert(Mrec.constants.include?(x)) }
221
+ assert_equal(1, Mrec::M.constants.size)
222
+ ['Y'].each { |x| assert(Mrec::M.constants.include?(x)) }
223
+ end
224
+
225
+ module Tree
226
+
227
+ class A; end
228
+ class B < A; end
229
+ class C < B; end
230
+ class D < B; end
231
+ class E < A; end
232
+ module M
233
+ class F < A; end
234
+ class G < F; end
235
+ class H < D; end
236
+ class I < H; end
237
+ class J < E; end
238
+ class Y; end
239
+ end
240
+ class Z; end
241
+ FOO = 42
242
+
243
+ end
244
+
245
+ def test_sub_classes_tree_rec
246
+ r = Tree.sub_classes_tree(HierarchyTest::Tree::A, false, true)
247
+ ref = {
248
+ HierarchyTest::Tree::A => {
249
+ HierarchyTest::Tree::M::F => {
250
+ HierarchyTest::Tree::M::G => {}
251
+ },
252
+ HierarchyTest::Tree::B => {
253
+ HierarchyTest::Tree::C => {},
254
+ HierarchyTest::Tree::D => {
255
+ HierarchyTest::Tree::M::H => {
256
+ HierarchyTest::Tree::M::I=>{}
257
+ }
258
+ }
259
+ },
260
+ HierarchyTest::Tree::E=> {
261
+ HierarchyTest::Tree::M::J=>{}
262
+ }
263
+ }
264
+ }
265
+ d = r.diff(ref)
266
+ assert_equal({}, d[:different])
267
+ assert_equal({}, d[:additional])
268
+ assert_equal({}, d[:missing])
269
+ end
270
+
271
+ def test_sub_classes_tree
272
+ r = Tree.sub_classes_tree(HierarchyTest::Tree::A)
273
+ ref = {
274
+ HierarchyTest::Tree::A => {
275
+ HierarchyTest::Tree::B => {
276
+ HierarchyTest::Tree::D => {},
277
+ HierarchyTest::Tree::C => {}
278
+ },
279
+ HierarchyTest::Tree::E => {}
280
+ }
281
+ }
282
+ d = r.diff(ref)
283
+ assert_equal({}, d[:different])
284
+ assert_equal({}, d[:additional])
285
+ assert_equal({}, d[:missing])
286
+ end
287
+
288
+ module Error
289
+ module M; end
290
+ end
291
+
292
+ def test_sub_classes_error
293
+ assert_raises(TypeError) do
294
+ Error.sub_classes_tree(HierarchyTest::Error::M)
295
+ end
296
+ assert_raises(TypeError) do
297
+ Error.sub_classes(HierarchyTest::Error::M)
298
+ end
299
+ assert_raises(TypeError) do
300
+ Error.remove_class(HierarchyTest::Error::M)
301
+ end
302
+ end
303
+
304
+ module RemoveClass
305
+
306
+ class A; end
307
+ class B < A; end
308
+ class C < B; end
309
+ class D < B; end
310
+ class E < D; end
311
+ class F < A; end
312
+ class Z; end
313
+ module M
314
+ class Y; end
315
+ end
316
+ BAR = 51
317
+
318
+ end
319
+
320
+ def test_remove_class
321
+ r = RemoveClass.remove_class(HierarchyTest::RemoveClass::B)
322
+ assert_equal(4, r.size)
323
+ ['B', 'D', 'E', 'C'].each { |x| assert(r.include?(x)) }
324
+ assert_equal(5, RemoveClass.constants.size)
325
+ ['A', 'F', 'Z', 'M', 'BAR'].each do |x|
326
+ assert(RemoveClass.constants.include?(x))
327
+ end
328
+ end
329
+
330
+ end # class HierarchyTest
331
+
332
+
333
+ end
334
+
335
+
@@ -0,0 +1,73 @@
1
+ # Copyright: Copyright (c) 2004 Nicolas Despres. All rights reserved.
2
+ # Author: Nicolas Despres <polrop@lrde.epita.fr>.
3
+ # License: Gnu General Public License.
4
+
5
+ # $LastChangedBy: polrop $
6
+ # $Id: instance_method_visibility.rb 143 2005-01-30 14:06:43Z polrop $
7
+
8
+
9
+ class Module
10
+
11
+ def instance_method_visibility(name)
12
+ if private_method_defined?(name)
13
+ :private
14
+ elsif protected_method_defined?(name)
15
+ :protected
16
+ elsif public_method_defined?(name)
17
+ :public
18
+ else
19
+ raise(NoMethodError,
20
+ "`#{name}' - not an instance method of `#{self.name}'")
21
+ end
22
+ end
23
+
24
+ end # class Module
25
+
26
+
27
+ if (not defined? INSTANCE_METHOD_VISIBILITY_TESTED) and
28
+ (defined? TEST_MODE or __FILE__ == $0)
29
+ INSTANCE_METHOD_VISIBILITY_TESTED = true
30
+
31
+ require 'test/unit/ui/console/testrunner'
32
+
33
+
34
+ class InstanceMethodVisibilityTest < Test::Unit::TestCase
35
+
36
+ class A
37
+
38
+ def pub; end
39
+ def pri; end
40
+ def pro; end
41
+
42
+ public :pub
43
+ private :pri
44
+ protected :pro
45
+
46
+ end # class A
47
+
48
+ def test_simple
49
+ assert_equal(:public, A.instance_method_visibility('pub'))
50
+ assert_equal(:private, A.instance_method_visibility('pri'))
51
+ assert_equal(:protected, A.instance_method_visibility('pro'))
52
+ assert_raises(NoMethodError) { A.instance_method_visibility('new') }
53
+ end
54
+
55
+ class AA
56
+ def f; end
57
+ end
58
+ class BB < AA
59
+ private :f
60
+ end
61
+
62
+ def test_inheritance
63
+ assert_equal(:public, AA.instance_method_visibility('f'))
64
+ assert(! BB.public_method_defined?('f'))
65
+ assert_equal(:private, BB.instance_method_visibility('f'))
66
+ end
67
+
68
+ end # class InstanceMethodVisibilityTest
69
+
70
+
71
+ end
72
+
73
+
@@ -0,0 +1,11 @@
1
+ # Copyright: Copyright (c) 2004 Nicolas Despres. All rights reserved.
2
+ # Author: Nicolas Despres <polrop@lrde.epita.fr>.
3
+ # License: Gnu General Public License.
4
+
5
+ # $LastChangedBy: polrop $
6
+ # $Id: module_ex.rb 107 2005-01-17 03:31:39Z polrop $
7
+
8
+
9
+ Dir["#{File.dirname(__FILE__)}/module/**/*.rb"].each do |filename|
10
+ require filename
11
+ end
@@ -0,0 +1,80 @@
1
+ # Copyright: Copyright (c) 2004 Nicolas Despres. All rights reserved.
2
+ # Author: Nicolas Despres <polrop@lrde.epita.fr>.
3
+ # License: Gnu General Public License.
4
+
5
+ # $LastChangedBy: polrop $
6
+ # $Id: node.rb 171 2005-03-29 09:12:47Z polrop $
7
+
8
+
9
+ class Node
10
+
11
+ def initialize(data=nil, *sub_nodes)
12
+ @data = data
13
+ sub_nodes.each { |sub_node| check_sub_node_type(sub_node) }
14
+ @sub_nodes = sub_nodes
15
+ end
16
+
17
+ attr_reader :data, :sub_nodes
18
+
19
+ def [](sub_node_index)
20
+ @sub_nodes[sub_node_index]
21
+ end
22
+
23
+ def []=(sub_node_index, sub_node)
24
+ check_sub_node_type(sub_node)
25
+ @sub_nodes[sub_node_index] = sub_node
26
+ end
27
+
28
+ def <<(sub_node)
29
+ check_sub_node_type(sub_node)
30
+ @sub_nodes << sub_node
31
+ end
32
+
33
+ def each_pair(&block)
34
+ @sub_nodes.each_with_index { |sub_node, i| block[i, sub_node] }
35
+ end
36
+
37
+ def each_node(&block)
38
+ @sub_nodes.each_with_index { |sub_node, index| block[sub_node] }
39
+ end
40
+
41
+ alias :each :each_node
42
+
43
+ def each_index(&block)
44
+ @sub_nodes.each_with_index { |sub_node, index| block[index] }
45
+ end
46
+
47
+ def delete(sub_node)
48
+ @sub_nodes.delete(sub_node)
49
+ end
50
+
51
+ def nb_sub_nodes
52
+ @sub_nodes.size
53
+ end
54
+
55
+ alias size nb_sub_nodes
56
+ alias length nb_sub_nodes
57
+
58
+ def leaf?
59
+ @sub_nodes.empty?
60
+ end
61
+
62
+ def pre_depth_first(&block)
63
+ block[self]
64
+ @sub_nodes.each { |sub_node| sub_node.pre_depth_first(&block) }
65
+ nil
66
+ end
67
+
68
+ # FIXME: implement me
69
+ # def breadth_first(&block)
70
+ # end
71
+
72
+ protected
73
+ def check_sub_node_type(sub_node)
74
+ unless sub_node.is_a?(self.class)
75
+ raise(TypeError, "`#{sub_node}' - must be a #{self.class}")
76
+ end
77
+ end
78
+
79
+ end # class Node
80
+