simple_mapper 0.0.1

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.
@@ -0,0 +1,194 @@
1
+ require 'test_helper'
2
+ require 'delegate'
3
+ class CollectionArrayTest < Test::Unit::TestCase
4
+ context 'A SimpleMapper::Collection::Array' do
5
+ setup do
6
+ @class = SimpleMapper::Collection::Array
7
+ end
8
+
9
+ should 'be equivalent to a standard Array' do
10
+ array = [:a, :b, :c]
11
+ instance = @class.new(array)
12
+ assert_equal array, instance
13
+ assert_equal @class, instance.class
14
+ end
15
+
16
+ should 'allow array-style assignments, lookups, size checks, etc' do
17
+ instance = @class.new
18
+ assert_equal 0, instance.size
19
+ instance << :a
20
+ assert_equal 1, instance.size
21
+ assert_equal :a, instance[0]
22
+ instance[4] = :b
23
+ assert_equal 5, instance.size
24
+ assert_equal [:a, nil, nil, nil, :b], instance
25
+ end
26
+
27
+ should 'disallow non-integer indexes' do
28
+ instance = @class.new
29
+ assert_raise(TypeError) { instance[:a] = 'a' }
30
+ end
31
+
32
+ should 'provide list of indexes via :keys' do
33
+ assert_equal (0..3).to_a, @class.new([:a, :b, :c, :d]).keys
34
+ end
35
+
36
+ should 'yield key/value pairs instead of just value for :inject' do
37
+ source = [:a, :b, :c, :d, :e]
38
+ expected = (0..(source.size - 1)).to_a.collect {|key| [key, source[key]]}
39
+ result = @class.new(source).inject([]) {|a, pair| a << pair; a}
40
+ assert_equal expected, result
41
+ end
42
+
43
+ should 'have an :attribute attribute' do
44
+ instance = @class.new
45
+ assert_equal nil, instance.attribute
46
+ attrib = stub('attribute')
47
+ assert_equal attrib, instance.attribute = attrib
48
+ assert_equal attrib, instance.attribute
49
+ end
50
+
51
+ should 'allow creation of new mapper instances through :build' do
52
+ mapper = stub('mapper')
53
+ attrib = stub('attrib', :mapper => mapper)
54
+ seq = sequence('build invocations')
55
+ mapper.expects(:new).with.in_sequence(seq).returns(:one)
56
+ mapper.expects(:new).with(:a => 'A').in_sequence(seq).returns(:two)
57
+ mapper.expects(:new).with(:a, :b, :c).in_sequence(seq).returns(:three)
58
+ instance = @class.new
59
+ instance.attribute = attrib
60
+ result = []
61
+ result << instance.build
62
+ result << instance.build(:a => 'A')
63
+ result << instance.build(:a, :b, :c)
64
+ assert_equal [:one, :two, :three], result
65
+ end
66
+
67
+ context 'with change_tracking true' do
68
+ context 'when appending to the array' do
69
+ setup do
70
+ @instance = @class.new
71
+ @instance.attribute = stub('attrib', :mapper => nil)
72
+ @instance.change_tracking = false
73
+ @values = [:a, :b, :c]
74
+ @instance.push(*@values)
75
+ @instance.change_tracking = true
76
+ end
77
+
78
+ should 'mark new item for :<< as changed' do
79
+ @instance << :zzz
80
+ assert_equal [@values.size], @instance.changed_members
81
+ end
82
+
83
+ should 'mark new items for :push as changed' do
84
+ @instance.push(:xxx, :yyy, :zzz)
85
+ assert_equal [@values.size, @values.size + 1, @values.size + 2], @instance.changed_members
86
+ end
87
+ end
88
+ end
89
+
90
+ context 'when deleting from the array' do
91
+ setup do
92
+ @instance = @class.new
93
+ @instance.attribute = stub('attrib', :mapper => nil)
94
+ @instance.change_tracking = false
95
+ @values = ('a'..'z').to_a
96
+ @instance.push(*@values)
97
+ @instance.change_tracking = true
98
+ end
99
+
100
+ context 'with slice! given a range' do
101
+ should 'identify all indexes from the range minimum to the original size as changed' do
102
+ range = 5..10
103
+ expected_list = @values.clone
104
+ expected_slice = expected_list.slice!(range)
105
+ expected_changes = (5..(@values.size - 1)).to_a
106
+ assert_equal expected_slice, @instance.slice!(range)
107
+ assert_equal expected_list, @instance
108
+ assert_equal expected_changes, @instance.changed_members
109
+ end
110
+
111
+ should 'not mark changes for indexes outside the original size' do
112
+ range = (@values.size - 2)..(@values.size + 1)
113
+ expected_list = @values.clone
114
+ expected_slice = expected_list.slice!(range)
115
+ assert_equal expected_slice, @instance.slice!(range)
116
+ assert_equal expected_list, @instance
117
+ assert_equal [@values.size - 2, @values.size - 1], @instance.changed_members
118
+ end
119
+ end
120
+
121
+ context 'with slice! given a start index and length' do
122
+ should 'identify all indexes from start up to original size as changed' do
123
+ start = @values.size - 5
124
+ expected_list = @values.clone
125
+ expected_slice = expected_list.slice!(start, 3)
126
+ assert_equal expected_slice, @instance.slice!(start, 3)
127
+ assert_equal expected_list, @instance
128
+ assert_equal ((@values.size - 5)..(@values.size - 1)).to_a, @instance.changed_members
129
+ end
130
+
131
+ should 'not mark changes for indexes outside the original size' do
132
+ start = @values.size - 2
133
+ expected_list = @values.clone
134
+ expected_slice = expected_list.slice!(start, 1000)
135
+ assert_equal expected_slice, @instance.slice!(start, 1000)
136
+ assert_equal expected_list, @instance
137
+ assert_equal [@values.size - 2, @values.size - 1], @instance.changed_members
138
+ end
139
+
140
+ should 'handle negative start index properly' do
141
+ expected_list = @values.clone
142
+ expected_slice = expected_list.slice!(-5, 1)
143
+ assert_equal expected_slice, @instance.slice!(-5, 1)
144
+ assert_equal expected_list, @instance
145
+ assert_equal ((@values.size - 5)..(@values.size - 1)).to_a, @instance.changed_members
146
+ end
147
+ end
148
+
149
+ context 'with delete_at' do
150
+ should 'return deleted item and marked index to end as changed if index is in range' do
151
+ index = @values.size - 5
152
+ assert_equal @values[index], @instance.delete_at(index)
153
+ assert_equal @values.slice(0, index) + @values.slice(index + 1, 4), @instance
154
+ assert_equal (index..@values.size - 1).to_a, @instance.changed_members
155
+ end
156
+
157
+ should 'do nothing but return nil if index out of range' do
158
+ assert_equal nil, @instance.delete_at(@values.size)
159
+ assert_equal @values, @instance
160
+ assert_equal [], @instance.changed_members
161
+ end
162
+ end
163
+
164
+ context 'with reject!' do
165
+ should 'return the altered list and mark all items as changed from first removed to end' do
166
+ start_index = @values.size - 10
167
+ expected_values = @values.clone.reject! {|x| x >= @values[start_index]}
168
+ assert_equal(expected_values, @instance.reject! {|val| val >= @values[start_index]})
169
+ assert_equal (start_index..@values.size - 1).to_a, @instance.changed_members
170
+ end
171
+
172
+ should 'do nothing but return nil if nothing is deleted' do
173
+ assert_equal(nil, @instance.reject! {|v| false})
174
+ assert_equal [], @instance.changed_members
175
+ end
176
+ end
177
+
178
+ context 'with delete_if' do
179
+ should 'return the altered list and mark all items as changed from first removed to end' do
180
+ start = @values.size - 10
181
+ key = @values[start]
182
+ expected_values = @values.find_all {|x| x < key}
183
+ assert_equal(expected_values, @instance.delete_if {|val| val >= key})
184
+ assert_equal (start..@values.size-1).to_a, @instance.changed_members
185
+ end
186
+
187
+ should 'do nothing but return unchanged list if nothing is deleted' do
188
+ assert_equal(@values, @instance.delete_if {|val| false})
189
+ assert_equal [], @instance.changed_members
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,139 @@
1
+ require 'test_helper'
2
+
3
+ class CollectionHashTest < Test::Unit::TestCase
4
+ context 'A SimpleMapper::Collection::Hash' do
5
+ setup do
6
+ @class = SimpleMapper::Collection::Hash
7
+ end
8
+
9
+ should 'be equivalent to a hash passed to the constructor' do
10
+ hash = {:a => 'A', :b => 'B'}
11
+ instance = @class.new(hash)
12
+ assert_equal hash, instance
13
+ assert_equal @class, instance.class
14
+ end
15
+
16
+ should 'allow hash-style lookups, assignments, etc.' do
17
+ instance = @class.new
18
+ assert_equal nil, instance[:foo]
19
+ assert_equal :bar, instance[:foo] = :bar
20
+ assert_equal :bar, instance[:foo]
21
+ assert_equal true, instance.has_key?(:foo)
22
+ assert_equal false, instance.has_key?(:blah)
23
+ assert_equal :blee, instance[:boo] = :blee
24
+ assert_equal :blee, instance[:boo]
25
+ assert_equal([:boo, :foo], instance.keys.sort_by {|k| k.to_s})
26
+ end
27
+
28
+ should 'have an :attribute attribute' do
29
+ instance = @class.new
30
+ assert_equal nil, instance.attribute
31
+ attrib = stub('attribute_object')
32
+ assert_equal attrib, instance.attribute = attrib
33
+ assert_equal attrib, instance.attribute
34
+ end
35
+
36
+ should 'allow construction of new mapper instances through :build' do
37
+ mapper = stub('mapper')
38
+ attrib = stub('attribute_object', :mapper => mapper)
39
+ seq = sequence('build invocations')
40
+ mapper.expects(:new).with.in_sequence(seq).returns(:one)
41
+ mapper.expects(:new).with(:a => 'A').in_sequence(seq).returns(:two)
42
+ mapper.expects(:new).with(:foo, :bar, :a => 'A', :b => 'B').in_sequence(seq).returns(:three)
43
+ instance = @class.new
44
+ instance.attribute = attrib
45
+ result = []
46
+ result << instance.build
47
+ result << instance.build(:a => 'A')
48
+ result << instance.build(:foo, :bar, :a => 'A', :b => 'B')
49
+ assert_equal [:one, :two, :three], result
50
+ end
51
+
52
+ context 'tracking changes' do
53
+ should 'be off by default' do
54
+ instance = @class.new
55
+ assert_equal false, (instance.change_tracking || false)
56
+ end
57
+
58
+ context 'for assignment' do
59
+ should 'mark newly-assigned key as changed and mark entre value as changed if new value supports :all_changed' do
60
+ instance = @class.new
61
+ instance.change_tracking = true
62
+ assert_equal([], instance.changed_members)
63
+ instance[:a] = 'A'
64
+ assert_equal([:a], instance.changed_members)
65
+
66
+ change_me = stub('change_me')
67
+ change_me.expects(:all_changed!).with
68
+ instance[:b] = change_me
69
+ assert_equal([:a, :b], instance.changed_members)
70
+ end
71
+
72
+ should 'mark replaced key as changed' do
73
+ instance = @class.new({:a => 'A'})
74
+ instance.change_tracking = true
75
+ assert_equal([], instance.changed_members)
76
+ instance[:a] = 'Aprime'
77
+ assert_equal([:a], instance.changed_members)
78
+ end
79
+
80
+ should 'mark replaced key as changed and mark entire value as changed if new value supports :all_changed!' do
81
+ instance = @class.new({:a => 'A'})
82
+ instance.change_tracking = true
83
+ assert_equal([], instance.changed_members)
84
+ obj = stub('new value')
85
+ obj.expects(:all_changed!).with.returns(true)
86
+ instance[:a] = obj
87
+ assert_equal([:a], instance.changed_members)
88
+ end
89
+
90
+ should 'not track any changes on assignment if track_changes is false' do
91
+ instance = @class.new({:a => 'A', :b => 'B'})
92
+ instance.change_tracking = false
93
+ assert_equal([], instance.changed_members)
94
+ instance[:a] = 'A prime'
95
+ instance[:b] = nil
96
+ instance[:c] = 'C'
97
+ assert_equal([], instance.changed_members)
98
+ end
99
+ end
100
+
101
+ context 'for deletion' do
102
+ setup do
103
+ @key = :delete_me
104
+ @value = 'delete me!'
105
+ @keep_key = :keep_me
106
+ @keep_value = 'keep me!'
107
+ @instance = @class.new({@key => @value, @keep_key => @keep_value})
108
+ @instance.change_tracking = true
109
+ end
110
+
111
+ should 'mark key as changed when key is specified in a :delete' do
112
+ assert_equal @value, @instance.delete(@key)
113
+ assert_equal false, @instance.key?(@key)
114
+ assert_equal [@key], @instance.changed_members
115
+ end
116
+
117
+ should 'mark key as changed when key is removed via :delete_if' do
118
+ assert_equal({@keep_key => @keep_value}, @instance.delete_if {|k,v| k == @key})
119
+ assert_equal false, @instance.key?(@key)
120
+ assert_equal [@key], @instance.changed_members
121
+ end
122
+
123
+ should 'mark key as changed when key is removed via :reject!' do
124
+ assert_equal({@keep_key => @keep_value}, @instance.reject! {|k,v| k == @key})
125
+ assert_equal false, @instance.key?(@key)
126
+ assert_equal [@key], @instance.changed_members
127
+ end
128
+
129
+ should 'mark nothing as changed if change_tracking is false at delete time' do
130
+ @instance.change_tracking = false
131
+ @instance.delete(:a)
132
+ @instance.delete_if {|k, v| true}
133
+ assert_equal [], @instance.changed_members
134
+ end
135
+ end
136
+ end
137
+
138
+ end
139
+ end
@@ -0,0 +1,314 @@
1
+ require 'test_helper'
2
+ require 'date'
3
+
4
+ class AttributesTypesTest < Test::Unit::TestCase
5
+ context 'The Float type' do
6
+ setup do
7
+ @type = SimpleMapper::Attributes::Types::Float
8
+ end
9
+
10
+ should 'decode the empty string to nil' do
11
+ assert_equal nil, @type.decode('')
12
+ end
13
+
14
+ should 'decode simple integer strings' do
15
+ [1, 2, 3, 5, 7, 11, 13, 17, 21129].each do |num|
16
+ result = @type.decode(num.to_s)
17
+ assert_equal Float, result.class
18
+ assert_equal num.to_f, result
19
+ end
20
+ end
21
+
22
+ should 'decode actual integers' do
23
+ [1, 13, 21000, 4600].each do |num|
24
+ result = @type.decode(num)
25
+ assert_equal Float, result.class
26
+ assert_equal num.to_f, result
27
+ end
28
+ end
29
+
30
+ should 'decode valid floats' do
31
+ [1.31, 46.723, 0.00290004, 16.161616].each do |num|
32
+ result = @type.decode(num.to_s)
33
+ assert_equal Float, result.class
34
+ assert_equal num, result
35
+ end
36
+ end
37
+
38
+ should 'handle float with decimal point but not fractional portion' do
39
+ assert_equal 21.0, @type.decode('21.')
40
+ end
41
+
42
+ should 'throw a TypeConversionException given invalid decode input' do
43
+ ['total junk', :more_garbage, '12a32b23c', ' '].each do |value|
44
+ assert_raise(SimpleMapper::TypeConversionException) { @type.decode(value) }
45
+ end
46
+ end
47
+
48
+ should 'encode numeric inputs to a string' do
49
+ [1, 1.0, 0, 0.0, 21129, 46.723, 15.1515].each do |num|
50
+ assert_equal num.to_f.to_s, @type.encode(num)
51
+ end
52
+ end
53
+
54
+ should 'encode nil as nil' do
55
+ assert_equal nil, @type.encode(nil)
56
+ end
57
+
58
+ should 'throw a TypeConversionException given non-numeric inputs for encode' do
59
+ ['', :xyz, 'abc', ' 0 . 77asd '].each do |input|
60
+ assert_raise(SimpleMapper::TypeConversionException) { @type.encode(input) }
61
+ end
62
+ end
63
+
64
+ should 'provide a default of nil' do
65
+ assert_equal nil, @type.default
66
+ end
67
+
68
+ should 'be registered as :float' do
69
+ assert_equal({:name => :float,
70
+ :expected_type => Float,
71
+ :converter => @type,}, SimpleMapper::Attributes.type_for(:float))
72
+ end
73
+ end
74
+
75
+ context 'the String type' do
76
+ setup do
77
+ @type = SimpleMapper::Attributes::Types::String
78
+ end
79
+
80
+ should 'encode nil as nil' do
81
+ assert_equal nil, @type.encode(nil)
82
+ end
83
+
84
+ should 'decode nil as nil' do
85
+ assert_equal nil, @type.decode(nil)
86
+ end
87
+
88
+ context 'given input with to_s' do
89
+ setup do
90
+ @value = 'to_s result'
91
+ @input = mock('input')
92
+ @input.expects(:to_s).with.returns(@value)
93
+ end
94
+
95
+ should 'return result of :to_s on input for :encode' do
96
+ assert_equal @value, @type.encode(@input)
97
+ end
98
+
99
+ should 'return result of :to_s on input for :decode' do
100
+ assert_equal @value, @type.decode(@input)
101
+ end
102
+ end
103
+
104
+ should 'provide empty string for the default' do
105
+ assert_equal '', @type.default
106
+ end
107
+
108
+ should 'be registered as :string' do
109
+ assert_equal({:name => :string,
110
+ :expected_type => String,
111
+ :converter => @type,}, SimpleMapper::Attributes.type_for(:string))
112
+ end
113
+ end
114
+
115
+ context 'the SimpleUUID type' do
116
+ setup do
117
+ @type = SimpleMapper::Attributes::Types::SimpleUUID
118
+ @class = SimpleUUID::UUID
119
+ end
120
+
121
+ should 'decode and encode nil as nil' do
122
+ [:decode, :encode].each {|op| assert_equal nil, @type.send(op, nil)}
123
+ end
124
+
125
+ should 'return GUID string representation for :encode of actual UUID object' do
126
+ @uuid = @class.new
127
+ result = @type.encode(@uuid)
128
+ assert_equal String, result.class
129
+ assert_equal @uuid.to_guid, result
130
+ end
131
+
132
+ should 'return GUID string representation for :encode of a GUID-conforming string' do
133
+ @uuid = @class.new
134
+ result = @type.encode(@uuid.to_guid)
135
+ assert_equal String, result.class
136
+ assert_equal @uuid.to_guid, result
137
+ end
138
+
139
+ should 'parse string and return GUID-conforming string for :decode' do
140
+ @uuid = @class.new
141
+ assert_equal @uuid.to_guid, @type.decode(@uuid.to_guid)
142
+ end
143
+
144
+ should ':decode a SimpleUUID::UUID instance to a GUID string' do
145
+ @uuid = @class.new
146
+ assert_equal @uuid.to_guid, @type.decode(@uuid)
147
+ end
148
+
149
+ should 'a new SimpleUUID::UUID-based GUID string for the default' do
150
+ assert_equal String, (first = @type.default).class
151
+ assert_equal String, (second = @type.default).class
152
+ assert_not_equal first, second
153
+ assert_not_equal @class.new(first).to_guid, @class.new(second).to_guid
154
+ assert_equal first, @class.new(first).to_guid
155
+ assert_equal second, @class.new(second).to_guid
156
+ end
157
+
158
+ should 'be registered as :simple_uuid' do
159
+ assert_equal({:name => :simple_uuid,
160
+ :expected_type => nil,
161
+ :converter => @type}, SimpleMapper::Attributes.type_for(:simple_uuid))
162
+ end
163
+ end
164
+
165
+ context 'the Timestamp type' do
166
+ setup do
167
+ @type = SimpleMapper::Attributes::Types::Timestamp
168
+ @class = DateTime
169
+ @format = '%Y-%m-%d %H:%M:%S%z'
170
+ # get time with appropriate resolution
171
+ @now = @class.strptime(@class.now.strftime(@format), @format)
172
+ end
173
+
174
+ should ':decode a timestamp string with %Y-%m-%d %H:%M:%S%z' do
175
+ result = @type.decode(@now.strftime(@format))
176
+ assert_equal @now, result
177
+ end
178
+
179
+ should ':encode a timestamp as string with %Y-%m-%d %H:%M:%S%z structure' do
180
+ assert_equal @now.strftime(@format), @type.encode(@now)
181
+ end
182
+
183
+ should 'pass nil through untouched for :encode and :decode' do
184
+ assert_equal nil, @type.encode(nil)
185
+ assert_equal nil, @type.decode(nil)
186
+ end
187
+
188
+ should 'provide current date/time as default' do
189
+ # yes, if another process/thread preempts this one on a heavily-loaded server,
190
+ # there's a possibility that these won't match. I'm willing to live with that.
191
+ assert_equal @now.strftime(@format), (default = @type.default) && default.strftime(@format)
192
+ end
193
+
194
+ should ':decode a DateTime instance as identity (pass through)' do
195
+ assert_equal @now, @type.decode(@now)
196
+ end
197
+
198
+ should 'be registered as :timestamp type' do
199
+ assert_equal({:name => :timestamp,
200
+ :expected_type => @class,
201
+ :converter => @type}, SimpleMapper::Attributes.type_for(:timestamp))
202
+ end
203
+ end
204
+
205
+ context 'the Integer type' do
206
+ setup do
207
+ @type = SimpleMapper::Attributes::Types::Integer
208
+ @class = Integer
209
+ @ints = [0, 54, 32, 65535, -12, -65535]
210
+ end
211
+
212
+ should 'convert a numeric string into something string-like' do
213
+ @ints.each do |int|
214
+ assert_equal int, @type.decode(int.to_s)
215
+ assert_equal int.to_s, @type.encode(int.to_s)
216
+ end
217
+ end
218
+
219
+ should 'convert integer values as strings' do
220
+ @ints.each do |int|
221
+ assert_equal int, @type.decode(int)
222
+ assert_equal int.to_s, @type.encode(int)
223
+ end
224
+ end
225
+
226
+ should 'handle non-integer objects gracefully if they have integer values' do
227
+ [1.0, '1.0', -12.0, '-12.0', 1245.0, '1245.0', Rational(1,1), Rational(4500,4500)].each do |val|
228
+ assert_equal val.to_i, @type.decode(val)
229
+ assert_equal val.to_i, @type.encode(val).to_i
230
+ end
231
+ end
232
+
233
+ should 'throw type conversion exception if integer value does not appear to match input' do
234
+ ['abc', nil, 14.5, Rational(5,16)].each do |val|
235
+ assert_raise(SimpleMapper::TypeConversionException) { @type.encode(val) }
236
+ assert_raise(SimpleMapper::TypeConversionException) { @type.decode(val) }
237
+ end
238
+ end
239
+
240
+ should 'be registered as :integer type' do
241
+ assert_equal({:expected_type => @class,
242
+ :name => :integer,
243
+ :converter => @type}, SimpleMapper::Attributes.type_for(:integer))
244
+ end
245
+ end
246
+
247
+ context 'the TimestampHighRes type' do
248
+ require 'bigdecimal'
249
+ setup do
250
+ @type = SimpleMapper::Attributes::Types::TimestampHighRes
251
+ @class = DateTime
252
+ @name = :timestamp_high_res
253
+ # put the time in UTC, since this loses time zone info
254
+ @time = DateTime.now
255
+ @format = '%Y-%m-%d %H:%M:%S.%N%z'
256
+ @time_string = @time.strftime(@format)
257
+ @offsets = [0, 1, 2, 3, -1, -2, -3]
258
+ end
259
+
260
+ should 'pass along DateTimes when given DateTime to decode' do
261
+ @offsets.each do |offset|
262
+ assert_equal @time.new_offset(offset), @type.decode(@time.new_offset(offset))
263
+ end
264
+ end
265
+
266
+ should 'decode conformant strings into DateTimes' do
267
+ @offsets.each do |offset|
268
+ assert_equal @time.new_offset(offset).strftime(@format),
269
+ @type.decode(@time.new_offset(offset).strftime(@format)).strftime(@format)
270
+ end
271
+ end
272
+
273
+ # addresses bug in decoding from strings when fractional portion is smaller than .1 sec.
274
+ should 'decode conformat strings with small fractional portions into DateTimes' do
275
+ # get timestamp with no fractional seconds
276
+ time = DateTime.parse(DateTime.now.to_s)
277
+ # cycle through fractions: 0.111111111, 0.011111111, 0.001111111, etc.
278
+ checks = (9..18).collect do |factor|
279
+ sec_fraction = Rational(111111111, 10 ** factor)
280
+ fractional_time = time + (sec_fraction * Rational(1, 24 * 60 * 60))
281
+ fractional_time.strftime(@format)
282
+ end
283
+ results = checks.collect {|input| @type.decode(input).strftime(@format)}
284
+ assert_equal checks, results
285
+ end
286
+
287
+ should 'encode nil as nil' do
288
+ assert_equal true, @type.encode(nil).nil?
289
+ end
290
+
291
+ should 'decode nil as nil' do
292
+ assert_equal true, @type.decode(nil).nil?
293
+ end
294
+
295
+ should 'throw type conversion exceptions if strings do not conform' do
296
+ ['', 'abc', '7145.abc.234'].each do |string|
297
+ assert_raise(SimpleMapper::TypeConversionException) { @type.decode(string) }
298
+ end
299
+ end
300
+
301
+ should 'encode DateTime values into conformant strings' do
302
+ @offsets.each do |offset|
303
+ assert_equal @time.new_offset(offset).strftime(@format),
304
+ @type.encode(@time.new_offset(offset))
305
+ end
306
+ end
307
+
308
+ should 'be registered as the :timestamp_hi_res type' do
309
+ assert_equal({:expected_type => DateTime,
310
+ :name => @name,
311
+ :converter => @type}, SimpleMapper::Attributes.type_for(@name))
312
+ end
313
+ end
314
+ end