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,25 +1,28 @@
1
- module RSchema
2
- module Coercers
1
+ # frozen_string_literal: true
3
2
 
4
- module Symbol
5
- extend self
3
+ module RSchema
4
+ module Coercers
5
+ #
6
+ # Coerces `String`s to `Symbol`s
7
+ #
8
+ module Symbol
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
- case value
13
- when ::Symbol then Result.success(value)
14
- when ::String then Result.success(value.to_sym)
15
- else Result.failure
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
- def will_affect?(value)
20
- not value.is_a?(Symbol)
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
- module Time
5
- extend self
10
+ def build(_schema)
11
+ self
12
+ end
6
13
 
7
- def build(schema)
8
- self
9
- end
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
- def call(value)
12
- case value
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
- def will_affect?(value)
24
- not value.is_a?(Time)
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
- def builder_for_schema(schema)
25
- @builder_by_schema.fetch(schema.class) do
26
- if schema.is_a?(Schemas::Type)
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
- def builder_for_type(type)
35
- # polymorphic lookup
36
- type.ancestors.each do |ancestor|
37
- builder = @builder_by_type[ancestor]
38
- return builder if builder
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
- def wrap_with_coercer(schema)
45
- builder = builder_for_schema(schema)
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
- RACK_PARAMS = CoercionWrapper.new do
7
- coerce_type Symbol, with: Coercers::Symbol
8
- coerce_type Integer, with: Coercers::Integer
9
- coerce_type Float, with: Coercers::Float
10
- coerce_type Time, with: Coercers::Time
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
- coerce Schemas::Maybe, with: Coercers::NilEmptyStrings
14
- coerce Schemas::Boolean, with: Coercers::Boolean
15
- coerce Schemas::FixedHash, with: Coercers::Chain[
16
- Coercers::FixedHash::SymbolizeKeys,
17
- Coercers::FixedHash::RemoveExtraneousAttributes,
18
- Coercers::FixedHash::DefaultBooleansToFalse,
19
- Coercers::FixedHash::DefaultArraysToEmpty,
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
-
@@ -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 elements
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
- alias_method :hash, :fixed_hash
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 {Schemas::FixedHash::Attribute}.
147
- # Primarily for use with {Schemas::FixedHash#merge}.
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(key, inconvenience(value_schema), optional)
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(valid_values, subschema || type(valid_values.first.class))
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 respond_to?(sym, include_all=false)
341
+ def respond_to_missing?(sym, include_all = false)
335
342
  # check if method starts with an underscore followed by a capital
336
- super || !!sym.to_s.match(/\A_[A-Z]/)
343
+ super || sym.to_s.match(/\A_[A-Z]/)
337
344
  end
338
345
  end
339
346
  end
@@ -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("vars must be a hash") unless vars.is_a?(Hash)
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
- .map{ |k, v| "#{k}=#{v.inspect}" }
23
- .join(' ')
27
+ .map { |k, v| "#{k}=#{v.inspect}" }
28
+ .join(' ')
24
29
 
25
- "<#{self.class} #{to_s} #{attrs}>"
30
+ "<#{self.class} #{self} #{attrs}>"
26
31
  end
27
32
  end
28
33
  end
@@ -1,4 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RSchema
4
+ #
5
+ # Settings, passed in as an argument when calling schemas
6
+ #
2
7
  class Options
3
8
  def self.default
4
9
  @default ||= new
@@ -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
@@ -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
- not valid?
36
+ !valid?
32
37
  end
33
38
 
34
39
  def value
35
- if valid?
36
- @value
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
- class InvalidError < StandardError; end
44
+ attr_reader :error
47
45
 
48
46
  # @!visibility private
49
47
  NIL_SUCCESS = new(true, nil, nil)