validates_type 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 095e33afbb9a0aebd616f77ee2be6a6c6f44598b
4
+ data.tar.gz: 27aa41f31c5dd6935ce8ccc41c5fde70ac54844f
5
+ SHA512:
6
+ metadata.gz: e4996fa85a21db804072016958f327f6831e9a4dd34c9702b483467f62411d713b5cbb034a53eb5e309674751eb8a8e6a51f5f988337412c46429e44e703d5de
7
+ data.tar.gz: 0ba9a4f043b605d20bed49fc6b7a6d78c77614d34af5bdd20cf9f60f5b851332a015c0c0acbaae252a3123cc9bf265bf9b1bd25a3e20179d46af64d1618cfe57
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ .ruby*
2
+ *.gem
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm: 2.2.1
3
+ script: bundle exec rspec spec
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'activemodel'
4
+ gem 'ruby-boolean'
5
+
6
+ group :test do
7
+ gem 'rspec'
8
+ gem 'pry'
9
+ gem 'activerecord'
10
+ gem 'sqlite3'
11
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,58 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ activemodel (4.2.1)
5
+ activesupport (= 4.2.1)
6
+ builder (~> 3.1)
7
+ activerecord (4.2.1)
8
+ activemodel (= 4.2.1)
9
+ activesupport (= 4.2.1)
10
+ arel (~> 6.0)
11
+ activesupport (4.2.1)
12
+ i18n (~> 0.7)
13
+ json (~> 1.7, >= 1.7.7)
14
+ minitest (~> 5.1)
15
+ thread_safe (~> 0.3, >= 0.3.4)
16
+ tzinfo (~> 1.1)
17
+ arel (6.0.0)
18
+ builder (3.2.2)
19
+ coderay (1.1.0)
20
+ diff-lcs (1.2.5)
21
+ i18n (0.7.0)
22
+ json (1.8.2)
23
+ method_source (0.8.2)
24
+ minitest (5.6.1)
25
+ pry (0.10.1)
26
+ coderay (~> 1.1.0)
27
+ method_source (~> 0.8.1)
28
+ slop (~> 3.4)
29
+ rspec (3.2.0)
30
+ rspec-core (~> 3.2.0)
31
+ rspec-expectations (~> 3.2.0)
32
+ rspec-mocks (~> 3.2.0)
33
+ rspec-core (3.2.3)
34
+ rspec-support (~> 3.2.0)
35
+ rspec-expectations (3.2.1)
36
+ diff-lcs (>= 1.2.0, < 2.0)
37
+ rspec-support (~> 3.2.0)
38
+ rspec-mocks (3.2.1)
39
+ diff-lcs (>= 1.2.0, < 2.0)
40
+ rspec-support (~> 3.2.0)
41
+ rspec-support (3.2.2)
42
+ ruby-boolean (1.3.3)
43
+ slop (3.6.0)
44
+ sqlite3 (1.3.10)
45
+ thread_safe (0.3.5)
46
+ tzinfo (1.2.2)
47
+ thread_safe (~> 0.1)
48
+
49
+ PLATFORMS
50
+ ruby
51
+
52
+ DEPENDENCIES
53
+ activemodel
54
+ activerecord
55
+ pry
56
+ rspec
57
+ ruby-boolean
58
+ sqlite3
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Jake Yesbeck
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,90 @@
1
+ ## validates_type
2
+
3
+ ![Build Status](https://travis-ci.org/yez/validates_type.svg?branch=master)
4
+
5
+ ### Rails type validation
6
+
7
+ #### Purpose
8
+
9
+ Most Rails applications will have types coerced by their ORM connection adapters (like the `pg` gem or `mysql2`). However, this only useful for applications with very well defined schemas. If your application has a legacy storage layer that you can no longer modify or a lot of `store_accessor` columns, this solution is a nice middle ground to ensure your data is robust.
10
+
11
+ This also prevents your data from being coerced into values that you did not intend by ActiveRecord adapters.
12
+
13
+ #### Usage
14
+
15
+ ##### With ActiveRecord
16
+
17
+ ```ruby
18
+ class Foo < ActiveRecord::Base
19
+
20
+ # validate that attribute :bar is a String
21
+ validates_type :bar, :string
22
+
23
+ # validate that attribute :baz is an Integer with a custom error message
24
+ validates_type :baz, :integer, message: 'Baz must be an Integer'
25
+
26
+ # validate that attribute :qux is an Array, allow blank
27
+ validates_type :qux, :array, allow_blank: true
28
+
29
+ # validate that attribute :whatever is a Boolean
30
+ validates_type :whatever, :boolean
31
+
32
+ # validate that attribute :thing is a Float or nil
33
+ validates_type :thing, :float, allow_nil: true
34
+ end
35
+ ```
36
+
37
+ ##### With ActiveModel
38
+
39
+ ```ruby
40
+ class Bar
41
+ include ActiveModel::Validations
42
+
43
+ attr_accessor :foo, :qux
44
+
45
+ validates_type :foo, :string
46
+
47
+ # Custom error message support
48
+ validates_type :qux, :boolean, message: 'Attribute qux must be a boolean!'
49
+ end
50
+ ```
51
+
52
+ ##### With shortcut syntax
53
+
54
+ ```ruby
55
+ class Banana < ActiveRecord::Base
56
+
57
+ # The banana's peel attribute must be a string
58
+ validates :peel, type: { type: :string }
59
+
60
+ # Custom error message for ripeness of banana
61
+ validates :ripe, type: { type: :boolean, message: 'Only ripe bananas allowed' }
62
+ end
63
+ ```
64
+
65
+ ##### With multiple modifiers
66
+
67
+ ```ruby
68
+ class Foo < ActiveRecord::Base
69
+ # validate that attribute :baz is an Integer with a custom error message
70
+ # only if :conditional_method evaluates to true
71
+ validates_type :baz, :integer, message: 'Baz must be an Integer', if: :conditional_method
72
+
73
+ def conditional_method
74
+ # some kind of logic that is important to pass
75
+ end
76
+
77
+ # validate that attribute :baz is a Float and is included in a specific array
78
+ validates_type :foo, :float, in: [1.0, 2.5, 3.0]
79
+ end
80
+ ```
81
+
82
+ #### Supported types
83
+
84
+ - `:array`
85
+ - `:boolean`
86
+ - `:float`
87
+ - `:hash`
88
+ - `:integer`
89
+ - `:string`
90
+ - `:symbol`
@@ -0,0 +1,4 @@
1
+ # Error class to raise if unsupported type given to validates_url
2
+ module ValidatesType
3
+ class UnsupportedType < StandardError; end
4
+ end
data/lib/arguments.rb ADDED
@@ -0,0 +1,44 @@
1
+ module ValidatesType
2
+ # Wrapper class for arguments consumed by validates_with
3
+ class Arguments
4
+ # @initialize
5
+ # param: attribute_name <Symbol> - name of attribute that will be validated
6
+ # param: attribute_type <Symbol> - type for which to validate the attribute against
7
+ # param: options <Hash> - extra options to pass along to the validator
8
+ # i.e. allow_nil: true, message: 'my custom message'
9
+ # return: nil
10
+ def initialize(attribute_name, attribute_type, options)
11
+ @attribute_name = attribute_name
12
+ @attribute_type = attribute_type
13
+ @options = options.is_a?(Hash) ? options : {}
14
+ end
15
+
16
+ # format expected by _merge_attributes
17
+ #
18
+ # @to_validation_attributes
19
+ # return: <Array> - cardinality of 2
20
+ def to_validation_attributes
21
+ [@attribute_name, merged_options]
22
+ end
23
+
24
+ private
25
+
26
+ # helper method to compact all the options together along
27
+ # with the type for validation
28
+ #
29
+ # @merged_options
30
+ # return: <Hash>
31
+ def merged_options
32
+ type.merge(@options)
33
+ end
34
+
35
+ # helper method to impose the type for validation into an option
36
+ # that will be merged later
37
+ #
38
+ # @type
39
+ # return: <Hash>
40
+ def type
41
+ { :type => @attribute_type }
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,138 @@
1
+ require 'ruby-boolean'
2
+ require 'active_model'
3
+
4
+ require_relative '../errors/unsupported_type'
5
+ require_relative './arguments'
6
+
7
+ module ActiveModel
8
+ module Validations
9
+ class TypeValidator < ActiveModel::EachValidator
10
+ # Set default message for failure here
11
+ #
12
+ # @initialize
13
+ # param: options <Hash> - options hash of how to validate this attribute
14
+ # including custom messaging due to failures, specifying
15
+ # the type of the attribute to validate against, etc.
16
+ # return: result of ActiveModel::Validations::EachValidator initialize
17
+ def initialize(options)
18
+ merged_options = {
19
+ :message => "is expected to be a #{ symbol_class(options[:type]) } and is not."
20
+ }.merge(options)
21
+
22
+ super(merged_options)
23
+ end
24
+
25
+ # Validate that the value is the type we expect
26
+ #
27
+ # @validate_each
28
+ # param: record <Object> - subject containing attribute to validate
29
+ # param: attribute <Symbol> - name of attribute to validate
30
+ # param: value <Variable> - value of attribute to validate
31
+ # return: nil
32
+ def validate_each(record, attribute, value)
33
+ value_to_test = type_before_coercion(record, attribute, value)
34
+ expected_type = symbol_class(options[:type])
35
+
36
+ add_errors_or_raise(options, record, attribute) unless value_to_test.is_a?(expected_type)
37
+ end
38
+
39
+ private
40
+
41
+ # Helper method to either add messages to the errors object
42
+ # or raise an exception in :strict mode
43
+ #
44
+ # @add_errors_or_raise
45
+ # param: options <Hash> - options hash with strict flag or class
46
+ # param: record <Object> - subject containg attribute to validate
47
+ # param: attribute <Symbol> - name of attribute under validation
48
+ # return: nil
49
+ def add_errors_or_raise(options, record, attribute)
50
+ error = options_error(options[:strict])
51
+
52
+ raise error unless error.nil?
53
+
54
+ record.errors.add(attribute, options[:message])
55
+ end
56
+
57
+ # Helper method to return the base expected error:
58
+ # ActiveModel::StrictValidationFailed, a custom error, or nil
59
+ #
60
+ # @options_error
61
+ # param: strict_error <true or subclass of Exception> - either the flag
62
+ # to raise an error or the actual error to raise
63
+ # return: custom error, ActiveModel::StrictValidationFailed, or nil
64
+ def options_error(strict_error)
65
+ if strict_error == true
66
+ ActiveModel::StrictValidationFailed
67
+ elsif strict_error.try(:ancestors).try(:include?, Exception)
68
+ strict_error
69
+ end
70
+ end
71
+
72
+ # Helper method to convert a symbol into a class constant
73
+ #
74
+ # ex:
75
+ # symbol_class(:string) -> String
76
+ # symbol_class(:boolean) -> Boolean
77
+ # symbol_class(:hash) -> Hash
78
+ #
79
+ # @symbol_class
80
+ # param: symbol <Symbol> - symbol to turn into a classconstant
81
+ # return: class constant of supported types or raises UnsupportedType
82
+ def symbol_class(symbol)
83
+ @symbol_class ||= {
84
+ :array => Array,
85
+ :boolean => Boolean,
86
+ :float => Float,
87
+ :hash => Hash,
88
+ :integer => Integer,
89
+ :string => String,
90
+ :symbol => Symbol,
91
+ }[symbol] || fail(ValidatesType::UnsupportedType,
92
+ "Unsupported type #{ symbol.to_s.camelize } given for validates_type.")
93
+ end
94
+
95
+ # Helper method to circumvent active record's coercion
96
+ #
97
+ # @type_before_coercion
98
+ # param: record <Object> - subject of validation
99
+ # param: value <Variable> - current value of attribute
100
+ # return: the value of the attribute before active record's coercion
101
+ # or the current value
102
+ def type_before_coercion(record, attribute, value)
103
+ record.try(:"#{ attribute }_before_type_cast") || value
104
+ end
105
+ end
106
+
107
+ module ClassMethods
108
+ # Validates the type of an attribute with supported types:
109
+ # - :array
110
+ # - :boolean
111
+ # - :float
112
+ # - :hash
113
+ # - :integer
114
+ # - :string
115
+ # - :symbol
116
+ #
117
+ # class Foo
118
+ # include ActiveModel::Validations
119
+ #
120
+ # attr_accessor :thing, :something
121
+ #
122
+ # validates_type :thing, :boolean
123
+ # validates_type :something, :array
124
+ # end
125
+ #
126
+ # @validates_type
127
+ # param: attribute_name <Symbol> - name of attribute to validate
128
+ # param: attribute_type <Symbol> - type of attribute to validate against
129
+ # param: options <Hash> - other common options to validate methods calls
130
+ # i.e. message: 'my custom error message'
131
+ # return: nil
132
+ def validates_type(attribute_name, attribute_type, options = {})
133
+ args = ValidatesType::Arguments.new(attribute_name, attribute_type, options)
134
+ validates_with TypeValidator, _merge_attributes(args.to_validation_attributes)
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,33 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe 'ValidatesType' do
4
+ context 'validates_type :attribute' do
5
+ subject { ActiveModel::TypeValidationTestClass.set_accessor_and_long_validator(:string) }
6
+
7
+ it 'adds a validator to the subject' do
8
+ klass = subject.class
9
+ expect(klass.validators).to_not be_empty
10
+ expect(klass.validators).to include(ActiveModel::Validations::TypeValidator)
11
+ end
12
+
13
+ it 'adds the correct validator to the subject' do
14
+ validator = subject.class.validators.find { |v| v.is_a?(ActiveModel::Validations::TypeValidator) }
15
+ expect(validator.options[:type]).to eq(:string)
16
+ end
17
+ end
18
+
19
+ context 'validates :attribute, type: { type: type }.merge(other_options)' do
20
+ subject { ActiveModel::TypeValidationTestClass.set_accessor_and_validator(:string) }
21
+
22
+ it 'adds a validator to the subject' do
23
+ klass = subject.class
24
+ expect(klass.validators).to_not be_empty
25
+ expect(klass.validators).to include(ActiveModel::Validations::TypeValidator)
26
+ end
27
+
28
+ it 'adds the correct validator to the subject' do
29
+ validator = subject.class.validators.find { |v| v.is_a?(ActiveModel::Validations::TypeValidator) }
30
+ expect(validator.options[:type]).to eq(:string)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,319 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe 'ValidatesType' do
4
+ %i[set_accessor_and_long_validator
5
+ set_accessor_and_validator].each do |validate_version|
6
+ context "#{ validate_version }" do
7
+ context 'custom modifiers' do
8
+ before do
9
+ subject.attribute = value
10
+ end
11
+
12
+ describe 'message' do
13
+ subject do
14
+ ActiveModel::TypeValidationTestClass.send(validate_version,
15
+ :string, message: 'is not a String!')
16
+ end
17
+
18
+ context 'when validation fails' do
19
+ let(:value) { 1 }
20
+
21
+ specify do
22
+ subject.validate
23
+ expect(subject.errors.messages[:attribute][0]).to match(/is not a String!/)
24
+ end
25
+ end
26
+ end
27
+
28
+ describe 'allow_nil' do
29
+ subject do
30
+ ActiveModel::TypeValidationTestClass.send(validate_version,
31
+ :string, allow_nil: true)
32
+ end
33
+
34
+ context 'field value is nil' do
35
+ let(:value) { nil }
36
+
37
+ specify do
38
+ expect(subject).to be_valid
39
+ end
40
+ end
41
+
42
+ context 'field value is not nil' do
43
+ context 'field value is the specified type' do
44
+ let(:value) { 'I am a string'}
45
+
46
+ specify do
47
+ expect(subject).to be_valid
48
+ end
49
+ end
50
+
51
+ context 'field value is not specified type' do
52
+ let(:value) { -1 }
53
+
54
+ specify do
55
+ expect(subject).to_not be_valid
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ describe 'allow_blank' do
62
+ subject do
63
+ ActiveModel::TypeValidationTestClass.send(validate_version,
64
+ :integer, allow_blank: true)
65
+ end
66
+
67
+ context 'field value is nil' do
68
+ let(:value) { nil }
69
+
70
+ specify do
71
+ expect(subject).to be_valid
72
+ end
73
+ end
74
+
75
+ context 'field value is an empty string' do
76
+ let(:value) { '' }
77
+
78
+ specify do
79
+ expect(subject).to be_valid
80
+ end
81
+ end
82
+
83
+ context 'field value is not nil' do
84
+ context 'field value is the specified type' do
85
+ let(:value) { 1 }
86
+
87
+ specify do
88
+ expect(subject).to be_valid
89
+ end
90
+ end
91
+
92
+ context 'field value is not specified type' do
93
+ let(:value) { { foo: :bar } }
94
+
95
+ specify do
96
+ expect(subject).to_not be_valid
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ describe 'strict' do
103
+ subject do
104
+ ActiveModel::TypeValidationTestClass.send(validate_version,
105
+ :string, strict: true)
106
+ end
107
+
108
+ context 'field value is expected type' do
109
+ let(:value) { 'I am a string' }
110
+
111
+ specify do
112
+ expect do
113
+ subject.valid?
114
+ end.to_not raise_error
115
+ end
116
+ end
117
+
118
+ context 'field value is not expected type' do
119
+ let(:value) { -1 }
120
+
121
+ specify do
122
+ expect do
123
+ subject.valid?
124
+ end.to raise_error(ActiveModel::StrictValidationFailed)
125
+ end
126
+
127
+ context 'with a custom error class' do
128
+ class UhOhSpaghettios < StandardError; end
129
+ subject do
130
+ ActiveModel::TypeValidationTestClass.send(validate_version,
131
+ :string, strict: UhOhSpaghettios)
132
+ end
133
+
134
+ specify do
135
+ expect do
136
+ subject.valid?
137
+ end.to raise_error(UhOhSpaghettios)
138
+ end
139
+ end
140
+ end
141
+ end
142
+
143
+ describe 'if' do
144
+ subject do
145
+ ActiveModel::TypeValidationTestClass.send(validate_version,
146
+ :string, if: condition)
147
+ end
148
+
149
+ context 'field value is expected type' do
150
+ let(:value) { 'I am a string' }
151
+
152
+ context 'condition is true' do
153
+ let(:condition) { ->{ true } }
154
+
155
+ specify do
156
+ expect(subject).to be_valid
157
+ end
158
+ end
159
+
160
+ context 'condition is false' do
161
+ let(:condition) { ->{ false } }
162
+
163
+ specify do
164
+ expect(subject).to be_valid
165
+ end
166
+ end
167
+ end
168
+
169
+ context 'field value is not expected type' do
170
+ let(:value) { -1 }
171
+
172
+ context 'condition is true' do
173
+ let(:condition) { ->{ true } }
174
+
175
+ specify do
176
+ expect(subject).to_not be_valid
177
+ end
178
+ end
179
+
180
+ context 'condition is false' do
181
+ let(:condition) { ->{ false } }
182
+
183
+ specify do
184
+ expect(subject).to be_valid
185
+ end
186
+ end
187
+ end
188
+ end
189
+
190
+ describe 'unless' do
191
+ subject do
192
+ ActiveModel::TypeValidationTestClass.send(validate_version,
193
+ :string, unless: condition)
194
+ end
195
+
196
+ context 'field value is expected type' do
197
+ let(:value) { 'I am a string' }
198
+
199
+ context 'condition is true' do
200
+ let(:condition) { ->{ true } }
201
+
202
+ specify do
203
+ expect(subject).to be_valid
204
+ end
205
+ end
206
+
207
+ context 'condition is false' do
208
+ let(:condition) { ->{ false } }
209
+
210
+ specify do
211
+ expect(subject).to be_valid
212
+ end
213
+ end
214
+ end
215
+
216
+ context 'field value is not expected type' do
217
+ let(:value) { -1 }
218
+
219
+ context 'condition is true' do
220
+ let(:condition) { ->{ true } }
221
+
222
+ specify do
223
+ expect(subject).to be_valid
224
+ end
225
+ end
226
+
227
+ context 'condition is false' do
228
+ let(:condition) { ->{ false } }
229
+
230
+ specify do
231
+ expect(subject).to_not be_valid
232
+ end
233
+ end
234
+ end
235
+ end
236
+
237
+ describe 'on' do
238
+ let(:value) { nil }
239
+ subject do
240
+ ActiveModel::TypeValidationTestClass.send(validate_version,
241
+ :string, on: :some_test_method)
242
+ end
243
+
244
+ before do
245
+ allow(subject).to receive(:some_test_method) { subject.validate }
246
+ end
247
+
248
+ context 'on: criteria is met' do
249
+ specify do
250
+ expect(subject).to receive(:validate)
251
+ subject.some_test_method
252
+ end
253
+ end
254
+
255
+ context 'on: criteria is not met' do
256
+ specify do
257
+ expect(subject).to_not receive(:validate)
258
+ subject.valid?
259
+ end
260
+ end
261
+ end
262
+ end
263
+
264
+ context 'multiple custom modifiers' do
265
+ before do
266
+ subject.attribute = value
267
+ end
268
+
269
+ describe 'message: with if:' do
270
+ subject do
271
+ ActiveModel::TypeValidationTestClass.send(validate_version,
272
+ :string, message: 'This is bad!', if: condition)
273
+ end
274
+
275
+ context 'condition is true' do
276
+ let(:condition) { -> { true } }
277
+
278
+ context 'field value is the expected type' do
279
+ let(:value) { 'I am a string' }
280
+
281
+ specify do
282
+ expect(subject).to be_valid
283
+ end
284
+ end
285
+
286
+ context 'field value is not the expected type' do
287
+ let(:value) { -1 }
288
+
289
+ specify do
290
+ expect(subject).to_not be_valid
291
+ expect(subject.errors.messages[:attribute][0]).to match(/This is bad!/)
292
+ end
293
+ end
294
+ end
295
+
296
+ context 'condition is false' do
297
+ let(:condition) { -> { false } }
298
+
299
+ context 'field value is the expected type' do
300
+ let(:value) { 'I am a string' }
301
+
302
+ specify do
303
+ expect(subject).to be_valid
304
+ end
305
+ end
306
+
307
+ context 'field value is not the expected type' do
308
+ let(:value) { -1 }
309
+
310
+ specify do
311
+ expect(subject).to be_valid
312
+ end
313
+ end
314
+ end
315
+ end
316
+ end
317
+ end
318
+ end
319
+ end