jinx 2.1.3 → 2.1.4
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/Gemfile.lock +27 -0
- data/History.md +4 -0
- data/lib/jinx/helpers/class.rb +16 -12
- data/lib/jinx/helpers/collection.rb +277 -29
- data/lib/jinx/helpers/collections.rb +35 -2
- data/lib/jinx/helpers/conditional_enumerator.rb +2 -2
- data/lib/jinx/helpers/filter.rb +8 -2
- data/lib/jinx/helpers/flattener.rb +2 -2
- data/lib/jinx/helpers/hash.rb +3 -2
- data/lib/jinx/helpers/{hashable.rb → hasher.rb} +125 -77
- data/lib/jinx/helpers/module.rb +1 -1
- data/lib/jinx/helpers/multi_enumerator.rb +25 -9
- data/lib/jinx/helpers/options.rb +4 -3
- data/lib/jinx/helpers/partial_order.rb +16 -8
- data/lib/jinx/helpers/pretty_print.rb +14 -4
- data/lib/jinx/helpers/transformer.rb +3 -1
- data/lib/jinx/helpers/transitive_closure.rb +3 -3
- data/lib/jinx/helpers/visitor.rb +33 -42
- data/lib/jinx/import/java.rb +40 -27
- data/lib/jinx/importer.rb +86 -33
- data/lib/jinx/metadata/attribute_enumerator.rb +5 -11
- data/lib/jinx/metadata/dependency.rb +65 -30
- data/lib/jinx/metadata/id_alias.rb +1 -0
- data/lib/jinx/metadata/introspector.rb +21 -9
- data/lib/jinx/metadata/inverse.rb +14 -11
- data/lib/jinx/metadata/java_property.rb +15 -26
- data/lib/jinx/metadata/propertied.rb +80 -19
- data/lib/jinx/metadata/property.rb +13 -8
- data/lib/jinx/metadata/property_characteristics.rb +2 -2
- data/lib/jinx/resource.rb +62 -32
- data/lib/jinx/resource/inversible.rb +4 -0
- data/lib/jinx/resource/match_visitor.rb +0 -1
- data/lib/jinx/resource/mergeable.rb +16 -6
- data/lib/jinx/resource/reference_enumerator.rb +1 -2
- data/lib/jinx/version.rb +1 -1
- data/test/lib/jinx/helpers/collections_test.rb +29 -14
- data/test/lib/jinx/helpers/visitor_test.rb +7 -20
- data/test/lib/jinx/import/mixed_case_test.rb +17 -3
- metadata +4 -4
- 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
|
-
|
276
|
-
|
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
|
-
|
290
|
-
if
|
291
|
-
|
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
|
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
|
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
|
data/lib/jinx/resource.rb
CHANGED
@@ -29,13 +29,16 @@ module Jinx
|
|
29
29
|
# module Domain
|
30
30
|
# include Jinx::Resource
|
31
31
|
# # The caTissue Java package name.
|
32
|
-
#
|
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
|
-
# @
|
138
|
-
|
139
|
-
|
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
|
-
#
|
200
|
-
#
|
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]
|
211
|
-
# @raise [NoMethodError] if this Resource's class does not have
|
212
|
-
|
213
|
-
|
214
|
-
if
|
215
|
-
|
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
|
-
|
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.
|
232
|
-
|
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
|
402
|
-
def
|
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.
|
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
|
-
|
712
|
-
raise
|
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
|
-
|
44
|
-
|
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.
|
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
|
data/lib/jinx/version.rb
CHANGED
@@ -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
|
-
|
100
|
-
assert_equal([1, 2, 4],
|
101
|
-
assert(
|
102
|
-
assert(
|
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],
|
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,
|
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(
|
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
|
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
|
-
|
309
|
-
|
310
|
-
assert_equal(
|
311
|
-
|
312
|
-
|
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, "
|
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).
|
126
|
-
assert_equal([
|
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
|
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).
|
139
|
-
assert_equal([
|
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
|