rschema 3.0.1.pre5 → 3.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2e570ef90c1907cc057e0b5edf7cd2bac22ace47
4
- data.tar.gz: 05039b6e5b72eea3f86b0fa1295f61055dab7a16
3
+ metadata.gz: e971b0b2b3d6a20e7d3a4d30c0426ea3f58e02f7
4
+ data.tar.gz: a1cacce7540b7a25698c48e5aa1f57847dc55a85
5
5
  SHA512:
6
- metadata.gz: 111ad7ee2f51e27bd18636566d108d5313367ea77c1ffcc11db890db571e1887712af9fab9d35d7a27e57bb6cb314f3358f662e8322d0bc849e05f3c4e61a9d9
7
- data.tar.gz: 2434658702e5ea3b87ba91cea91868d01f30bdcee10a430cf3fc3a5669f4126167cf7fc2a7f971c53136cdfdb59d749441e375ebf9d2145a4ce76e988eb54851
6
+ metadata.gz: 05194c41bc61d36b6ebacc94583ab20fa693e8ad3d3cd69858fcdcd3ac110b73e5953180f8fbc9f5f18b3fa9099f210c925136feb08a90cf27efa4c28463e17f
7
+ data.tar.gz: 2f446b9473c06e7ad75ee426a872ec6843271a86b594d87dc07a282b50b51878ac8cbd9ec512cf1a010c55a0d36faf64c517506ea83ba21c7deb17edd9893fd9
data/README.md CHANGED
@@ -22,7 +22,7 @@ First you create a schema:
22
22
 
23
23
  blog_post_schema = RSchema.define_hash {{
24
24
  title: _String,
25
- tags: Array(_Symbol),
25
+ tags: array(_Symbol),
26
26
  body: _String,
27
27
  }}
28
28
 
@@ -33,59 +33,58 @@ Then you can use the schema to validate data:
33
33
  tags: [:trick, :developers, :unbeleivable],
34
34
  body: '<p>blah blah</p>'
35
35
  }
36
- blog_post_schema.call(input).valid? #=> true
36
+ blog_post_schema.valid?(input) #=> true
37
37
 
38
38
  What Is A Schema?
39
39
  -----------------
40
40
 
41
41
  Schemas are objects that _describe and validate a value_.
42
42
 
43
- The simplest schemas are `Type` schemas, which just validate the type of a value.
43
+ The simplest schemas are `Type` schemas, which just check the type of a value.
44
44
 
45
45
  schema = RSchema.define { _Integer }
46
46
  schema.class #=> RSchema::Schemas::Type
47
47
 
48
- schema.call(1234).valid? #=> true
49
- schema.call('hi').valid? #=> false
48
+ schema.valid?(1234) #=> true
49
+ schema.valid?('hi') #=> false
50
50
 
51
51
  Then there are composite schemas, which are schemas composed of subschemas.
52
52
 
53
- Arrays are composite schemas:
53
+ Array schemas are composite schemas:
54
54
 
55
- schema = RSchema.define { Array(_Integer) }
56
- schema.call([10, 11, 12]).valid? #=> true
57
- schema.call([10, 11, :hi]).valid? #=> false
55
+ schema = RSchema.define { array(_Integer) }
56
+ schema.valid?([10, 11, 12]) #=> true
57
+ schema.valid?([10, 11, :hi]) #=> false
58
58
 
59
- And so are hashes:
59
+ And so are hash schemas:
60
60
 
61
- schema = RSchema.define do
62
- Hash(fname: _String, age: _Integer)
63
- end
61
+ schema = RSchema.define_hash {{
62
+ fname: _String,
63
+ age: _Integer,
64
+ }}
64
65
 
65
- schema.call({ fname: 'Jane', age: 27 }).valid? #=> true
66
- schema.call({ fname: 'Johnny' }).valid? #=> false
66
+ schema.valid?({ fname: 'Jane', age: 27 }) #=> true
67
+ schema.valid?({ fname: 'Johnny no age' }) #=> false
67
68
 
68
69
  Schema objects are composable – they are designed to be combined.
69
70
  This allows schemas to describe complex, nested data structures.
70
71
 
71
- schema = RSchema.define_hash do
72
- {
73
- fname: predicate { |n| n.is_a?(String) && n.size > 0 },
74
- favourite_foods: Set(_Symbol),
75
- children_by_age: VariableHash(_Integer => _String)
76
- }
77
- end
72
+ schema = RSchema.define_hash {{
73
+ fname: maybe(_String),
74
+ favourite_foods: set(_Symbol),
75
+ children_by_age: variable_hash(_Integer => _String)
76
+ }}
78
77
 
79
78
  input = {
80
- fname: 'Johnny',
81
- favourite_foods: Set.new([:bacon, :cheese, :onion]),
79
+ fname: nil,
80
+ favourite_foods: Set[:bacon, :cheese, :onion],
82
81
  children_by_age: {
83
82
  7 => 'Jenny',
84
83
  5 => 'Simon',
85
84
  },
86
85
  }
87
86
 
88
- schema.call(input).valid? #=> true
87
+ schema.valid?(input) #=> true
89
88
 
90
89
  RSchema provides many different kinds of schema classes for common tasks, but
91
90
  you can also write custom schema classes if you need to.
@@ -95,34 +94,35 @@ The DSL
95
94
  -------
96
95
 
97
96
  Schemas are usually created and composed via a DSL using `RSchema.define`.
98
- They can be created manually, although this is often too verbose.
97
+ Schemas can be created without the DSL, but the DSL is much more succinct.
99
98
 
100
99
  For example, the following two schemas are identical. `schema1` is created via the
101
100
  DSL, and `schema2` is created manually.
102
101
 
103
- schema1 = RSchema.define { Array(_Symbol) }
102
+ schema1 = RSchema.define { array(_Symbol) }
104
103
 
105
- schema2 = RSchema::Schemas::VariableArray.new(
106
- RSchema::Schemas::Type.new(Symbol)
104
+ schema2 = RSchema::Schemas::Convenience.wrap(
105
+ RSchema::Schemas::VariableLengthArray.new(
106
+ RSchema::Schemas::Type.new(Symbol)
107
+ )
107
108
  )
108
109
 
109
- You will probably never need to create schemas manually unless you are doing
110
- something advanced, like writing your own DSL.
110
+ You will probably not need to create schemas manually unless you are doing
111
+ something advanced.
111
112
 
112
- The DSL is designed to be extensible. You can add your own methods to the
113
- default DSL, or create a separate, custom DSL to suite your needs.
113
+ For a full list of DSL methods, see the API documentation for `RSchema::DSL`.
114
114
 
115
115
 
116
- When Validation Fails
117
- ---------------------
116
+ Errors (When Validation Fails)
117
+ ------------------------------
118
118
 
119
- When data fails validation, it is often important to know exactly _which
119
+ When something fails validation, it is often important to know exactly _which
120
120
  values_ were invalid, and _why_. RSchema provides details about every
121
121
  failure within a result object.
122
122
 
123
123
  schema = RSchema.define do
124
- Array(
125
- Hash(
124
+ array(
125
+ fixed_hash(
126
126
  name: _String,
127
127
  hair: enum([:red, :brown, :blonde, :black])
128
128
  )
@@ -136,13 +136,11 @@ failure within a result object.
136
136
  { name: 'Chris', hair: :red },
137
137
  ]
138
138
 
139
- result = schema.call(input)
139
+ result = schema.validate(input)
140
140
 
141
- result.class #=> RSchema::Result
141
+ result.class #=> RSchema::Result
142
142
  result.valid? #=> false
143
- result.error #=> { 2 => { :hair => #<RSchema::Error> } }
144
- result.error[2][:hair].to_s
145
- #=> "Error RSchema::Schemas::Enum/not_a_member for value: :blond"
143
+ result.error #=> { 2 => { :hair => #<RSchema::Error> } }
146
144
 
147
145
  The error above says that the value `:blond`, which exists at location
148
146
  `input[2][:hair]`, is not a valid enum member. Looking back at the schema, we
@@ -156,13 +154,11 @@ error messages for developers or users.
156
154
 
157
155
  error.value #=> :blond
158
156
  error.symbolic_name #=> :not_a_member
159
- error.schema #=> #<RSchema::Schemas::Enum>
160
- error.to_s #=> "Error RSchema::Schemas::Enum/not_a_member for value: :blond"
161
- error.to_s(:detailed) #=>
162
- # Error: not_a_member
163
- # Schema: RSchema::Schemas::Enum
164
- # Value: :blond
165
- # Vars: nil
157
+ error.schema #=> #<RSchema::Schemas::Enum ...>
158
+ error.schema.members #=> [:red, :brown, :blonde, :black]
159
+ error.to_s #=> "RSchema::Schemas::Enum/not_a_member"
160
+ error.inspect
161
+ #=> "<RSchema::Error RSchema::Schemas::Enum/not_a_member value=:blond>"
166
162
 
167
163
 
168
164
  Type Schemas
@@ -172,8 +168,8 @@ The most basic kind of schema is a `Type` schema.
172
168
  Type schemas validate the class of a value using `is_a?`.
173
169
 
174
170
  schema = RSchema.define { type(String) }
175
- schema.call('hi').valid? #=> true
176
- schema.call(1234).valid? #=> false
171
+ schema.valid?('hi') #=> true
172
+ schema.valid?(1234) #=> false
177
173
 
178
174
  Type schemas are so common that the RSchema DSL provides a shorthand way to
179
175
  create them, using an underscore prefix:
@@ -186,22 +182,21 @@ Because type schemas use `is_a?`, they handle subclasses, and can also be used
186
182
  to check for `include`d modules like `Enumerable`:
187
183
 
188
184
  schema = RSchema.define { _Enumerable }
189
- schema.call([1, 2, 3]).valid? #=> true
190
- schema.call({ a: 1, b: 2 }).valid? #=> true
185
+ schema.valid?([1, 2, 3]) #=> true
186
+ schema.valid?({ a: 12 }) #=> true
191
187
 
192
188
  Variable-length Array Schemas
193
189
  -----------------------------
194
190
 
195
191
  There are two types of array schemas.
196
- The first type are `VariableLengthArray` schemas, where every element in the
197
- array conforms to a single subschema:
192
+ The first type are `VariableLengthArray` schemas, which check that all elements
193
+ in the array conform to a single subschema:
198
194
 
199
- schema = RSchema.define { Array(_Symbol) }
200
- schema.class #=> RSchema::Schemas::VariableLengthArray
195
+ schema = RSchema.define { array(_Symbol) }
201
196
 
202
- schema.call([:a, :b, :c]).valid? #=> true
203
- schema.call([:a]).valid? #=> true
204
- schema.call([]).valid? #=> true
197
+ schema.valid?([:a, :b, :c]) #=> true
198
+ schema.valid?([:a]) #=> true
199
+ schema.valid?([]) #=> true
205
200
 
206
201
  Fixed-length Array Schemas
207
202
  --------------------------
@@ -209,37 +204,38 @@ Fixed-length Array Schemas
209
204
  There are also `FixedLengthArray` schemas, where the array must have a specific
210
205
  length, and each element of the array has a separate subschema:
211
206
 
212
- schema = RSchema.define{ Array(_Integer, _String) }
213
- schema.class #=> RSchema::Schemas::FixedLengthArray
207
+ schema = RSchema.define { array(_Integer, _String) }
214
208
 
215
- schema.call([10, 'hello']).valid? #=> true
216
- schema.call([22, 'world']).valid? #=> true
217
- schema.call(['heyoo', 33]).valid? #=> false
209
+ schema.valid?([10, 'hello']) #=> true
210
+ schema.valid?(['heyoo', 33]) #=> false
218
211
 
219
212
  Fixed Hash Schemas
220
213
  ------------------
221
214
 
222
215
  There are also two kinds of hash schemas.
223
216
 
224
- `FixedHash` schemas describes hashes where they keys are known constants:
217
+ `FixedHash` schemas describe hashes where they keys are known constants:
225
218
 
226
219
  schema = RSchema.define do
227
- Hash(name: _String, age: _Integer)
220
+ fixed_hash(
221
+ name: _String,
222
+ age: _Integer,
223
+ )
228
224
  end
229
225
 
230
- schema.call({ name: 'George', age: 2 }).valid? #=> true
226
+ schema.valid?({ name: 'George', age: 2 }) #=> true
231
227
 
232
228
  Elements can be optional:
233
229
 
234
230
  schema = RSchema.define do
235
- Hash(
231
+ fixed_hash(
236
232
  name: _String,
237
233
  optional(:age) => _Integer,
238
234
  )
239
235
  end
240
236
 
241
- schema.call({ name: 'Lucy', age: 21 }).valid? #=> true
242
- schema.call({ name: 'Tom' }).valid? #=> true
237
+ schema.valid?({ name: 'Lucy', age: 21 }) #=> true
238
+ schema.valid?({ name: 'Ageless Tommy' }) #=> true
243
239
 
244
240
  `FixedHash` schemas are common, so the `RSchema.define_hash` method exists
245
241
  to make their creation more convenient:
@@ -252,13 +248,13 @@ to make their creation more convenient:
252
248
  Variable Hash Schemas
253
249
  ---------------------
254
250
 
255
- `VariableHash` schemas are for hashes where the keys are _not_ known constants.
251
+ `VariableHash` schemas are for hashes where the keys are _not_ known ahead of time.
256
252
  They contain one subschema for keys, and another subschema for values.
257
253
 
258
- schema = RSchema.define { VariableHash(_Symbol => _Integer) }
259
- schema.call({}).valid? #=> true
260
- schema.call({ a: 1 }).valid? #=> true
261
- schema.call({ a: 1, b: 2 }).valid? #=> true
254
+ schema = RSchema.define { variable_hash(_Symbol => _Integer) }
255
+ schema.valid?({}) #=> true
256
+ schema.valid?({ a: 1 }) #=> true
257
+ schema.valid?({ a: 1, b: 2 }) #=> true
262
258
 
263
259
  Other Schema Types
264
260
  ------------------
@@ -266,40 +262,40 @@ Other Schema Types
266
262
  RSchema provides a few other schema types through its DSL:
267
263
 
268
264
  # boolean (only true or false)
269
- boolean_schema = RSchema.define { Boolean() }
270
- boolean_schema.call(true).valid? #=> true
271
- boolean_schema.call(false).valid? #=> true
272
- boolean_schema.call(nil).valid? #=> false
265
+ boolean_schema = RSchema.define { boolean }
266
+ boolean_schema.valid?(true) #=> true
267
+ boolean_schema.valid?(false) #=> true
268
+ boolean_schema.valid?(nil) #=> false
273
269
 
274
270
  # anything (literally any value)
275
271
  anything_schema = RSchema.define { anything }
276
- anything_schema.call('Hi').valid? #=> true
277
- anything_schema.call(true).valid? #=> true
278
- anything_schema.call(1234).valid? #=> true
279
- anything_schema.call(nil).valid? #=> true
272
+ anything_schema.valid?('Hi') #=> true
273
+ anything_schema.valid?(true) #=> true
274
+ anything_schema.valid?(1234) #=> true
275
+ anything_schema.valid?(nil) #=> true
280
276
 
281
277
  # either (sum types)
282
278
  either_schema = RSchema.define { either(_String, _Integer, _Float) }
283
- either_schema.call('hi').valid? #=> true
284
- either_schema.call(5555).valid? #=> true
285
- either_schema.call(77.1).valid? #=> true
279
+ either_schema.valid?('hi') #=> true
280
+ either_schema.valid?(5555) #=> true
281
+ either_schema.valid?(77.2) #=> true
286
282
 
287
283
  # maybe (allows nil)
288
284
  maybe_schema = RSchema.define { maybe(_Integer) }
289
- maybe_schema.call(5).valid? #=> true
290
- maybe_schema.call(nil).valid? #=> true
285
+ maybe_schema.valid?(5) #=> true
286
+ maybe_schema.valid?(nil) #=> true
291
287
 
292
288
  # enum (a set of valid values)
293
289
  enum_schema = RSchema.define { enum([:a, :b, :c]) }
294
- enum_schema.call(:a).valid? #=> true
295
- enum_schema.call(:z).valid? #=> false
290
+ enum_schema.valid?(:a) #=> true
291
+ enum_schema.valid?(:z) #=> false
296
292
 
297
293
  # predicate (block returns true for valid values)
298
294
  predicate_schema = RSchema.define do
299
295
  predicate { |x| x.even? }
300
296
  end
301
- predicate_schema.call(4).valid? #=> true
302
- predicate_schema.call(5).valid? #=> false
297
+ predicate_schema.valid?(4) #=> true
298
+ predicate_schema.valid?(5) #=> false
303
299
 
304
300
  # pipeline (apply multiple schemas to a single value, in order)
305
301
  pipeline_schema = RSchema.define do
@@ -308,46 +304,12 @@ RSchema provides a few other schema types through its DSL:
308
304
  predicate { |x| x.positive? },
309
305
  )
310
306
  end
311
- pipeline_schema.call(123).valid? #=> true
312
- pipeline_schema.call(5.1).valid? #=> true
313
- pipeline_schema.call(-24).valid? #=> false
314
-
315
-
316
- Coercion
317
- --------
318
-
319
- Coercers convert invalid data into valid data where possible, according to a
320
- schema.
321
-
322
- Take HTTP params as an example. Web forms often contain database IDs, which
323
- are integers, but are submitted as strings by the browser. Param hash keys
324
- are often expected to be `Symbol`s, but are also strings. The `HTTPCoercer`
325
- can automatically convert these strings into the appropriate type, based on a
326
- schema.
327
-
328
- # Input keys and values are all strings.
329
- input_params = {
330
- 'whatever_id' => '5',
331
- 'amount' => '123.45',
332
- }
333
-
334
- # The schema expects symbol keys, an integer value, and a float value.
335
- param_schema = RSchema.define_hash {{
336
- whatever_id: _Integer,
337
- amount: _Float,
338
- }}
339
-
340
- # The schema is wrapped in a HTTPCoercer.
341
- coercer = RSchema::HTTPCoercer.wrap(param_schema)
342
-
343
- # Use the coercer like a normal schema object.
344
- result = coercer.call(input_params)
345
-
346
- # The result object contains the coerced value
347
- result.valid? #=> true
348
- result.value #=> { :whatever_id => 5, :amount => 123.45 }
307
+ pipeline_schema.valid?(123) #=> true
308
+ pipeline_schema.valid?(5.1) #=> true
309
+ pipeline_schema.valid?(-24) #=> false
349
310
 
350
- TODO: explain how to create custom coercers
311
+ For a full list of built-in schema types, see the API documentation for all
312
+ classes in the `RSchema::Schemas` module.
351
313
 
352
314
  Extending The DSL
353
315
  -----------------
@@ -371,26 +333,25 @@ And your methods will be available via `RSchema.define`:
371
333
 
372
334
  schema = RSchema.define { palendrome }
373
335
 
374
- schema.call('racecar').valid? #=> true
375
- schema.call('ferrari').valid? #=> false
376
-
377
- This is the preferred way for other gems to extend RSchema with new kinds
378
- of schema classes.
336
+ schema.valid?('racecar') #=> true
337
+ schema.valid?('ferrari') #=> false
379
338
 
339
+ This is the preferred way for you, and other gems, to extend RSchema with new
340
+ DSL methods.
380
341
 
381
342
  Creating Your Own DSL
382
343
  ---------------------
383
344
 
384
- The default DSL is designed to be extended (i.e. modified) by external gems/code.
385
- If you want a DSL that isn't affected by external factors, you can create one
386
- yourself.
345
+ The default DSL is designed to be extended (i.e. modified) by you, and
346
+ third-party gems. If you want a DSL that isn't affected by external factors,
347
+ you can create one yourself.
387
348
 
388
- Create a new class, and include `RSchema::DSL` to get all the standard DSL
389
- methods that come built-in to RSchema. You can define your own custom methods
390
- on this class.
349
+ Create a new class, and include `RSchema::DSL` if you want have all the
350
+ standard DSL methods that come built-in to RSchema. You can define your own
351
+ custom methods on this class.
391
352
 
392
353
  class MyCustomDSL
393
- include RSchema::DSL
354
+ include RSchema::DSL # this is optional
394
355
 
395
356
  def palendrome
396
357
  pipeline(
@@ -400,22 +361,76 @@ on this class.
400
361
  end
401
362
  end
402
363
 
403
- Then simply use `instance_eval` to make use of your custom DSL.
364
+ Then pass an instance of your DSL class into `RSchema.define`:
404
365
 
405
- schema = MyCustomDSL.new.instance_eval { palendrome }
406
- schema.call('racecar').valid? #=> true
366
+ dsl = MyCustomDSL.new
367
+ schema = RSchema.define(dsl) { palendrome }
368
+ schema.valid?('racecar') #=> true
407
369
 
408
- See the implementation of `RSchema.define` for reference.
370
+ Coercion
371
+ --------
409
372
 
373
+ Coercers convert invalid data into valid data where possible, according to a
374
+ schema.
375
+
376
+ Take HTTP params as an example. Web forms often contain database IDs, which
377
+ are integers, but are submitted as strings by the browser. Param hash keys
378
+ are often expected to be `Symbol`s, but are also strings. RSchema can
379
+ automatically convert these strings into the appropriate type, based on a
380
+ schema.
410
381
 
411
- Custom Schema Types
412
- -------------------
382
+ # Input keys and values are all strings
383
+ input_params = {
384
+ 'whatever_id' => '5',
385
+ 'amount' => '123.45',
386
+ }
387
+
388
+ # The schema expects symbol keys, an integer value, and a float value
389
+ schema = RSchema.define_hash {{
390
+ whatever_id: _Integer,
391
+ amount: _Float,
392
+ }}
393
+
394
+ # A coercer is created by wrapping the schema
395
+ coercer = RSchema::CoercionWrapper::RACK_PARAMS.wrap(schema)
396
+
397
+ # Use the coercer like a normal schema object
398
+ result = coercer.validate(input_params)
399
+
400
+ # The result object contains the coerced value
401
+ result.valid? #=> true
402
+ result.value #=> { :whatever_id => 5, :amount => 123.45 }
403
+
404
+ Custom Coercion
405
+ ---------------
406
+
407
+ Coercion is designed to be totally extensible. You'll have to take my word
408
+ for it, because there isn't much documentation at the moment.
409
+
410
+ See `lib/rschema/coercion_wrapper/rack_params.rb` for an example of how to
411
+ make a coercion wrapper.
412
+
413
+ See all the classes in `lib/rschema/coercers/` for examples of how to make
414
+ individual coercers.
415
+
416
+ If you have any problems or questions about implementing custom coercion, feel
417
+ free to contact me (Tom Dalling).
418
+
419
+
420
+ Implementing Your Own Schema Types
421
+ ----------------------------------
413
422
 
414
423
  Schemas are objects that conform to a certain interface (i.e. a duck type).
415
424
  To create your own schema types, you just need to implement this interface.
416
425
 
426
+ The interface consists of two methods: `call` and `with_wrapped_subschemas`.
427
+ The `call` method is the interface for validating values.
428
+ The `with_wrapped_subschemas` is necessary for coercion to work.
429
+ If you have any problems or questions regarding this schema interface, feel
430
+ free to contact me (Tom Dalling).
431
+
417
432
  Below is a custom schema for pairs – arrays with two elements of the same type.
418
- This is already possible using existing schemas (e.g. `Array(_String, _String)`),
433
+ This is already possible using existing schemas (e.g. `array(_String, _String)`),
419
434
  and is only shown here for the purpose of demonstration.
420
435
 
421
436
  class PairSchema
@@ -423,19 +438,31 @@ and is only shown here for the purpose of demonstration.
423
438
  @subschema = subschema
424
439
  end
425
440
 
426
- def call(pair, options=RSchema::Options.default)
441
+ #
442
+ # This method is mandatory.
443
+ #
444
+ # `pair` is the value to validate.
445
+ # `options` is an `RSchema::Options` object.
446
+ # This method must return a `RSchema::Result` object
447
+ #
448
+ def call(pair, options)
427
449
  return not_an_array_failure(pair) unless pair.is_a?(Array)
428
450
  return not_a_pair_failure(pair) unless pair.size == 2
429
451
 
452
+ # pass both array elements to `@subschema.call`
430
453
  subresults = pair.map { |x| @subschema.call(x, options) }
431
454
 
455
+ # check if both elements are valid according to @subschema
432
456
  if subresults.all?(&:valid?)
433
- RSchema::Result.success(subresults.map(&:value).to_a)
457
+ RSchema::Result.success(subresults.map(&:value))
434
458
  else
435
459
  RSchema::Result.failure(subschema_error(subresults))
436
460
  end
437
461
  end
438
462
 
463
+ #
464
+ # This method is necessary for coercion to work
465
+ #
439
466
  def with_wrapped_subschemas(wrapper)
440
467
  PairSchema.new(wrapper.wrap(@subschema))
441
468
  end
@@ -458,25 +485,22 @@ and is only shown here for the purpose of demonstration.
458
485
  symbolic_name: :not_a_pair,
459
486
  schema: self,
460
487
  value: pair,
461
- vars: {
462
- expected_size: 2,
463
- actual_size: pair.size,
464
- }
465
488
  )
466
489
  )
467
490
  end
468
491
 
492
+ #
493
+ # Returns a hash of index => error
494
+ #
469
495
  def subschema_error(subresults)
470
496
  subresults
471
497
  .each_with_index
472
498
  .select { |(result, idx)| result.invalid? }
473
- .map(&:reverse)
499
+ .map { |(result, idx)| [idx, result.error] }
474
500
  .to_h
475
501
  end
476
502
  end
477
503
 
478
- TODO: need to explain how to implement `#call` and `#with_wrapped_subschemas`
479
-
480
504
  Add your new schema class to the default DSL:
481
505
 
482
506
  module PairSchemaDSL
@@ -490,13 +514,13 @@ Add your new schema class to the default DSL:
490
514
  Then your schema is accessible from `RSchema.define`:
491
515
 
492
516
  gps_coordinate_schema = RSchema.define { pair(_Float) }
493
- gps_coordinate_schema.call([1.2, 3.4]).valid? #=> true
517
+ gps_coordinate_schema.valid?([1.2, 3.4]) #=> true
494
518
 
495
519
  Coercion should work, as long as `#with_wrapped_subschemas` was implemented
496
520
  correctly.
497
521
 
498
- coercer = RSchema::HTTPCoercer.wrap(gps_coordinate_schema)
499
- result = coercer.call(['1', '2'])
522
+ coercer = RSchema::CoercionWrapper::RACK_PARAMS.wrap(gps_coordinate_schema)
523
+ result = coercer.validate(["1", "2"])
500
524
  result.valid? #=> true
501
525
  result.value #=> [1.0, 2.0]
502
526