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.
@@ -0,0 +1,217 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe 'ValidatesType' do
4
+ context 'supported types' do
5
+ before do
6
+ subject.attribute = value
7
+ end
8
+
9
+ describe 'String' do
10
+ subject { ActiveModel::TypeValidationTestClass.set_accessor_and_long_validator(:string) }
11
+
12
+ context 'field value is a String' do
13
+ let(:value) { 'some string' }
14
+
15
+ specify do
16
+ expect(subject).to be_valid
17
+ end
18
+ end
19
+
20
+ context 'field value is not a String' do
21
+ let(:value) { -1 }
22
+ specify do
23
+ expect(subject).to_not be_valid
24
+ end
25
+
26
+ specify do
27
+ subject.validate
28
+ expect(subject.errors).to_not be_empty
29
+ expect(subject.errors.messages[:attribute][0]).to match(/is expected to be a String and is not/)
30
+ end
31
+ end
32
+ end
33
+
34
+ describe 'Integer' do
35
+
36
+ subject { ActiveModel::TypeValidationTestClass.set_accessor_and_long_validator(:integer) }
37
+
38
+ context 'field value is an Integer' do
39
+ let(:value) { 20 }
40
+
41
+ specify do
42
+ expect(subject).to be_valid
43
+ end
44
+ end
45
+
46
+ context 'field value is not an Integer' do
47
+ let(:value) { {} }
48
+
49
+ specify do
50
+ expect(subject).to_not be_valid
51
+ end
52
+
53
+ specify do
54
+ subject.validate
55
+ expect(subject.errors).to_not be_empty
56
+ expect(subject.errors.messages[:attribute][0]).to match(/is expected to be a Integer and is not/)
57
+ end
58
+ end
59
+ end
60
+
61
+ describe 'Float' do
62
+
63
+ subject { ActiveModel::TypeValidationTestClass.set_accessor_and_long_validator(:float) }
64
+
65
+ context 'field value is a Float' do
66
+ let(:value) { 1.2 }
67
+
68
+ specify do
69
+ expect(subject).to be_valid
70
+ end
71
+ end
72
+
73
+ context 'field value is not a Float' do
74
+ let(:value) { 10 }
75
+ specify do
76
+ expect(subject).to_not be_valid
77
+ end
78
+
79
+ specify do
80
+ subject.validate
81
+ expect(subject.errors).to_not be_empty
82
+ expect(subject.errors.messages[:attribute][0]).to match(/is expected to be a Float and is not/)
83
+ end
84
+ end
85
+ end
86
+
87
+ describe 'Boolean' do
88
+
89
+ subject { ActiveModel::TypeValidationTestClass.set_accessor_and_long_validator(:boolean) }
90
+
91
+ context 'field value is a Boolean' do
92
+ let(:value) { true }
93
+
94
+ specify do
95
+ expect(subject).to be_valid
96
+ end
97
+ end
98
+
99
+ context 'field value is not a Boolean' do
100
+ let(:value) { 'true' }
101
+ specify do
102
+ expect(subject).to_not be_valid
103
+ end
104
+
105
+ specify do
106
+ subject.validate
107
+ expect(subject.errors).to_not be_empty
108
+ expect(subject.errors.messages[:attribute][0]).to match(/is expected to be a Boolean and is not/)
109
+ end
110
+ end
111
+ end
112
+
113
+ describe 'Hash' do
114
+
115
+ subject { ActiveModel::TypeValidationTestClass.set_accessor_and_long_validator(:hash) }
116
+
117
+ context 'field value is a Hash' do
118
+ let(:value) { {} }
119
+
120
+ specify do
121
+ expect(subject).to be_valid
122
+ end
123
+ end
124
+
125
+ context 'field value is not a Hash' do
126
+ let(:value) { [] }
127
+ specify do
128
+ expect(subject).to_not be_valid
129
+ end
130
+
131
+ specify do
132
+ subject.validate
133
+ expect(subject.errors).to_not be_empty
134
+ expect(subject.errors.messages[:attribute][0]).to match(/is expected to be a Hash and is not/)
135
+ end
136
+ end
137
+ end
138
+
139
+ describe 'Array' do
140
+
141
+ subject { ActiveModel::TypeValidationTestClass.set_accessor_and_long_validator(:array) }
142
+
143
+ context 'field value is an Array' do
144
+ let(:value) { [] }
145
+
146
+ specify do
147
+ expect(subject).to be_valid
148
+ end
149
+ end
150
+
151
+ context 'field value is not an Array' do
152
+ let(:value) { true }
153
+ specify do
154
+ expect(subject).to_not be_valid
155
+ end
156
+
157
+ specify do
158
+ subject.validate
159
+ expect(subject.errors).to_not be_empty
160
+ expect(subject.errors.messages[:attribute][0]).to match(/is expected to be a Array and is not/)
161
+ end
162
+ end
163
+ end
164
+
165
+ describe 'Symbol' do
166
+
167
+ subject { ActiveModel::TypeValidationTestClass.set_accessor_and_long_validator(:symbol) }
168
+
169
+ context 'field value is a Symbol' do
170
+ let(:value) { :foo }
171
+
172
+ specify do
173
+ expect(subject).to be_valid
174
+ end
175
+ end
176
+
177
+ context 'field value is not a Symbol' do
178
+ let(:value) { [] }
179
+ specify do
180
+ expect(subject).to_not be_valid
181
+ end
182
+
183
+ specify do
184
+ subject.validate
185
+ expect(subject.errors).to_not be_empty
186
+ expect(subject.errors.messages[:attribute][0]).to match(/is expected to be a Symbol and is not/)
187
+ end
188
+ end
189
+ end
190
+
191
+ context 'passing in a custom message' do
192
+ subject { ActiveModel::TypeValidationTestClass.set_accessor_and_long_validator(:string, message: 'is not a String!') }
193
+
194
+ context 'when validation fails' do
195
+ let(:value) { 1 }
196
+
197
+ specify do
198
+ subject.validate
199
+ expect(subject.errors.messages[:attribute][0]).to match(/is not a String!/)
200
+ end
201
+ end
202
+ end
203
+ end
204
+
205
+ context 'unsupported types' do
206
+ describe 'Foo' do
207
+ specify do
208
+ expect do
209
+ subject = ActiveModel::TypeValidationTestClass.set_accessor_and_long_validator(:foo)
210
+ subject.valid?
211
+ end.to raise_error(
212
+ ValidatesType::UnsupportedType,
213
+ "Unsupported type Foo given for validates_type.")
214
+ end
215
+ end
216
+ end
217
+ end
@@ -0,0 +1,47 @@
1
+ module ActiveModel
2
+ class TypeValidationTestClass
3
+ include Validations
4
+
5
+ # Creates and returns a new instance of TypeValidationTestClass with
6
+ # a "long" style validator:
7
+ #
8
+ # class TypeValidationTestClass
9
+ # attr_accessor :attribute
10
+ #
11
+ # validates_type :attribute, :string
12
+ # end
13
+ #
14
+ # @set_accessor_and_long_validator
15
+ # param: type <Symbol> - type which to validate against
16
+ # param: options<Hash *Optional*> - extra modifiers/custom messaging
17
+ # return: TypeValidationTestClass instance with set validator
18
+ def self.set_accessor_and_long_validator(type, options = {})
19
+ self.new.tap do |test_class|
20
+ test_class._validators = {}
21
+ test_class.class_eval { attr_accessor :attribute }
22
+ test_class.class_eval { validates_type :attribute, type.to_sym, options }
23
+ end
24
+ end
25
+
26
+ # Creates and returns a new instance of TypeValidationTestClass with
27
+ # a "short" style validator:
28
+ #
29
+ # class TypeValidationTestClass
30
+ # attr_accessor :attribute
31
+ #
32
+ # validates :attribute, type: { type: :string }
33
+ # end
34
+ #
35
+ # @set_accessor_and_long_validator
36
+ # param: type <Symbol> - type which to validate against
37
+ # param: options<Hash *Optional*> - extra modifiers/custom messaging
38
+ # return: TypeValidationTestClass instance with set validator
39
+ def self.set_accessor_and_validator(type, options = {})
40
+ self.new.tap do |test_class|
41
+ test_class._validators = {}
42
+ test_class.class_eval { attr_accessor :attribute }
43
+ test_class.class_eval { validates :attribute, type: { type: type }.merge(options) }
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,33 @@
1
+ ActiveRecord::Base.establish_connection(
2
+ "adapter" => "sqlite3",
3
+ "database" => ":memory:"
4
+ )
5
+
6
+ ActiveRecord::Migration.verbose = false
7
+ ActiveRecord::Schema.define do
8
+ create_table :type_validation_tests do |t|
9
+ t.string :test_attribute
10
+ end
11
+ end
12
+
13
+ def drop_and_create_column_with_type(column_type)
14
+ ActiveRecord::Schema.define do
15
+ change_column :type_validation_tests, :test_attribute, column_type.to_sym
16
+ end
17
+ end
18
+
19
+ class TypeValidationTest < ActiveRecord::Base
20
+ def self.set_accessor_and_long_validator(type, options = {})
21
+ self.new.tap do |test_class|
22
+ test_class._validators = {}
23
+ test_class.class_eval { validates_type :test_attribute, type.to_sym, options }
24
+ end
25
+ end
26
+
27
+ def self.set_accessor_and_validator(type, options = {})
28
+ self.new.tap do |test_class|
29
+ test_class._validators = {}
30
+ test_class.class_eval { validates :test_attribute, type: { type: type }.merge(options) }
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe 'ValidatesType' do
4
+ context 'validates_type :attribute' do
5
+ subject { TypeValidationTest.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 { TypeValidationTest.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.test_attribute = value
10
+ end
11
+
12
+ describe 'message' do
13
+ subject do
14
+ TypeValidationTest.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[:test_attribute][0]).to match(/is not a String!/)
24
+ end
25
+ end
26
+ end
27
+
28
+ describe 'allow_nil' do
29
+ subject do
30
+ TypeValidationTest.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
+ TypeValidationTest.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
+ TypeValidationTest.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
+ TypeValidationTest.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
+ TypeValidationTest.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
+ TypeValidationTest.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
+ TypeValidationTest.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.test_attribute = value
267
+ end
268
+
269
+ describe 'message: with if:' do
270
+ subject do
271
+ TypeValidationTest.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[:test_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