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.
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,74 +1,75 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'set'
2
4
 
3
5
  module RSchema
4
- module Coercers
5
- module FixedHash
6
+ module Coercers
7
+ module FixedHash
8
+ # The HTTP standard says that when a form is submitted, all unchecked
9
+ # check boxes will _not_ be sent to the server. That is, they will not
10
+ # be present at all in the params hash.
11
+ #
12
+ # This class coerces these missing values into `false`.
13
+ class DefaultBooleansToFalse
14
+ attr_reader :hash_attributes
6
15
 
7
- # The HTTP standard says that when a form is submitted, all unchecked
8
- # check boxes will _not_ be sent to the server. That is, they will not
9
- # be present at all in the params hash.
10
- #
11
- # This class coerces these missing values into `false`.
12
- class DefaultBooleansToFalse
13
- attr_reader :hash_attributes
16
+ def self.build(schema)
17
+ new(schema)
18
+ end
14
19
 
15
- def self.build(schema)
16
- new(schema)
17
- end
20
+ def initialize(fixed_hash_schema)
21
+ # TODO: make fixed hash attributes frozen, and eliminate dup
22
+ @hash_attributes = fixed_hash_schema.attributes.map(&:dup)
23
+ end
18
24
 
19
- def initialize(fixed_hash_schema)
20
- #TODO: make fixed hash attributes frozen, and eliminate dup
21
- @hash_attributes = fixed_hash_schema.attributes.map(&:dup)
22
- end
25
+ def call(value)
26
+ Result.success(default_bools_to_false(value))
27
+ end
23
28
 
24
- def call(value)
25
- Result.success(default_bools_to_false(value))
26
- end
29
+ def will_affect?(value)
30
+ keys_to_default(value).any?
31
+ end
27
32
 
28
- def will_affect?(value)
29
- keys_to_default(value).any?
30
- end
33
+ private
31
34
 
32
- private
33
- def default_bools_to_false(hash)
34
- missing_keys = keys_to_default(hash)
35
+ def default_bools_to_false(hash)
36
+ missing_keys = keys_to_default(hash)
35
37
 
36
- if missing_keys.any?
37
- defaults = missing_keys.map{ |k| [k, false] }.to_h
38
- hash.merge(defaults)
39
- else
40
- hash # no coercion necessary
38
+ if missing_keys.any?
39
+ defaults = missing_keys.map { |k| [k, false] }.to_h
40
+ hash.merge(defaults)
41
+ else
42
+ hash # no coercion necessary
43
+ end
41
44
  end
42
- end
43
45
 
44
- def keys_to_default(value)
45
- if value.is_a?(Hash)
46
- keys_for_bool_defaulting - value.keys
47
- else
48
- []
46
+ def keys_to_default(value)
47
+ if value.is_a?(Hash)
48
+ keys_for_bool_defaulting - value.keys
49
+ else
50
+ []
51
+ end
49
52
  end
50
- end
51
-
52
- def keys_for_bool_defaulting
53
- @keys_for_bool_defaulting ||= Set.new(
54
- hash_attributes
55
- .reject(&:optional)
56
- .select { |attr| is_bool_schema?(attr.value_schema) }
57
- .map(&:key)
58
- )
59
- end
60
53
 
61
- def is_bool_schema?(schema)
62
- # dig through all the coercers
63
- non_coercer = schema
64
- while non_coercer.is_a?(Schemas::Coercer)
65
- non_coercer = non_coercer.subschema
54
+ def keys_for_bool_defaulting
55
+ @keys_for_bool_defaulting ||= Set.new(
56
+ hash_attributes
57
+ .reject(&:optional)
58
+ .select { |attr| bool_schema?(attr.value_schema) }
59
+ .map(&:key),
60
+ )
66
61
  end
67
62
 
68
- non_coercer.is_a?(Schemas::Boolean)
63
+ def bool_schema?(schema)
64
+ # dig through all the coercers
65
+ non_coercer = schema
66
+ while non_coercer.is_a?(Schemas::Coercer)
67
+ non_coercer = non_coercer.subschema
68
+ end
69
+
70
+ non_coercer.is_a?(Schemas::Boolean)
71
+ end
69
72
  end
73
+ end
70
74
  end
71
-
72
- end
73
- end
74
75
  end
@@ -1,54 +1,58 @@
1
- module RSchema
2
- module Coercers
3
- module FixedHash
4
-
5
- class RemoveExtraneousAttributes
6
- attr_reader :hash_attributes
1
+ # frozen_string_literal: true
7
2
 
8
- def self.build(schema)
9
- new(schema)
10
- end
3
+ module RSchema
4
+ module Coercers
5
+ module FixedHash
6
+ #
7
+ # Removes elements from `Hash` values that are not defined in the given
8
+ # `FixedHash` schema.
9
+ #
10
+ class RemoveExtraneousAttributes
11
+ attr_reader :hash_attributes
12
+
13
+ def self.build(schema)
14
+ new(schema)
15
+ end
11
16
 
12
- def initialize(fixed_hash_schema)
13
- #TODO: make fixed hash attributes frozen, and eliminate dup
14
- @hash_attributes = fixed_hash_schema.attributes.map(&:dup)
15
- end
17
+ def initialize(fixed_hash_schema)
18
+ # TODO: make fixed hash attributes frozen, and eliminate dup
19
+ @hash_attributes = fixed_hash_schema.attributes.map(&:dup)
20
+ end
16
21
 
17
- def call(value)
18
- Result.success(remove_extraneous_elements(value))
19
- end
22
+ def call(value)
23
+ Result.success(remove_extraneous_elements(value))
24
+ end
20
25
 
21
- def will_affect?(value)
22
- keys_to_remove(value).any?
23
- end
26
+ def will_affect?(value)
27
+ keys_to_remove(value).any?
28
+ end
24
29
 
25
- private
30
+ private
26
31
 
27
- def remove_extraneous_elements(hash)
28
- extra_keys = keys_to_remove(hash)
32
+ def remove_extraneous_elements(hash)
33
+ extra_keys = keys_to_remove(hash)
29
34
 
30
- if extra_keys.any?
31
- hash.dup.tap do |stripped_hash|
32
- extra_keys.each { |k| stripped_hash.delete(k) }
35
+ if extra_keys.any?
36
+ hash.dup.tap do |stripped_hash|
37
+ extra_keys.each { |k| stripped_hash.delete(k) }
38
+ end
39
+ else
40
+ hash
33
41
  end
34
- else
35
- hash
36
42
  end
37
- end
38
43
 
39
- def keys_to_remove(value)
40
- if value.is_a?(Hash)
41
- value.keys - valid_keys
42
- else
43
- []
44
+ def keys_to_remove(value)
45
+ if value.is_a?(Hash)
46
+ value.keys - valid_keys
47
+ else
48
+ []
49
+ end
44
50
  end
45
- end
46
51
 
47
- def valid_keys
48
- @valid_keys ||= hash_attributes.map(&:key)
52
+ def valid_keys
53
+ @valid_keys ||= hash_attributes.map(&:key)
54
+ end
49
55
  end
56
+ end
50
57
  end
51
-
52
- end
53
- end
54
58
  end
@@ -1,70 +1,74 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'set'
2
4
 
3
5
  module RSchema
4
- module Coercers
5
- module FixedHash
6
-
7
- class SymbolizeKeys
8
- attr_reader :hash_attributes
6
+ module Coercers
7
+ module FixedHash
8
+ #
9
+ # Coerces `String` keys into `Symbol`s according to the attributes in
10
+ # a given `FixedHash` schema.
11
+ #
12
+ class SymbolizeKeys
13
+ attr_reader :hash_attributes
9
14
 
10
- def self.build(schema)
11
- new(schema)
12
- end
15
+ def self.build(schema)
16
+ new(schema)
17
+ end
13
18
 
14
- def initialize(fixed_hash_schema)
15
- #TODO: make fixed hash attributes frozen, and eliminate dup
16
- @hash_attributes = fixed_hash_schema.attributes.map(&:dup)
17
- end
19
+ def initialize(fixed_hash_schema)
20
+ # TODO: make fixed hash attributes frozen, and eliminate dup
21
+ @hash_attributes = fixed_hash_schema.attributes.map(&:dup)
22
+ end
18
23
 
19
- def call(value)
20
- Result.success(symbolize_keys(value))
21
- end
24
+ def call(value)
25
+ Result.success(symbolize_keys(value))
26
+ end
22
27
 
23
- def will_affect?(value)
24
- keys_to_symbolize(hash).any?
25
- end
28
+ def will_affect?(_value)
29
+ keys_to_symbolize(hash).any?
30
+ end
26
31
 
27
- private
32
+ private
28
33
 
29
- def symbolize_keys(hash)
30
- keys = keys_to_symbolize(hash)
31
- if keys.any?
32
- hash.dup.tap do |new_hash|
33
- keys.each { |k| new_hash[k.to_sym] = new_hash.delete(k) }
34
+ def symbolize_keys(hash)
35
+ keys = keys_to_symbolize(hash)
36
+ if keys.any?
37
+ hash.dup.tap do |new_hash|
38
+ keys.each { |k| new_hash[k.to_sym] = new_hash.delete(k) }
39
+ end
40
+ else
41
+ hash
34
42
  end
35
- else
36
- hash
37
43
  end
38
- end
39
44
 
40
- def keys_to_symbolize(value)
41
- if value.is_a?(Hash)
42
- non_string_keys = Set.new(value.keys) - string_keys
43
- non_string_keys.intersection(symbol_keys_as_strings)
44
- else
45
- []
45
+ def keys_to_symbolize(value)
46
+ if value.is_a?(Hash)
47
+ non_string_keys = Set.new(value.keys) - string_keys
48
+ non_string_keys.intersection(symbol_keys_as_strings)
49
+ else
50
+ []
51
+ end
46
52
  end
47
- end
48
53
 
49
- def symbol_keys_as_strings
50
- @symbol_keys_as_strings ||= Set.new(
51
- all_keys
52
- .select{ |k| k.is_a?(::Symbol) }
53
- .map(&:to_s)
54
- )
55
- end
54
+ def symbol_keys_as_strings
55
+ @symbol_keys_as_strings ||= Set.new(
56
+ all_keys
57
+ .select { |k| k.is_a?(::Symbol) }
58
+ .map(&:to_s),
59
+ )
60
+ end
56
61
 
57
- def string_keys
58
- @string_keys ||= Set.new(
59
- all_keys.select { |k| k.is_a?(::String) }
60
- )
61
- end
62
+ def string_keys
63
+ @string_keys ||= Set.new(
64
+ all_keys.select { |k| k.is_a?(::String) },
65
+ )
66
+ end
62
67
 
63
- def all_keys
64
- @all_keys ||= hash_attributes.map(&:key)
68
+ def all_keys
69
+ @all_keys ||= hash_attributes.map(&:key)
70
+ end
65
71
  end
72
+ end
66
73
  end
67
-
68
- end
69
- end
70
74
  end
@@ -1,22 +1,29 @@
1
- module RSchema
2
- module Coercers
1
+ # frozen_string_literal: true
3
2
 
4
- module Float
5
- extend self
3
+ module RSchema
4
+ module Coercers
5
+ #
6
+ # Coerces values into `Float` objects using `Kernel#Float`
7
+ #
8
+ module Float
9
+ extend self
6
10
 
7
- def build(schema)
8
- self
9
- end
11
+ def build(_schema)
12
+ self
13
+ end
10
14
 
11
- def call(value)
12
- flt = Float(value) rescue nil
13
- flt ? Result.success(flt) : Result.failure
14
- end
15
+ def call(value)
16
+ flt = begin
17
+ Float(value)
18
+ rescue
19
+ nil
20
+ end
21
+ flt ? Result.success(flt) : Result.failure
22
+ end
15
23
 
16
- def will_affect?(value)
17
- not value.is_a?(Float)
24
+ def will_affect?(value)
25
+ !value.is_a?(Float)
26
+ end
18
27
  end
19
28
  end
20
-
21
- end
22
29
  end
@@ -1,22 +1,28 @@
1
- module RSchema
2
- module Coercers
1
+ # frozen_string_literal: true
3
2
 
4
- module Integer
5
- extend self
3
+ module RSchema
4
+ module Coercers
5
+ #
6
+ # Coerces values to `Integer`s using `Kernel#Integer`
7
+ module Integer
8
+ extend self
6
9
 
7
- def build(schema)
8
- self
9
- end
10
+ def build(_schema)
11
+ self
12
+ end
10
13
 
11
- def call(value)
12
- int = Integer(value) rescue nil
13
- int ? Result.success(int) : Result.failure
14
- end
14
+ def call(value)
15
+ int = begin
16
+ Integer(value)
17
+ rescue
18
+ nil
19
+ end
20
+ int ? Result.success(int) : Result.failure
21
+ end
15
22
 
16
- def will_affect?(value)
17
- not value.is_a?(Integer)
23
+ def will_affect?(value)
24
+ !value.is_a?(Integer)
25
+ end
18
26
  end
19
27
  end
20
-
21
- end
22
28
  end
@@ -1,25 +1,28 @@
1
- module RSchema
2
- module Coercers
1
+ # frozen_string_literal: true
3
2
 
4
- module NilEmptyStrings
5
- extend self
3
+ module RSchema
4
+ module Coercers
5
+ #
6
+ # Coerces empty strings to `nil`
7
+ #
8
+ module NilEmptyStrings
9
+ extend self
6
10
 
7
- def build(schema)
8
- self
9
- end
11
+ def build(_schema)
12
+ self
13
+ end
10
14
 
11
- def call(value)
12
- if "" == value
13
- Result.success(nil)
14
- else
15
- Result.success(value)
15
+ def call(value)
16
+ if value == ''
17
+ Result.success(nil)
18
+ else
19
+ Result.success(value)
20
+ end
16
21
  end
17
- end
18
22
 
19
- def will_affect?(value)
20
- "" == value
23
+ def will_affect?(value)
24
+ value == ''
25
+ end
21
26
  end
22
27
  end
23
-
24
- end
25
28
  end