attributor 5.0.2 → 5.1.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.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +30 -0
  3. data/.travis.yml +6 -4
  4. data/CHANGELOG.md +6 -1
  5. data/Gemfile +1 -1
  6. data/Guardfile +14 -8
  7. data/Rakefile +4 -5
  8. data/attributor.gemspec +34 -29
  9. data/lib/attributor.rb +23 -29
  10. data/lib/attributor/attribute.rb +108 -127
  11. data/lib/attributor/attribute_resolver.rb +12 -26
  12. data/lib/attributor/dsl_compiler.rb +17 -21
  13. data/lib/attributor/dumpable.rb +1 -2
  14. data/lib/attributor/example_mixin.rb +5 -8
  15. data/lib/attributor/exceptions.rb +5 -6
  16. data/lib/attributor/extensions/randexp.rb +3 -5
  17. data/lib/attributor/extras/field_selector.rb +4 -4
  18. data/lib/attributor/extras/field_selector/transformer.rb +6 -7
  19. data/lib/attributor/families/numeric.rb +0 -2
  20. data/lib/attributor/families/temporal.rb +1 -4
  21. data/lib/attributor/hash_dsl_compiler.rb +22 -25
  22. data/lib/attributor/type.rb +24 -32
  23. data/lib/attributor/types/bigdecimal.rb +7 -14
  24. data/lib/attributor/types/boolean.rb +5 -8
  25. data/lib/attributor/types/class.rb +9 -10
  26. data/lib/attributor/types/collection.rb +34 -44
  27. data/lib/attributor/types/container.rb +9 -15
  28. data/lib/attributor/types/csv.rb +7 -10
  29. data/lib/attributor/types/date.rb +20 -25
  30. data/lib/attributor/types/date_time.rb +7 -14
  31. data/lib/attributor/types/float.rb +4 -6
  32. data/lib/attributor/types/hash.rb +171 -196
  33. data/lib/attributor/types/ids.rb +2 -6
  34. data/lib/attributor/types/integer.rb +12 -17
  35. data/lib/attributor/types/model.rb +39 -48
  36. data/lib/attributor/types/object.rb +2 -4
  37. data/lib/attributor/types/polymorphic.rb +118 -0
  38. data/lib/attributor/types/regexp.rb +4 -5
  39. data/lib/attributor/types/string.rb +6 -7
  40. data/lib/attributor/types/struct.rb +8 -15
  41. data/lib/attributor/types/symbol.rb +3 -6
  42. data/lib/attributor/types/tempfile.rb +5 -6
  43. data/lib/attributor/types/time.rb +11 -11
  44. data/lib/attributor/types/uri.rb +9 -10
  45. data/lib/attributor/version.rb +1 -1
  46. data/spec/attribute_resolver_spec.rb +57 -78
  47. data/spec/attribute_spec.rb +174 -216
  48. data/spec/attributor_spec.rb +11 -15
  49. data/spec/dsl_compiler_spec.rb +19 -33
  50. data/spec/dumpable_spec.rb +6 -7
  51. data/spec/extras/field_selector/field_selector_spec.rb +1 -1
  52. data/spec/families_spec.rb +1 -3
  53. data/spec/hash_dsl_compiler_spec.rb +65 -74
  54. data/spec/spec_helper.rb +9 -3
  55. data/spec/support/hashes.rb +2 -3
  56. data/spec/support/models.rb +30 -36
  57. data/spec/support/polymorphics.rb +10 -0
  58. data/spec/type_spec.rb +38 -61
  59. data/spec/types/bigdecimal_spec.rb +11 -15
  60. data/spec/types/boolean_spec.rb +12 -39
  61. data/spec/types/class_spec.rb +10 -11
  62. data/spec/types/collection_spec.rb +72 -81
  63. data/spec/types/container_spec.rb +22 -26
  64. data/spec/types/csv_spec.rb +15 -16
  65. data/spec/types/date_spec.rb +16 -33
  66. data/spec/types/date_time_spec.rb +16 -33
  67. data/spec/types/file_upload_spec.rb +1 -2
  68. data/spec/types/float_spec.rb +7 -14
  69. data/spec/types/hash_spec.rb +285 -289
  70. data/spec/types/ids_spec.rb +5 -7
  71. data/spec/types/integer_spec.rb +37 -46
  72. data/spec/types/model_spec.rb +111 -128
  73. data/spec/types/polymorphic_spec.rb +134 -0
  74. data/spec/types/regexp_spec.rb +4 -7
  75. data/spec/types/string_spec.rb +17 -21
  76. data/spec/types/struct_spec.rb +40 -47
  77. data/spec/types/tempfile_spec.rb +1 -2
  78. data/spec/types/temporal_spec.rb +9 -0
  79. data/spec/types/time_spec.rb +16 -32
  80. data/spec/types/type_spec.rb +15 -0
  81. data/spec/types/uri_spec.rb +6 -7
  82. metadata +77 -25
@@ -1,49 +1,45 @@
1
1
  require File.join(File.dirname(__FILE__), '../spec_helper.rb')
2
2
 
3
-
4
3
  describe Attributor::Container do
5
-
6
4
  context '.decode_json' do
7
-
8
5
  let(:mock_type) do
9
6
  Class.new do
10
7
  include Attributor::Container
11
8
  def self.native_type
12
- ::Hash
9
+ ::Hash
13
10
  end
14
11
  end
15
- end
12
+ end
16
13
  context 'for valid JSON strings' do
17
- it "parses JSON string into the native type" do
18
- a_hash = {"a" => 1, "b" => 2}
19
- json_hash = JSON.dump( a_hash )
20
- mock_type.decode_json(json_hash).should == a_hash
14
+ it 'parses JSON string into the native type' do
15
+ a_hash = { 'a' => 1, 'b' => 2 }
16
+ json_hash = JSON.dump(a_hash)
17
+ expect(mock_type.decode_json(json_hash)).to eq a_hash
21
18
  end
22
19
  it 'complains when trying to decode a non-String value' do
23
- expect{
20
+ expect do
24
21
  mock_type.decode_json(Object.new)
25
- }.to raise_error(Attributor::DeserializationError, /Error deserializing a Object using JSON/)
22
+ end.to raise_error(Attributor::DeserializationError, /Error deserializing a Object using JSON/)
26
23
  end
27
-
24
+
28
25
  it 'complains when the deserialized value is not of the native_type' do
29
- expect{
30
- mock_type.decode_json("[1,2,3]")
31
- }.to raise_error(Attributor::CoercionError, /Error coercing from Array/)
26
+ expect do
27
+ mock_type.decode_json('[1,2,3]')
28
+ end.to raise_error(Attributor::CoercionError, /Error coercing from Array/)
32
29
  end
33
-
30
+
34
31
  it 'complains if there is a error deserializing the string' do
35
- expect{
36
- mock_type.decode_json("{invalid_json}")
37
- }.to raise_error(Attributor::DeserializationError, /Error deserializing a String using JSON/)
32
+ expect do
33
+ mock_type.decode_json('{invalid_json}')
34
+ end.to raise_error(Attributor::DeserializationError, /Error deserializing a String using JSON/)
38
35
  end
39
-
36
+
40
37
  it 'uses the passed context in the output error' do
41
- expect{
42
- mock_type.decode_json("{invalid_json}",["my_context","attribute_name"])
43
- }.to raise_error(Attributor::DeserializationError, /Error deserializing a String using JSON.* while loading my_context.attribute_name/)
38
+ expect do
39
+ mock_type.decode_json('{invalid_json}', %w(my_context attribute_name))
40
+ end.to raise_error(Attributor::DeserializationError,
41
+ /Error deserializing a String using JSON.* while loading my_context.attribute_name/)
44
42
  end
45
-
46
43
  end
47
44
  end
48
-
49
- end
45
+ end
@@ -1,7 +1,6 @@
1
1
  require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb')
2
2
 
3
3
  describe Attributor::CSV do
4
-
5
4
  subject!(:csv) { Attributor::CSV.of(Integer) }
6
5
 
7
6
  context '.load' do
@@ -9,7 +8,7 @@ describe Attributor::CSV do
9
8
  let!(:value) { array.join(',') }
10
9
 
11
10
  it 'parses the value and returns an array with the right types' do
12
- csv.load(value).should =~ array
11
+ expect(csv.load(value)).to match_array array
13
12
  end
14
13
  end
15
14
 
@@ -18,46 +17,46 @@ describe Attributor::CSV do
18
17
  let!(:loaded_example) { csv.load(example) }
19
18
 
20
19
  it 'generates a String example' do
21
- example.should be_a(String)
20
+ expect(example).to be_a(String)
22
21
  end
23
22
 
24
23
  it 'generates a comma-separated list of Integer values' do
25
- loaded_example.should be_a(csv)
26
- loaded_example.size.should be > 1
27
- loaded_example.each { |e| e.should be_a(Integer) }
24
+ expect(loaded_example).to be_a(csv)
25
+ expect(loaded_example.size).to be > 1
26
+ loaded_example.each { |e| expect(e).to be_a(Integer) }
28
27
  end
29
28
  end
30
29
 
31
30
  context '.dump' do
32
31
  let!(:int_vals) { [1, 2, 3] }
33
- let!(:str_vals) { (0..2).collect { /\w+/.gen} }
32
+ let!(:str_vals) { (0..2).collect { /\w+/.gen } }
34
33
 
35
34
  it 'dumps a String value' do
36
- csv.dump(int_vals).should be_a(String)
35
+ expect(csv.dump(int_vals)).to be_a(String)
37
36
  end
38
37
 
39
38
  it 'dumps a comma-separated list of Integers' do
40
- csv.dump(int_vals).should eq(int_vals.join(','))
39
+ expect(csv.dump(int_vals)).to eq(int_vals.join(','))
41
40
  end
42
41
 
43
42
  it 'dumps non-Integer values also' do
44
- csv.dump(str_vals).should eq(str_vals.join(','))
43
+ expect(csv.dump(str_vals)).to eq(str_vals.join(','))
45
44
  end
46
45
 
47
46
  it 'dumps nil values as nil' do
48
- csv.dump(nil).should eq(nil)
47
+ expect(csv.dump(nil)).to eq(nil)
49
48
  end
50
49
  end
51
50
 
52
51
  context '.describe' do
53
- let(:example){ csv.example }
54
- subject(:described){ csv.describe(example: example)}
52
+ let(:example) { csv.example }
53
+ subject(:described) { csv.describe(example: example) }
55
54
  it 'adds a string example if an example is passed' do
56
- described.should have_key(:example)
57
- described[:example].should eq(csv.dump(example))
55
+ expect(described).to have_key(:example)
56
+ expect(described[:example]).to eq(csv.dump(example))
58
57
  end
59
58
  it 'ensures no member_attribute key exists from underlying Collection' do
60
- described.should_not have_key(:member_attribute)
59
+ expect(described).not_to have_key(:member_attribute)
61
60
  end
62
61
  end
63
62
  end
@@ -1,11 +1,10 @@
1
1
  require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb')
2
2
 
3
3
  describe Attributor::Date do
4
-
5
4
  subject(:type) { Attributor::Date }
6
5
 
7
6
  it 'it is not Dumpable' do
8
- type.new.is_a?(Attributor::Dumpable).should_not be(true)
7
+ expect(type.new.is_a?(Attributor::Dumpable)).not_to be(true)
9
8
  end
10
9
 
11
10
  context '.native_type' do
@@ -17,45 +16,40 @@ describe Attributor::Date do
17
16
  end
18
17
 
19
18
  context '.dump' do
20
- let(:example) { type.example}
19
+ let(:example) { type.example }
21
20
  subject(:value) { type.dump(example) }
22
21
  it 'is formatted correctly' do
23
- value.should match(/\d{4}-\d{2}-\d{2}T00:00:00\+00:00/)
22
+ expect(value).to match(/\d{4}-\d{2}-\d{2}T00:00:00\+00:00/)
24
23
  end
25
24
  context 'nil values' do
26
25
  it 'should be nil' do
27
- type.dump(nil).should be_nil
26
+ expect(type.dump(nil)).to be_nil
28
27
  end
29
28
  end
30
29
  end
31
30
 
32
-
33
31
  context '.load' do
34
-
35
32
  it 'returns nil for nil' do
36
- type.load(nil).should be(nil)
33
+ expect(type.load(nil)).to be(nil)
37
34
  end
38
35
 
39
36
  context 'for incoming objects' do
40
-
41
- it "returns correct Date for Time objects" do
37
+ it 'returns correct Date for Time objects' do
42
38
  object = Time.now
43
39
  loaded = type.load(object)
44
- loaded.should be_a(::Date)
45
- loaded.to_date.should == object.to_date
40
+ expect(loaded).to be_a(::Date)
41
+ expect(loaded.to_date).to eq object.to_date
46
42
  end
47
43
 
48
- it "returns correct Date for DateTime objects" do
44
+ it 'returns correct Date for DateTime objects' do
49
45
  object = DateTime.now
50
46
  loaded = type.load(object)
51
- loaded.should be_a(::Date)
52
- loaded.should be(object)
47
+ expect(loaded).to be_a(::Date)
48
+ expect(loaded).to be(object)
53
49
  end
54
-
55
50
  end
56
51
 
57
52
  context 'for incoming strings' do
58
-
59
53
  [
60
54
  '2001-02-03T04:05:06+07:00',
61
55
  'Sat, 03 Feb 2001 04:05:06 GMT',
@@ -67,11 +61,9 @@ describe Attributor::Date do
67
61
  '2007-10-19T04:11:33Z',
68
62
  '2001-02-03T04:05:06+07:00.123456', # custom format with microseconds
69
63
  ].each do |value|
70
-
71
64
  it "returns correct Date for #{value.inspect}" do
72
- type.load(value).should == Date.parse(value)
65
+ expect(type.load(value)).to eq Date.parse(value)
73
66
  end
74
-
75
67
  end
76
68
 
77
69
  [
@@ -80,13 +72,11 @@ describe Attributor::Date do
80
72
  '2007-10-33T04:11:33Z',
81
73
  '2001-02-33T04:05:06+07:00.123456', # custom format with microseconds
82
74
  ].each do |value|
83
-
84
75
  it "raises Attributor::AttributorException for #{value.inspect}" do
85
- expect {
76
+ expect do
86
77
  type.load(value)
87
- }.to raise_error(Attributor::DeserializationError, /Error deserializing a String using Date/)
78
+ end.to raise_error(Attributor::DeserializationError, /Error deserializing a String using Date/)
88
79
  end
89
-
90
80
  end
91
81
 
92
82
  [
@@ -94,19 +84,12 @@ describe Attributor::Date do
94
84
  'foobar',
95
85
  'Sat, 30 Feb 2001 04:05:06 FOOBAR', # No such date format exists
96
86
  ].each do |value|
97
-
98
87
  it "raises Attributor::AttributorException for #{value.inspect}" do
99
- expect {
88
+ expect do
100
89
  type.load(value)
101
- }.to raise_error(Attributor::DeserializationError, /Error deserializing a String using Date/)
90
+ end.to raise_error(Attributor::DeserializationError, /Error deserializing a String using Date/)
102
91
  end
103
-
104
92
  end
105
-
106
93
  end
107
-
108
94
  end
109
-
110
95
  end
111
-
112
-
@@ -1,11 +1,10 @@
1
1
  require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb')
2
2
 
3
3
  describe Attributor::DateTime do
4
-
5
4
  subject(:type) { Attributor::DateTime }
6
5
 
7
6
  it 'it is not Dumpable' do
8
- type.new.is_a?(Attributor::Dumpable).should_not be(true)
7
+ expect(type.new.is_a?(Attributor::Dumpable)).not_to be(true)
9
8
  end
10
9
 
11
10
  context '.native_type' do
@@ -17,45 +16,40 @@ describe Attributor::DateTime do
17
16
  end
18
17
 
19
18
  context '.dump' do
20
- let(:example) { type.example}
19
+ let(:example) { type.example }
21
20
  subject(:value) { type.dump(example) }
22
21
  it 'is formatted correctly' do
23
- value.should match(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[\+-]\d{2}:\d{2}/)
22
+ expect(value).to match(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[\+-]\d{2}:\d{2}/)
24
23
  end
25
24
  context 'nil values' do
26
25
  it 'should be nil' do
27
- type.dump(nil).should be_nil
26
+ expect(type.dump(nil)).to be_nil
28
27
  end
29
28
  end
30
29
  end
31
30
 
32
-
33
31
  context '.load' do
34
-
35
32
  it 'returns nil for nil' do
36
- type.load(nil).should be(nil)
33
+ expect(type.load(nil)).to be(nil)
37
34
  end
38
35
 
39
36
  context 'for incoming objects' do
40
-
41
- it "returns correct DateTime for Time objects" do
37
+ it 'returns correct DateTime for Time objects' do
42
38
  object = Time.now
43
39
  loaded = type.load(object)
44
- loaded.should be_a(::DateTime)
45
- loaded.to_time.should == object
40
+ expect(loaded).to be_a(::DateTime)
41
+ expect(loaded.to_time).to eq object
46
42
  end
47
43
 
48
- it "returns correct DateTime for DateTime objects" do
44
+ it 'returns correct DateTime for DateTime objects' do
49
45
  object = DateTime.now
50
46
  loaded = type.load(object)
51
- loaded.should be_a(::DateTime)
52
- loaded.should be( object )
47
+ expect(loaded).to be_a(::DateTime)
48
+ expect(loaded).to be(object)
53
49
  end
54
-
55
50
  end
56
51
 
57
52
  context 'for incoming strings' do
58
-
59
53
  [
60
54
  '2001-02-03T04:05:06+07:00',
61
55
  'Sat, 03 Feb 2001 04:05:06 GMT',
@@ -67,11 +61,9 @@ describe Attributor::DateTime do
67
61
  '2007-10-19T04:11:33Z',
68
62
  '2001-02-03T04:05:06+07:00.123456', # custom format with microseconds
69
63
  ].each do |value|
70
-
71
64
  it "returns correct DateTime for #{value.inspect}" do
72
- type.load(value).should == DateTime.parse(value)
65
+ expect(type.load(value)).to eq DateTime.parse(value)
73
66
  end
74
-
75
67
  end
76
68
 
77
69
  [
@@ -80,13 +72,11 @@ describe Attributor::DateTime do
80
72
  '2007-10-33T04:11:33Z',
81
73
  '2001-02-33T04:05:06+07:00.123456', # custom format with microseconds
82
74
  ].each do |value|
83
-
84
75
  it "raises Attributor::AttributorException for #{value.inspect}" do
85
- expect {
76
+ expect do
86
77
  type.load(value)
87
- }.to raise_error(Attributor::DeserializationError, /Error deserializing a String using DateTime/)
78
+ end.to raise_error(Attributor::DeserializationError, /Error deserializing a String using DateTime/)
88
79
  end
89
-
90
80
  end
91
81
 
92
82
  [
@@ -94,19 +84,12 @@ describe Attributor::DateTime do
94
84
  'foobar',
95
85
  'Sat, 30 Feb 2001 04:05:06 FOOBAR', # No such date format exists
96
86
  ].each do |value|
97
-
98
87
  it "raises Attributor::AttributorException for #{value.inspect}" do
99
- expect {
88
+ expect do
100
89
  type.load(value)
101
- }.to raise_error(Attributor::DeserializationError, /Error deserializing a String using DateTime/)
90
+ end.to raise_error(Attributor::DeserializationError, /Error deserializing a String using DateTime/)
102
91
  end
103
-
104
92
  end
105
-
106
93
  end
107
-
108
94
  end
109
-
110
95
  end
111
-
112
-
@@ -1,6 +1,5 @@
1
1
  require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb')
2
2
 
3
-
4
3
  describe Attributor::FileUpload do
5
4
  it 'has specs'
6
- end
5
+ end
@@ -1,11 +1,10 @@
1
1
  require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb')
2
2
 
3
3
  describe Attributor::Float do
4
-
5
4
  subject(:type) { Attributor::Float }
6
5
 
7
6
  it 'it is not Dumpable' do
8
- type.new.is_a?(Attributor::Dumpable).should_not be(true)
7
+ expect(type.new.is_a?(Attributor::Dumpable)).not_to be(true)
9
8
  end
10
9
 
11
10
  context '.native_type' do
@@ -19,7 +18,7 @@ describe Attributor::Float do
19
18
  let(:min) { 1 }
20
19
  let(:max) { 2 }
21
20
 
22
- subject(:examples) { (0..100).collect { type.example(options:{min:min, max:max})}}
21
+ subject(:examples) { (0..100).collect { type.example(options: { min: min, max: max }) } }
23
22
 
24
23
  its(:min) { should be > min }
25
24
  its(:max) { should be < max }
@@ -30,34 +29,31 @@ describe Attributor::Float do
30
29
  let(:value) { nil }
31
30
 
32
31
  it 'returns nil for nil' do
33
- type.load(nil).should be(nil)
32
+ expect(type.load(nil)).to be(nil)
34
33
  end
35
34
 
36
35
  context 'for incoming Float values' do
37
-
38
36
  it 'returns the incoming value' do
39
37
  [0.0, -1.0, 1.0, 1e-10].each do |value|
40
- type.load(value).should be(value)
38
+ expect(type.load(value)).to be(value)
41
39
  end
42
40
  end
43
41
  end
44
42
 
45
43
  context 'for incoming Integer values' do
46
-
47
44
  context 'with an integer value' do
48
45
  let(:value) { 1 }
49
46
  it 'decodes it if the Integer represents a Float' do
50
- type.load(value).should == 1.0
47
+ expect(type.load(value)).to eq 1.0
51
48
  end
52
49
  end
53
50
  end
54
51
 
55
52
  context 'for incoming String values' do
56
-
57
53
  context 'that are valid Floats' do
58
54
  ['0.0', '-1.0', '1.0', '1e-10'].each do |value|
59
55
  it 'decodes it if the String represents a Float' do
60
- type.load(value).should == Float(value)
56
+ expect(type.load(value)).to eq Float(value)
61
57
  end
62
58
  end
63
59
  end
@@ -65,12 +61,11 @@ describe Attributor::Float do
65
61
  context 'that are valid Integers' do
66
62
  let(:value) { '1' }
67
63
  it 'decodes it if the String represents an Integer' do
68
- type.load(value).should == 1.0
64
+ expect(type.load(value)).to eq 1.0
69
65
  end
70
66
  end
71
67
 
72
68
  context 'that are not valid Floats' do
73
-
74
69
  context 'with simple alphanumeric text' do
75
70
  let(:value) { 'not a Float' }
76
71
 
@@ -78,9 +73,7 @@ describe Attributor::Float do
78
73
  expect { type.load(value) }.to raise_error(/invalid value/)
79
74
  end
80
75
  end
81
-
82
76
  end
83
-
84
77
  end
85
78
  end
86
79
  end