compel 0.1.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,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