attributor 5.4 → 6.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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +17 -0
  3. data/lib/attributor/attribute.rb +101 -84
  4. data/lib/attributor/extras/field_selector.rb +4 -0
  5. data/lib/attributor/families/numeric.rb +19 -6
  6. data/lib/attributor/families/temporal.rb +16 -9
  7. data/lib/attributor/hash_dsl_compiler.rb +6 -5
  8. data/lib/attributor/type.rb +26 -3
  9. data/lib/attributor/types/bigdecimal.rb +6 -1
  10. data/lib/attributor/types/boolean.rb +5 -0
  11. data/lib/attributor/types/collection.rb +19 -0
  12. data/lib/attributor/types/csv.rb +4 -0
  13. data/lib/attributor/types/date.rb +7 -1
  14. data/lib/attributor/types/date_time.rb +7 -1
  15. data/lib/attributor/types/float.rb +4 -3
  16. data/lib/attributor/types/hash.rb +86 -23
  17. data/lib/attributor/types/integer.rb +7 -1
  18. data/lib/attributor/types/model.rb +9 -21
  19. data/lib/attributor/types/object.rb +5 -0
  20. data/lib/attributor/types/polymorphic.rb +0 -1
  21. data/lib/attributor/types/string.rb +19 -0
  22. data/lib/attributor/types/symbol.rb +5 -0
  23. data/lib/attributor/types/tempfile.rb +4 -0
  24. data/lib/attributor/types/time.rb +6 -2
  25. data/lib/attributor/types/uri.rb +8 -0
  26. data/lib/attributor/version.rb +1 -1
  27. data/lib/attributor.rb +3 -7
  28. data/spec/attribute_spec.rb +148 -124
  29. data/spec/extras/field_selector/field_selector_spec.rb +9 -0
  30. data/spec/hash_dsl_compiler_spec.rb +5 -5
  31. data/spec/spec_helper.rb +0 -2
  32. data/spec/support/integers.rb +7 -0
  33. data/spec/support/models.rb +7 -7
  34. data/spec/types/bigdecimal_spec.rb +8 -0
  35. data/spec/types/boolean_spec.rb +10 -0
  36. data/spec/types/collection_spec.rb +16 -0
  37. data/spec/types/date_spec.rb +9 -0
  38. data/spec/types/date_time_spec.rb +9 -0
  39. data/spec/types/float_spec.rb +8 -0
  40. data/spec/types/hash_spec.rb +181 -22
  41. data/spec/types/integer_spec.rb +9 -0
  42. data/spec/types/model_spec.rb +7 -1
  43. data/spec/types/string_spec.rb +10 -0
  44. data/spec/types/temporal_spec.rb +5 -1
  45. data/spec/types/time_spec.rb +9 -0
  46. data/spec/types/uri_spec.rb +9 -0
  47. metadata +5 -6
  48. data/lib/attributor/attribute_resolver.rb +0 -111
  49. data/spec/attribute_resolver_spec.rb +0 -237
@@ -11,7 +11,7 @@ describe Attributor::Attribute do
11
11
 
12
12
  context 'initialize' do
13
13
  its(:type) { should be type }
14
- its(:options) { should be attribute_options }
14
+ its(:options) { should eq attribute_options }
15
15
 
16
16
  it 'calls check_options!' do
17
17
  expect_any_instance_of(Attributor::Attribute).to receive(:check_options!)
@@ -36,17 +36,49 @@ describe Attributor::Attribute do
36
36
  it { should eq other_attribute }
37
37
  end
38
38
 
39
+ context 'describe_json_schema' do
40
+ let(:type) { PositiveIntegerType }
41
+
42
+ let(:attribute_options) do
43
+ {
44
+ values: [1,20],
45
+ description: "something",
46
+ example: 20,
47
+ max: 1000,
48
+ default: 1
49
+ }
50
+ end
51
+
52
+ context 'reports all of the possible attributes' do
53
+ let(:js){ subject.as_json_schema(example: 20) }
54
+
55
+ it 'including the attribute-specific ones' do
56
+ expect(js[:enum]).to eq( [1,20])
57
+ expect(js[:description]).to eq( "something")
58
+ expect(js[:default]).to eq(1)
59
+ expect(js[:example]).to eq(20)
60
+ end
61
+
62
+ it 'as well as the type-specific ones' do
63
+ expect(js[:type]).to eq(:integer)
64
+ end
65
+
66
+ end
67
+
68
+ end
69
+
39
70
  context 'describe' do
40
- let(:attribute_options) { { required: true, values: ['one'], description: 'something', min: 0 } }
71
+ let(:attribute_options) { {required: true, values: ['one'], description: "something", min: 0} }
41
72
  let(:expected) do
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 }
73
+ h = {type: {name: 'String', id: type.id, family: type.family}}
74
+ common = attribute_options.select{|k,v| Attributor::Attribute::TOP_LEVEL_OPTIONS.include? k }
44
75
  h.merge!(common)
45
- h[:options] = { min: 0 }
76
+ h[:options] = {min: 0}
46
77
  h
47
78
  end
48
79
 
49
- its(:describe) { should eq expected }
80
+ # It has both the type-included options (min) as well as the attribute options (max)
81
+ its(:describe) { should == expected }
50
82
 
51
83
  context 'with example options' do
52
84
  let(:attribute_options) { { description: 'something', example: 'ex_def' } }
@@ -163,6 +195,49 @@ describe Attributor::Attribute do
163
195
  end.not_to raise_error
164
196
  end
165
197
  end
198
+
199
+ context 'custom_options' do
200
+ let(:option_name) { :foo }
201
+ let(:custom_option_args) { [option_name, String] }
202
+
203
+ around do |example|
204
+ Attributor::Attribute.custom_option *custom_option_args
205
+ example.run
206
+ Attributor::Attribute.custom_options.delete option_name
207
+ end
208
+
209
+ it 'raises ArgumentError if given an existing option' do
210
+ expect {
211
+ Attributor::Attribute.custom_option :default, Object
212
+ }.to raise_error(ArgumentError)
213
+ end
214
+
215
+ it 'accepts custom options' do
216
+ expect do
217
+ Attributor::Attribute.new(Integer, foo: 'unvalidated')
218
+ end.not_to raise_error
219
+ end
220
+
221
+ context 'can validate the custom option value' do
222
+ let(:custom_option_args) { [option_name, String, values: ['valid']] }
223
+ it 'does not raise with a valid option value' do
224
+ expect do
225
+ Attributor::Attribute.new(Integer, foo: 'valid')
226
+ end.not_to raise_error
227
+ end
228
+ it 'raises with an invalid option value' do
229
+ expect do
230
+ Attributor::Attribute.new(Integer, foo: 'invalid')
231
+ end.to raise_error(Attributor::AttributorException)
232
+ end
233
+ end
234
+
235
+ it 'appear in as_json_schema' do
236
+ attribute = Attributor::Attribute.new(Integer, foo: 'valid')
237
+ json_schema = attribute.as_json_schema
238
+ expect(json_schema[:'x-foo']).to eq 'valid'
239
+ end
240
+ end
166
241
  end
167
242
 
168
243
  context 'example' do
@@ -383,10 +458,49 @@ describe Attributor::Attribute do
383
458
  context 'applying attribute options' do
384
459
  context ':required' do
385
460
  let(:attribute_options) { { required: true } }
461
+ context 'has no effect on a bare attribute' do
462
+ let(:value) { 'val' }
463
+ it 'it does not error, as we do not know if the parent attribute key was passed in (done at the Hash level)' do
464
+ expect(attribute.validate(value, context)).to be_empty
465
+ end
466
+ end
467
+ end
468
+ context ':null false (non-nullable)' do
469
+ let(:attribute_options) { { null: false } }
386
470
  context 'with a nil value' do
387
471
  let(:value) { nil }
388
472
  it 'returns an error' do
389
- expect(attribute.validate(value, context).first).to eq 'Attribute context is required'
473
+ expect(attribute.validate(value, context).first).to eq 'Attribute context is not nullable'
474
+ end
475
+ end
476
+ end
477
+ context ':null true (nullable)' do
478
+ let(:attribute_options) { { null: true } }
479
+ context 'with a nil value' do
480
+ let(:value) { nil }
481
+ it 'does not error' do
482
+ expect(attribute.validate(value, context)).to be_empty
483
+ end
484
+ end
485
+ end
486
+ context 'defaults to non-nullable if null not defined' do
487
+ let(:attribute_options) { { } }
488
+ context 'with a nil value' do
489
+ let(:value) { nil }
490
+ it 'returns an error' do
491
+ expect(Attributor::Attribute.default_for_null).to be(false)
492
+ expect(attribute.validate(value, context).first).to eq 'Attribute context is not nullable'
493
+ end
494
+ end
495
+ end
496
+
497
+ context 'default can be overrideable with true' do
498
+ let(:attribute_options) { { } }
499
+ context 'with a nil value' do
500
+ let(:value) { nil }
501
+ it 'suceeds' do
502
+ expect(Attributor::Attribute).to receive(:default_for_null).and_return(true)
503
+ expect(attribute.validate(value, context)).to be_empty
390
504
  end
391
505
  end
392
506
  end
@@ -432,6 +546,13 @@ describe Attributor::Attribute do
432
546
  end
433
547
  end
434
548
 
549
+ context 'with a nil value' do
550
+ let(:value) { nil }
551
+ it 'returns no errors' do
552
+ expect(errors).to be_empty
553
+ end
554
+ end
555
+
435
556
  context 'with a value of a value different than the native_type' do
436
557
  let(:value) { 1 }
437
558
 
@@ -441,58 +562,6 @@ describe Attributor::Attribute do
441
562
  end
442
563
  end
443
564
  end
444
-
445
- context '#validate_missing_value' do
446
- let(:key) { '$.instance.ssh_key.name' }
447
- let(:value) { /\w+/.gen }
448
-
449
- let(:attribute_options) { { required_if: key } }
450
-
451
- let(:ssh_key) { double('ssh_key', name: value) }
452
- let(:instance) { double('instance', ssh_key: ssh_key) }
453
-
454
- before { Attributor::AttributeResolver.current.register('instance', instance) }
455
-
456
- let(:attribute_context) { ['$', 'params', 'key_material'] }
457
- subject(:errors) { attribute.validate_missing_value(attribute_context) }
458
-
459
- context 'for a simple dependency without a predicate' do
460
- context 'that is satisfied' do
461
- it { should_not be_empty }
462
- end
463
-
464
- context 'that is missing' do
465
- let(:value) { nil }
466
- it { should be_empty }
467
- end
468
- end
469
-
470
- context 'with a dependency that has a predicate' do
471
- let(:value) { 'default_ssh_key_name' }
472
- # subject(:errors) { attribute.validate_missing_value('') }
473
-
474
- context 'where the target attribute exists, and matches the predicate' do
475
- let(:attribute_options) { { required_if: { key => /default/ } } }
476
-
477
- it { should_not be_empty }
478
-
479
- its(:first) { should match(/Attribute #{Regexp.quote(Attributor.humanize_context(attribute_context))} is required when #{Regexp.quote(key)} matches/) }
480
- end
481
-
482
- context 'where the target attribute exists, but does not match the predicate' do
483
- let(:attribute_options) { { required_if: { key => /other/ } } }
484
-
485
- it { should be_empty }
486
- end
487
-
488
- context 'where the target attribute does not exist' do
489
- let(:attribute_options) { { required_if: { key => /default/ } } }
490
- let(:ssh_key) { double('ssh_key', name: nil) }
491
-
492
- it { should be_empty }
493
- end
494
- end
495
- end
496
565
  end
497
566
 
498
567
  context 'for an attribute for a subclass of Model' do
@@ -561,71 +630,6 @@ describe Attributor::Attribute do
561
630
  end
562
631
  end
563
632
  end
564
-
565
- context '#validate_missing_value' do
566
- let(:type) { Duck }
567
- let(:attribute_name) { nil }
568
- let(:attribute) { Duck.attributes[attribute_name] }
569
-
570
- let(:attribute_context) { ['$', 'duck', attribute_name.to_s] }
571
- subject(:errors) { attribute.validate_missing_value(attribute_context) }
572
-
573
- before do
574
- Attributor::AttributeResolver.current.register('duck', duck)
575
- end
576
-
577
- context 'for a dependency with no predicate' do
578
- let(:attribute_name) { :email }
579
-
580
- let(:duck) do
581
- d = Duck.new
582
- d.age = 1
583
- d.name = 'Donald'
584
- d
585
- end
586
-
587
- context 'where the target attribute exists, and matches the predicate' do
588
- it { should_not be_empty }
589
- its(:first) { should eq 'Attribute $.duck.email is required when name (for $.duck) is present.' }
590
- end
591
- context 'where the target attribute does not exist' do
592
- before do
593
- duck.name = nil
594
- end
595
- it { should be_empty }
596
- end
597
- end
598
-
599
- context 'for a dependency with a predicate' do
600
- let(:attribute_name) { :age }
601
-
602
- let(:duck) do
603
- d = Duck.new
604
- d.name = 'Daffy'
605
- d.email = 'daffy@darkwing.uoregon.edu' # he's a duck,get it?
606
- d
607
- end
608
-
609
- context 'where the target attribute exists, and matches the predicate' do
610
- it { should_not be_empty }
611
- its(:first) { should match(/Attribute #{Regexp.quote('$.duck.age')} is required when name #{Regexp.quote('(for $.duck)')} matches/) }
612
- end
613
-
614
- context 'where the target attribute exists, and does not match the predicate' do
615
- before do
616
- duck.name = 'Donald'
617
- end
618
- it { should be_empty }
619
- end
620
-
621
- context 'where the target attribute does not exist' do
622
- before do
623
- duck.name = nil
624
- end
625
- it { should be_empty }
626
- end
627
- end
628
- end
629
633
  end
630
634
  end
631
635
 
@@ -682,4 +686,24 @@ describe Attributor::Attribute do
682
686
  end
683
687
  end
684
688
  end
689
+
690
+ context '.nullable_attribute?' do
691
+ subject { described_class.nullable_attribute?(options) }
692
+ context 'with null: true option' do
693
+ let(:options) { { null: true } }
694
+ it { should be_truthy }
695
+ end
696
+ context 'with null: false option' do
697
+ let(:options) { { null: false } }
698
+ it { should be_falsey }
699
+ end
700
+ context 'defaults to false without any null option' do
701
+ let(:options) { { } }
702
+ it { should be_falsey }
703
+ end
704
+ context 'defaults to false if null: nil' do
705
+ let(:options) { { null: nil } }
706
+ it { should be_falsey }
707
+ end
708
+ end
685
709
  end
@@ -33,4 +33,13 @@ describe Attributor::FieldSelector do
33
33
  end
34
34
  end
35
35
  end
36
+
37
+ context '.as_json_schema' do
38
+ subject(:js){ type.as_json_schema }
39
+ it 'adds the right attributes' do
40
+ expect(js.keys).to include(:type, :'x-type_name')
41
+ expect(js[:type]).to eq(:string)
42
+ expect(js[:'x-type_name']).to eq('FieldSelector')
43
+ end
44
+ end
36
45
  end
@@ -109,31 +109,31 @@ describe Attributor::HashDSLCompiler do
109
109
  context 'for :all' do
110
110
  let(:arguments) { { all: [:one, :two, :three] } }
111
111
  let(:value) { [:one] }
112
- let(:validation_error) { ['Key two is required for $.', 'Key three is required for $.'] }
112
+ let(:validation_error) { ["Attribute $.key(:two) is required.", "Attribute $.key(:three) is required."] }
113
113
  it { expect(subject).to include(*validation_error) }
114
114
  end
115
115
  context 'for :exactly' do
116
116
  let(:requirement) { req_class.new(exactly: 1).of(:one, :two) }
117
117
  let(:value) { [:one, :two] }
118
- let(:validation_error) { 'Exactly 1 of the following keys [:one, :two] are required for $. Found 2 instead: [:one, :two]' }
118
+ let(:validation_error) { 'Exactly 1 of the following attributes [:one, :two] are required for $. Found 2 instead: [:one, :two]' }
119
119
  it { expect(subject).to include(validation_error) }
120
120
  end
121
121
  context 'for :at_least' do
122
122
  let(:requirement) { req_class.new(at_least: 2).of(:one, :two, :three) }
123
123
  let(:value) { [:one] }
124
- let(:validation_error) { 'At least 2 keys out of [:one, :two, :three] are required to be passed in for $. Found [:one]' }
124
+ let(:validation_error) { 'At least 2 attributes out of [:one, :two, :three] are required to be passed in for $. Found [:one]' }
125
125
  it { expect(subject).to include(validation_error) }
126
126
  end
127
127
  context 'for :at_most' do
128
128
  let(:requirement) { req_class.new(at_most: 1).of(:one, :two, :three) }
129
129
  let(:value) { [:one, :two] }
130
- let(:validation_error) { 'At most 1 keys out of [:one, :two, :three] can be passed in for $. Found [:one, :two]' }
130
+ let(:validation_error) { 'At most 1 attributes out of [:one, :two, :three] can be passed in for $. Found [:one, :two]' }
131
131
  it { expect(subject).to include(validation_error) }
132
132
  end
133
133
  context 'for :exclusive' do
134
134
  let(:arguments) { { exclusive: [:one, :two] } }
135
135
  let(:value) { [:one, :two] }
136
- let(:validation_error) { 'keys [:one, :two] are mutually exclusive for $.' }
136
+ let(:validation_error) { 'Attributes [:one, :two] are mutually exclusive for $.' }
137
137
  it { expect(subject).to include(validation_error) }
138
138
  end
139
139
  end
data/spec/spec_helper.rb CHANGED
@@ -23,9 +23,7 @@ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
23
23
 
24
24
  RSpec.configure do |config|
25
25
  config.around(:each) do |example|
26
- Attributor::AttributeResolver.current = Attributor::AttributeResolver.new
27
26
  example.run
28
- Attributor::AttributeResolver.current = nil
29
27
  end
30
28
  end
31
29
 
@@ -0,0 +1,7 @@
1
+ class PositiveIntegerType < Attributor::Integer
2
+
3
+ def self.options
4
+ { min: 0 }
5
+ end
6
+
7
+ end
@@ -11,9 +11,9 @@ end
11
11
 
12
12
  class Duck < Attributor::Model
13
13
  attributes do
14
- attribute :age, Attributor::Integer, required_if: { 'name' => 'Daffy' }
14
+ attribute :age, Attributor::Integer
15
15
  attribute :name, Attributor::String
16
- attribute :email, Attributor::String, required_if: 'name'
16
+ attribute :email, Attributor::String
17
17
  attribute :angry, Attributor::Boolean, default: true, example: /true|false/, description: 'Angry bird?'
18
18
  attribute :weight, Attributor::Float, example: /\d{1,2}\.\d/, description: 'The weight of the duck'
19
19
  attribute :type, Attributor::Symbol, values: [:duck]
@@ -50,15 +50,15 @@ class Cormorant < Attributor::Model
50
50
  end
51
51
 
52
52
  # This will be a collection of arbitrary Ruby Objects
53
- attribute :fish, Attributor::Collection, description: 'All kinds of fish for feeding the babies'
53
+ attribute :all_the_fish, Attributor::Collection, description: 'All kinds of fish for feeding the babies'
54
54
 
55
55
  # This will be a collection of Cormorants (note, this relationship is circular)
56
- attribute :neighbors, Attributor::Collection.of(Cormorant), description: 'Neighbor cormorants'
56
+ attribute :neighbors, Attributor::Collection.of(Cormorant), member_options: {null: false}, description: 'Neighbor cormorants', null: false
57
57
 
58
58
  # This will be a collection of instances of an anonymous Struct class, each having two well-defined attributes
59
59
 
60
60
  attribute :babies, Attributor::Collection.of(Attributor::Struct), description: 'All the babies', member_options: { identity: :name } do
61
- attribute :name, Attributor::String, example: /[:name]/, description: 'The name of the baby cormorant'
61
+ attribute :name, Attributor::String, example: /[:name]/, description: 'The name of the baby cormorant', required: true
62
62
  attribute :months, Attributor::Integer, default: 0, min: 0, description: 'The age in months of the baby cormorant'
63
63
  attribute :weight, Attributor::Float, example: /\d{1,2}\.\d{3}/, description: 'The weight in kg of the baby cormorant'
64
64
  end
@@ -76,8 +76,8 @@ end
76
76
 
77
77
  class Address < Attributor::Model
78
78
  attributes do
79
- attribute :name, String, example: /\w+/
80
- attribute :state, String, values: %w(OR CA)
79
+ attribute :name, String, example: /\w+/, null: true
80
+ attribute :state, String, values: %w(OR CA), null: false
81
81
  attribute :person, Person, example: proc { |address, context| Person.example(context, address: address) }
82
82
  requires :name
83
83
  end
@@ -45,4 +45,12 @@ describe Attributor::BigDecimal do
45
45
  end
46
46
  end
47
47
  end
48
+ context '.as_json_schema' do
49
+ subject(:js){ type.as_json_schema }
50
+ it 'adds the right attributes' do
51
+ expect(js.keys).to include(:type, :'x-type_name')
52
+ expect(js[:type]).to eq(:number)
53
+ expect(js[:'x-type_name']).to eq('BigDecimal')
54
+ end
55
+ end
48
56
  end
@@ -7,6 +7,8 @@ describe Attributor::Boolean do
7
7
  expect(type.new.is_a?(Attributor::Dumpable)).not_to be(true)
8
8
  end
9
9
 
10
+ its(:json_schema_type){ should eq(:boolean)}
11
+
10
12
  context '.valid_type?' do
11
13
  context 'for incoming Boolean values' do
12
14
  [false, true].each do |value|
@@ -63,4 +65,12 @@ describe Attributor::Boolean do
63
65
  end
64
66
  end
65
67
  end
68
+ context '.as_json_schema' do
69
+ subject(:js){ type.as_json_schema }
70
+ it 'adds the right attributes' do
71
+ expect(js.keys).to include(:type, :'x-type_name')
72
+ expect(js[:type]).to eq(:boolean)
73
+ expect(js[:'x-type_name']).to eq('Boolean')
74
+ end
75
+ end
66
76
  end
@@ -344,4 +344,20 @@ describe Attributor::Collection do
344
344
  end.to_not raise_error
345
345
  end
346
346
  end
347
+
348
+ context '.as_json_schema' do
349
+ let(:member_type) { Attributor::String }
350
+ let(:type) { Attributor::Collection.of(member_type) }
351
+ let(:attribute_options) do
352
+ {}
353
+ end
354
+ subject(:js){ type.as_json_schema(attribute_options: attribute_options) }
355
+
356
+ it 'adds the right attributes' do
357
+ expect(js.keys).to include(:type, :'x-type_name', :items)
358
+ expect(js[:type]).to eq(:array)
359
+ expect(js[:'x-type_name']).to eq('Collection')
360
+ expect(js[:items]).to eq(member_type.as_json_schema)
361
+ end
362
+ end
347
363
  end
@@ -92,4 +92,13 @@ describe Attributor::Date do
92
92
  end
93
93
  end
94
94
  end
95
+ context '.as_json_schema' do
96
+ subject(:js){ type.as_json_schema }
97
+ it 'adds the right attributes' do
98
+ expect(js.keys).to include(:type, :'x-type_name')
99
+ expect(js[:type]).to eq(:string)
100
+ expect(js[:format]).to eq(:'date')
101
+ expect(js[:'x-type_name']).to eq('Date')
102
+ end
103
+ end
95
104
  end
@@ -92,4 +92,13 @@ describe Attributor::DateTime do
92
92
  end
93
93
  end
94
94
  end
95
+ context '.as_json_schema' do
96
+ subject(:js){ type.as_json_schema }
97
+ it 'adds the right attributes' do
98
+ expect(js.keys).to include(:type, :'x-type_name', :format)
99
+ expect(js[:type]).to eq(:string)
100
+ expect(js[:format]).to eq(:'date-time')
101
+ expect(js[:'x-type_name']).to eq('DateTime')
102
+ end
103
+ end
95
104
  end
@@ -76,4 +76,12 @@ describe Attributor::Float do
76
76
  end
77
77
  end
78
78
  end
79
+ context '.as_json_schema' do
80
+ subject(:js){ type.as_json_schema }
81
+ it 'adds the right attributes' do
82
+ expect(js.keys).to include(:type, :'x-type_name')
83
+ expect(js[:type]).to eq(:number)
84
+ expect(js[:'x-type_name']).to eq('Float')
85
+ end
86
+ end
79
87
  end