attributor 5.0.2 → 5.1.0

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