attributor 2.2.1 → 2.3.0
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +12 -4
- data/lib/attributor.rb +4 -2
- data/lib/attributor/attribute.rb +8 -9
- data/lib/attributor/exceptions.rb +6 -6
- data/lib/attributor/extensions/randexp.rb +5 -0
- data/lib/attributor/types/bigdecimal.rb +28 -0
- data/lib/attributor/types/boolean.rb +2 -0
- data/lib/attributor/types/collection.rb +6 -5
- data/lib/attributor/types/csv.rb +21 -0
- data/lib/attributor/types/date.rb +37 -0
- data/lib/attributor/types/date_time.rb +1 -1
- data/lib/attributor/types/hash.rb +64 -18
- data/lib/attributor/types/model.rb +14 -6
- data/lib/attributor/types/string.rb +1 -1
- data/lib/attributor/types/time.rb +39 -0
- data/lib/attributor/version.rb +1 -1
- data/spec/attribute_spec.rb +5 -12
- data/spec/support/hashes.rb +7 -0
- data/spec/support/models.rb +3 -1
- data/spec/types/bigdecimal_spec.rb +48 -0
- data/spec/types/boolean_spec.rb +4 -0
- data/spec/types/collection_spec.rb +12 -1
- data/spec/types/csv_spec.rb +31 -0
- data/spec/types/date_spec.rb +94 -0
- data/spec/types/date_time_spec.rb +21 -17
- data/spec/types/float_spec.rb +4 -0
- data/spec/types/hash_spec.rb +122 -11
- data/spec/types/ids_spec.rb +2 -2
- data/spec/types/integer_spec.rb +3 -0
- data/spec/types/model_spec.rb +16 -4
- data/spec/types/string_spec.rb +4 -0
- data/spec/types/time_spec.rb +91 -0
- metadata +12 -1
data/spec/types/float_spec.rb
CHANGED
data/spec/types/hash_spec.rb
CHANGED
@@ -52,6 +52,16 @@ describe Attributor::Hash do
|
|
52
52
|
let(:value) { {one: 'two', three: 4} }
|
53
53
|
subject(:hash) { type.load(value) }
|
54
54
|
|
55
|
+
context 'for nil with recurse: true' do
|
56
|
+
let(:value) { nil }
|
57
|
+
subject(:hash) { HashWithModel.load(value, recurse:true) }
|
58
|
+
|
59
|
+
it 'works' do
|
60
|
+
hash[:name].should eq('Turkey McDucken')
|
61
|
+
hash[:chicken].age.should eq(1)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
55
65
|
context 'for a simple hash' do
|
56
66
|
it { should eq(value) }
|
57
67
|
it 'equals the hash' do
|
@@ -259,17 +269,17 @@ describe Attributor::Hash do
|
|
259
269
|
|
260
270
|
it 'returns a hash with the dumped values and keys' do
|
261
271
|
subtype.should_receive(:dump).exactly(2).times.and_call_original
|
272
|
+
|
262
273
|
dumped_value = type.dump(value, opts)
|
263
274
|
dumped_value.should be_kind_of(::Hash)
|
264
|
-
dumped_value.keys.should =~ [
|
275
|
+
dumped_value.keys.should =~ ['id1','id2']
|
265
276
|
dumped_value.values.should have(2).items
|
266
|
-
|
267
|
-
|
268
|
-
dumped_value[:id1].should == value1
|
269
|
-
dumped_value[:id2].should == value2
|
277
|
+
dumped_value['id1'].should == value1
|
278
|
+
dumped_value['id2'].should == value2
|
270
279
|
end
|
271
280
|
|
272
281
|
end
|
282
|
+
|
273
283
|
end
|
274
284
|
|
275
285
|
context '#validate' do
|
@@ -390,21 +400,71 @@ describe Attributor::Hash do
|
|
390
400
|
end
|
391
401
|
let(:hash) { {one: value_type.example} }
|
392
402
|
|
393
|
-
it 'works too' do
|
394
|
-
#pp output
|
395
|
-
end
|
396
|
-
|
397
403
|
end
|
398
404
|
context 'will always return a top level hash' do
|
399
405
|
subject(:type_dump){ type.dump(value) }
|
400
406
|
let(:key_type){ Attributor::Object }
|
401
407
|
let(:value_type){ Attributor::Object }
|
402
|
-
|
408
|
+
|
403
409
|
it 'even when key/types are object' do
|
404
410
|
subject.should be_kind_of(::Hash)
|
405
411
|
subject.should eq( hash )
|
406
412
|
end
|
407
413
|
end
|
414
|
+
|
415
|
+
context 'for a hash with defined keys' do
|
416
|
+
let(:type) do
|
417
|
+
Class.new(Attributor::Hash) do
|
418
|
+
keys do
|
419
|
+
key 'id', Integer
|
420
|
+
key 'chicken', Chicken
|
421
|
+
end
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
let(:chicken) { {'name' => 'bob'} }
|
426
|
+
|
427
|
+
let(:value) { {'id' => '1', 'chicken' => chicken } }
|
428
|
+
let(:expected) { {'id' => 1, 'chicken' => Chicken.dump(chicken) } }
|
429
|
+
|
430
|
+
it 'properly dumps the values' do
|
431
|
+
type.dump(value).should eq(expected)
|
432
|
+
end
|
433
|
+
|
434
|
+
context 'with allow_extra: true' do
|
435
|
+
let(:type) do
|
436
|
+
Class.new(Attributor::Hash) do
|
437
|
+
keys allow_extra: true do
|
438
|
+
key 'id', Integer
|
439
|
+
key 'chicken', Chicken
|
440
|
+
end
|
441
|
+
end
|
442
|
+
end
|
443
|
+
|
444
|
+
let(:value) { {'id' => '1', 'chicken' => chicken, 'rank' => 'bob rank'} }
|
445
|
+
let(:expected) { {'id' => 1, 'chicken' => Chicken.dump(chicken), 'rank' => 'bob rank' } }
|
446
|
+
it 'preserves the extra keys at the top level' do
|
447
|
+
type.dump(value).should eq(expected)
|
448
|
+
end
|
449
|
+
|
450
|
+
context 'with extra option' do
|
451
|
+
let(:type) do
|
452
|
+
Class.new(Attributor::Hash) do
|
453
|
+
keys allow_extra: true do
|
454
|
+
key 'id', Integer
|
455
|
+
key 'chicken', Chicken
|
456
|
+
extra 'other', Attributor::Hash
|
457
|
+
end
|
458
|
+
end
|
459
|
+
end
|
460
|
+
|
461
|
+
let(:expected) { {'id' => 1, 'chicken' => Chicken.dump(chicken), 'other' => {'rank' => 'bob rank' }} }
|
462
|
+
it 'dumps the extra keys inside the subhash' do
|
463
|
+
type.dump(value).should eq(expected)
|
464
|
+
end
|
465
|
+
end
|
466
|
+
end
|
467
|
+
end
|
408
468
|
end
|
409
469
|
|
410
470
|
context 'with case_insensitive_load option for string keys' do
|
@@ -453,7 +513,7 @@ describe Attributor::Hash do
|
|
453
513
|
output[:two].should eq('two')
|
454
514
|
output[:three].should eq('3')
|
455
515
|
end
|
456
|
-
|
516
|
+
|
457
517
|
its( :validate ){ should be_empty }
|
458
518
|
end
|
459
519
|
|
@@ -486,6 +546,57 @@ describe Attributor::Hash do
|
|
486
546
|
end
|
487
547
|
end
|
488
548
|
|
549
|
+
context '#get and #set' do
|
550
|
+
let(:type) do
|
551
|
+
Class.new(Attributor::Hash) do
|
552
|
+
keys do
|
553
|
+
key 'id', Integer
|
554
|
+
key 'chicken', Chicken
|
555
|
+
extra 'others', Attributor::Hash, default: {}
|
556
|
+
end
|
557
|
+
end
|
558
|
+
end
|
559
|
+
|
560
|
+
let(:chicken) { {name: 'bob'} }
|
561
|
+
subject(:hash) { type.new }
|
562
|
+
|
563
|
+
context '#set' do
|
564
|
+
it 'sets values into "extra" keys if appplicable' do
|
565
|
+
hash.should_not have_key('others')
|
566
|
+
hash.set 'foo', 'bar'
|
567
|
+
hash['others'].should have_key('foo')
|
568
|
+
hash.should have_key('others')
|
569
|
+
hash['others']['foo'].should eq('bar')
|
570
|
+
end
|
571
|
+
|
572
|
+
it 'loads values before saving into the contents' do
|
573
|
+
hash.set 'chicken', chicken
|
574
|
+
hash['chicken'].should be_a(Chicken)
|
575
|
+
end
|
576
|
+
end
|
577
|
+
|
578
|
+
context '#get' do
|
579
|
+
before do
|
580
|
+
hash['chicken'] = chicken
|
581
|
+
hash['chicken'].should eq(chicken)
|
582
|
+
end
|
583
|
+
|
584
|
+
it 'loads and updates the saved value' do
|
585
|
+
hash.get('chicken').should be_a(Chicken)
|
586
|
+
hash['chicken'].should be_a(Chicken)
|
587
|
+
end
|
588
|
+
|
589
|
+
it 'retrieves values from an "extra" key' do
|
590
|
+
bar = double('bar')
|
591
|
+
hash.set 'foo', bar
|
592
|
+
hash.get('others').get('foo').should be(bar)
|
593
|
+
|
594
|
+
hash.get('foo').should be(bar)
|
595
|
+
end
|
596
|
+
end
|
597
|
+
|
598
|
+
end
|
599
|
+
|
489
600
|
end
|
490
601
|
|
491
602
|
end
|
data/spec/types/ids_spec.rb
CHANGED
@@ -16,8 +16,8 @@ describe Attributor::Ids do
|
|
16
16
|
ids.load(value).should eq(emails)
|
17
17
|
end
|
18
18
|
|
19
|
-
it 'generates valid examples' do
|
20
|
-
ids.validate(ids.example).should be_empty
|
19
|
+
it 'generates valid, loadable examples' do
|
20
|
+
ids.validate(ids.load(ids.example)).should be_empty
|
21
21
|
end
|
22
22
|
|
23
23
|
end
|
data/spec/types/integer_spec.rb
CHANGED
data/spec/types/model_spec.rb
CHANGED
@@ -66,7 +66,7 @@ describe Attributor::Model do
|
|
66
66
|
context 'with infinitely-expanding sub-attributes' do
|
67
67
|
let(:model_class) do
|
68
68
|
Class.new(Attributor::Model) do
|
69
|
-
this = self
|
69
|
+
this = self
|
70
70
|
attributes do
|
71
71
|
attribute :name, String
|
72
72
|
attribute :child, this
|
@@ -120,6 +120,16 @@ describe Attributor::Model do
|
|
120
120
|
it 'returns nil' do
|
121
121
|
Chicken.load(nil).should be_nil
|
122
122
|
end
|
123
|
+
|
124
|
+
context 'with recurse: true' do
|
125
|
+
subject(:turducken) { Turducken.load(nil, [], recurse: true) }
|
126
|
+
|
127
|
+
it 'loads with default values' do
|
128
|
+
turducken.name.should eq("Turkey McDucken")
|
129
|
+
turducken.chicken.age.should be(1)
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
123
133
|
end
|
124
134
|
|
125
135
|
context 'with a JSON-serialized hash' do
|
@@ -128,7 +138,7 @@ describe Attributor::Model do
|
|
128
138
|
let(:json) { hash.to_json }
|
129
139
|
before do
|
130
140
|
Chicken.should_receive(:from_hash).
|
131
|
-
with(expected_hash,context)
|
141
|
+
with(expected_hash,context, recurse: false)
|
132
142
|
JSON.should_receive(:parse).with(json).and_call_original
|
133
143
|
end
|
134
144
|
|
@@ -186,6 +196,7 @@ describe Attributor::Model do
|
|
186
196
|
end
|
187
197
|
|
188
198
|
|
199
|
+
|
189
200
|
end
|
190
201
|
|
191
202
|
end
|
@@ -208,7 +219,7 @@ describe Attributor::Model do
|
|
208
219
|
it 'and sets them in loaded format onto the instance attributes' do
|
209
220
|
Chicken.should_receive(:load).with(attributes_data).and_call_original
|
210
221
|
attributes_data.keys.each do |attr_name|
|
211
|
-
Chicken.attributes[attr_name].should_receive(:load).with(attributes_data[attr_name],instance_of(Array)).and_call_original
|
222
|
+
Chicken.attributes[attr_name].should_receive(:load).with(attributes_data[attr_name],instance_of(Array), recurse: false).and_call_original
|
212
223
|
end
|
213
224
|
subject.age.should be(1)
|
214
225
|
subject.email.should be(attributes_data[:email])
|
@@ -220,7 +231,7 @@ describe Attributor::Model do
|
|
220
231
|
it 'and sets them in loaded format onto the instance attributes' do
|
221
232
|
Chicken.should_receive(:load).with(attributes_data).and_call_original
|
222
233
|
attributes_hash.keys.each do |attr_name|
|
223
|
-
Chicken.attributes[attr_name].should_receive(:load).with(attributes_hash[attr_name],instance_of(Array)).and_call_original
|
234
|
+
Chicken.attributes[attr_name].should_receive(:load).with(attributes_hash[attr_name],instance_of(Array), recurse: false).and_call_original
|
224
235
|
end
|
225
236
|
subject.age.should be(1)
|
226
237
|
subject.email.should == attributes_hash[:email]
|
@@ -398,4 +409,5 @@ describe Attributor::Model do
|
|
398
409
|
end
|
399
410
|
|
400
411
|
|
412
|
+
|
401
413
|
end
|
data/spec/types/string_spec.rb
CHANGED
@@ -0,0 +1,91 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb')
|
2
|
+
|
3
|
+
describe Attributor::Time do
|
4
|
+
|
5
|
+
subject(:type) { Attributor::Time }
|
6
|
+
|
7
|
+
context '.native_type' do
|
8
|
+
its(:native_type) { should be(::Time) }
|
9
|
+
end
|
10
|
+
|
11
|
+
context '.example' do
|
12
|
+
its(:example) { should be_a(::Time) }
|
13
|
+
end
|
14
|
+
|
15
|
+
context '.load' do
|
16
|
+
|
17
|
+
it 'returns nil for nil' do
|
18
|
+
type.load(nil).should be(nil)
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'for incoming objects' do
|
22
|
+
|
23
|
+
it "returns correct Time for DateTime objects" do
|
24
|
+
object = Time.now
|
25
|
+
loaded = type.load(object)
|
26
|
+
loaded.should be_a(::Time)
|
27
|
+
loaded.to_time.should == object
|
28
|
+
end
|
29
|
+
|
30
|
+
it "returns correct Time for DateTime objects" do
|
31
|
+
object = DateTime.now
|
32
|
+
loaded = type.load(object)
|
33
|
+
loaded.should be_a(::Time)
|
34
|
+
loaded.should eq(object.to_time)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'for incoming strings' do
|
40
|
+
|
41
|
+
[
|
42
|
+
'2001-02-03T04:05:06+07:00',
|
43
|
+
'Sat, 03 Feb 2001 04:05:06 GMT',
|
44
|
+
'20010203T040506+0700',
|
45
|
+
'2001-W05-6T04:05:06+07:00',
|
46
|
+
'H13.02.03T04:05:06+07:00',
|
47
|
+
'Sat, 3 Feb 2001 04:05:06 +0700',
|
48
|
+
'2013/08/23 00:39:55 +0000',
|
49
|
+
'2007-10-19T04:11:33Z',
|
50
|
+
'2001-02-03T04:05:06+07:00.123456', # custom format with microseconds
|
51
|
+
].each do |value|
|
52
|
+
|
53
|
+
it "returns correct Time for #{value.inspect}" do
|
54
|
+
type.load(value).should == Time.parse(value)
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
[
|
60
|
+
'2013/08/33 00:39:55 +0000',
|
61
|
+
'2007-10-33T04:11:33Z',
|
62
|
+
'2001-02-33T04:05:06+07:00.123456', # custom format with microseconds
|
63
|
+
].each do |value|
|
64
|
+
|
65
|
+
it "raises Attributor::AttributorException for #{value.inspect}" do
|
66
|
+
expect {
|
67
|
+
type.load(value)
|
68
|
+
}.to raise_error(Attributor::DeserializationError, /Error deserializing a String using Time/)
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
[
|
74
|
+
'',
|
75
|
+
'foobar'
|
76
|
+
].each do |value|
|
77
|
+
|
78
|
+
it "raises Attributor::AttributorException for #{value.inspect}" do
|
79
|
+
expect {
|
80
|
+
type.load(value)
|
81
|
+
}.to raise_error(Attributor::DeserializationError, /Error deserializing a String using Time/)
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: attributor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Josep M. Blanquer
|
@@ -260,10 +260,12 @@ files:
|
|
260
260
|
- lib/attributor/exceptions.rb
|
261
261
|
- lib/attributor/extensions/randexp.rb
|
262
262
|
- lib/attributor/type.rb
|
263
|
+
- lib/attributor/types/bigdecimal.rb
|
263
264
|
- lib/attributor/types/boolean.rb
|
264
265
|
- lib/attributor/types/collection.rb
|
265
266
|
- lib/attributor/types/container.rb
|
266
267
|
- lib/attributor/types/csv.rb
|
268
|
+
- lib/attributor/types/date.rb
|
267
269
|
- lib/attributor/types/date_time.rb
|
268
270
|
- lib/attributor/types/file_upload.rb
|
269
271
|
- lib/attributor/types/float.rb
|
@@ -275,19 +277,23 @@ files:
|
|
275
277
|
- lib/attributor/types/string.rb
|
276
278
|
- lib/attributor/types/struct.rb
|
277
279
|
- lib/attributor/types/tempfile.rb
|
280
|
+
- lib/attributor/types/time.rb
|
278
281
|
- lib/attributor/version.rb
|
279
282
|
- spec/attribute_resolver_spec.rb
|
280
283
|
- spec/attribute_spec.rb
|
281
284
|
- spec/attributor_spec.rb
|
282
285
|
- spec/dsl_compiler_spec.rb
|
283
286
|
- spec/spec_helper.rb
|
287
|
+
- spec/support/hashes.rb
|
284
288
|
- spec/support/models.rb
|
285
289
|
- spec/support/types.rb
|
286
290
|
- spec/type_spec.rb
|
291
|
+
- spec/types/bigdecimal_spec.rb
|
287
292
|
- spec/types/boolean_spec.rb
|
288
293
|
- spec/types/collection_spec.rb
|
289
294
|
- spec/types/container_spec.rb
|
290
295
|
- spec/types/csv_spec.rb
|
296
|
+
- spec/types/date_spec.rb
|
291
297
|
- spec/types/date_time_spec.rb
|
292
298
|
- spec/types/file_upload_spec.rb
|
293
299
|
- spec/types/float_spec.rb
|
@@ -298,6 +304,7 @@ files:
|
|
298
304
|
- spec/types/string_spec.rb
|
299
305
|
- spec/types/struct_spec.rb
|
300
306
|
- spec/types/tempfile_spec.rb
|
307
|
+
- spec/types/time_spec.rb
|
301
308
|
homepage: https://github.com/rightscale/attributor
|
302
309
|
licenses:
|
303
310
|
- MIT
|
@@ -328,13 +335,16 @@ test_files:
|
|
328
335
|
- spec/attributor_spec.rb
|
329
336
|
- spec/dsl_compiler_spec.rb
|
330
337
|
- spec/spec_helper.rb
|
338
|
+
- spec/support/hashes.rb
|
331
339
|
- spec/support/models.rb
|
332
340
|
- spec/support/types.rb
|
333
341
|
- spec/type_spec.rb
|
342
|
+
- spec/types/bigdecimal_spec.rb
|
334
343
|
- spec/types/boolean_spec.rb
|
335
344
|
- spec/types/collection_spec.rb
|
336
345
|
- spec/types/container_spec.rb
|
337
346
|
- spec/types/csv_spec.rb
|
347
|
+
- spec/types/date_spec.rb
|
338
348
|
- spec/types/date_time_spec.rb
|
339
349
|
- spec/types/file_upload_spec.rb
|
340
350
|
- spec/types/float_spec.rb
|
@@ -345,4 +355,5 @@ test_files:
|
|
345
355
|
- spec/types/string_spec.rb
|
346
356
|
- spec/types/struct_spec.rb
|
347
357
|
- spec/types/tempfile_spec.rb
|
358
|
+
- spec/types/time_spec.rb
|
348
359
|
has_rdoc:
|