jinx 2.1.3 → 2.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/Gemfile.lock +27 -0
  2. data/History.md +4 -0
  3. data/lib/jinx/helpers/class.rb +16 -12
  4. data/lib/jinx/helpers/collection.rb +277 -29
  5. data/lib/jinx/helpers/collections.rb +35 -2
  6. data/lib/jinx/helpers/conditional_enumerator.rb +2 -2
  7. data/lib/jinx/helpers/filter.rb +8 -2
  8. data/lib/jinx/helpers/flattener.rb +2 -2
  9. data/lib/jinx/helpers/hash.rb +3 -2
  10. data/lib/jinx/helpers/{hashable.rb → hasher.rb} +125 -77
  11. data/lib/jinx/helpers/module.rb +1 -1
  12. data/lib/jinx/helpers/multi_enumerator.rb +25 -9
  13. data/lib/jinx/helpers/options.rb +4 -3
  14. data/lib/jinx/helpers/partial_order.rb +16 -8
  15. data/lib/jinx/helpers/pretty_print.rb +14 -4
  16. data/lib/jinx/helpers/transformer.rb +3 -1
  17. data/lib/jinx/helpers/transitive_closure.rb +3 -3
  18. data/lib/jinx/helpers/visitor.rb +33 -42
  19. data/lib/jinx/import/java.rb +40 -27
  20. data/lib/jinx/importer.rb +86 -33
  21. data/lib/jinx/metadata/attribute_enumerator.rb +5 -11
  22. data/lib/jinx/metadata/dependency.rb +65 -30
  23. data/lib/jinx/metadata/id_alias.rb +1 -0
  24. data/lib/jinx/metadata/introspector.rb +21 -9
  25. data/lib/jinx/metadata/inverse.rb +14 -11
  26. data/lib/jinx/metadata/java_property.rb +15 -26
  27. data/lib/jinx/metadata/propertied.rb +80 -19
  28. data/lib/jinx/metadata/property.rb +13 -8
  29. data/lib/jinx/metadata/property_characteristics.rb +2 -2
  30. data/lib/jinx/resource.rb +62 -32
  31. data/lib/jinx/resource/inversible.rb +4 -0
  32. data/lib/jinx/resource/match_visitor.rb +0 -1
  33. data/lib/jinx/resource/mergeable.rb +16 -6
  34. data/lib/jinx/resource/reference_enumerator.rb +1 -2
  35. data/lib/jinx/version.rb +1 -1
  36. data/test/lib/jinx/helpers/collections_test.rb +29 -14
  37. data/test/lib/jinx/helpers/visitor_test.rb +7 -20
  38. data/test/lib/jinx/import/mixed_case_test.rb +17 -3
  39. metadata +4 -4
  40. data/lib/jinx/helpers/enumerable.rb +0 -245
@@ -272,8 +272,8 @@ module Jinx
272
272
  end
273
273
  @flags << flag
274
274
  case flag
275
- when :owner then owner_flag_set
276
- when :dependent then dependent_flag_set
275
+ when :owner then owner_flag_set
276
+ when :dependent then dependent_flag_set
277
277
  end
278
278
  end
279
279
 
@@ -286,12 +286,17 @@ module Jinx
286
286
  if dependent? then
287
287
  raise MetadataError.new("#{declarer.qp}.#{self} cannot be set as a #{type.qp} owner since it is already defined as a #{type.qp} dependent")
288
288
  end
289
- inv_attr = type.dependent_attribute(@declarer)
290
- if inv_attr.nil? then
291
- raise MetadataError.new("#{@declarer.qp} owner attribute #{self} does not have a #{type.qp} dependent inverse")
289
+ inv_prop = inverse_property
290
+ if inv_prop then
291
+ inv_prop.qualify(:dependent) unless inv_prop.dependent?
292
+ else
293
+ inv_attr = type.dependent_attribute(@declarer)
294
+ if inv_attr.nil? then
295
+ raise MetadataError.new("The #{@declarer.qp} owner attribute #{self} of type #{type.qp} does not have an inverse")
296
+ end
297
+ logger.debug { "#{declarer.qp}.#{self} inverse is the #{type.qp} dependent attribute #{inv_attr}." }
298
+ self.inverse = inv_attr
292
299
  end
293
- logger.debug { "#{declarer.qp}.#{self} inverse is the #{type.qp} dependent attribute #{inv_attr}." }
294
- self.inverse = inv_attr
295
300
  end
296
301
 
297
302
  # Validates that this is not an owner attribute.
@@ -299,7 +304,7 @@ module Jinx
299
304
  # @raise [MetadataError] if this is an owner attribute
300
305
  def dependent_flag_set
301
306
  if owner? then
302
- raise MetadataError.new("#{declarer.qp}.#{self} cannot be set as a #{type.qp} dependent since it is already defined as a #{type.qp} owner")
307
+ raise MetadataError.new("#{declarer.qp}.#{self} cannot be set as a #{type.qp} dependent since it is already defined as a #{type.qp} owner")
303
308
  end
304
309
  end
305
310
  end
@@ -30,8 +30,8 @@ module Jinx
30
30
  # @return [Boolean] whether the subject attribute returns a domain object or a collection
31
31
  # of domain objects
32
32
  def domain?
33
- # the type must be a Ruby class rather than a Java Class, and include the Domain mix-in
34
- Class === type and type < Resource
33
+ # the type must be a Ruby class that extends the {Metadata} mix-in
34
+ Class === type and type < Resource and type.introspected?
35
35
  end
36
36
 
37
37
  # @return [Boolean] whether the subject attribute is not a domain object attribute
@@ -29,13 +29,16 @@ module Jinx
29
29
  # module Domain
30
30
  # include Jinx::Resource
31
31
  # # The caTissue Java package name.
32
- # packages 'app.domain'
32
+ # package 'app.domain'
33
33
  # # The JRuby mix-ins directory.
34
34
  # definitions File.expand_path('domain', dirname(__FILE__))
35
35
  # end
36
36
  module Resource
37
37
  include Mergeable, Inversible
38
38
 
39
+ # A proxied subclass can override the +domain_class+.
40
+ alias :domain_class :class
41
+
39
42
  # @quirk JRuby Bug #5090 - JRuby 1.5 object_id is no longer a reserved method, and results
40
43
  # in a String value rather than an Integer (cf. http://jira.codehaus.org/browse/JRUBY-5090).
41
44
  # Work-around is to make a proxy object id.
@@ -45,6 +48,13 @@ module Jinx
45
48
  # make a hash code on demand
46
49
  @_hc ||= (Object.new.object_id * 31) + 17
47
50
  end
51
+
52
+ # Initialization hook. This default implementation is a no-op.
53
+ #
54
+ # @quirk JRuby calling super in a module initialize method results in an infinite loop.
55
+ # The work-around is to add a post_initialize method that mix-in modules can override.
56
+ def post_initialize
57
+ end
48
58
 
49
59
  # Prints this object's class demodulized name and object id.
50
60
  def print_class_and_id
@@ -134,12 +144,13 @@ module Jinx
134
144
  # is preserved, e.g. an Array value is assigned to a set domain type by first clearing the set
135
145
  # and then merging the array content into the set.
136
146
  #
137
- # @see Mergeable#merge_attribute
138
- def set_property_value(attribute, value)
139
- prop = self.class.property(attribute)
147
+ # @param [Property, Symbol] prop the property or attribute to set
148
+ # @param value the new value
149
+ def set_property_value(prop, value)
150
+ prop = self.class.property(prop) if Symbol === prop
140
151
  if prop.domain? and prop.collection? then
141
- clear_attribute(attribute)
142
- merge_attribute(attribute, value)
152
+ clear_attribute(prop.attribute)
153
+ merge_attribute(prop.attribute, value)
143
154
  else
144
155
  set_typed_property_value(prop, value)
145
156
  end
@@ -196,8 +207,10 @@ module Jinx
196
207
  self.class.owner_attributes.detect_value { |pa| send(pa) }
197
208
  end
198
209
 
199
- # @return [(Property, Resource), nil] the (property, value) pair for which there is an
200
- # owner reference, or nil if this domain object does not reference an owner
210
+ # Returns the (property, value) pair for which there is an owner reference, or nil if
211
+ # this domain object does not reference an owner.
212
+ #
213
+ # @return [(Property, Resource), nil] the owner (property, value) pair
201
214
  def effective_owner_property_value
202
215
  self.class.owner_properties.detect_value do |op|
203
216
  ref = send(op.attribute)
@@ -207,12 +220,17 @@ module Jinx
207
220
 
208
221
  # Sets this dependent's owner attribute to the given domain object.
209
222
  #
210
- # @param [Resource] owner the owner domain object
211
- # @raise [NoMethodError] if this Resource's class does not have exactly one owner attribute
212
- def owner=(owner)
213
- pa = self.class.owner_attribute
214
- if pa.nil? then raise NoMethodError.new("#{self.class.qp} does not have a unique owner attribute") end
215
- set_property_value(pa, owner)
223
+ # @param [Resource] obj the owner domain object
224
+ # @raise [NoMethodError] if this Resource's class does not have an owner property
225
+ # which accepts the given domain object
226
+ def owner=(obj)
227
+ if obj.nil? then
228
+ op, ov = effective_owner_property_value || return
229
+ else
230
+ op = self.class.owner_properties.detect { |prop| prop.type === obj }
231
+ end
232
+ if op.nil? then raise NoMethodError.new("#{self.class.qp} does not have an owner attribute for #{obj}") end
233
+ set_property_value(op.attribute, obj)
216
234
  end
217
235
 
218
236
  # @param [Resource] other the domain object to check
@@ -220,26 +238,36 @@ module Jinx
220
238
  # {#owner_ancestor?} of this object's {#owner}
221
239
  def owner_ancestor?(other)
222
240
  ownr = self.owner
223
- ownr and (ownr == other or ownr.owner_ancestor?(other))
241
+ if ownr then
242
+ ownr == other or ownr.owner_ancestor?(other)
243
+ elsif self.class.owner_types.size == self.class.owner_properties.size then
244
+ false
245
+ else
246
+ path = other.class.dependency_path_to(self.class)
247
+ !!path and other.reachable?(self, path)
248
+ end
224
249
  end
225
250
 
251
+ # @param [Resource] other the domain object to check
252
+ # @param [<Property>] the property path to follow
253
+ # @return [Boolean] whether following there is a path from this object through the given
254
+ # properties to the other object
255
+ def reachable?(other, path)
256
+ return false if path.empty?
257
+ prop = path.shift
258
+ send(prop.attribute).to_enum.any? do |ref|
259
+ ref == other or ref.reachable?(other, path)
260
+ end
261
+ end
262
+
226
263
  # @param [Resource] other the domain object to check
227
264
  # @return [Boolean] whether the other domain object is a dependent of this object
228
265
  # and has an update-only non-domain attribute.
229
266
  def dependent_update_only?(other)
230
267
  other.owner == self and
231
- other.class.nondomain_attributes.detect_with_property { |prop| prop.updatable? and not prop.creatable? }
232
- end
233
-
234
- # Returns an attribute => value hash for the specified attributes with a non-nil, non-empty value.
235
- # The default attributes are this domain object's class {Propertied#attributes}.
236
- # Only non-nil attributes defined by this Resource are included in the result hash.
237
- #
238
- # @param [<Symbol>, nil] attributes the attributes to merge
239
- # @return [{Symbol => Object}] the attribute => value hash
240
- def value_hash(attributes=nil)
241
- attributes ||= self.class.attributes
242
- attributes.to_compact_hash { |pa| send(pa) if self.class.method_defined?(pa) }
268
+ other.class.nondomain_attributes.detect_attribute_with_property do |prop|
269
+ prop.updatable? and not prop.creatable?
270
+ end
243
271
  end
244
272
 
245
273
  # Returns the domain object references for the given attributes.
@@ -398,13 +426,15 @@ module Jinx
398
426
  #
399
427
  # @param [Resource] other the domain object to compare
400
428
  # @param [<Symbol>, nil] attributes the attributes to compare
401
- # @return (see Hashable#diff)
402
- def diff(other, attributes=nil)
429
+ # @return (see Hasher#diff)
430
+ def difference(other, attributes=nil)
403
431
  attributes ||= self.class.nondomain_attributes
404
432
  vh = value_hash(attributes)
405
433
  ovh = other.value_hash(attributes)
406
434
  vh.diff(ovh) { |key, v1, v2| Resource.value_equal?(v1, v2) }
407
435
  end
436
+
437
+ alias :diff :difference
408
438
 
409
439
  # Returns the domain object in others which matches this dependent domain object
410
440
  # within the scope of a parent on a minimally acceptable constraint. This method
@@ -696,7 +726,7 @@ module Jinx
696
726
  end
697
727
  end
698
728
  # If there is an owner reference attribute, then there must be an owner.
699
- if self.class.bidirectional_dependent? then
729
+ if self.class.bidirectional_java_dependent? then
700
730
  raise ValidationError.new("Dependent #{self} does not reference an owner")
701
731
  end
702
732
  end
@@ -708,8 +738,8 @@ module Jinx
708
738
  begin
709
739
  send(property.writer, value)
710
740
  rescue TypeError
711
- # Add the attribute to the error message.
712
- raise TypeError.new("Cannot set #{self.class.qp} #{property} to #{value.qp} - " + $!)
741
+ logger.error("Cannot set #{self.class.qp} #{property} to #{value.qp} - " + $!)
742
+ raise
713
743
  end
714
744
  end
715
745
 
@@ -34,8 +34,10 @@ module Jinx
34
34
  clr_wtr = self.class === oldval && oldval.send(rdr).equal?(self) ? wtr : inverse_writer
35
35
  oldval.send(clr_wtr, nil)
36
36
  end
37
+
37
38
  # call the writer
38
39
  send(wtr, newval)
40
+
39
41
  # call the inverse writer on self
40
42
  if newval then
41
43
  newval.send(inverse_writer, self)
@@ -67,8 +69,10 @@ module Jinx
67
69
  coll = oldval.send(inverse)
68
70
  coll.delete(self) if coll
69
71
  end
72
+
70
73
  # call the writer on this object
71
74
  send(wtr, newval)
75
+
72
76
  # add self to the inverse collection
73
77
  if newval then
74
78
  coll = newval.send(inverse)
@@ -149,7 +149,6 @@ module Jinx
149
149
  # add residual matches
150
150
  rsd_mtchs.each { |src, tgt| add_match(src, tgt) }
151
151
  logger.debug { "#{qp} matched #{rsd_mtchs.qp}..." } if @verbose and not rsd_mtchs.empty?
152
-
153
152
  # The source => target match hash.
154
153
  # If there is a copier, then copy each unmatched source.
155
154
  matches = srcs.to_compact_hash { |src| match_for(src) or copy_unmatched(src) }
@@ -39,10 +39,9 @@ module Jinx
39
39
  return self if other.nil? or other.equal?(self)
40
40
  attributes = [attributes] if Symbol === attributes
41
41
  attributes ||= self.class.mergeable_attributes
42
-
43
- # if the source object is not a hash, then convert it to an attribute => value hash
44
- vh = Hashable === other ? other : other.value_hash(attributes)
45
- # merge the value hash
42
+ # If the source object is not a hash, then convert it to an attribute => value hash.
43
+ vh = Hasher === other ? other : other.value_hash(attributes)
44
+ # Merge the Java values hash.
46
45
  vh.each { |pa, value| merge_attribute(pa, value, matches, &filter) }
47
46
  self
48
47
  end
@@ -100,6 +99,17 @@ module Jinx
100
99
  end
101
100
  end
102
101
 
102
+ # Returns an attribute => value hash for the specified attributes with a non-nil, non-empty value.
103
+ # The default attributes are this domain object's class {Propertied#attributes}.
104
+ # Only non-nil values of attributes defined by this domain object are included in the result hash.
105
+ #
106
+ # @param [<Symbol>, nil] attributes the attributes to merge
107
+ # @return [{Symbol => Object}] the attribute => value hash
108
+ def value_hash(attributes=nil)
109
+ attributes ||= self.class.attributes
110
+ attributes.to_compact_hash { |pa| send(pa) rescue nil }
111
+ end
112
+
103
113
  private
104
114
 
105
115
  # @see #merge_attribute
@@ -118,7 +128,7 @@ module Jinx
118
128
  # the dependent owner writer method, if any
119
129
  if property.dependent? then
120
130
  val = property.collection? ? newval.first : newval
121
- klass = val.class if val
131
+ klass = val.domain_class if val
122
132
  inv_prop = self.class.inverse_property(property, klass)
123
133
  if inv_prop and not inv_prop.collection? then
124
134
  owtr = inv_prop.writer
@@ -178,8 +188,8 @@ module Jinx
178
188
  # If the target is a dependent, then set the dependent owner, which will in turn
179
189
  # set the attribute to the dependent. Otherwise, set the attribute to the target.
180
190
  owtr ? delegate_to_inverse_setter(property, ref, owtr) : set_typed_property_value(property, ref)
191
+ ref
181
192
  end
182
- newval
183
193
  end
184
194
 
185
195
  # @quirk Java Java TreeSet comparison uses the TreeSet comparator rather than an
@@ -2,14 +2,13 @@ require 'enumerator'
2
2
  require 'generator'
3
3
  require 'jinx/helpers/options'
4
4
  require 'jinx/helpers/collections'
5
-
6
5
  require 'jinx/helpers/validation'
7
6
  require 'jinx/helpers/visitor'
8
7
 
9
8
  module Jinx
10
9
  # A ReferenceEnumerator iterates over domain property references.
11
10
  class ReferenceEnumerator
12
- include Enumerable
11
+ include Enumerable, Collection
13
12
 
14
13
  # @return [Resource] the domain object containing the references
15
14
  attr_reader :subject
@@ -1,3 +1,3 @@
1
1
  module Jinx
2
- VERSION = '2.1.3'
2
+ VERSION = '2.1.4'
3
3
  end
@@ -66,6 +66,10 @@ class CollectionsTest < Test::Unit::TestCase
66
66
  assert_nil([1, 2, 3].detect_value { |item| item * 2 if item > 3 }, "Value incorrectly detected")
67
67
  end
68
68
 
69
+ def test_hash_detect_value
70
+ assert_equal(4, {1 => 2, 3 => 4}.detect_value { |k, v| v if k > 1 }, "Detect hash value incorrect")
71
+ end
72
+
69
73
  def test_detect_with_value
70
74
  assert_equal([2, 1], [1, 2].detect_with_value { |item| item / 2 if item % 2 == 0 }, "Detect with value incorrect")
71
75
  end
@@ -96,12 +100,15 @@ class CollectionsTest < Test::Unit::TestCase
96
100
 
97
101
  def test_union
98
102
  base = [1, 2]
99
- sum = base.union([4])
100
- assert_equal([1, 2, 4], sum.to_a, 'Enumerator union incorrect')
101
- assert(sum.include?(2), "Enumerator union missing first array element")
102
- assert(sum.include?(4), "Enumerator union missing second array element")
103
+ u = base.union([4])
104
+ assert_equal([1, 2, 4], u.to_a, 'Enumerator on union incorrect')
105
+ assert(u.include?(2), "Enumerator on union missing first array element")
106
+ assert(u.include?(4), "Enumerator on union missing second array element")
103
107
  base << 3
104
- assert_equal([1, 2, 3, 4], sum.to_a, 'Enumerator union does not reflect operand modification')
108
+ assert_equal([1, 2, 3, 4], u.to_a, 'Enumerator on union does not reflect operand modification')
109
+ # A union of a unions yields the flattened result.
110
+ uu = u.union([5])
111
+ assert_equal([1, 2, 3, 4, 5], uu.to_a, 'Enumerator on union of unions incorrect')
105
112
  end
106
113
 
107
114
  def test_intersection
@@ -137,9 +144,16 @@ class CollectionsTest < Test::Unit::TestCase
137
144
  end
138
145
 
139
146
  def test_partial_sort
140
- sorted = [Array, Object, Numeric, Enumerable, Set].partial_sort
147
+ sorted = [Enumerable, Array, Object, String].partial_sort
141
148
  assert(sorted.index(Array) < sorted.index(Enumerable), "Partial sort order incorrect")
142
- assert(sorted.index(Set) < sorted.index(Enumerable), "Partial sort order incorrect")
149
+ assert(sorted.index(Array) < sorted.index(Object), "Partial sort order incorrect")
150
+ assert(sorted.index(String) < sorted.index(Enumerable), "Partial sort order incorrect")
151
+ assert(sorted.index(String) < sorted.index(Object), "Partial sort order incorrect")
152
+ end
153
+
154
+ def test_partial_sort_with_block
155
+ sorted = [Array, Module, Enumerable].partial_sort { |a, b| b <=> a }
156
+ assert(sorted.index(Array) > sorted.index(Enumerable), "Partial sort order incorrect")
143
157
  end
144
158
 
145
159
  def test_hash_union
@@ -182,7 +196,7 @@ class CollectionsTest < Test::Unit::TestCase
182
196
  assert_equal(expected, actual, 'Association hash incorrect')
183
197
  end
184
198
 
185
- def test_hashable_equal
199
+ def test_hasher_equal
186
200
  assert_equal({:a => 1}, {:a => 1}.filter, "Hash equal incorrect")
187
201
  end
188
202
 
@@ -305,11 +319,12 @@ class CollectionsTest < Test::Unit::TestCase
305
319
  end
306
320
 
307
321
  def test_key_transformer_hash
308
- hash = Jinx::KeyTransformerHash.new { |k| k % 2 }
309
- hash[1] = :a
310
- assert_equal(:a, hash[1], 'Key transformer hash entered value not found')
311
- assert_nil(hash[2], 'Transformed hash unentered value found')
312
- assert_equal(:a, hash[3], 'Key transformer hash equivalent value not found')
322
+ h = {1 => :a, 2 => :b}
323
+ xfm = h.transform_key { |n| n * 2 }
324
+ assert_equal([2, 4], xfm.keys, 'Key transformer hash keys incorrect')
325
+ assert_equal(:a, xfm[2], 'Key transformer hash access incorrect')
326
+ h[3] = :c
327
+ assert_equal(:c, xfm[6], 'Key transformer hash does not reflect change to underlying hash')
313
328
  end
314
329
 
315
330
  def test_transform_value
@@ -347,7 +362,7 @@ class CollectionsTest < Test::Unit::TestCase
347
362
  assert_nil(hash[:c], "Hashinator has association not in the base")
348
363
  base.first[1] = 3
349
364
  assert_equal(3, hash[:a], "Hashinator does not reflect change to underlying Enumerator")
350
- assert_equal(base, hash.to_hash.to_a, "Hashable to_hash incorrect")
365
+ assert_equal(base, hash.to_hash.to_a, "Hasher to_hash incorrect")
351
366
  end
352
367
 
353
368
  def test_collector
@@ -83,21 +83,6 @@ class VistorTest < Test::Unit::TestCase
83
83
  assert_equal(3, child.value, "Child visited twice")
84
84
  end
85
85
 
86
- def test_root_cycle
87
- parent = Node.new(1)
88
- c1 = Node.new(2, parent)
89
- c2 = Node.new(3, parent)
90
- c2.children << parent
91
- gc11 = Node.new(4, c1)
92
- gc12 = Node.new(5, c1)
93
- gc12.children << c1
94
- gc121 = Node.new(6, gc12)
95
- gc121.children << parent
96
- visitor = Jinx::Visitor.new { |node| node.children }
97
- result = visitor.visit(parent)
98
- assert_equal([[2, 5, 2], [1, 2, 5, 6, 1], [1, 3, 1]], visitor.cycles.map { |cycle| cycle.map { |node| node.value } }, "Root cycles incorrect")
99
- end
100
-
101
86
  def increment(parent, limit)
102
87
  visitor = Jinx::Visitor.new { |node| node.children }
103
88
  visitor.visit(parent) { |node| node.value < limit ? node.value += 1 : return }
@@ -122,11 +107,13 @@ class VistorTest < Test::Unit::TestCase
122
107
  c1 = Node.new(2, parent)
123
108
  c2 = Node.new(3, parent)
124
109
  visitor = Jinx::Visitor.new { |node| node.children }
125
- result = visitor.to_enum(parent).map { |node| node.value }
126
- assert_equal([1, 2, 3], result, "Enumeration result incorrect")
110
+ result = visitor.to_enum(parent).to_a
111
+ assert_equal([parent, c1, c2], result, "Enumeration result incorrect")
127
112
  end
128
113
 
129
- def test_exclude_cycles
114
+ def test_prune_cycle
115
+ # Make a graph with paths parent -> c1 -> gc11 -> c1 and
116
+ # parent -> c2 -> gc21 -> parent.
130
117
  parent = Node.new(1)
131
118
  c1 = Node.new(2, parent)
132
119
  gc11 = Node.new(3, c1)
@@ -135,8 +122,8 @@ class VistorTest < Test::Unit::TestCase
135
122
  gc21 = Node.new(5, c2)
136
123
  gc21.children << parent
137
124
  visitor = Jinx::Visitor.new(:prune_cycle) { |node| node.children }
138
- result = visitor.to_enum(parent).map { |node| node.value }
139
- assert_equal([1, 2, 3], result, "Exclude result incorrect")
125
+ result = visitor.to_enum(parent).to_a
126
+ assert_equal([parent, c1], result, "Exclude result incorrect")
140
127
  end
141
128
 
142
129
  def test_missing_block