rschema 3.1.1 → 3.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.
- checksums.yaml +4 -4
- data/lib/rschema.rb +13 -5
- data/lib/rschema/coercers.rb +2 -0
- data/lib/rschema/coercers/any.rb +37 -31
- data/lib/rschema/coercers/boolean.rb +33 -23
- data/lib/rschema/coercers/chain.rb +38 -32
- data/lib/rschema/coercers/date.rb +29 -20
- data/lib/rschema/coercers/fixed_hash/default_arrays_to_empty.rb +57 -56
- data/lib/rschema/coercers/fixed_hash/default_booleans_to_false.rb +56 -55
- data/lib/rschema/coercers/fixed_hash/remove_extraneous_attributes.rb +43 -39
- data/lib/rschema/coercers/fixed_hash/symbolize_keys.rb +55 -51
- data/lib/rschema/coercers/float.rb +22 -15
- data/lib/rschema/coercers/integer.rb +21 -15
- data/lib/rschema/coercers/nil_empty_strings.rb +20 -17
- data/lib/rschema/coercers/symbol.rb +20 -17
- data/lib/rschema/coercers/time.rb +29 -20
- data/lib/rschema/coercion_wrapper.rb +25 -26
- data/lib/rschema/coercion_wrapper/rack_params.rb +18 -19
- data/lib/rschema/dsl.rb +20 -13
- data/lib/rschema/error.rb +9 -4
- data/lib/rschema/options.rb +5 -0
- data/lib/rschema/rails.rb +60 -0
- data/lib/rschema/result.rb +9 -11
- data/lib/rschema/schemas.rb +2 -0
- data/lib/rschema/schemas/anything.rb +23 -24
- data/lib/rschema/schemas/boolean.rb +36 -30
- data/lib/rschema/schemas/coercer.rb +47 -46
- data/lib/rschema/schemas/convenience.rb +122 -123
- data/lib/rschema/schemas/enum.rb +41 -34
- data/lib/rschema/schemas/fixed_hash.rb +165 -162
- data/lib/rschema/schemas/fixed_length_array.rb +66 -58
- data/lib/rschema/schemas/maybe.rb +28 -28
- data/lib/rschema/schemas/pipeline.rb +35 -35
- data/lib/rschema/schemas/predicate.rb +40 -34
- data/lib/rschema/schemas/set.rb +57 -48
- data/lib/rschema/schemas/sum.rb +31 -34
- data/lib/rschema/schemas/type.rb +44 -38
- data/lib/rschema/schemas/variable_hash.rb +63 -61
- data/lib/rschema/schemas/variable_length_array.rb +57 -51
- data/lib/rschema/version.rb +3 -1
- metadata +54 -25
@@ -1,25 +1,28 @@
|
|
1
|
-
|
2
|
-
module Coercers
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
4
|
-
|
5
|
-
|
3
|
+
module RSchema
|
4
|
+
module Coercers
|
5
|
+
#
|
6
|
+
# Coerces `String`s to `Symbol`s
|
7
|
+
#
|
8
|
+
module Symbol
|
9
|
+
extend self
|
6
10
|
|
7
|
-
|
8
|
-
|
9
|
-
|
11
|
+
def build(_schema)
|
12
|
+
self
|
13
|
+
end
|
10
14
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
15
|
+
def call(value)
|
16
|
+
case value
|
17
|
+
when ::Symbol then Result.success(value)
|
18
|
+
when ::String then Result.success(value.to_sym)
|
19
|
+
else Result.failure
|
20
|
+
end
|
16
21
|
end
|
17
|
-
end
|
18
22
|
|
19
|
-
|
20
|
-
|
23
|
+
def will_affect?(value)
|
24
|
+
!value.is_a?(Symbol)
|
25
|
+
end
|
21
26
|
end
|
22
27
|
end
|
23
|
-
|
24
|
-
end
|
25
28
|
end
|
@@ -1,29 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module RSchema
|
2
|
-
module Coercers
|
4
|
+
module Coercers
|
5
|
+
#
|
6
|
+
# Coerces `String`s to `Time`s using `Time.parse`
|
7
|
+
module Time
|
8
|
+
extend self
|
3
9
|
|
4
|
-
|
5
|
-
|
10
|
+
def build(_schema)
|
11
|
+
self
|
12
|
+
end
|
6
13
|
|
7
|
-
|
8
|
-
|
9
|
-
|
14
|
+
def call(value)
|
15
|
+
case value
|
16
|
+
when ::Time then Result.success(value)
|
17
|
+
when ::String then coerce_string(value)
|
18
|
+
else Result.failure
|
19
|
+
end
|
20
|
+
end
|
10
21
|
|
11
|
-
|
12
|
-
|
13
|
-
when ::Time
|
14
|
-
Result.success(value)
|
15
|
-
when ::String
|
16
|
-
time = ::Time.parse(value) rescue nil
|
17
|
-
time ? Result.success(time) : Result.failure
|
18
|
-
else
|
19
|
-
Result.failure
|
22
|
+
def will_affect?(value)
|
23
|
+
!value.is_a?(Time)
|
20
24
|
end
|
21
|
-
end
|
22
25
|
|
23
|
-
|
24
|
-
|
26
|
+
private
|
27
|
+
|
28
|
+
def coerce_string(str)
|
29
|
+
time = begin
|
30
|
+
::Time.parse(str)
|
31
|
+
rescue
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
time ? Result.success(time) : Result.failure
|
35
|
+
end
|
25
36
|
end
|
26
37
|
end
|
27
|
-
|
28
|
-
end
|
29
38
|
end
|
@@ -1,4 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module RSchema
|
4
|
+
#
|
5
|
+
# Builds coercing schemas, by wrapping coercers around an existing schema.
|
6
|
+
#
|
2
7
|
class CoercionWrapper
|
3
8
|
def initialize(&initializer)
|
4
9
|
@builder_by_schema = {}
|
@@ -21,36 +26,30 @@ module RSchema
|
|
21
26
|
|
22
27
|
private
|
23
28
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
builder_for_type(schema.type)
|
28
|
-
else
|
29
|
-
nil
|
30
|
-
end
|
31
|
-
end
|
29
|
+
def builder_for_schema(schema)
|
30
|
+
@builder_by_schema.fetch(schema.class) do
|
31
|
+
builder_for_type(schema.type) if schema.is_a?(Schemas::Type)
|
32
32
|
end
|
33
|
+
end
|
33
34
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
end
|
40
|
-
|
41
|
-
nil
|
35
|
+
def builder_for_type(type)
|
36
|
+
# polymorphic lookup
|
37
|
+
type.ancestors.each do |ancestor|
|
38
|
+
builder = @builder_by_type[ancestor]
|
39
|
+
return builder if builder
|
42
40
|
end
|
43
41
|
|
44
|
-
|
45
|
-
|
46
|
-
if builder
|
47
|
-
coercer = builder.build(schema)
|
48
|
-
Schemas::Coercer.new(coercer, schema)
|
49
|
-
else
|
50
|
-
schema
|
51
|
-
end
|
52
|
-
end
|
42
|
+
nil
|
43
|
+
end
|
53
44
|
|
45
|
+
def wrap_with_coercer(schema)
|
46
|
+
builder = builder_for_schema(schema)
|
47
|
+
if builder
|
48
|
+
coercer = builder.build(schema)
|
49
|
+
Schemas::Coercer.new(coercer, schema)
|
50
|
+
else
|
51
|
+
schema
|
52
|
+
end
|
53
|
+
end
|
54
54
|
end
|
55
55
|
end
|
56
|
-
|
@@ -1,25 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'date'
|
2
4
|
|
3
5
|
module RSchema
|
4
|
-
class CoercionWrapper
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
coerce_type Date, with: Coercers::Date
|
6
|
+
class CoercionWrapper
|
7
|
+
RACK_PARAMS = CoercionWrapper.new do
|
8
|
+
coerce_type Symbol, with: Coercers::Symbol
|
9
|
+
coerce_type Integer, with: Coercers::Integer
|
10
|
+
coerce_type Float, with: Coercers::Float
|
11
|
+
coerce_type Time, with: Coercers::Time
|
12
|
+
coerce_type Date, with: Coercers::Date
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
14
|
+
coerce Schemas::Maybe, with: Coercers::NilEmptyStrings
|
15
|
+
coerce Schemas::Boolean, with: Coercers::Boolean
|
16
|
+
coerce Schemas::FixedHash, with: Coercers::Chain[
|
17
|
+
Coercers::FixedHash::SymbolizeKeys,
|
18
|
+
Coercers::FixedHash::RemoveExtraneousAttributes,
|
19
|
+
Coercers::FixedHash::DefaultBooleansToFalse,
|
20
|
+
Coercers::FixedHash::DefaultArraysToEmpty,
|
21
|
+
]
|
22
|
+
end
|
21
23
|
end
|
22
|
-
|
23
24
|
end
|
24
|
-
end
|
25
|
-
|
data/lib/rschema/dsl.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module RSchema
|
2
4
|
#
|
3
5
|
# A mixin containing all the standard RSchema DSL methods.
|
@@ -45,15 +47,15 @@ module RSchema
|
|
45
47
|
# Creates a {Schemas::VariableLengthArray} if given one argument, otherwise
|
46
48
|
# creates a {Schemas::FixedLengthArray}
|
47
49
|
#
|
48
|
-
# @param subschemas [Array<schema>] one or more schema objects representing
|
49
|
-
# in the array.
|
50
|
+
# @param subschemas [Array<schema>] one or more schema objects representing
|
51
|
+
# elements in the array.
|
50
52
|
# @return [Schemas::VariableLengthArray, Schemas::FixedLengthArray]
|
51
53
|
#
|
52
54
|
# @example (see Schemas::VariableLengthArray)
|
53
55
|
# @example (see Schemas::FixedLengthArray)
|
54
56
|
#
|
55
57
|
def array(*subschemas)
|
56
|
-
subschemas = subschemas.map{ |ss| inconvenience(ss) }
|
58
|
+
subschemas = subschemas.map { |ss| inconvenience(ss) }
|
57
59
|
|
58
60
|
if subschemas.count == 1
|
59
61
|
Schemas::VariableLengthArray.new(subschemas.first)
|
@@ -87,7 +89,7 @@ module RSchema
|
|
87
89
|
def fixed_hash(attribute_hash)
|
88
90
|
Schemas::FixedHash.new(attributes(attribute_hash))
|
89
91
|
end
|
90
|
-
|
92
|
+
alias hash fixed_hash
|
91
93
|
|
92
94
|
#
|
93
95
|
# Creates a {Schemas::Set} schema
|
@@ -143,8 +145,9 @@ module RSchema
|
|
143
145
|
end
|
144
146
|
|
145
147
|
#
|
146
|
-
# Turns an "attribute hash" into an array of
|
147
|
-
# Primarily for use with
|
148
|
+
# Turns an "attribute hash" into an array of
|
149
|
+
# {Schemas::FixedHash::Attribute}. Primarily for use with
|
150
|
+
# {Schemas::FixedHash#merge}.
|
148
151
|
#
|
149
152
|
# @param attribute_hash [Hash<key, schema>] A hash of keys to subschemas.
|
150
153
|
# The values of this hash must be schema objects.
|
@@ -161,7 +164,9 @@ module RSchema
|
|
161
164
|
attribute_hash.map do |dsl_key, value_schema|
|
162
165
|
optional = dsl_key.is_a?(OptionalWrapper)
|
163
166
|
key = optional ? dsl_key.key : dsl_key
|
164
|
-
Schemas::FixedHash::Attribute.new(
|
167
|
+
Schemas::FixedHash::Attribute.new(
|
168
|
+
key, inconvenience(value_schema), optional,
|
169
|
+
)
|
165
170
|
end
|
166
171
|
end
|
167
172
|
|
@@ -190,9 +195,11 @@ module RSchema
|
|
190
195
|
#
|
191
196
|
# @example (see Schemas::Enum)
|
192
197
|
#
|
193
|
-
def enum(valid_values, subschema=nil)
|
198
|
+
def enum(valid_values, subschema = nil)
|
194
199
|
subschema = inconvenience(subschema) if subschema
|
195
|
-
Schemas::Enum.new(
|
200
|
+
Schemas::Enum.new(
|
201
|
+
valid_values, subschema || type(valid_values.first.class),
|
202
|
+
)
|
196
203
|
end
|
197
204
|
|
198
205
|
#
|
@@ -205,7 +212,7 @@ module RSchema
|
|
205
212
|
# @example (see Schemas::Sum)
|
206
213
|
#
|
207
214
|
def either(*subschemas)
|
208
|
-
subschemas = subschemas.map{ |ss| inconvenience(ss) }
|
215
|
+
subschemas = subschemas.map { |ss| inconvenience(ss) }
|
209
216
|
Schemas::Sum.new(subschemas)
|
210
217
|
end
|
211
218
|
|
@@ -239,7 +246,7 @@ module RSchema
|
|
239
246
|
# @example (see Schemas::Pipeline)
|
240
247
|
#
|
241
248
|
def pipeline(*subschemas)
|
242
|
-
subschemas = subschemas.map{ |ss| inconvenience(ss) }
|
249
|
+
subschemas = subschemas.map { |ss| inconvenience(ss) }
|
243
250
|
Schemas::Pipeline.new(subschemas)
|
244
251
|
end
|
245
252
|
|
@@ -331,9 +338,9 @@ module RSchema
|
|
331
338
|
end
|
332
339
|
|
333
340
|
# @!visibility private
|
334
|
-
def
|
341
|
+
def respond_to_missing?(sym, include_all = false)
|
335
342
|
# check if method starts with an underscore followed by a capital
|
336
|
-
super ||
|
343
|
+
super || sym.to_s.match(/\A_[A-Z]/)
|
337
344
|
end
|
338
345
|
end
|
339
346
|
end
|
data/lib/rschema/error.rb
CHANGED
@@ -1,9 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module RSchema
|
4
|
+
#
|
5
|
+
# Contains info about how a schema failed validation
|
6
|
+
#
|
2
7
|
class Error
|
3
8
|
attr_reader :schema, :value, :symbolic_name, :vars
|
4
9
|
|
5
10
|
def initialize(schema:, value:, symbolic_name:, vars: {})
|
6
|
-
raise ArgumentError.new(
|
11
|
+
raise ArgumentError.new('vars must be a hash') unless vars.is_a?(Hash)
|
7
12
|
|
8
13
|
@schema = schema
|
9
14
|
@value = value
|
@@ -19,10 +24,10 @@ module RSchema
|
|
19
24
|
|
20
25
|
def inspect
|
21
26
|
attrs = vars.merge(value: value)
|
22
|
-
|
23
|
-
|
27
|
+
.map { |k, v| "#{k}=#{v.inspect}" }
|
28
|
+
.join(' ')
|
24
29
|
|
25
|
-
"<#{self.class} #{
|
30
|
+
"<#{self.class} #{self} #{attrs}>"
|
26
31
|
end
|
27
32
|
end
|
28
33
|
end
|
data/lib/rschema/options.rb
CHANGED
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rschema'
|
4
|
+
require 'rschema/coercion_wrapper/rack_params'
|
5
|
+
|
6
|
+
module RSchema
|
7
|
+
module Rails
|
8
|
+
#
|
9
|
+
# A mixin for ActionController that provides methods for validating params.
|
10
|
+
#
|
11
|
+
module Controller
|
12
|
+
def self.included(klass)
|
13
|
+
klass.include(InstanceMethods)
|
14
|
+
klass.extend(ClassMethods)
|
15
|
+
end
|
16
|
+
|
17
|
+
#
|
18
|
+
# Instance methods added to ActionController classes
|
19
|
+
#
|
20
|
+
module InstanceMethods
|
21
|
+
def param_schema(&schema_block)
|
22
|
+
self.class.param_schema(&schema_block)
|
23
|
+
end
|
24
|
+
|
25
|
+
def validate_params(schema = nil, &schema_block)
|
26
|
+
schema ||= param_schema(&schema_block)
|
27
|
+
schema.validate(request.parameters.to_hash)
|
28
|
+
end
|
29
|
+
|
30
|
+
def validate_params!(*args, &block)
|
31
|
+
result = validate_params(*args, &block)
|
32
|
+
raise InvalidParams.new(result.error) if result.invalid?
|
33
|
+
result.value
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# Class methods added to ActionController classes
|
39
|
+
#
|
40
|
+
module ClassMethods
|
41
|
+
def param_schema(&schema_block)
|
42
|
+
schema = RSchema.define_hash(&schema_block)
|
43
|
+
RSchema::CoercionWrapper::RACK_PARAMS.wrap(schema)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# Raised when {validate_params!} fails
|
50
|
+
#
|
51
|
+
class InvalidParams < StandardError
|
52
|
+
attr_reader :error
|
53
|
+
|
54
|
+
def initialize(error)
|
55
|
+
@error = error
|
56
|
+
super('Parameters do not conform to schema')
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/rschema/result.rb
CHANGED
@@ -1,4 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module RSchema
|
4
|
+
#
|
5
|
+
# The return value when calling a schema
|
6
|
+
#
|
2
7
|
class Result
|
3
8
|
def self.success(value = nil)
|
4
9
|
if value.nil?
|
@@ -28,22 +33,15 @@ module RSchema
|
|
28
33
|
end
|
29
34
|
|
30
35
|
def invalid?
|
31
|
-
|
36
|
+
!valid?
|
32
37
|
end
|
33
38
|
|
34
39
|
def value
|
35
|
-
if
|
36
|
-
|
37
|
-
else
|
38
|
-
raise InvalidError
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def error
|
43
|
-
@error
|
40
|
+
raise RSchema::Invalid.new(error) if invalid?
|
41
|
+
@value
|
44
42
|
end
|
45
43
|
|
46
|
-
|
44
|
+
attr_reader :error
|
47
45
|
|
48
46
|
# @!visibility private
|
49
47
|
NIL_SUCCESS = new(true, nil, nil)
|