jinx 2.1.1 → 2.1.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|