compel 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,72 @@
1
+ module Compel
2
+
3
+ module Validation
4
+
5
+ def valid?(value, options)
6
+ validate(value, options).length == 0
7
+ end
8
+
9
+ def validate!(value, options)
10
+ errors = validate(value, options)
11
+
12
+ if errors.length > 0
13
+ raise ParamValidationError, errors[0]
14
+ end
15
+ end
16
+
17
+ def validate(value, options)
18
+ errors = []
19
+
20
+ options.each do |option, option_value|
21
+ # most of this code snippet is from sinatra-param gem
22
+ # https://github.com/mattt/sinatra-param
23
+ # by Mattt Thompson (@mattt)
24
+ begin
25
+ case option.to_sym
26
+ when :required
27
+ raise ParamValidationError, 'is required' if option_value && value.nil?
28
+ when :blank
29
+ raise ParamValidationError, 'cannot be blank' if !option_value && case value
30
+ when String
31
+ !(/\S/ === value)
32
+ when Array, Hash
33
+ value.empty?
34
+ else
35
+ value.nil?
36
+ end
37
+ when :format
38
+ raise ParamValidationError, 'must be a string if using the format validation' unless value.kind_of?(String)
39
+ raise ParamValidationError, "must match format #{option_value}" unless value =~ option_value
40
+ when :is
41
+ raise ParamValidationError, "must be #{option_value}" unless value === option_value
42
+ when :in, :within, :range
43
+ raise ParamValidationError, "must be within #{option_value}" unless value.nil? || case option_value
44
+ when Range
45
+ option_value.include?(value)
46
+ else
47
+ Array(option_value).include?(value)
48
+ end
49
+ when :min
50
+ raise ParamValidationError, "cannot be less than #{option_value}" unless value.nil? || option_value <= value
51
+ when :max
52
+ raise ParamValidationError, "cannot be greater than #{option_value}" unless value.nil? || option_value >= value
53
+ when :length
54
+ raise ParamValidationError, "cannot have length different than #{option_value}" unless value.nil? || option_value == "#{value}".length
55
+ when :min_length
56
+ raise ParamValidationError, "cannot have length less than #{option_value}" unless value.nil? || option_value <= value.length
57
+ when :max_length
58
+ raise ParamValidationError, "cannot have length greater than #{option_value}" unless value.nil? || option_value >= value.length
59
+ end
60
+ rescue ParamValidationError => exception
61
+ errors << exception.message
62
+ end
63
+ end
64
+
65
+ errors
66
+ end
67
+
68
+ extend self
69
+
70
+ end
71
+
72
+ end
@@ -0,0 +1,3 @@
1
+ module Compel
2
+ VERSION = '0.1.1'
3
+ end
data/lib/compel.rb ADDED
@@ -0,0 +1,42 @@
1
+ require 'json'
2
+ require 'hashie'
3
+ require 'hashie/extensions/symbolize_keys'
4
+
5
+ require 'compel/coercion/type'
6
+ require 'compel/coercion/integer'
7
+ require 'compel/coercion/float'
8
+ require 'compel/coercion/string'
9
+ require 'compel/coercion/date'
10
+ require 'compel/coercion/time'
11
+ require 'compel/coercion/datetime'
12
+ require 'compel/coercion/hash'
13
+ require 'compel/coercion/json'
14
+ require 'compel/coercion/boolean'
15
+
16
+ require 'compel/invalid_params_error'
17
+ require 'compel/param_validation_error'
18
+ require 'compel/param_type_error'
19
+
20
+ require 'compel/param'
21
+ require 'compel/contract'
22
+ require 'compel/coercion'
23
+ require 'compel/validation'
24
+ require 'compel/errors'
25
+
26
+ module Compel
27
+
28
+ Boolean = Coercion::Boolean
29
+
30
+ def self.run!(params, &block)
31
+ Contract.new(params, &block).validate.raise?
32
+ end
33
+
34
+ def self.run?(params, &block)
35
+ Contract.new(params, &block).validate.valid?
36
+ end
37
+
38
+ def self.run(params, &block)
39
+ Contract.new(params, &block).validate.serialize
40
+ end
41
+
42
+ end
@@ -0,0 +1,186 @@
1
+ describe Compel::Coercion do
2
+
3
+ context 'Type coercion' do
4
+
5
+ context 'Integer' do
6
+
7
+ it 'should coerce' do
8
+ value = Compel::Coercion.coerce!(123, Integer)
9
+ expect(value).to eq(123)
10
+ end
11
+
12
+ it 'should coerce 1' do
13
+ value = Compel::Coercion.coerce!('123', Integer)
14
+ expect(value).to eq(123)
15
+ end
16
+
17
+ it 'should coerce 2' do
18
+ value = Compel::Coercion.coerce!(123.3, Integer)
19
+ expect(value).to eq(123)
20
+ end
21
+
22
+ it 'should not coerce' do
23
+ expect(Compel::Coercion.valid?('123abc', Integer)).to eq(false)
24
+
25
+ expect { Compel::Coercion.coerce!('123abc', Integer) }.to \
26
+ raise_error Compel::ParamTypeError, "'123abc' is not a valid Integer"
27
+ end
28
+
29
+ end
30
+
31
+ context 'Float' do
32
+
33
+ it 'should coerce' do
34
+ value = Compel::Coercion.coerce!('1.2', Float)
35
+
36
+ expect(value).to eq(1.2)
37
+ end
38
+
39
+ it 'should not coerce' do
40
+ expect(Compel::Coercion.valid?('1.a233', Float)).to eq(false)
41
+
42
+ expect { Compel::Coercion.coerce!('1.a233', Float) }.to \
43
+ raise_error Compel::ParamTypeError, "'1.a233' is not a valid Float"
44
+ end
45
+
46
+ end
47
+
48
+ context 'String' do
49
+
50
+ it 'should coerce' do
51
+ value = Compel::Coercion.coerce!('abc', String)
52
+
53
+ expect(value).to eq('abc')
54
+ end
55
+
56
+ it 'should not coerce' do
57
+ value = 1.2
58
+
59
+ expect(Compel::Coercion.valid?(value, String)).to eq(false)
60
+
61
+ expect { Compel::Coercion.coerce!(value, String) }.to \
62
+ raise_error Compel::ParamTypeError, "'#{value}' is not a valid String"
63
+ end
64
+
65
+ end
66
+
67
+ context 'Date' do
68
+
69
+ it 'should coerce' do
70
+ value = Compel::Coercion.coerce!('2015-12-22', Date)
71
+
72
+ expect(value).to eq(Date.parse('2015-12-22'))
73
+ end
74
+
75
+ it 'should not coerce' do
76
+ value = '2015-0'
77
+
78
+ expect(Compel::Coercion.valid?(value, Date)).to eq(false)
79
+
80
+ expect { Compel::Coercion.coerce!(value, Date) }.to \
81
+ raise_error Compel::ParamTypeError, "'#{value}' is not a valid Date"
82
+ end
83
+
84
+ end
85
+
86
+ context 'DateTime' do
87
+
88
+ it 'should coerce' do
89
+ value = Compel::Coercion.coerce!('2015-12-22', DateTime)
90
+
91
+ expect(value).to be_a DateTime
92
+ expect(value.year).to eq(2015)
93
+ expect(value.month).to eq(12)
94
+ expect(value.day).to eq(22)
95
+ end
96
+
97
+ end
98
+
99
+ context 'Hash' do
100
+
101
+ it 'should coerce' do
102
+ value = Compel::Coercion.coerce!({
103
+ first_name: 'Joaquim',
104
+ last_name: 'Adráz'
105
+ }, Hash)
106
+
107
+ expect(value).to eq({
108
+ 'first_name' => 'Joaquim',
109
+ 'last_name' => 'Adráz'
110
+ })
111
+ end
112
+
113
+ it 'should coerce 1' do
114
+ value = Compel::Coercion.coerce!({
115
+ 'first_name' => 'Joaquim',
116
+ 'last_name' => 'Adráz'
117
+ }, Hash)
118
+
119
+ expect(value).to eq({
120
+ 'first_name' => 'Joaquim',
121
+ 'last_name' => 'Adráz'
122
+ })
123
+ end
124
+
125
+ it 'should coerce 2' do
126
+ value = Compel::Coercion.coerce!(Hashie::Mash.new({
127
+ first_name: 'Joaquim',
128
+ last_name: 'Adráz'
129
+ }), Hash)
130
+
131
+ expect(value).to eq({
132
+ 'first_name' => 'Joaquim',
133
+ 'last_name' => 'Adráz'
134
+ })
135
+ end
136
+
137
+ it 'should not coerce' do
138
+ expect { Compel::Coercion.coerce!(123, Hash) }.to \
139
+ raise_error Compel::ParamTypeError, "'123' is not a valid Hash"
140
+ end
141
+
142
+ it 'should not coerce 1' do
143
+ expect { Compel::Coercion.coerce!('hash', Hash) }.to \
144
+ raise_error Compel::ParamTypeError, "'hash' is not a valid Hash"
145
+ end
146
+
147
+ it 'should not coerce 2' do
148
+ expect { Compel::Coercion.coerce!(['hash'], Hash) }.to \
149
+ raise_error Compel::ParamTypeError, "'[\"hash\"]' is not a valid Hash"
150
+ end
151
+
152
+ end
153
+
154
+ context 'JSON' do
155
+
156
+ it 'should coerce' do
157
+ value = Compel::Coercion.coerce! \
158
+ "{\"first_name\":\"Joaquim\",\"last_name\":\"Adráz\"}", JSON
159
+
160
+ expect(value).to eq({
161
+ 'first_name' => 'Joaquim',
162
+ 'last_name' => 'Adráz'
163
+ })
164
+ end
165
+
166
+ end
167
+
168
+ context 'Boolean' do
169
+
170
+ it 'should coerce' do
171
+ value = Compel::Coercion.coerce!('f', Compel::Boolean)
172
+
173
+ expect(value).to eq(false)
174
+ end
175
+
176
+ it 'should coerce' do
177
+ value = Compel::Coercion.coerce!('0', Compel::Boolean)
178
+
179
+ expect(value).to eq(false)
180
+ end
181
+
182
+ end
183
+
184
+ end
185
+
186
+ end
@@ -0,0 +1,279 @@
1
+ describe Compel do
2
+
3
+ def make_the_call(method, params)
4
+ Compel.send(method, params) do
5
+ param :first_name, String, required: true
6
+ param :last_name, String, required: true
7
+ param :birth_date, DateTime
8
+ end
9
+ end
10
+
11
+ context '#run!' do
12
+
13
+ it 'should raise InvalidParamsError exception' do
14
+ params = {
15
+ first_name: 'Joaquim'
16
+ }
17
+
18
+ expect{ make_the_call(:run!, params) }.to \
19
+ raise_error Compel::InvalidParamsError, 'params are invalid'
20
+ end
21
+
22
+ it 'shoudl raise InvalidParamsError exception with errors' do
23
+ params = {
24
+ first_name: 'Joaquim'
25
+ }
26
+
27
+ expect{ make_the_call(:run!, params) }.to raise_error do |exception|
28
+ expect(exception.params).to eq \
29
+ Hashie::Mash.new(first_name: 'Joaquim')
30
+
31
+ expect(exception.errors).to eq \
32
+ Hashie::Mash.new(last_name: ['is required'])
33
+ end
34
+ end
35
+
36
+ end
37
+
38
+ context '#run?' do
39
+
40
+ it 'should return true' do
41
+ params = {
42
+ first_name: 'Joaquim',
43
+ last_name: 'Adráz',
44
+ birth_date: '1989-08-06T09:00:00'
45
+ }
46
+
47
+ expect(make_the_call(:run?, params)).to eq(true)
48
+ end
49
+
50
+ it 'should return false' do
51
+ params = {
52
+ first_name: 'Joaquim'
53
+ }
54
+
55
+ expect(make_the_call(:run?, params)).to eq(false)
56
+ end
57
+
58
+ end
59
+
60
+ context '#run' do
61
+
62
+ def make_the_call(method, params)
63
+ Compel.send(method, params) do
64
+ param :first_name, String, required: true
65
+ param :last_name, String, required: true
66
+ param :birth_date, DateTime
67
+ param :age, Integer
68
+ param :admin, Compel::Boolean
69
+ param :blog_role, Hash do
70
+ param :admin, Compel::Boolean, required: true
71
+ end
72
+ end
73
+ end
74
+
75
+ it 'should compel returning coerced values' do
76
+ params = {
77
+ first_name: 'Joaquim',
78
+ last_name: 'Adráz',
79
+ birth_date: '1989-08-06T09:00:00',
80
+ age: '26',
81
+ admin: 'f',
82
+ blog_role: {
83
+ admin: '0'
84
+ }
85
+ }
86
+
87
+ expect(make_the_call(:run, params)).to eq \
88
+ Hashie::Mash.new({
89
+ first_name: 'Joaquim',
90
+ last_name: 'Adráz',
91
+ birth_date: DateTime.parse('1989-08-06T09:00:00'),
92
+ age: 26,
93
+ admin: false,
94
+ blog_role: {
95
+ admin: false
96
+ }
97
+ })
98
+ end
99
+
100
+ it 'should not compel for invalid params' do
101
+ expect{ make_the_call(:run, 1) }.to \
102
+ raise_error Compel::ParamTypeError, 'params must be an Hash'
103
+ end
104
+
105
+ it 'should not compel for invalid params 1' do
106
+ expect{ make_the_call(:run, nil) }.to \
107
+ raise_error Compel::ParamTypeError, 'params must be an Hash'
108
+ end
109
+
110
+ it 'should not compel' do
111
+ params = {
112
+ first_name: 'Joaquim'
113
+ }
114
+
115
+ expect(make_the_call(:run, params)).to eq \
116
+ Hashie::Mash.new({
117
+ first_name: 'Joaquim',
118
+ errors: {
119
+ last_name: ['is required']
120
+ }
121
+ })
122
+ end
123
+
124
+ context 'nested Hash' do
125
+
126
+ def make_the_call(method, params)
127
+ Compel.send(method, params) do
128
+ param :address, Hash, required: true do
129
+ param :line_one, String, required: true
130
+ param :line_two, String
131
+ param :post_code, Hash, required: true do
132
+ param :prefix, Integer, length: 4, required: true
133
+ param :suffix, Integer, length: 3
134
+ param :county, Hash do
135
+ param :code, String, length: 2, required: true
136
+ param :name, String
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
142
+
143
+ it 'should run?' do
144
+ params = {
145
+ address: {
146
+ line_one: 'Lisbon',
147
+ line_two: 'Portugal',
148
+ post_code: {
149
+ prefix: 1100,
150
+ suffix: 100
151
+ }
152
+ }
153
+ }
154
+
155
+ expect(make_the_call(:run?, params)).to eq(true)
156
+ end
157
+
158
+ it 'should not compel' do
159
+ params = {
160
+ address: {
161
+ line_two: 'Portugal'
162
+ }
163
+ }
164
+
165
+ expect(make_the_call(:run, params)).to eq \
166
+ Hashie::Mash.new({
167
+ address: {
168
+ line_two: 'Portugal'
169
+ },
170
+ errors: {
171
+ address: {
172
+ line_one: ['is required'],
173
+ post_code: ['params must be an Hash']
174
+ }
175
+ }
176
+ })
177
+ end
178
+
179
+ it 'should not compel 1' do
180
+ params = {
181
+ address: {
182
+ line_two: 'Portugal',
183
+ post_code: {
184
+ prefix: '1',
185
+ county: {
186
+ code: 'LX'
187
+ }
188
+ }
189
+ }
190
+ }
191
+
192
+ expect(make_the_call(:run, params)).to eq \
193
+ Hashie::Mash.new({
194
+ address: {
195
+ line_two: 'Portugal',
196
+ post_code: {
197
+ prefix: 1,
198
+ county: {
199
+ code: 'LX'
200
+ }
201
+ }
202
+ },
203
+ errors: {
204
+ address: {
205
+ line_one: ['is required'],
206
+ post_code: {
207
+ prefix: ['cannot have length different than 4']
208
+ }
209
+ }
210
+ }
211
+ })
212
+ end
213
+
214
+ it 'should not compel 2' do
215
+ params = {
216
+ address: {
217
+ post_code: {
218
+ suffix: '1234'
219
+ }
220
+ }
221
+ }
222
+
223
+ expect(make_the_call(:run, params)).to eq \
224
+ Hashie::Mash.new({
225
+ address: {
226
+ post_code: {
227
+ suffix: 1234
228
+ }
229
+ },
230
+ errors: {
231
+ address: {
232
+ line_one: ['is required'],
233
+ post_code: {
234
+ prefix: ['is required'],
235
+ suffix: ['cannot have length different than 3']
236
+ }
237
+ }
238
+ }
239
+ })
240
+ end
241
+
242
+ it 'should not compel 3' do
243
+ params = {
244
+ address: {
245
+ post_code: {
246
+ prefix: '1100',
247
+ suffix: '100',
248
+ county: {}
249
+ },
250
+ }
251
+ }
252
+
253
+ expect(make_the_call(:run, params)).to eq \
254
+ Hashie::Mash.new({
255
+ address: {
256
+ post_code: {
257
+ prefix: 1100,
258
+ suffix: 100,
259
+ county: {}
260
+ }
261
+ },
262
+ errors: {
263
+ address: {
264
+ line_one: ['is required'],
265
+ post_code: {
266
+ county: {
267
+ code: ['is required']
268
+ }
269
+ }
270
+ }
271
+ }
272
+ })
273
+ end
274
+
275
+ end
276
+
277
+ end
278
+
279
+ end
@@ -0,0 +1,36 @@
1
+ describe Compel::Contract do
2
+
3
+ context '#conditions' do
4
+
5
+ it 'should build with conditions block option' do
6
+ contract = \
7
+ Compel::Contract.new({ first_name: 'Joaquim' }) do
8
+ param :address, Hash, required: true do
9
+ param :post_code, String
10
+ end
11
+ end
12
+
13
+ expect(contract.conditions[:address].type).to eq(Hash)
14
+ expect(contract.conditions[:address].required?).to eq(true)
15
+ expect(contract.conditions[:address].conditions?).to eq(true)
16
+ end
17
+
18
+ end
19
+
20
+ context 'params' do
21
+
22
+ it 'should return coerced params' do
23
+ contract = \
24
+ Compel::Contract.new(date: '2015-12-22') { param :date, DateTime }
25
+ .validate
26
+
27
+ expect(contract.coerced_params.date).to be_a DateTime
28
+ expect(contract.coerced_params.date.year).to eq(2015)
29
+ expect(contract.coerced_params.date.month).to eq(12)
30
+ expect(contract.coerced_params.date.day).to eq(22)
31
+ end
32
+
33
+ end
34
+
35
+
36
+ end
@@ -0,0 +1,48 @@
1
+ describe Compel::Errors do
2
+
3
+ it 'should add error' do
4
+ errors = Compel::Errors.new
5
+ errors.add(:first_name, 'is required')
6
+
7
+ expect(errors.to_hash[:first_name]).to include('is required')
8
+ end
9
+
10
+ it 'should add multiple errors' do
11
+ errors = Compel::Errors.new
12
+ errors.add(:first_name, 'is required')
13
+ errors.add(:first_name, 'is invalid')
14
+
15
+ expect(errors.to_hash[:first_name]).to include('is required')
16
+ expect(errors.to_hash[:first_name]).to include('is invalid')
17
+ end
18
+
19
+ it 'should add Compel::Errors' do
20
+ address_errors = Compel::Errors.new
21
+ address_errors.add(:line_one, 'is required')
22
+ address_errors.add(:post_code, 'must be an Hash')
23
+
24
+ errors = Compel::Errors.new
25
+ errors.add(:address, address_errors)
26
+
27
+ expect(errors.to_hash[:address][:line_one]).to include('is required')
28
+ expect(errors.to_hash[:address][:post_code]).to include('must be an Hash')
29
+ end
30
+
31
+ it 'should add nested Compel::Errors' do
32
+ post_code_errors = Compel::Errors.new
33
+ post_code_errors.add(:prefix, 'is invalid')
34
+ post_code_errors.add(:suffix, 'is required')
35
+
36
+ address_errors = Compel::Errors.new
37
+ address_errors.add(:line_one, 'is required')
38
+ address_errors.add(:post_code, post_code_errors)
39
+
40
+ errors = Compel::Errors.new
41
+ errors.add(:address, address_errors)
42
+
43
+ expect(errors.to_hash[:address][:line_one]).to include('is required')
44
+ expect(errors.to_hash[:address][:post_code][:prefix]).to include('is invalid')
45
+ expect(errors.to_hash[:address][:post_code][:suffix]).to include('is required')
46
+ end
47
+
48
+ end
@@ -0,0 +1,25 @@
1
+ describe Compel::Param do
2
+
3
+ context 'default value' do
4
+
5
+ it 'should override default value when value is given' do
6
+ param = Compel::Param.new(:number, Integer, nil, { default: 123 })
7
+
8
+ expect(param.value).to eq(123)
9
+ end
10
+
11
+ it 'should use default value when not given a value' do
12
+ param = Compel::Param.new(:number, Integer, 123, { default: 456 })
13
+
14
+ expect(param.value).to eq(123)
15
+ end
16
+
17
+ it 'should allow proc as default value' do
18
+ param = Compel::Param.new(:year, Integer, nil, { default: Proc.new { Time.now.year } })
19
+
20
+ expect(param.value).to eq(Time.now.year)
21
+ end
22
+
23
+ end
24
+
25
+ end