jinx 2.1.1 → 2.1.2
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/History.md +4 -0
- data/lib/jinx.rb +0 -1
- data/lib/jinx/cli/application.rb +3 -3
- data/lib/jinx/cli/command.rb +1 -1
- data/lib/jinx/helpers/array.rb +3 -3
- data/lib/jinx/helpers/associative.rb +41 -0
- data/lib/jinx/helpers/collections.rb +0 -1
- data/lib/jinx/helpers/enumerable.rb +5 -2
- data/lib/jinx/helpers/hash.rb +1 -1
- data/lib/jinx/helpers/hashable.rb +1 -7
- data/lib/jinx/helpers/inflector.rb +1 -1
- data/lib/jinx/helpers/log.rb +63 -11
- data/lib/jinx/helpers/math.rb +20 -11
- data/lib/jinx/helpers/options.rb +4 -4
- data/lib/jinx/helpers/pretty_print.rb +0 -1
- data/lib/jinx/helpers/transitive_closure.rb +1 -1
- data/lib/jinx/helpers/uniquifier.rb +50 -17
- data/lib/jinx/helpers/visitor.rb +3 -3
- data/lib/jinx/import/java.rb +1 -1
- data/lib/jinx/importer.rb +3 -2
- data/lib/jinx/metadata/attribute_enumerator.rb +1 -1
- data/lib/jinx/metadata/dependency.rb +3 -3
- data/lib/jinx/metadata/introspector.rb +46 -42
- data/lib/jinx/metadata/inverse.rb +17 -1
- data/lib/jinx/metadata/java_property.rb +10 -10
- data/lib/jinx/metadata/propertied.rb +19 -16
- data/lib/jinx/metadata/property.rb +11 -11
- data/lib/jinx/resource.rb +86 -14
- data/lib/jinx/resource/match_visitor.rb +7 -5
- data/lib/jinx/resource/merge_visitor.rb +3 -10
- data/lib/jinx/resource/mergeable.rb +16 -16
- data/lib/jinx/resource/reference_enumerator.rb +0 -1
- data/lib/jinx/resource/reference_path_visitor.rb +1 -1
- data/lib/jinx/resource/reference_visitor.rb +5 -6
- data/lib/jinx/resource/unique.rb +1 -1
- data/lib/jinx/version.rb +1 -1
- data/test/lib/jinx/helpers/associative_test.rb +26 -0
- data/test/lib/jinx/helpers/collections_test.rb +14 -2
- data/test/lib/jinx/helpers/uniquifier_test.rb +11 -0
- metadata +9 -8
- data/Gemfile.lock +0 -27
- data/lib/jinx/helpers/error.rb +0 -15
- data/lib/jinx/helpers/key_transformer_hash.rb +0 -43
data/lib/jinx/resource.rb
CHANGED
@@ -85,7 +85,7 @@ module Jinx
|
|
85
85
|
# @return [Resource] this domain object
|
86
86
|
# @raise (see #validate_local)
|
87
87
|
def validate
|
88
|
-
|
88
|
+
unless @validated then
|
89
89
|
validate_local
|
90
90
|
@validated = true
|
91
91
|
end
|
@@ -104,7 +104,7 @@ module Jinx
|
|
104
104
|
if attributes.empty? then
|
105
105
|
attributes = self.class.nondomain_attributes
|
106
106
|
elsif Enumerable === attributes.first then
|
107
|
-
|
107
|
+
raise ArgumentError.new("#{qp} copy attributes argument is not a Symbol: #{attributes.first}") unless attributes.size == 1
|
108
108
|
attributes = attributes.first
|
109
109
|
end
|
110
110
|
self.class.new.merge_attributes(self, attributes)
|
@@ -136,10 +136,13 @@ module Jinx
|
|
136
136
|
#
|
137
137
|
# @see Mergeable#merge_attribute
|
138
138
|
def set_property_value(attribute, value)
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
139
|
+
prop = self.class.property(attribute)
|
140
|
+
if prop.domain? and prop.collection? then
|
141
|
+
clear_attribute(attribute)
|
142
|
+
merge_attribute(attribute, value)
|
143
|
+
else
|
144
|
+
set_typed_property_value(prop, value)
|
145
|
+
end
|
143
146
|
end
|
144
147
|
|
145
148
|
# Returns the first non-nil {#key_value} for the primary, secondary
|
@@ -208,7 +211,7 @@ module Jinx
|
|
208
211
|
# @raise [NoMethodError] if this Resource's class does not have exactly one owner attribute
|
209
212
|
def owner=(owner)
|
210
213
|
pa = self.class.owner_attribute
|
211
|
-
if pa.nil? then
|
214
|
+
if pa.nil? then raise NoMethodError.new("#{self.class.qp} does not have a unique owner attribute") end
|
212
215
|
set_property_value(pa, owner)
|
213
216
|
end
|
214
217
|
|
@@ -324,6 +327,26 @@ module Jinx
|
|
324
327
|
matches_key_attributes?(other, self.class.alternate_key_attributes)
|
325
328
|
end
|
326
329
|
|
330
|
+
# Matches this domain object with the other domain object. The match
|
331
|
+
# succeeds if and only if the object classes match and for each
|
332
|
+
# attribute, at least one of the following conditions hold:
|
333
|
+
# * this object's attribute value is nil or empty
|
334
|
+
# * the other object's attribute value is nil or empty
|
335
|
+
# * if the attribute is a nondomain attribute, then the respective values
|
336
|
+
# are equal
|
337
|
+
# * if the attribute value is a Resource, then the value recursively
|
338
|
+
# matches the other value
|
339
|
+
# * if the attribute value is a Resource collection, then every
|
340
|
+
# item in the collection matches some item in the other collection
|
341
|
+
#
|
342
|
+
# @param [Resource] the domain object to match
|
343
|
+
# @return [Boolean] whether this object matches the other object on class
|
344
|
+
# and content
|
345
|
+
def content_matches?(other)
|
346
|
+
logger.debug { "Matching #{self} content against #{other}..." }
|
347
|
+
content_matches_recursive?(other)
|
348
|
+
end
|
349
|
+
|
327
350
|
# Matches this dependent domain object with the others on type and key attributes
|
328
351
|
# in the scope of a parent object.
|
329
352
|
# Returns the object in others which matches this domain object, or nil if none.
|
@@ -448,7 +471,7 @@ module Jinx
|
|
448
471
|
#
|
449
472
|
# @yield [dep] operation on the visited domain object
|
450
473
|
# @yieldparam [Resource] dep the domain object to visit
|
451
|
-
def visit_dependents(&operator)
|
474
|
+
def visit_dependents(&operator)
|
452
475
|
DEPENDENT_VISITOR.visit(self, &operator)
|
453
476
|
end
|
454
477
|
|
@@ -472,9 +495,6 @@ module Jinx
|
|
472
495
|
# Prints this domain object's content and recursively prints the referenced content.
|
473
496
|
# The optional selector block determines the attributes to print. The default is the
|
474
497
|
# {Propertied#java_attributes}.
|
475
|
-
#
|
476
|
-
#
|
477
|
-
# TODO caRuby override to do_without_lazy_loader
|
478
498
|
#
|
479
499
|
# @yield [owner] the owner attribute selector
|
480
500
|
# @yieldparam [Resource] owner the domain object to print
|
@@ -569,6 +589,46 @@ module Jinx
|
|
569
589
|
# Recurse to the dependents.
|
570
590
|
each_defaultable_reference { |ref| ref.add_defaults_recursive }
|
571
591
|
end
|
592
|
+
|
593
|
+
# @param [Resource] (see #content_matches?)
|
594
|
+
# @param [<Resource>] visited the matched references
|
595
|
+
# @return (see #content_matches?)
|
596
|
+
def content_matches_recursive?(other, visited=Set.new)
|
597
|
+
return false unless self.class == other.class
|
598
|
+
return false unless self.class.nondomain_attributes.all? do |pa|
|
599
|
+
v = send(pa)
|
600
|
+
ov = other.send(pa)
|
601
|
+
if v.nil? || ov.nil? or Resource.value_equal?(v, ov) then
|
602
|
+
true
|
603
|
+
else
|
604
|
+
logger.debug { "#{self} does not match #{other} on property #{pa}: #{v.qp} vs #{ov.qp}" }
|
605
|
+
false
|
606
|
+
end
|
607
|
+
end
|
608
|
+
self.class.domain_attributes.all? do |pa|
|
609
|
+
v = send(pa)
|
610
|
+
ov = other.send(pa)
|
611
|
+
if v.nil_or_empty? or ov.nil_or_empty? or visited.include?(v) then
|
612
|
+
true
|
613
|
+
else
|
614
|
+
logger.debug { "Matching #{self} #{pa} value #{v.qp} against #{other} #{pa} value #{ov.qp}..." }
|
615
|
+
if Enumerable === v then
|
616
|
+
v.all? do |ref|
|
617
|
+
oref = ref.match_in(ov)
|
618
|
+
if oref.nil? then
|
619
|
+
logger.debug { "#{self} does not match #{other} on property #{pa}: #{v.pp_s} vs #{ov.pp_s}" }
|
620
|
+
false
|
621
|
+
else
|
622
|
+
ref.content_matches_recursive?(oref, visited)
|
623
|
+
end
|
624
|
+
end
|
625
|
+
else
|
626
|
+
visited << v
|
627
|
+
v.content_matches_recursive?(ov, visited)
|
628
|
+
end
|
629
|
+
end
|
630
|
+
end
|
631
|
+
end
|
572
632
|
|
573
633
|
private
|
574
634
|
|
@@ -614,7 +674,7 @@ module Jinx
|
|
614
674
|
invalid = missing_mandatory_attributes
|
615
675
|
unless invalid.empty? then
|
616
676
|
logger.error("Validation of #{qp} unsuccessful - missing #{invalid.join(', ')}:\n#{dump}")
|
617
|
-
|
677
|
+
raise ValidationError.new("Required attribute value missing for #{self}: #{invalid.join(', ')}")
|
618
678
|
end
|
619
679
|
validate_owner
|
620
680
|
end
|
@@ -632,12 +692,24 @@ module Jinx
|
|
632
692
|
if self.class.owner_attributes.size > 1 then
|
633
693
|
vh = value_hash(self.class.owner_attributes)
|
634
694
|
if vh.size > 1 then
|
635
|
-
|
695
|
+
raise ValidationError.new("Dependent #{self} references multiple owners #{vh.pp_s}:\n#{dump}")
|
636
696
|
end
|
637
697
|
end
|
638
698
|
# If there is an owner reference attribute, then there must be an owner.
|
639
699
|
if self.class.bidirectional_dependent? then
|
640
|
-
|
700
|
+
raise ValidationError.new("Dependent #{self} does not reference an owner")
|
701
|
+
end
|
702
|
+
end
|
703
|
+
|
704
|
+
# @param [Property] property the property to set
|
705
|
+
# @param value the new value
|
706
|
+
# @raise [TypeError] if the value is incompatible with the property
|
707
|
+
def set_typed_property_value(property, value)
|
708
|
+
begin
|
709
|
+
send(property.writer, value)
|
710
|
+
rescue TypeError
|
711
|
+
# Add the attribute to the error message.
|
712
|
+
raise TypeError.new("Cannot set #{self.class.qp} #{property} to #{value.qp} - " + $!)
|
641
713
|
end
|
642
714
|
end
|
643
715
|
|
@@ -20,7 +20,7 @@ module Jinx
|
|
20
20
|
# @yield (see ReferenceVisitor#initialize)
|
21
21
|
# @yieldparam [Resource] source the matched source object
|
22
22
|
def initialize(opts=nil)
|
23
|
-
|
23
|
+
raise ArgumentError.new("Reference visitor missing domain reference selector") unless block_given?
|
24
24
|
opts = Options.to_hash(opts)
|
25
25
|
@matcher = opts.delete(:matcher) || DEF_MATCHER
|
26
26
|
@matchable = opts.delete(:matchable)
|
@@ -35,9 +35,11 @@ module Jinx
|
|
35
35
|
# only a matched reference is visited.
|
36
36
|
super do |src|
|
37
37
|
tgt = @matches[src]
|
38
|
-
|
39
|
-
|
40
|
-
|
38
|
+
# the attributes to match on
|
39
|
+
mas = yield(src)
|
40
|
+
# match the attribute references
|
41
|
+
match_references(src, tgt, mas)
|
42
|
+
mas
|
41
43
|
end
|
42
44
|
end
|
43
45
|
|
@@ -98,7 +100,7 @@ module Jinx
|
|
98
100
|
# @raise [ValidationError] if there is no match
|
99
101
|
def match_for_visited(source)
|
100
102
|
target = @matches[source]
|
101
|
-
if target.nil? then
|
103
|
+
if target.nil? then raise ValidationError.new("Match visitor target not found for #{source}") end
|
102
104
|
target
|
103
105
|
end
|
104
106
|
|
@@ -54,20 +54,13 @@ module Jinx
|
|
54
54
|
return target if source.equal?(target)
|
55
55
|
# the domain attributes to merge
|
56
56
|
mas = @mergeable.call(source)
|
57
|
-
|
57
|
+
unless mas.empty? then
|
58
|
+
logger.debug { "Merging #{source.qp} #{mas.to_series} into #{target.qp}..." }
|
59
|
+
end
|
58
60
|
# merge the non-domain attributes
|
59
61
|
target.merge_attributes(source)
|
60
62
|
# merge the source domain attributes into the target
|
61
63
|
target.merge(source, mas, @matches)
|
62
64
|
end
|
63
|
-
|
64
|
-
# @param source (see #merge)
|
65
|
-
# @param target (see #merge)
|
66
|
-
# @param attributes (see Mergeable#merge)
|
67
|
-
# @return [String] the log message
|
68
|
-
def format_merge_log_message(source, target, attributes)
|
69
|
-
attr_clause = " including domain attributes #{attributes.to_series}" unless attributes.empty?
|
70
|
-
"Merging #{source.qp} into #{target.qp}#{attr_clause}..."
|
71
|
-
end
|
72
65
|
end
|
73
66
|
end
|
@@ -87,19 +87,19 @@ module Jinx
|
|
87
87
|
# Discriminate between a domain and non-domain attribute.
|
88
88
|
prop = self.class.property(attribute)
|
89
89
|
if prop.domain? then
|
90
|
-
|
90
|
+
merge_domain_property_value(prop, oldval, newval, matches)
|
91
91
|
else
|
92
|
-
|
92
|
+
merge_nondomain_property_value(prop, oldval, newval)
|
93
93
|
end
|
94
94
|
end
|
95
95
|
|
96
96
|
private
|
97
97
|
|
98
98
|
# @see #merge_attribute
|
99
|
-
def
|
99
|
+
def merge_nondomain_property_value(property, oldval, newval)
|
100
100
|
if oldval.nil? then
|
101
|
-
|
102
|
-
elsif
|
101
|
+
set_typed_property_value(property, newval)
|
102
|
+
elsif property.collection? then
|
103
103
|
oldval.merge(newval)
|
104
104
|
else
|
105
105
|
oldval
|
@@ -107,12 +107,12 @@ module Jinx
|
|
107
107
|
end
|
108
108
|
|
109
109
|
# @see #merge_attribute
|
110
|
-
def
|
110
|
+
def merge_domain_property_value(property, oldval, newval, matches)
|
111
111
|
# the dependent owner writer method, if any
|
112
|
-
if
|
113
|
-
val =
|
112
|
+
if property.dependent? then
|
113
|
+
val = property.collection? ? newval.first : newval
|
114
114
|
klass = val.class if val
|
115
|
-
inv_prop = self.class.inverse_property(
|
115
|
+
inv_prop = self.class.inverse_property(property, klass)
|
116
116
|
if inv_prop and not inv_prop.collection? then
|
117
117
|
owtr = inv_prop.writer
|
118
118
|
end
|
@@ -122,14 +122,14 @@ module Jinx
|
|
122
122
|
# collection value and add each unmatched source to the collection.
|
123
123
|
# Otherwise, if the attribute is not yet set and there is a new value, then set it
|
124
124
|
# to the new value match or the new value itself if unmatched.
|
125
|
-
if
|
125
|
+
if property.collection? then
|
126
126
|
# TODO - refactor into method
|
127
127
|
if oldval.nil? then
|
128
|
-
|
128
|
+
raise ValidationError.new("Merge into #{qp} #{property} with nil collection value is not supported")
|
129
129
|
end
|
130
130
|
# the references to add
|
131
131
|
adds = []
|
132
|
-
logger.debug { "Merging #{newval.qp} into #{qp} #{
|
132
|
+
logger.debug { "Merging #{newval.qp} into #{qp} #{property} #{oldval.qp}..." } unless newval.nil_or_empty?
|
133
133
|
newval.enumerate do |src|
|
134
134
|
# If the match target is in the current collection, then update the matched
|
135
135
|
# target from the source.
|
@@ -150,11 +150,11 @@ module Jinx
|
|
150
150
|
end
|
151
151
|
end
|
152
152
|
# add the unmatched sources
|
153
|
-
logger.debug { "Adding #{qp} #{
|
153
|
+
logger.debug { "Adding #{qp} #{property} unmatched #{adds.qp}..." } unless adds.empty?
|
154
154
|
adds.each do |ref|
|
155
155
|
# If there is an owner writer attribute, then add the ref to the attribute collection by
|
156
156
|
# delegating to the owner writer. Otherwise, add the ref to the attribute collection directly.
|
157
|
-
owtr ? delegate_to_inverse_setter(
|
157
|
+
owtr ? delegate_to_inverse_setter(property, ref, owtr) : oldval << ref
|
158
158
|
end
|
159
159
|
oldval
|
160
160
|
elsif newval.nil? then
|
@@ -167,10 +167,10 @@ module Jinx
|
|
167
167
|
# No target; set the attribute to the source.
|
168
168
|
# The target is either a source match or the source itself.
|
169
169
|
ref = (matches[newval] if matches) || newval
|
170
|
-
logger.debug { "Setting #{qp} #{
|
170
|
+
logger.debug { "Setting #{qp} #{property} reference #{ref.qp}..." }
|
171
171
|
# If the target is a dependent, then set the dependent owner, which will in turn
|
172
172
|
# set the attribute to the dependent. Otherwise, set the attribute to the target.
|
173
|
-
owtr ? delegate_to_inverse_setter(
|
173
|
+
owtr ? delegate_to_inverse_setter(property, ref, owtr) : set_typed_property_value(property, ref)
|
174
174
|
end
|
175
175
|
newval
|
176
176
|
end
|
@@ -28,7 +28,7 @@ module Jinx
|
|
28
28
|
super(opts) do |ref|
|
29
29
|
# Collect the path attributes whose type is the ref type up to the
|
30
30
|
# next position in the path.
|
31
|
-
max = lineage.size
|
31
|
+
max = Math.min(lineage.size, path.size)
|
32
32
|
pas = (0...max).map { |i| path[i].attribute if path[i].declarer === ref }
|
33
33
|
pas.compact!
|
34
34
|
ref.class.attribute_filter(pas)
|
@@ -5,7 +5,6 @@ require 'jinx/helpers/collections'
|
|
5
5
|
|
6
6
|
require 'jinx/helpers/validation'
|
7
7
|
require 'jinx/helpers/visitor'
|
8
|
-
require 'jinx/helpers/math'
|
9
8
|
|
10
9
|
module Jinx
|
11
10
|
# A ReferenceVisitor traverses reference attributes.
|
@@ -23,7 +22,7 @@ module Jinx
|
|
23
22
|
# current domain object
|
24
23
|
# @yieldparam [Resource] obj the current domain object
|
25
24
|
def initialize(opts=nil, &selector)
|
26
|
-
|
25
|
+
raise ArgumentError.new("Reference visitor missing domain reference selector") unless block_given?
|
27
26
|
# the property selector
|
28
27
|
@flt_sel = selector
|
29
28
|
# the reference filter
|
@@ -33,11 +32,11 @@ module Jinx
|
|
33
32
|
@ref_enums = {}
|
34
33
|
super do |ref|
|
35
34
|
# the reference property filter
|
36
|
-
|
37
|
-
if
|
38
|
-
logger.debug { "#{qp} visiting #{ref} attributes #{
|
35
|
+
ras = attributes_to_visit(ref)
|
36
|
+
if ras then
|
37
|
+
logger.debug { "#{qp} visiting #{ref} attributes #{ras.pp_s(:single_line)}..." } if @verbose
|
39
38
|
# an enumerator on the reference properties
|
40
|
-
enum = ReferenceEnumerator.new(ref,
|
39
|
+
enum = ReferenceEnumerator.new(ref, ras.properties)
|
41
40
|
# If there is a reference filter, then apply it to the enum references.
|
42
41
|
flt ? enum.filter(&flt) : enum
|
43
42
|
end
|
data/lib/jinx/resource/unique.rb
CHANGED
@@ -8,7 +8,7 @@ module Jinx
|
|
8
8
|
# Raises TypeError if value is neither nil nor a String.
|
9
9
|
def uniquify_value(value)
|
10
10
|
unless String === value or value.nil? then
|
11
|
-
|
11
|
+
raise TypeError.new("Cannot uniquify #{qp} non-String value #{value}")
|
12
12
|
end
|
13
13
|
Uniquifier.instance.uniquify(self, value)
|
14
14
|
end
|
data/lib/jinx/version.rb
CHANGED
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../../helper'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'jinx/helpers/associative'
|
4
|
+
|
5
|
+
class AssociativeTest < Test::Unit::TestCase
|
6
|
+
def test_get
|
7
|
+
hash = {'a' => 1}
|
8
|
+
assoc = Jinx::Associative.new { |k| hash[k.to_s] }
|
9
|
+
assert_equal(1, assoc[:a], "Associative access incorrect.")
|
10
|
+
assert_nil(assoc[:b], "Associative access incorrectly returns a value.")
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_set
|
14
|
+
hash = {'a' => 1}
|
15
|
+
assoc = Jinx::Associative.new { |k| hash[k.to_s] }.writer { |k, v| hash[k.to_s] = v }
|
16
|
+
assert_equal(1, assoc[:a], "Associative access incorrect.")
|
17
|
+
assert_nil(assoc[:b], "Associative access incorrectly returns a value.")
|
18
|
+
assoc[:b] = 2
|
19
|
+
assert_equal(2, assoc[:b], "Associative writer incorrect.")
|
20
|
+
# reset the value
|
21
|
+
assoc[:a] = 3
|
22
|
+
assert_equal(3, assoc[:a], "Associative writer incorrect.")
|
23
|
+
# test the ||= idiom
|
24
|
+
assert_equal(4, assoc[:c] ||= 4, "Associative writer incorrect.")
|
25
|
+
end
|
26
|
+
end
|
@@ -3,7 +3,6 @@ require 'test/unit'
|
|
3
3
|
require 'jinx/helpers/collections'
|
4
4
|
require 'jinx/helpers/lazy_hash'
|
5
5
|
require 'jinx/helpers/case_insensitive_hash'
|
6
|
-
require 'jinx/helpers/key_transformer_hash'
|
7
6
|
require 'jinx/helpers/conditional_enumerator'
|
8
7
|
require 'jinx/helpers/multi_enumerator'
|
9
8
|
|
@@ -313,7 +312,7 @@ class CollectionsTest < Test::Unit::TestCase
|
|
313
312
|
assert_equal(:a, hash[3], 'Key transformer hash equivalent value not found')
|
314
313
|
end
|
315
314
|
|
316
|
-
def
|
315
|
+
def test_transform_value
|
317
316
|
hash = {:a => 1, :b => 2}
|
318
317
|
xfm = hash.transform_value { |v| v * 2 }
|
319
318
|
assert_equal(2, xfm[:a], 'Transformed hash accessor incorrect')
|
@@ -326,6 +325,19 @@ class CollectionsTest < Test::Unit::TestCase
|
|
326
325
|
assert_equal(8, xfm[:c], 'Transformed hash does not reflect base hash change')
|
327
326
|
end
|
328
327
|
|
328
|
+
def test_transform_key
|
329
|
+
hash = {'a' => 1, 'b' => 2}
|
330
|
+
xfm = hash.transform_key { |k| k.to_sym }
|
331
|
+
assert_equal(1, xfm[:a], 'Transformed hash accessor incorrect')
|
332
|
+
assert_equal([:a, :b], xfm.keys.sort { |k1, k2| k1.to_s <=> k2.to_s }, 'Transformed hash keys incorrect')
|
333
|
+
assert(xfm.has_key?(:a), 'Transformed hash key query incorrect')
|
334
|
+
assert(!xfm.has_key?('a'), 'Transformed hash key query incorrect')
|
335
|
+
# base hash should be reflected in transformed hash
|
336
|
+
hash['b'] = 3; hash['c'] = 4
|
337
|
+
assert_equal(3, xfm[:b], 'Transformed hash does not reflect base hash change')
|
338
|
+
assert_equal(4, xfm[:c], 'Transformed hash does not reflect base hash change')
|
339
|
+
end
|
340
|
+
|
329
341
|
def test_hashinator
|
330
342
|
base = {:a => 1, :b => 2}.to_a
|
331
343
|
hash = Jinx::Hashinator.new(base)
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../../helper'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'jinx/helpers/uniquifier'
|
4
|
+
|
5
|
+
class UniquifierTest < Test::Unit::TestCase
|
6
|
+
def test_uniquify
|
7
|
+
u1 = Jinx::Uniquifier.instance.uniquify('Groucho')
|
8
|
+
u2 = Jinx::Uniquifier.instance.uniquify('Groucho')
|
9
|
+
assert_not_equal(u1, u2, "Consequetive uniquifier calls not unique")
|
10
|
+
end
|
11
|
+
end
|