rschema 3.1.1 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/lib/rschema.rb +13 -5
  3. data/lib/rschema/coercers.rb +2 -0
  4. data/lib/rschema/coercers/any.rb +37 -31
  5. data/lib/rschema/coercers/boolean.rb +33 -23
  6. data/lib/rschema/coercers/chain.rb +38 -32
  7. data/lib/rschema/coercers/date.rb +29 -20
  8. data/lib/rschema/coercers/fixed_hash/default_arrays_to_empty.rb +57 -56
  9. data/lib/rschema/coercers/fixed_hash/default_booleans_to_false.rb +56 -55
  10. data/lib/rschema/coercers/fixed_hash/remove_extraneous_attributes.rb +43 -39
  11. data/lib/rschema/coercers/fixed_hash/symbolize_keys.rb +55 -51
  12. data/lib/rschema/coercers/float.rb +22 -15
  13. data/lib/rschema/coercers/integer.rb +21 -15
  14. data/lib/rschema/coercers/nil_empty_strings.rb +20 -17
  15. data/lib/rschema/coercers/symbol.rb +20 -17
  16. data/lib/rschema/coercers/time.rb +29 -20
  17. data/lib/rschema/coercion_wrapper.rb +25 -26
  18. data/lib/rschema/coercion_wrapper/rack_params.rb +18 -19
  19. data/lib/rschema/dsl.rb +20 -13
  20. data/lib/rschema/error.rb +9 -4
  21. data/lib/rschema/options.rb +5 -0
  22. data/lib/rschema/rails.rb +60 -0
  23. data/lib/rschema/result.rb +9 -11
  24. data/lib/rschema/schemas.rb +2 -0
  25. data/lib/rschema/schemas/anything.rb +23 -24
  26. data/lib/rschema/schemas/boolean.rb +36 -30
  27. data/lib/rschema/schemas/coercer.rb +47 -46
  28. data/lib/rschema/schemas/convenience.rb +122 -123
  29. data/lib/rschema/schemas/enum.rb +41 -34
  30. data/lib/rschema/schemas/fixed_hash.rb +165 -162
  31. data/lib/rschema/schemas/fixed_length_array.rb +66 -58
  32. data/lib/rschema/schemas/maybe.rb +28 -28
  33. data/lib/rschema/schemas/pipeline.rb +35 -35
  34. data/lib/rschema/schemas/predicate.rb +40 -34
  35. data/lib/rschema/schemas/set.rb +57 -48
  36. data/lib/rschema/schemas/sum.rb +31 -34
  37. data/lib/rschema/schemas/type.rb +44 -38
  38. data/lib/rschema/schemas/variable_hash.rb +63 -61
  39. data/lib/rschema/schemas/variable_length_array.rb +57 -51
  40. data/lib/rschema/version.rb +3 -1
  41. 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
- # A schema representing that a value may be `nil`
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
- def initialize(subschema)
18
- @subschema = subschema
19
- end
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
- def call(value, options)
22
- if nil == value
23
- Result.success(value)
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
- # A schema that chains together an ordered list of other schemas
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
- def initialize(subschemas)
22
- @subschemas = subschemas
23
- end
26
+ def call(value, options)
27
+ result = Result.success(value)
24
28
 
25
- def call(value, options)
26
- result = Result.success(value)
29
+ subschemas.each do |subsch|
30
+ result = subsch.call(result.value, options)
31
+ break if result.invalid?
32
+ end
27
33
 
28
- subschemas.each do |subsch|
29
- result = subsch.call(result.value, options)
30
- break if result.invalid?
31
- end
32
-
33
- result
34
- end
34
+ result
35
+ end
35
36
 
36
- def with_wrapped_subschemas(wrapper)
37
- wrapped_subschemas = subschemas.map{ |ss| wrapper.wrap(ss) }
38
- self.class.new(wrapped_subschemas)
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
- # A schema that uses a given block to determine whether a value is valid
6
- #
7
- # @example A predicate that checks if numbers are odd
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
- def initialize(name = nil, &block)
18
- @block = block
19
- @name = name
20
- end
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
- def call(value, options)
23
- if block.call(value)
24
- Result.success(value)
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
- def with_wrapped_subschemas(wrapper)
36
- self
37
- end
35
+ private
38
36
 
39
- end
40
- end
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
@@ -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
- # A schema that matches `Set` objects (from the Ruby standard library)
8
- #
9
- # @example A set of integers
10
- # require 'set'
11
- # schema = RSchema.define { set(_Integer) }
12
- # schema.valid?(Set[1, 2, 3]) #=> true
13
- # schema.valid?(Set[:a, :b, :c]) #=> false
14
- #
15
- class Set
16
- attr_reader :subschema
17
-
18
- def initialize(subschema)
19
- @subschema = subschema
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
- def call(value, options)
23
- return not_a_set_result(value) unless value.is_a?(::Set)
23
+ def call(value, options)
24
+ return not_a_set_result(value) unless value.is_a?(::Set)
24
25
 
25
- result_value = ::Set.new
26
- result_errors = {}
26
+ validated_set, errors = apply_subschema(value, options)
27
27
 
28
- value.each do |subvalue|
29
- subresult = subschema.call(subvalue, options)
30
- if subresult.valid?
31
- result_value << subresult.value
32
- else
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
- break if options.fail_fast?
37
- end
35
+ def with_wrapped_subschemas(wrapper)
36
+ wrapped_subschema = wrapper.wrap(subschema)
37
+ self.class.new(wrapped_subschema)
38
+ end
38
39
 
39
- if result_errors.empty?
40
- Result.success(result_value)
41
- else
42
- Result.failure(result_errors)
43
- end
44
- end
40
+ private
45
41
 
46
- def with_wrapped_subschemas(wrapper)
47
- wrapped_subschema = wrapper.wrap(subschema)
48
- self.class.new(wrapped_subschema)
49
- end
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
- private
52
- def not_a_set_result(value)
53
- Result.failure(Error.new(
54
- schema: self,
55
- symbolic_name: :not_a_set,
56
- value: value,
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
@@ -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
- # A schema that represents a "sum type"
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
- def initialize(subschemas)
18
- @subschemas = subschemas
19
- end
22
+ def call(value, options)
23
+ suberrors = []
20
24
 
21
- def call(value, options)
22
- suberrors = []
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
- @subschemas.each do |subsch|
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
- def with_wrapped_subschemas(wrapper)
37
- wrapped_subschemas = subschemas.map{ |ss| wrapper.wrap(ss) }
38
- self.class.new(wrapped_subschemas)
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
@@ -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
- # A schema that matches values of a given type (i.e. `value.is_a?(type)`)
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
- def initialize(type)
24
- @type = type
25
- end
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
- def call(value, options)
28
- if value.is_a?(@type)
29
- Result.success(value)
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
- def with_wrapped_subschemas(wrapper)
40
- self
41
- end
40
+ private
42
41
 
43
- end
44
- end
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