compel 0.1.3 → 0.2.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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +7 -0
  3. data/README.md +65 -54
  4. data/lib/compel/builder/boolean.rb +13 -0
  5. data/lib/compel/builder/common.rb +24 -0
  6. data/lib/compel/builder/common_value.rb +34 -0
  7. data/lib/compel/builder/date.rb +23 -0
  8. data/lib/compel/builder/datetime.rb +23 -0
  9. data/lib/compel/builder/float.rb +15 -0
  10. data/lib/compel/builder/hash.rb +22 -0
  11. data/lib/compel/builder/integer.rb +15 -0
  12. data/lib/compel/builder/json.rb +13 -0
  13. data/lib/compel/builder/methods.rb +60 -0
  14. data/lib/compel/builder/schema.rb +27 -0
  15. data/lib/compel/builder/string.rb +30 -0
  16. data/lib/compel/builder/time.rb +23 -0
  17. data/lib/compel/coercion/boolean.rb +1 -1
  18. data/lib/compel/coercion/date.rb +19 -2
  19. data/lib/compel/coercion/datetime.rb +19 -2
  20. data/lib/compel/coercion/float.rb +1 -1
  21. data/lib/compel/coercion/hash.rb +1 -1
  22. data/lib/compel/coercion/integer.rb +1 -1
  23. data/lib/compel/coercion/json.rb +1 -1
  24. data/lib/compel/coercion/regexp.rb +17 -0
  25. data/lib/compel/coercion/string.rb +1 -1
  26. data/lib/compel/coercion/time.rb +19 -2
  27. data/lib/compel/coercion/type.rb +0 -13
  28. data/lib/compel/coercion.rb +8 -10
  29. data/lib/compel/contract.rb +18 -62
  30. data/lib/compel/exceptions/invalid_hash_error.rb +9 -0
  31. data/lib/compel/exceptions/type_error.rb +7 -0
  32. data/lib/compel/exceptions/validation_error.rb +7 -0
  33. data/lib/compel/validation.rb +36 -50
  34. data/lib/compel/validators/base.rb +19 -0
  35. data/lib/compel/validators/hash_validator.rb +51 -0
  36. data/lib/compel/validators/type_validator.rb +30 -0
  37. data/lib/compel/version.rb +1 -1
  38. data/lib/compel.rb +16 -11
  39. data/spec/compel/builder_spec.rb +226 -0
  40. data/spec/compel/coercion_spec.rb +85 -18
  41. data/spec/compel/compel_spec.rb +368 -160
  42. data/spec/compel/sinatra_integration_spec.rb +73 -0
  43. data/spec/compel/validation_spec.rb +122 -8
  44. data/spec/spec_helper.rb +19 -0
  45. data/spec/support/sinatra_app.rb +43 -0
  46. metadata +28 -10
  47. data/lib/compel/invalid_params_error.rb +0 -9
  48. data/lib/compel/param.rb +0 -46
  49. data/lib/compel/param_type_error.rb +0 -7
  50. data/lib/compel/param_validation_error.rb +0 -7
  51. data/spec/compel/contract_spec.rb +0 -36
  52. data/spec/compel/param_spec.rb +0 -25
@@ -0,0 +1,226 @@
1
+ describe Compel::Builder do
2
+
3
+ context 'Schema' do
4
+
5
+ subject(:builder) { Compel::Builder::String.new }
6
+
7
+ it 'shoudl' do
8
+ object = {
9
+ first_name: 'Joaquim',
10
+ birth_date: '1989-0',
11
+ address: {
12
+ line_one: 'Lisboa',
13
+ post_code: '1100',
14
+ country_code: 'PT'
15
+ }
16
+ }
17
+
18
+ schema = Compel.hash.keys({
19
+ first_name: Compel.string.required,
20
+ last_name: Compel.string.required,
21
+ birth_date: Compel.date.iso8601,
22
+ address: Compel.hash.keys({
23
+ line_one: Compel.string.required,
24
+ line_two: Compel.string.default('-'),
25
+ post_code: Compel.string.format(/^\d{4}-\d{3}$/).required,
26
+ country_code: Compel.string.in(['PT', 'GB']).default('PT')
27
+ })
28
+ })
29
+
30
+ result = Compel.run(object, schema)
31
+
32
+ expect(result).to \
33
+ eq({
34
+ "first_name" => "Joaquim",
35
+ "birth_date" => "1989-0",
36
+ "address" => {
37
+ "line_one" => "Lisboa",
38
+ "post_code" => "1100",
39
+ "country_code" => "PT",
40
+ "line_two" => "-"
41
+ },
42
+ "errors" => {
43
+ "last_name" => ["is required"],
44
+ "birth_date" => ["'1989-0' is not a parsable date with format: %Y-%m-%d"],
45
+ "address" => {
46
+ "post_code" => ["must match format ^\\d{4}-\\d{3}$"]
47
+ }
48
+ }
49
+ })
50
+ end
51
+
52
+ it 'should build new Schema for givin type' do
53
+ expect(builder.type).to be(Compel::Coercion::String)
54
+ expect(builder.options.keys).to eq([])
55
+ expect(builder.required?).to be false
56
+ expect(builder.default_value).to be nil
57
+ end
58
+
59
+ it 'should build Schema' do
60
+ schema = Compel.hash.keys({
61
+ a: Compel.float,
62
+ b: Compel.string,
63
+ c: Compel.hash.keys({
64
+ cc: Compel.integer.length(1)
65
+ }),
66
+ d: Compel.json,
67
+ e: Compel.time,
68
+ f: Compel.datetime,
69
+ g: Compel.date,
70
+ h: Compel.integer
71
+ })
72
+
73
+ keys_schemas = schema.options[:keys]
74
+
75
+ expect(keys_schemas.a.type).to be Compel::Coercion::Float
76
+ expect(keys_schemas.b.type).to be Compel::Coercion::String
77
+ expect(keys_schemas.c.type).to be Compel::Coercion::Hash
78
+ expect(keys_schemas.d.type).to be Compel::Coercion::JSON
79
+ expect(keys_schemas.e.type).to be Compel::Coercion::Time
80
+ expect(keys_schemas.f.type).to be Compel::Coercion::DateTime
81
+ expect(keys_schemas.g.type).to be Compel::Coercion::Date
82
+ expect(keys_schemas.h.type).to be Compel::Coercion::Integer
83
+ end
84
+
85
+ it 'should validate hash object from schema' do
86
+ schema = Compel.hash.keys({
87
+ a: Compel.float.required
88
+ })
89
+
90
+ expect(schema.validate({ a: nil }).errors.a).to \
91
+ include('is required')
92
+ end
93
+
94
+ context 'Builder::CommonValue' do
95
+
96
+ context '#in, #range, #min, #max' do
97
+
98
+ it 'should have value' do
99
+ [:in, :range, :min, :max].each do |method|
100
+ builder.send(method, "#{method}")
101
+ expect(builder.options[method]).to eq("#{method}")
102
+ end
103
+ end
104
+
105
+ end
106
+
107
+ end
108
+
109
+ context 'Builder::Common' do
110
+
111
+ context '#is, #default' do
112
+
113
+ it 'should have value' do
114
+ [:is, :default].each do |method|
115
+ builder.send(method, "#{method}")
116
+ expect(builder.options[method]).to eq("#{method}")
117
+ end
118
+ end
119
+
120
+ end
121
+
122
+ context '#required' do
123
+
124
+ it 'should be false' do
125
+ expect(builder.required?).to be false
126
+ end
127
+
128
+ it 'should be true' do
129
+ builder.required
130
+
131
+ expect(builder.required?).to be true
132
+ end
133
+
134
+ end
135
+
136
+ context '#length' do
137
+
138
+ it 'should have value' do
139
+ builder.length(5)
140
+
141
+ expect(builder.options[:length]).to be 5
142
+ end
143
+
144
+ it 'should raise exception for invalid type' do
145
+ expect { builder.length('abc') }.to \
146
+ raise_error Compel::TypeError, "'abc' is not a valid Integer"
147
+ end
148
+
149
+ end
150
+
151
+ end
152
+
153
+ context 'Date' do
154
+
155
+ subject(:builder) { Compel::Builder::Date.new }
156
+
157
+ context '#format' do
158
+
159
+ it 'should have value' do
160
+ builder.format('%d-%m-%Y')
161
+
162
+ expect(builder.options[:format]).to eq('%d-%m-%Y')
163
+ end
164
+
165
+ it 'should have value' do
166
+ builder.iso8601
167
+
168
+ expect(builder.options[:format]).to eq('%Y-%m-%d')
169
+ end
170
+
171
+ end
172
+
173
+ end
174
+
175
+ context 'String' do
176
+
177
+ context '#format' do
178
+
179
+ it 'should raise exception for invalid type' do
180
+ expect { builder.format('abc') }.to \
181
+ raise_error Compel::TypeError, "'abc' is not a valid Regexp"
182
+ end
183
+
184
+ it 'should have value' do
185
+ builder.format(/1/)
186
+
187
+ expect(builder.options[:format]).to eq(/1/)
188
+ end
189
+
190
+ end
191
+
192
+ context '#min_length' do
193
+
194
+ it 'should raise exception for invalid type' do
195
+ expect { builder.min_length('a') }.to \
196
+ raise_error Compel::TypeError, "'a' is not a valid Integer"
197
+ end
198
+
199
+ it 'should have value' do
200
+ builder.min_length(4)
201
+
202
+ expect(builder.options[:min_length]).to eq(4)
203
+ end
204
+
205
+ end
206
+
207
+ context '#max_length' do
208
+
209
+ it 'should raise exception for invalid type' do
210
+ expect { builder.max_length('a') }.to \
211
+ raise_error Compel::TypeError, "'a' is not a valid Integer"
212
+ end
213
+
214
+ it 'should have value' do
215
+ builder.max_length(10)
216
+
217
+ expect(builder.options[:max_length]).to eq(10)
218
+ end
219
+
220
+ end
221
+
222
+ end
223
+
224
+ end
225
+
226
+ end
@@ -23,7 +23,7 @@ describe Compel::Coercion do
23
23
  expect(Compel::Coercion.valid?('123abc', Integer)).to eq(false)
24
24
 
25
25
  expect { Compel::Coercion.coerce!('123abc', Integer) }.to \
26
- raise_error Compel::ParamTypeError, "'123abc' is not a valid Integer"
26
+ raise_error Compel::TypeError, "'123abc' is not a valid Integer"
27
27
  end
28
28
 
29
29
  end
@@ -40,7 +40,7 @@ describe Compel::Coercion do
40
40
  expect(Compel::Coercion.valid?('1.a233', Float)).to eq(false)
41
41
 
42
42
  expect { Compel::Coercion.coerce!('1.a233', Float) }.to \
43
- raise_error Compel::ParamTypeError, "'1.a233' is not a valid Float"
43
+ raise_error Compel::TypeError, "'1.a233' is not a valid Float"
44
44
  end
45
45
 
46
46
  end
@@ -59,39 +59,89 @@ describe Compel::Coercion do
59
59
  expect(Compel::Coercion.valid?(value, String)).to eq(false)
60
60
 
61
61
  expect { Compel::Coercion.coerce!(value, String) }.to \
62
- raise_error Compel::ParamTypeError, "'#{value}' is not a valid String"
62
+ raise_error Compel::TypeError, "'#{value}' is not a valid String"
63
63
  end
64
64
 
65
65
  end
66
66
 
67
67
  context 'Date' do
68
68
 
69
- it 'should coerce' do
69
+ it 'should coerce with default format' do
70
70
  value = Compel::Coercion.coerce!('2015-12-22', Date)
71
71
 
72
72
  expect(value).to eq(Date.parse('2015-12-22'))
73
73
  end
74
74
 
75
+ it 'should coerce with format' do
76
+ value = Compel::Coercion.coerce!('22-12-2015', Date, { format: '%d-%m-%Y'})
77
+
78
+ expect(value).to eq(Date.strptime('22-12-2015', '%d-%m-%Y'))
79
+ end
80
+
75
81
  it 'should not coerce' do
76
82
  value = '2015-0'
77
83
 
78
84
  expect(Compel::Coercion.valid?(value, Date)).to eq(false)
79
85
 
80
86
  expect { Compel::Coercion.coerce!(value, Date) }.to \
81
- raise_error Compel::ParamTypeError, "'#{value}' is not a valid Date"
87
+ raise_error \
88
+ Compel::TypeError,
89
+ "'#{value}' is not a parsable date with format: %Y-%m-%d"
90
+ end
91
+
92
+ it 'should not coerce 1' do
93
+ default_format = '%Y-%m-%d'
94
+ value = '22-12-2015'
95
+
96
+ expect { Compel::Coercion.coerce!(value, Date) }.to \
97
+ raise_error \
98
+ Compel::TypeError,
99
+ "'#{value}' is not a parsable date with format: #{default_format}"
82
100
  end
83
101
 
84
102
  end
85
103
 
86
- context 'DateTime' do
104
+ context 'DateTime & Time' do
87
105
 
88
106
  it 'should coerce' do
89
- value = Compel::Coercion.coerce!('2015-12-22', DateTime)
107
+ [DateTime, Time].each do |time_klass|
108
+ value = Compel::Coercion.coerce!('2015-12-22', time_klass, format: '%Y-%m-%d')
109
+
110
+ expect(value).to be_a time_klass
111
+ expect(value.year).to eq(2015)
112
+ expect(value.month).to eq(12)
113
+ expect(value.day).to eq(22)
114
+ end
115
+
116
+ end
117
+
118
+ it 'should coerce iso8601 format' do
119
+ [DateTime, Time].each do |time_klass|
120
+ value = Compel::Coercion.coerce!('2015-12-22T09:30:10', time_klass)
121
+
122
+ expect(value).to be_a time_klass
123
+ expect(value.year).to eq(2015)
124
+ expect(value.month).to eq(12)
125
+ expect(value.day).to eq(22)
126
+ expect(value.hour).to eq(9)
127
+ expect(value.min).to eq(30)
128
+ expect(value.sec).to eq(10)
129
+ end
130
+ end
131
+
132
+ it 'should not coerce' do
133
+ default_format = '%FT%T'
134
+ value = '22-12-2015'
135
+
136
+ [DateTime, Time].each do |time_klass|
137
+ type_down_cased = "#{time_klass}".downcase
138
+
139
+ expect { Compel::Coercion.coerce!('22-12-2015', time_klass) }.to \
140
+ raise_error \
141
+ Compel::TypeError,
142
+ "'#{value}' is not a parsable #{type_down_cased} with format: #{default_format}"
143
+ end
90
144
 
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
145
  end
96
146
 
97
147
  end
@@ -136,17 +186,17 @@ describe Compel::Coercion do
136
186
 
137
187
  it 'should not coerce' do
138
188
  expect { Compel::Coercion.coerce!(123, Hash) }.to \
139
- raise_error Compel::ParamTypeError, "'123' is not a valid Hash"
189
+ raise_error Compel::TypeError, "'123' is not a valid Hash"
140
190
  end
141
191
 
142
192
  it 'should not coerce 1' do
143
193
  expect { Compel::Coercion.coerce!('hash', Hash) }.to \
144
- raise_error Compel::ParamTypeError, "'hash' is not a valid Hash"
194
+ raise_error Compel::TypeError, "'hash' is not a valid Hash"
145
195
  end
146
196
 
147
197
  it 'should not coerce 2' do
148
198
  expect { Compel::Coercion.coerce!(['hash'], Hash) }.to \
149
- raise_error Compel::ParamTypeError, "'[\"hash\"]' is not a valid Hash"
199
+ raise_error Compel::TypeError, "'[\"hash\"]' is not a valid Hash"
150
200
  end
151
201
 
152
202
  end
@@ -167,18 +217,35 @@ describe Compel::Coercion do
167
217
 
168
218
  context 'Boolean' do
169
219
 
170
- it 'should coerce' do
171
- value = Compel::Coercion.coerce!('f', Compel::Boolean)
220
+ it 'should coerce false' do
221
+ value = Compel::Coercion.coerce!('f', Compel::Coercion::Boolean)
172
222
 
173
223
  expect(value).to eq(false)
174
224
  end
175
225
 
176
- it 'should coerce' do
177
- value = Compel::Coercion.coerce!('0', Compel::Boolean)
226
+ it 'should coerce false 1' do
227
+ value = Compel::Coercion.coerce!('0', Compel::Coercion::Boolean)
178
228
 
179
229
  expect(value).to eq(false)
180
230
  end
181
231
 
232
+ it 'should coerce true' do
233
+ value = Compel::Coercion.coerce!('t', Compel::Coercion::Boolean)
234
+
235
+ expect(value).to eq(true)
236
+ end
237
+
238
+ it 'should coerce true 1' do
239
+ value = Compel::Coercion.coerce!('true', Compel::Coercion::Boolean)
240
+
241
+ expect(value).to eq(true)
242
+ end
243
+
244
+ it 'should not coerce' do
245
+ expect{ Compel::Coercion.coerce!('trye', Compel::Coercion::Boolean) }.to \
246
+ raise_error Compel::TypeError, "'trye' is not a valid Boolean"
247
+ end
248
+
182
249
  end
183
250
 
184
251
  end