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,35 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module RSchema
|
2
|
-
module Schemas
|
4
|
+
module Schemas
|
5
|
+
#
|
6
|
+
# A schema representing that a value may be `nil`
|
7
|
+
#
|
8
|
+
# If the value is not `nil`, it must conform to the subschema
|
9
|
+
#
|
10
|
+
# @example A nil-able Integer
|
11
|
+
# schema = RSchema.define{ maybe(_Integer) }
|
12
|
+
# schema.valid?(5) #=> true
|
13
|
+
# schema.valid?(nil) #=> true
|
14
|
+
#
|
15
|
+
class Maybe
|
16
|
+
attr_reader :subschema
|
3
17
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
# If the value is not `nil`, it must conform to the subschema
|
8
|
-
#
|
9
|
-
# @example A nil-able Integer
|
10
|
-
# schema = RSchema.define{ maybe(_Integer) }
|
11
|
-
# schema.valid?(5) #=> true
|
12
|
-
# schema.valid?(nil) #=> true
|
13
|
-
#
|
14
|
-
class Maybe
|
15
|
-
attr_reader :subschema
|
18
|
+
def initialize(subschema)
|
19
|
+
@subschema = subschema
|
20
|
+
end
|
16
21
|
|
17
|
-
|
18
|
-
|
19
|
-
|
22
|
+
def call(value, options)
|
23
|
+
if value.nil?
|
24
|
+
Result.success(value)
|
25
|
+
else
|
26
|
+
@subschema.call(value, options)
|
27
|
+
end
|
28
|
+
end
|
20
29
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
else
|
25
|
-
@subschema.call(value, options)
|
30
|
+
def with_wrapped_subschemas(wrapper)
|
31
|
+
self.class.new(wrapper.wrap(subschema))
|
32
|
+
end
|
26
33
|
end
|
27
34
|
end
|
28
|
-
|
29
|
-
def with_wrapped_subschemas(wrapper)
|
30
|
-
self.class.new(wrapper.wrap(subschema))
|
31
|
-
end
|
32
|
-
|
33
|
-
end
|
34
|
-
end
|
35
35
|
end
|
@@ -1,43 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module RSchema
|
2
|
-
module Schemas
|
4
|
+
module Schemas
|
5
|
+
#
|
6
|
+
# A schema that chains together an ordered list of other schemas
|
7
|
+
#
|
8
|
+
# @example A schema for positive floats
|
9
|
+
# schema = RSchema.define do
|
10
|
+
# pipeline(
|
11
|
+
# _Float,
|
12
|
+
# predicate{ |f| f > 0.0 },
|
13
|
+
# )
|
14
|
+
# end
|
15
|
+
# schema.valid?(6.2) #=> true
|
16
|
+
# schema.valid?('hi') #=> false (because it's not a Float)
|
17
|
+
# schema.valid?(-6.2) #=> false (because predicate failed)
|
18
|
+
#
|
19
|
+
class Pipeline
|
20
|
+
attr_reader :subschemas
|
3
21
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
# @example A schema for positive floats
|
8
|
-
# schema = RSchema.define do
|
9
|
-
# pipeline(
|
10
|
-
# _Float,
|
11
|
-
# predicate{ |f| f > 0.0 },
|
12
|
-
# )
|
13
|
-
# end
|
14
|
-
# schema.valid?(6.2) #=> true
|
15
|
-
# schema.valid?('hi') #=> false (because it's not a Float)
|
16
|
-
# schema.valid?(-6.2) #=> false (because predicate failed)
|
17
|
-
#
|
18
|
-
class Pipeline
|
19
|
-
attr_reader :subschemas
|
22
|
+
def initialize(subschemas)
|
23
|
+
@subschemas = subschemas
|
24
|
+
end
|
20
25
|
|
21
|
-
|
22
|
-
|
23
|
-
end
|
26
|
+
def call(value, options)
|
27
|
+
result = Result.success(value)
|
24
28
|
|
25
|
-
|
26
|
-
|
29
|
+
subschemas.each do |subsch|
|
30
|
+
result = subsch.call(result.value, options)
|
31
|
+
break if result.invalid?
|
32
|
+
end
|
27
33
|
|
28
|
-
|
29
|
-
|
30
|
-
break if result.invalid?
|
31
|
-
end
|
32
|
-
|
33
|
-
result
|
34
|
-
end
|
34
|
+
result
|
35
|
+
end
|
35
36
|
|
36
|
-
|
37
|
-
|
38
|
-
|
37
|
+
def with_wrapped_subschemas(wrapper)
|
38
|
+
wrapped_subschemas = subschemas.map { |ss| wrapper.wrap(ss) }
|
39
|
+
self.class.new(wrapped_subschemas)
|
40
|
+
end
|
41
|
+
end
|
39
42
|
end
|
40
|
-
|
41
|
-
end
|
42
|
-
end
|
43
43
|
end
|
@@ -1,41 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module RSchema
|
2
|
-
module Schemas
|
4
|
+
module Schemas
|
5
|
+
#
|
6
|
+
# A schema that uses a given block to determine whether a value is valid
|
7
|
+
#
|
8
|
+
# @example A predicate that checks if numbers are odd
|
9
|
+
# schema = RSchema.define do
|
10
|
+
# predicate('odd'){ |x| x.odd? }
|
11
|
+
# end
|
12
|
+
# schema.valid?(5) #=> true
|
13
|
+
# schema.valid?(6) #=> false
|
14
|
+
#
|
15
|
+
class Predicate
|
16
|
+
attr_reader :block, :name
|
3
17
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
# schema = RSchema.define do
|
9
|
-
# predicate('odd'){ |x| x.odd? }
|
10
|
-
# end
|
11
|
-
# schema.valid?(5) #=> true
|
12
|
-
# schema.valid?(6) #=> false
|
13
|
-
#
|
14
|
-
class Predicate
|
15
|
-
attr_reader :block, :name
|
18
|
+
def initialize(name = nil, &block)
|
19
|
+
@block = block
|
20
|
+
@name = name
|
21
|
+
end
|
16
22
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
23
|
+
def call(value, _options)
|
24
|
+
if block.call(value)
|
25
|
+
Result.success(value)
|
26
|
+
else
|
27
|
+
Result.failure(error(value))
|
28
|
+
end
|
29
|
+
end
|
21
30
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
else
|
26
|
-
Result.failure(Error.new(
|
27
|
-
schema: self,
|
28
|
-
value: value,
|
29
|
-
symbolic_name: :false,
|
30
|
-
vars: { predicate_name: name }
|
31
|
-
))
|
32
|
-
end
|
33
|
-
end
|
31
|
+
def with_wrapped_subschemas(_wrapper)
|
32
|
+
self
|
33
|
+
end
|
34
34
|
|
35
|
-
|
36
|
-
self
|
37
|
-
end
|
35
|
+
private
|
38
36
|
|
39
|
-
|
40
|
-
|
37
|
+
def error(value)
|
38
|
+
Error.new(
|
39
|
+
schema: self,
|
40
|
+
value: value,
|
41
|
+
symbolic_name: :false,
|
42
|
+
vars: { predicate_name: name },
|
43
|
+
)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
41
47
|
end
|
data/lib/rschema/schemas/set.rb
CHANGED
@@ -1,61 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'set'
|
2
4
|
|
3
5
|
module RSchema
|
4
|
-
module Schemas
|
5
|
-
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
# schema
|
12
|
-
# schema.valid?(Set[
|
13
|
-
#
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end
|
6
|
+
module Schemas
|
7
|
+
#
|
8
|
+
# A schema that matches `Set` objects (from the Ruby standard library)
|
9
|
+
#
|
10
|
+
# @example A set of integers
|
11
|
+
# require 'set'
|
12
|
+
# schema = RSchema.define { set(_Integer) }
|
13
|
+
# schema.valid?(Set[1, 2, 3]) #=> true
|
14
|
+
# schema.valid?(Set[:a, :b, :c]) #=> false
|
15
|
+
#
|
16
|
+
class Set
|
17
|
+
attr_reader :subschema
|
18
|
+
|
19
|
+
def initialize(subschema)
|
20
|
+
@subschema = subschema
|
21
|
+
end
|
21
22
|
|
22
|
-
|
23
|
-
|
23
|
+
def call(value, options)
|
24
|
+
return not_a_set_result(value) unless value.is_a?(::Set)
|
24
25
|
|
25
|
-
|
26
|
-
result_errors = {}
|
26
|
+
validated_set, errors = apply_subschema(value, options)
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
result_errors[subvalue] = subresult.error
|
28
|
+
if errors.empty?
|
29
|
+
Result.success(validated_set)
|
30
|
+
else
|
31
|
+
Result.failure(errors)
|
32
|
+
end
|
34
33
|
end
|
35
34
|
|
36
|
-
|
37
|
-
|
35
|
+
def with_wrapped_subschemas(wrapper)
|
36
|
+
wrapped_subschema = wrapper.wrap(subschema)
|
37
|
+
self.class.new(wrapped_subschema)
|
38
|
+
end
|
38
39
|
|
39
|
-
|
40
|
-
Result.success(result_value)
|
41
|
-
else
|
42
|
-
Result.failure(result_errors)
|
43
|
-
end
|
44
|
-
end
|
40
|
+
private
|
45
41
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
42
|
+
def apply_subschema(set, options)
|
43
|
+
validated_set = ::Set.new
|
44
|
+
errors = {}
|
45
|
+
|
46
|
+
set.each do |subvalue|
|
47
|
+
subresult = subschema.call(subvalue, options)
|
48
|
+
if subresult.valid?
|
49
|
+
validated_set << subresult.value
|
50
|
+
else
|
51
|
+
errors[subvalue] = subresult.error
|
52
|
+
break if options.fail_fast?
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
[validated_set, errors]
|
57
|
+
end
|
50
58
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
59
|
+
def not_a_set_result(value)
|
60
|
+
Result.failure(
|
61
|
+
Error.new(
|
62
|
+
schema: self,
|
63
|
+
symbolic_name: :not_a_set,
|
64
|
+
value: value,
|
65
|
+
),
|
66
|
+
)
|
67
|
+
end
|
58
68
|
end
|
59
|
-
end
|
60
|
-
end
|
69
|
+
end
|
61
70
|
end
|
data/lib/rschema/schemas/sum.rb
CHANGED
@@ -1,43 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module RSchema
|
2
|
-
module Schemas
|
4
|
+
module Schemas
|
5
|
+
#
|
6
|
+
# A schema that represents a "sum type"
|
7
|
+
#
|
8
|
+
# Values must conform to one of the subschemas.
|
9
|
+
#
|
10
|
+
# @example A schema that matches both Integers and Strings
|
11
|
+
# schema = RSchema.define { either(_String, _Integer) }
|
12
|
+
# schema.valid?("hello") #=> true
|
13
|
+
# schema.valid?(5) #=> true
|
14
|
+
#
|
15
|
+
class Sum
|
16
|
+
attr_reader :subschemas
|
3
17
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
# Values must conform to one of the subschemas.
|
8
|
-
#
|
9
|
-
# @example A schema that matches both Integers and Strings
|
10
|
-
# schema = RSchema.define { either(_String, _Integer) }
|
11
|
-
# schema.valid?("hello") #=> true
|
12
|
-
# schema.valid?(5) #=> true
|
13
|
-
#
|
14
|
-
class Sum
|
15
|
-
attr_reader :subschemas
|
18
|
+
def initialize(subschemas)
|
19
|
+
@subschemas = subschemas
|
20
|
+
end
|
16
21
|
|
17
|
-
|
18
|
-
|
19
|
-
end
|
22
|
+
def call(value, options)
|
23
|
+
suberrors = []
|
20
24
|
|
21
|
-
|
22
|
-
|
25
|
+
@subschemas.each do |ss|
|
26
|
+
result = ss.call(value, options)
|
27
|
+
return result if result.valid?
|
28
|
+
suberrors << result.error
|
29
|
+
end
|
23
30
|
|
24
|
-
|
25
|
-
result = subsch.call(value, options)
|
26
|
-
if result.valid?
|
27
|
-
return result
|
28
|
-
else
|
29
|
-
suberrors << result.error
|
31
|
+
Result.failure(suberrors)
|
30
32
|
end
|
31
|
-
end
|
32
|
-
|
33
|
-
Result.failure(suberrors)
|
34
|
-
end
|
35
33
|
|
36
|
-
|
37
|
-
|
38
|
-
|
34
|
+
def with_wrapped_subschemas(wrapper)
|
35
|
+
wrapped_subschemas = subschemas.map { |ss| wrapper.wrap(ss) }
|
36
|
+
self.class.new(wrapped_subschemas)
|
37
|
+
end
|
38
|
+
end
|
39
39
|
end
|
40
|
-
|
41
|
-
end
|
42
|
-
end
|
43
40
|
end
|
data/lib/rschema/schemas/type.rb
CHANGED
@@ -1,45 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module RSchema
|
2
|
-
module Schemas
|
4
|
+
module Schemas
|
5
|
+
#
|
6
|
+
# A schema that matches values of a given type (i.e. `value.is_a?(type)`)
|
7
|
+
#
|
8
|
+
# @example An Integer schema
|
9
|
+
# schema = RSchema.define { _Integer }
|
10
|
+
# schema.valid?(5) #=> true
|
11
|
+
#
|
12
|
+
# @example A namespaced type
|
13
|
+
# schema = RSchema.define do
|
14
|
+
# # This will not work:
|
15
|
+
# # _ActiveWhatever::Thing
|
16
|
+
#
|
17
|
+
# # This will work:
|
18
|
+
# type(ActiveWhatever::Thing)
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
class Type
|
22
|
+
attr_reader :type
|
3
23
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
# @example An Integer schema
|
8
|
-
# schema = RSchema.define { _Integer }
|
9
|
-
# schema.valid?(5) #=> true
|
10
|
-
#
|
11
|
-
# @example A namespaced type
|
12
|
-
# schema = RSchema.define do
|
13
|
-
# # This will not work:
|
14
|
-
# # _ActiveWhatever::Thing
|
15
|
-
#
|
16
|
-
# # This will work:
|
17
|
-
# type(ActiveWhatever::Thing)
|
18
|
-
# end
|
19
|
-
#
|
20
|
-
class Type
|
21
|
-
attr_reader :type
|
24
|
+
def initialize(type)
|
25
|
+
@type = type
|
26
|
+
end
|
22
27
|
|
23
|
-
|
24
|
-
|
25
|
-
|
28
|
+
def call(value, _options)
|
29
|
+
if value.is_a?(@type)
|
30
|
+
Result.success(value)
|
31
|
+
else
|
32
|
+
Result.failure(error(value))
|
33
|
+
end
|
34
|
+
end
|
26
35
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
else
|
31
|
-
Result.failure(Error.new(
|
32
|
-
schema: self,
|
33
|
-
value: value,
|
34
|
-
symbolic_name: :wrong_type,
|
35
|
-
))
|
36
|
-
end
|
37
|
-
end
|
36
|
+
def with_wrapped_subschemas(_wrapper)
|
37
|
+
self
|
38
|
+
end
|
38
39
|
|
39
|
-
|
40
|
-
self
|
41
|
-
end
|
40
|
+
private
|
42
41
|
|
43
|
-
|
44
|
-
|
42
|
+
def error(value)
|
43
|
+
Error.new(
|
44
|
+
schema: self,
|
45
|
+
value: value,
|
46
|
+
symbolic_name: :wrong_type,
|
47
|
+
)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
45
51
|
end
|