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,74 +1,69 @@
1
1
  require File.join(File.dirname(__FILE__), 'spec_helper.rb')
2
2
 
3
-
4
3
  describe Attributor::Attribute do
5
-
6
4
  let(:attribute_options) { Hash.new }
7
5
  let(:type) { Attributor::String }
8
6
 
9
7
  subject(:attribute) { Attributor::Attribute.new(type, attribute_options) }
10
8
 
11
- let(:context) { ["context"] }
12
- let(:value) { "one" }
9
+ let(:context) { ['context'] }
10
+ let(:value) { 'one' }
13
11
 
14
12
  context 'initialize' do
15
13
  its(:type) { should be type }
16
14
  its(:options) { should be attribute_options }
17
15
 
18
16
  it 'calls check_options!' do
19
- Attributor::Attribute.any_instance.should_receive(:check_options!)
17
+ expect_any_instance_of(Attributor::Attribute).to receive(:check_options!)
20
18
  Attributor::Attribute.new(type, attribute_options)
21
19
  end
22
20
 
23
21
  context 'for anonymous types (aka Structs)' do
24
22
  before do
25
- Attributor.should_receive(:resolve_type).once.with(Struct,attribute_options, anything()).and_call_original
23
+ expect(Attributor).to receive(:resolve_type).once.with(Struct, attribute_options, anything).and_call_original
26
24
  end
27
25
 
28
26
  it 'generates the class' do
29
- thing = Attributor::Attribute.new(Struct, attribute_options) do
27
+ Attributor::Attribute.new(Struct, attribute_options) do
30
28
  attribute :id, Integer
31
29
  end
32
30
  end
33
-
34
31
  end
35
-
36
32
  end
37
33
 
38
34
  context '==' do
39
35
  let(:other_attribute) { Attributor::Attribute.new(type, attribute_options) }
40
- it { should == other_attribute}
36
+ it { should eq other_attribute }
41
37
  end
42
38
 
43
39
  context 'describe' do
44
- let(:attribute_options) { {:required => true, :values => ["one"], :description => "something", :min => 0} }
40
+ let(:attribute_options) { { required: true, values: ['one'], description: 'something', min: 0 } }
45
41
  let(:expected) do
46
- h = {type: {name: 'String', id: type.id, family: type.family}}
47
- common = attribute_options.select{|k,v| Attributor::Attribute::TOP_LEVEL_OPTIONS.include? k }
48
- h.merge!( common )
49
- h[:options] = {:min => 0 }
42
+ h = { type: { name: 'String', id: type.id, family: type.family } }
43
+ common = attribute_options.select { |k, _v| Attributor::Attribute::TOP_LEVEL_OPTIONS.include? k }
44
+ h.merge!(common)
45
+ h[:options] = { min: 0 }
50
46
  h
51
47
  end
52
48
 
53
-
54
- its(:describe) { should == expected }
49
+ its(:describe) { should eq expected }
55
50
 
56
51
  context 'with example options' do
57
- let(:attribute_options) { {:description=> "something", :example => "ex_def"} }
52
+ let(:attribute_options) { { description: 'something', example: 'ex_def' } }
58
53
  its(:describe) { should have_key(:example_definition) }
59
54
  its(:describe) { should_not have_key(:example) }
60
55
  it 'should have the example value in the :example_definition key' do
61
- subject.describe[:example_definition].should == "ex_def"
56
+ expect(subject.describe[:example_definition]).to eq 'ex_def'
62
57
  end
63
58
  end
64
59
 
65
60
  context 'with custom_data' do
66
- let(:custom_data) { {loggable: true, visible_in_ui: false} }
67
- let(:attribute_options) { {custom_data: custom_data} }
61
+ let(:custom_data) { { loggable: true, visible_in_ui: false } }
62
+ let(:attribute_options) { { custom_data: custom_data } }
68
63
  its(:describe) { should have_key(:custom_data) }
69
64
 
70
65
  it 'keep the custom data attribute' do
71
- subject.describe[:custom_data].should == custom_data
66
+ expect(subject.describe[:custom_data]).to eq custom_data
72
67
  end
73
68
  end
74
69
 
@@ -80,99 +75,92 @@ describe Attributor::Attribute do
80
75
  end
81
76
  end
82
77
 
83
-
84
78
  subject(:description) { attribute.describe }
85
79
 
86
-
87
80
  it 'uses the name of the first non-anonymous ancestor' do
88
- description[:type][:name].should == 'Struct'
81
+ expect(description[:type][:name]).to eq 'Struct'
89
82
  end
90
83
 
91
84
  it 'includes sub-attributes' do
92
- description[:type][:attributes].should have_key(:id)
85
+ expect(description[:type][:attributes]).to have_key(:id)
93
86
  end
94
-
95
87
  end
96
88
 
97
89
  context 'with an example' do
98
-
99
- let(:attribute_options){ {} }
100
- let(:example){ attribute.example }
101
- subject(:described){ attribute.describe(false, example: example) }
90
+ let(:attribute_options) { {} }
91
+ let(:example) { attribute.example }
92
+ subject(:described) { attribute.describe(false, example: example) }
102
93
 
103
94
  context 'using a simple terminal type' do
104
95
  let(:type) { String }
105
- its(:keys){ should include(:example) }
96
+ its(:keys) { should include(:example) }
106
97
  it 'should have the passed example value' do
107
- described.should have_key(:example)
108
- described[:example].should eq(example)
98
+ expect(described).to have_key(:example)
99
+ expect(described[:example]).to eq(example)
109
100
  end
110
101
  it 'should have removed the example from the :type' do
111
- described[:type].should_not have_key(:example)
102
+ expect(described[:type]).not_to have_key(:example)
112
103
  end
113
-
114
104
  end
115
105
 
116
106
  context 'using a complex type' do
117
107
  let(:type) { Cormorant }
118
- its(:keys){ should_not include(:example) }
108
+ its(:keys) { should_not include(:example) }
119
109
 
120
110
  it 'Should see examples in the right places, depending on leaf/no-leaf types' do
121
111
  # String, a leaf attribute type: should have example
122
112
  name_attr = described[:type][:attributes][:name]
123
- name_attr.should include(:example)
124
- name_attr[:type].should_not include(:example)
113
+ expect(name_attr).to include(:example)
114
+ expect(name_attr[:type]).not_to include(:example)
125
115
 
126
116
  # Struct, a non-leaf attribute type: shouldn't have example
127
- ts_attr = described[:type][:attributes][:timestamps]
128
- ts_attr.should_not include(:example)
129
- ts_attr[:type].should_not include(:example)
117
+ ts_attr = described[:type][:attributes][:timestamps]
118
+ expect(ts_attr).not_to include(:example)
119
+ expect(ts_attr[:type]).not_to include(:example)
130
120
 
131
121
  # DateTime inside a Struct, a nested leaf attribute type: should have example
132
- born_attr = ts_attr[:type][:attributes][:born_at]
133
- born_attr.should include(:example)
134
- born_attr[:type].should_not include(:example)
122
+ born_attr = ts_attr[:type][:attributes][:born_at]
123
+ expect(born_attr).to include(:example)
124
+ expect(born_attr[:type]).not_to include(:example)
135
125
  end
136
126
  end
137
127
  end
138
128
  end
139
129
 
140
-
141
130
  context 'parse' do
142
- let(:loaded_object){ double("I'm loaded") }
131
+ let(:loaded_object) { double("I'm loaded") }
143
132
  it 'loads and validates' do
144
- attribute.should_receive(:load).with(value,Attributor::DEFAULT_ROOT_CONTEXT).and_return(loaded_object)
145
- attribute.should_receive(:validate).with(loaded_object,Attributor::DEFAULT_ROOT_CONTEXT).and_call_original
133
+ expect(attribute).to receive(:load).with(value, Attributor::DEFAULT_ROOT_CONTEXT).and_return(loaded_object)
134
+ expect(attribute).to receive(:validate).with(loaded_object, Attributor::DEFAULT_ROOT_CONTEXT).and_call_original
146
135
 
147
136
  attribute.parse(value)
148
137
  end
149
138
  end
150
139
 
151
-
152
140
  context 'checking options' do
153
141
  it 'raises for invalid options' do
154
- expect {
142
+ expect do
155
143
  Attributor::Attribute.new(Integer, unknown_opt: true)
156
- }.to raise_error(/unsupported option/)
144
+ end.to raise_error(/unsupported option/)
157
145
  end
158
146
 
159
147
  it 'has a spec that we try to validate the :default value' do
160
- expect {
161
- Attributor::Attribute.new(Integer, default: "not an okay integer")
162
- }.to raise_error(/Default value doesn't have the correct attribute type/)
148
+ expect do
149
+ Attributor::Attribute.new(Integer, default: 'not an okay integer')
150
+ end.to raise_error(/Default value doesn't have the correct attribute type/)
163
151
  end
164
152
 
165
153
  context 'custom_data' do
166
154
  it 'raises when not a hash' do
167
- expect {
155
+ expect do
168
156
  Attributor::Attribute.new(Integer, custom_data: 1)
169
- }.to raise_error(/custom_data must be a Hash/)
157
+ end.to raise_error(/custom_data must be a Hash/)
170
158
  end
171
159
 
172
160
  it 'does not raise for hashes' do
173
- expect {
174
- Attributor::Attribute.new(Integer, custom_data: {loggable: true})
175
- }.not_to raise_error
161
+ expect do
162
+ Attributor::Attribute.new(Integer, custom_data: { loggable: true })
163
+ end.not_to raise_error
176
164
  end
177
165
  end
178
166
  end
@@ -183,54 +171,53 @@ describe Attributor::Attribute do
183
171
  context 'with nothing specified' do
184
172
  let(:attribute_options) { {} }
185
173
  before do
186
- type.should_receive(:example).and_return(example)
174
+ expect(type).to receive(:example).and_return(example)
187
175
  end
188
176
 
189
177
  it 'defers to the type' do
190
- attribute.example.should be example
178
+ expect(attribute.example).to be example
191
179
  end
192
180
  end
193
181
 
194
182
  context 'with an attribute that has the values option set' do
195
- let(:values) { ["one", "two"] }
196
- let(:attribute_options) { {:values => values} }
183
+ let(:values) { %w(one two) }
184
+ let(:attribute_options) { { values: values } }
197
185
  it 'picks a random value' do
198
- values.should include subject.example
186
+ expect(values).to include subject.example
199
187
  end
200
-
201
188
  end
202
189
 
203
190
  context 'deterministic examples' do
204
191
  let(:example) { /\w+/ }
205
- let(:attribute_options) { {:example => example} }
192
+ let(:attribute_options) { { example: example } }
206
193
 
207
194
  it 'can take a context to pre-seed the random number generator' do
208
195
  example_1 = subject.example(['context'])
209
196
  example_2 = subject.example(['context'])
210
197
 
211
- example_1.should eq example_2
198
+ expect(example_1).to eq example_2
212
199
  end
213
200
 
214
201
  it 'can take a context to pre-seed the random number generator' do
215
202
  example_1 = subject.example(['context'])
216
203
  example_2 = subject.example(['different context'])
217
204
 
218
- example_1.should_not eq example_2
205
+ expect(example_1).not_to eq example_2
219
206
  end
220
207
  end
221
208
 
222
209
  context 'with an example option' do
223
- let(:example){ "Bob" }
224
- let(:attribute_options) { {example: example , regexp: /Bob/ } }
210
+ let(:example) { 'Bob' }
211
+ let(:attribute_options) { { example: example, regexp: /Bob/ } }
225
212
 
226
- its(:example){ should == example }
213
+ its(:example) { should eq example }
227
214
 
228
215
  context 'that is not valid' do
229
- let(:example){ "Frank" }
216
+ let(:example) { 'Frank' }
230
217
  it 'raises a validation error' do
231
- expect{
218
+ expect do
232
219
  subject.example
233
- }.to raise_error(Attributor::AttributorException, /Error generating example/)
220
+ end.to raise_error(Attributor::AttributorException, /Error generating example/)
234
221
  end
235
222
  end
236
223
  end
@@ -239,17 +226,17 @@ describe Attributor::Attribute do
239
226
  context 'example_from_options' do
240
227
  let(:example) { nil }
241
228
  let(:generated_example) { example }
242
- let(:attribute_options) { {:example => example} }
243
- let(:parent){ nil }
244
- let(:context){ Attributor::DEFAULT_ROOT_CONTEXT}
229
+ let(:attribute_options) { { example: example } }
230
+ let(:parent) { nil }
231
+ let(:context) { Attributor::DEFAULT_ROOT_CONTEXT }
245
232
 
246
- subject(:example_result) { attribute.example_from_options( parent, context ) }
233
+ subject(:example_result) { attribute.example_from_options(parent, context) }
247
234
  before do
248
- attribute.should_receive(:load).with( generated_example , an_instance_of(Array) ).and_call_original
235
+ expect(attribute).to receive(:load).with(generated_example, an_instance_of(Array)).and_call_original
249
236
  end
250
237
 
251
238
  context 'with a string' do
252
- let(:example) { "example" }
239
+ let(:example) { 'example' }
253
240
 
254
241
  it { should be example }
255
242
  end
@@ -265,9 +252,9 @@ describe Attributor::Attribute do
265
252
  let(:generated_example) { /\w+/.gen }
266
253
 
267
254
  it 'calls #gen on the regexp' do
268
- example.should_receive(:gen).and_return(generated_example)
255
+ expect(example).to receive(:gen).and_return(generated_example)
269
256
 
270
- example_result.should =~ example
257
+ expect(example_result).to match example
271
258
  end
272
259
 
273
260
  context 'for a type with a non-String native_type' do
@@ -276,113 +263,109 @@ describe Attributor::Attribute do
276
263
  let(:generated_example) { /\d{5}/.gen }
277
264
 
278
265
  it 'coerces the example value properly' do
279
- example.should_receive(:gen).and_return(generated_example)
280
- type.should_receive(:load).and_call_original
266
+ expect(example).to receive(:gen).and_return(generated_example)
267
+ expect(type).to receive(:load).and_call_original
281
268
 
282
- example_result.should be_kind_of(type.native_type)
269
+ expect(example_result).to be_kind_of(type.native_type)
283
270
  end
284
271
  end
285
-
286
272
  end
287
273
 
288
274
  context 'with a proc' do
289
- let(:parent){ Object.new }
275
+ let(:parent) { Object.new }
290
276
 
291
277
  context 'with one argument' do
292
- let(:example) { lambda { |obj| 'ok' } }
278
+ let(:example) { ->(_obj) { 'ok' } }
293
279
  let(:generated_example) { 'ok' }
294
280
 
295
281
  before do
296
- example.should_receive(:call).with(parent).and_return(generated_example)
282
+ expect(example).to receive(:call).with(parent).and_return(generated_example)
297
283
  end
298
284
 
299
285
  it 'passes any given parent through to the example proc' do
300
- example_result.should == 'ok'
286
+ expect(example_result).to eq 'ok'
301
287
  end
302
288
  end
303
289
 
304
290
  context 'with two arguments' do
305
- let(:example) { lambda { |obj, context| "#{context} ok" } }
291
+ let(:example) { ->(_obj, context) { "#{context} ok" } }
306
292
  let(:generated_example) { "#{context} ok" }
307
- let(:context){ ['some_context'] }
293
+ let(:context) { ['some_context'] }
308
294
  before do
309
- example.should_receive(:call).with(parent, context).and_return(generated_example)
295
+ expect(example).to receive(:call).with(parent, context).and_return(generated_example)
310
296
  end
311
297
 
312
298
  it 'passes any given parent through to the example proc' do
313
- example_result.should == "#{context} ok"
299
+ expect(example_result).to eq "#{context} ok"
314
300
  end
315
301
  end
316
-
317
302
  end
318
303
 
319
304
  context 'with an Collection (of Strings)' do
320
305
  let(:type) { Attributor::Collection.of(String) }
321
- let(:example) { ["one"] }
322
- it { should == example }
306
+ let(:example) { ['one'] }
307
+ it { should eq example }
323
308
  end
324
-
325
309
  end
326
310
 
327
311
  context 'load' do
328
- let(:context){ ['context'] }
312
+ let(:context) { ['context'] }
329
313
  let(:value) { '1' }
330
314
 
331
315
  it 'delegates to type.load' do
332
- type.should_receive(:load).with(value,context, {})
333
- attribute.load(value,context)
316
+ expect(type).to receive(:load).with(value, context, {})
317
+ attribute.load(value, context)
334
318
  end
335
319
 
336
320
  it 'passes options to type.load' do
337
- type.should_receive(:load).with(value, context, foo: 'bar')
321
+ expect(type).to receive(:load).with(value, context, foo: 'bar')
338
322
  attribute.load(value, context, foo: 'bar')
339
323
  end
340
324
 
341
325
  context 'applying default values' do
342
326
  let(:value) { nil }
343
- let(:default_value) { "default value" }
344
- let(:attribute_options) { {:default => default_value} }
327
+ let(:default_value) { 'default value' }
328
+ let(:attribute_options) { { default: default_value } }
345
329
 
346
330
  subject(:result) { attribute.load(value) }
347
331
 
348
332
  context 'for nil' do
349
- it { should == default_value}
333
+ it { should eq default_value }
350
334
  end
351
335
 
352
336
  context 'for false' do
353
337
  let(:type) { Attributor::Boolean }
354
338
  let(:default_value) { false }
355
- it { should == default_value}
356
-
339
+ it { should eq default_value }
357
340
  end
358
341
 
359
342
  context 'for a Proc-based default value' do
360
- let(:context){ ["$"] }
361
- subject(:result){ attribute.load(value,context) }
362
-
343
+ let(:context) { ['$'] }
344
+ subject(:result) { attribute.load(value, context) }
363
345
 
364
346
  context 'with no arguments arguments' do
365
- let(:default_value) { proc { "no_params" } }
366
- it { should == default_value.call }
347
+ let(:default_value) { proc { 'no_params' } }
348
+ it { should eq default_value.call }
367
349
  end
368
350
 
369
351
  context 'with 1 argument (the parent)' do
370
- let(:default_value) { proc {|parent| "parent is fake: #{parent.class}" } }
371
- it { should == "parent is fake: Attributor::FakeParent" }
352
+ let(:default_value) { proc { |parent| "parent is fake: #{parent.class}" } }
353
+ it { should eq 'parent is fake: Attributor::FakeParent' }
372
354
  end
373
355
 
374
356
  context 'with 2 argument (the parent and the contents)' do
375
- let(:default_value) { proc {|parent,context| "parent is fake: #{parent.class} and context is: #{context}" } }
376
- it { should == "parent is fake: Attributor::FakeParent and context is: [\"$\"]"}
357
+ let(:default_value) { proc { |parent, context| "parent is fake: #{parent.class} and context is: #{context}" } }
358
+ it { should eq 'parent is fake: Attributor::FakeParent and context is: ["$"]' }
377
359
  end
378
360
 
379
361
  context 'which attempts to use the parent (which is not supported for the moment)' do
380
- let(:default_value) { proc {|parent| "any parent method should spit out warning: [#{parent.something}]" } }
381
- it "should output a warning" do
362
+ let(:default_value) { proc { |parent| "any parent method should spit out warning: [#{parent.something}]" } }
363
+ it 'should output a warning' do
382
364
  begin
383
- old_verbose, $VERBOSE = $VERBOSE, nil
384
- Kernel.should_receive(:warn).and_call_original
385
- attribute.load(value,context).should == "any parent method should spit out warning: []"
365
+ old_verbose = $VERBOSE
366
+ $VERBOSE = nil
367
+ expect(Kernel).to receive(:warn).and_call_original
368
+ expect(attribute.load(value, context)).to eq 'any parent method should spit out warning: []'
386
369
  ensure
387
370
  $VERBOSE = old_verbose
388
371
  end
@@ -392,61 +375,56 @@ describe Attributor::Attribute do
392
375
  end
393
376
 
394
377
  context 'validating a value' do
395
-
396
378
  context '#validate' do
397
379
  context 'applying attribute options' do
398
380
  context ':required' do
399
- let(:attribute_options) { {:required => true} }
381
+ let(:attribute_options) { { required: true } }
400
382
  context 'with a nil value' do
401
383
  let(:value) { nil }
402
384
  it 'returns an error' do
403
- attribute.validate(value, context).first.should == 'Attribute context is required'
385
+ expect(attribute.validate(value, context).first).to eq 'Attribute context is required'
404
386
  end
405
387
  end
406
388
  end
407
389
 
408
390
  context ':values' do
409
- let(:values) { ['one','two'] }
410
- let(:attribute_options) { {:values => values} }
391
+ let(:values) { %w(one two) }
392
+ let(:attribute_options) { { values: values } }
411
393
  let(:value) { nil }
412
394
 
413
- subject(:errors) { attribute.validate(value, context)}
395
+ subject(:errors) { attribute.validate(value, context) }
414
396
 
415
397
  context 'with a value that is allowed' do
416
- let(:value) { "one" }
398
+ let(:value) { 'one' }
417
399
  it 'returns no errors' do
418
- errors.should be_empty
400
+ expect(errors).to be_empty
419
401
  end
420
402
  end
421
403
 
422
404
  context 'with a value that is not allowed' do
423
- let(:value) { "three" }
405
+ let(:value) { 'three' }
424
406
  it 'returns an error indicating the problem' do
425
- errors.first.should =~ /is not within the allowed values/
407
+ expect(errors.first).to match(/is not within the allowed values/)
426
408
  end
427
-
428
409
  end
429
410
  end
430
-
431
-
432
411
  end
433
412
 
434
413
  it 'calls the right validate_X methods?' do
435
- attribute.should_receive(:validate_type).with(value, context).and_call_original
436
- attribute.should_not_receive(:validate_dependency)
437
- type.should_receive(:validate).and_call_original
414
+ expect(attribute).to receive(:validate_type).with(value, context).and_call_original
415
+ expect(attribute).not_to receive(:validate_dependency)
416
+ expect(type).to receive(:validate).and_call_original
438
417
  attribute.validate(value, context)
439
418
  end
440
-
441
419
  end
442
420
 
443
421
  context '#validate_type' do
444
- subject(:errors) { attribute.validate_type(value, context)}
422
+ subject(:errors) { attribute.validate_type(value, context) }
445
423
 
446
424
  context 'with a value of the right type' do
447
- let(:value) { "one" }
425
+ let(:value) { 'one' }
448
426
  it 'returns no errors' do
449
- errors.should be_empty
427
+ expect(errors).to be_empty
450
428
  end
451
429
  end
452
430
 
@@ -454,30 +432,26 @@ describe Attributor::Attribute do
454
432
  let(:value) { 1 }
455
433
 
456
434
  it 'returns errors' do
457
- errors.should_not be_empty
458
- errors.first.should =~ /is of the wrong type/
435
+ expect(errors).not_to be_empty
436
+ expect(errors.first).to match(/is of the wrong type/)
459
437
  end
460
-
461
438
  end
462
-
463
-
464
439
  end
465
440
 
466
441
  context '#validate_missing_value' do
467
- let(:key) { "$.instance.ssh_key.name" }
442
+ let(:key) { '$.instance.ssh_key.name' }
468
443
  let(:value) { /\w+/.gen }
469
444
 
470
- let(:attribute_options) { {:required_if => key} }
445
+ let(:attribute_options) { { required_if: key } }
471
446
 
472
- let(:ssh_key) { double("ssh_key", :name => value) }
473
- let(:instance) { double("instance", :ssh_key => ssh_key) }
447
+ let(:ssh_key) { double('ssh_key', name: value) }
448
+ let(:instance) { double('instance', ssh_key: ssh_key) }
474
449
 
475
450
  before { Attributor::AttributeResolver.current.register('instance', instance) }
476
451
 
477
- let(:attribute_context) { ['$','params','key_material'] }
452
+ let(:attribute_context) { ['$', 'params', 'key_material'] }
478
453
  subject(:errors) { attribute.validate_missing_value(attribute_context) }
479
454
 
480
-
481
455
  context 'for a simple dependency without a predicate' do
482
456
  context 'that is satisfied' do
483
457
  it { should_not be_empty }
@@ -490,36 +464,33 @@ describe Attributor::Attribute do
490
464
  end
491
465
 
492
466
  context 'with a dependency that has a predicate' do
493
- let(:value) { "default_ssh_key_name" }
494
- #subject(:errors) { attribute.validate_missing_value('') }
467
+ let(:value) { 'default_ssh_key_name' }
468
+ # subject(:errors) { attribute.validate_missing_value('') }
495
469
 
496
470
  context 'where the target attribute exists, and matches the predicate' do
497
- let(:attribute_options) { {:required_if => {key => /default/} } }
471
+ let(:attribute_options) { { required_if: { key => /default/ } } }
498
472
 
499
473
  it { should_not be_empty }
500
474
 
501
- its(:first) { should =~ /Attribute #{Regexp.quote(Attributor.humanize_context( attribute_context ))} is required when #{Regexp.quote(key)} matches/ }
475
+ its(:first) { should match(/Attribute #{Regexp.quote(Attributor.humanize_context(attribute_context))} is required when #{Regexp.quote(key)} matches/) }
502
476
  end
503
477
 
504
478
  context 'where the target attribute exists, but does not match the predicate' do
505
- let(:attribute_options) { {:required_if => {key => /other/} } }
479
+ let(:attribute_options) { { required_if: { key => /other/ } } }
506
480
 
507
481
  it { should be_empty }
508
482
  end
509
483
 
510
484
  context 'where the target attribute does not exist' do
511
- let(:attribute_options) { {:required_if => {key => /default/} } }
512
- let(:ssh_key) { double("ssh_key", :name => nil) }
485
+ let(:attribute_options) { { required_if: { key => /default/ } } }
486
+ let(:ssh_key) { double('ssh_key', name: nil) }
513
487
 
514
488
  it { should be_empty }
515
489
  end
516
490
  end
517
-
518
491
  end
519
-
520
492
  end
521
493
 
522
-
523
494
  context 'for an attribute for a subclass of Model' do
524
495
  let(:type) { Chicken }
525
496
  let(:type_options) { Chicken.options }
@@ -527,46 +498,43 @@ describe Attributor::Attribute do
527
498
  subject(:attribute) { Attributor::Attribute.new(type, attribute_options) }
528
499
 
529
500
  it 'has attributes' do
530
- attribute.attributes.should == type.attributes
501
+ expect(attribute.attributes).to eq type.attributes
531
502
  end
532
503
 
533
- #it 'has compiled_definition' do
534
- # attribute.compiled_definition.should == type.definition
535
- #end
536
-
504
+ # it 'has compiled_definition' do
505
+ # attribute.compiled_definition.should eq type.definition
506
+ # end
537
507
 
538
508
  it 'merges its options with those of the compiled_definition' do
539
- attribute.options.should == attribute_options.merge(type_options)
509
+ expect(attribute.options).to eq attribute_options.merge(type_options)
540
510
  end
541
511
 
542
512
  it 'describe handles sub-attributes nicely' do
543
513
  describe = attribute.describe(false)
544
514
 
545
- describe[:type][:name].should == type.name
546
- common_options = attribute_options.select{|k,v| Attributor::Attribute.TOP_LEVEL_OPTIONS.include? k }
547
- special_options = attribute_options.reject{|k,v| Attributor::Attribute.TOP_LEVEL_OPTIONS.include? k }
548
- common_options.each do |k,v|
549
- describe[k].should == v
515
+ expect(describe[:type][:name]).to eq type.name
516
+ common_options = attribute_options.select { |k, _v| Attributor::Attribute.TOP_LEVEL_OPTIONS.include? k }
517
+ special_options = attribute_options.reject { |k, _v| Attributor::Attribute.TOP_LEVEL_OPTIONS.include? k }
518
+ common_options.each do |k, v|
519
+ expect(describe[k]).to eq v
550
520
  end
551
- special_options.each do |k,v|
552
- describe[:options][k].should == v
521
+ special_options.each do |k, v|
522
+ expect(describe[:options][k]).to eq v
553
523
  end
554
- type_options.each do |k,v|
555
- describe[:options][k].should == v
524
+ type_options.each do |k, v|
525
+ expect(describe[:options][k]).to eq v
556
526
  end
557
527
 
558
-
559
- attribute.attributes.each do |name, attr|
560
- describe[:type][:attributes].should have_key(name)
528
+ attribute.attributes.each do |name, _attr|
529
+ expect(describe[:type][:attributes]).to have_key(name)
561
530
  end
562
-
563
531
  end
564
532
 
565
533
  it 'supports deterministic examples' do
566
- example_1 = attribute.example(["Chicken context"])
567
- example_2 = attribute.example(["Chicken context"])
534
+ example_1 = attribute.example(['Chicken context'])
535
+ example_2 = attribute.example(['Chicken context'])
568
536
 
569
- example_1.attributes.should eq(example_2.attributes)
537
+ expect(example_1.attributes).to eq(example_2.attributes)
570
538
  end
571
539
 
572
540
  context '#validate' do
@@ -575,29 +543,27 @@ describe Attributor::Attribute do
575
543
 
576
544
  it 'validates sub-attributes' do
577
545
  errors = attribute.validate(chicken)
578
- errors.should be_empty
546
+ expect(errors).to be_empty
579
547
  end
580
548
 
581
549
  context 'with a failing validation' do
582
- subject(:chicken) { Chicken.example(age: 150, email: "foo") }
550
+ subject(:chicken) { Chicken.example(age: 150, email: 'foo') }
583
551
  let(:email_validation_response) { ["$.email value \(#{chicken.email}\) does not match regexp (/@/)"] }
584
552
  let(:age_validation_response) { ["$.age value \(#{chicken.age}\) is larger than the allowed max (120)"] }
585
553
 
586
554
  it 'collects sub-attribute validation errors' do
587
555
  errors = attribute.validate(chicken)
588
- errors.should =~ (age_validation_response | email_validation_response)
556
+ expect(errors).to match_array(age_validation_response | email_validation_response)
589
557
  end
590
558
  end
591
-
592
559
  end
593
560
 
594
-
595
561
  context '#validate_missing_value' do
596
562
  let(:type) { Duck }
597
563
  let(:attribute_name) { nil }
598
564
  let(:attribute) { Duck.attributes[attribute_name] }
599
565
 
600
- let(:attribute_context) { ['$','duck',"#{attribute_name}"] }
566
+ let(:attribute_context) { ['$', 'duck', attribute_name.to_s] }
601
567
  subject(:errors) { attribute.validate_missing_value(attribute_context) }
602
568
 
603
569
  before do
@@ -616,7 +582,7 @@ describe Attributor::Attribute do
616
582
 
617
583
  context 'where the target attribute exists, and matches the predicate' do
618
584
  it { should_not be_empty }
619
- its(:first) { should == "Attribute $.duck.email is required when name (for $.duck) is present." }
585
+ its(:first) { should eq 'Attribute $.duck.email is required when name (for $.duck) is present.' }
620
586
  end
621
587
  context 'where the target attribute does not exist' do
622
588
  before do
@@ -626,7 +592,6 @@ describe Attributor::Attribute do
626
592
  end
627
593
  end
628
594
 
629
-
630
595
  context 'for a dependency with a predicate' do
631
596
  let(:attribute_name) { :age }
632
597
 
@@ -639,7 +604,7 @@ describe Attributor::Attribute do
639
604
 
640
605
  context 'where the target attribute exists, and matches the predicate' do
641
606
  it { should_not be_empty }
642
- its(:first) { should =~ /Attribute #{Regexp.quote('$.duck.age')} is required when name #{Regexp.quote('(for $.duck)')} matches/ }
607
+ its(:first) { should match(/Attribute #{Regexp.quote('$.duck.age')} is required when name #{Regexp.quote('(for $.duck)')} matches/) }
643
608
  end
644
609
 
645
610
  context 'where the target attribute exists, and does not match the predicate' do
@@ -655,69 +620,62 @@ describe Attributor::Attribute do
655
620
  end
656
621
  it { should be_empty }
657
622
  end
658
-
659
623
  end
660
-
661
624
  end
662
-
663
625
  end
664
626
  end
665
627
 
666
628
  context 'for a Collection' do
667
629
  context 'of non-Model (or Struct) type' do
668
630
  let(:member_type) { Attributor::Integer }
669
- let(:type) { Attributor::Collection.of(member_type)}
670
- let(:member_options) { {:max => 10} }
671
- let(:attribute_options) { {:member_options => member_options} }
631
+ let(:type) { Attributor::Collection.of(member_type) }
632
+ let(:member_options) { { max: 10 } }
633
+ let(:attribute_options) { { member_options: member_options } }
672
634
 
673
635
  context 'the member_attribute of that type' do
674
636
  subject(:member_attribute) { attribute.type.member_attribute }
675
637
 
676
- it { should be_kind_of(Attributor::Attribute)}
638
+ it { should be_kind_of(Attributor::Attribute) }
677
639
  its(:type) { should be(member_type) }
678
640
  its(:options) { should eq(member_options) }
679
641
  end
680
642
 
681
- context "working with members" do
682
- let(:values) { ['1',2,12] }
643
+ context 'working with members' do
644
+ let(:values) { ['1', 2, 12] }
683
645
 
684
646
  it 'loads' do
685
- attribute.load(values).should =~ [1,2,12]
647
+ expect(attribute.load(values)).to match_array [1, 2, 12]
686
648
  end
687
649
 
688
650
  it 'validates' do
689
651
  object = attribute.load(values)
690
652
  errors = attribute.validate(object)
691
653
 
692
- errors.should have(1).item
693
- errors[0].should =~ /value \(12\) is larger/
654
+ expect(errors).to have(1).item
655
+ expect(errors[0]).to match(/value \(12\) is larger/)
694
656
  end
695
657
  end
696
-
697
-
698
658
  end
699
659
 
700
660
  context 'of a Model (or Struct) type' do
701
- subject(:attribute) { Attributor::Attribute.new(type, attribute_options, &attribute_block) }
661
+ subject(:attribute) { Attributor::Attribute.new(type, attribute_options, &attribute_block) }
702
662
 
703
- let(:attribute_block) { Proc.new{ attribute :angry , required: true } }
704
- let(:attribute_options) { {reference: Chicken, member_options: member_options} }
663
+ let(:attribute_block) { proc { attribute :angry, required: true } }
664
+ let(:attribute_options) { { reference: Chicken, member_options: member_options } }
705
665
  let(:member_type) { Attributor::Struct }
706
666
  let(:type) { Attributor::Collection.of(member_type) }
707
667
  let(:member_options) { {} }
708
668
 
709
669
  context 'the member_attribute of that type' do
710
670
  subject(:member_attribute) { attribute.type.member_attribute }
711
- it { should be_kind_of(Attributor::Attribute)}
671
+ it { should be_kind_of(Attributor::Attribute) }
712
672
  its(:options) { should eq(member_options.merge(reference: Chicken, identity: :email)) }
713
673
  its(:attributes) { should have_key :angry }
714
674
  it 'inherited the type and options from the reference' do
715
- member_attribute.attributes[:angry].type.should be(Chicken.attributes[:angry].type)
716
- member_attribute.attributes[:angry].options.should eq(Chicken.attributes[:angry].options.merge(required: true))
675
+ expect(member_attribute.attributes[:angry].type).to be(Chicken.attributes[:angry].type)
676
+ expect(member_attribute.attributes[:angry].options).to eq(Chicken.attributes[:angry].options.merge(required: true))
717
677
  end
718
678
  end
719
-
720
679
  end
721
680
  end
722
-
723
681
  end