schemacop 3.0.4 → 3.0.9

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2b28a407ae498b94fa8c7897895d052c1bea34ebeee991ab992b4a86aa94f7ab
4
- data.tar.gz: 7bd4dbd4f0a51ac591a7ff6189fd98e803597c0487f8a16e43c78774dc75c5e3
3
+ metadata.gz: 8081d63289f1f0078e74dc2d404b549fa5529c2d9ae977fcff713ecd01312f27
4
+ data.tar.gz: aff64b9eb5bc7a9a73a64c2ed0066584cfb59a18dff3566aadcd3c632f8599f8
5
5
  SHA512:
6
- metadata.gz: b40fb214b50815f65f87f978052f0c6bc38018340d00eeeddb68e392dfb44106e0d6ebeb5e908172113dadc57cd0f88bbdf1d230d38cd5dab31382f8d217f34a
7
- data.tar.gz: cf87908ded44c22bd53b824e732f7447ade01608104cb55a844879fb7f17b2172b78c13b47adb0f373abbba3a90f75d353cf0f63ec5d74b16128eee780743db6
6
+ metadata.gz: 3084d61eb18f126ce9b88341d0809614f10c5ba74f39ece8fe7f1cc29c9b7f3d3fa89fadd55fe48fa515602bd80d755c326cfc4ce3d4850993d9cbf0e694a48b
7
+ data.tar.gz: b55349b141d1c971923152ecf19793905fc1811bfcb8647271415706d3372bde616d159aa1e8202d4b5630a84443ae6c2d5ccb67acabb5f115db9270b9f0825b
data/.releaser_config CHANGED
@@ -1,3 +1,4 @@
1
1
  version_file: VERSION
2
2
  always_from_master: true
3
3
  gem_style: github
4
+ ruby_command: ruby -S
data/CHANGELOG.md CHANGED
@@ -1,14 +1,41 @@
1
1
  # Change log
2
2
 
3
- <!--
4
- ## master (unreleased)
3
+ ## 3.0.9 (2021-02-26)
5
4
 
6
- ### New features
5
+ * Fix casting of blank strings
7
6
 
8
- ### Bug fixes
7
+ * Add `allow_blank` option to `String` nodes
9
8
 
10
- ### Changes
11
- -->
9
+ ## 3.0.8 (2021-02-23)
10
+
11
+ * Fix `object` nodes in hashes with no classes
12
+
13
+ * Document `cast_str` option
14
+
15
+ ## 3.0.7 (2021-02-22)
16
+
17
+ * Adapt schema definitions handling when using Swagger-standard JSON
18
+
19
+ ## 3.0.6 (2021-02-14)
20
+
21
+ * Remove option `json_format` from {Schemacop::Schema3.as_json as_json} again.
22
+ If you need to use the swagger format, use
23
+ {Schemacop::V3::Context.with_json_format} instead.
24
+
25
+ * Rename `Schemacop::V3::Context.spawn_with` to
26
+ {Schemacop::V3::Context.with_json_format} and make keyword argument
27
+ `json_format` a positional argument.
28
+
29
+ ## 3.0.5 (2021-02-14)
30
+
31
+ * Allow option `pattern` to be a `Regexp` for `string` (`str`) nodes
32
+
33
+ * Remove `examples_keyword` from context again
34
+
35
+ * Add option `json_format` to {Schemacop::Schema3.as_json as_json}. This allows
36
+ generating a JSON schema that is [specific to
37
+ swagger](https://swagger.io/docs/specification/data-models/keywords/) by
38
+ passing `:swagger` to it.
12
39
 
13
40
  ## 3.0.4 (2021-02-15)
14
41
 
data/README_V3.md CHANGED
@@ -199,14 +199,19 @@ transformed into various types.
199
199
  * `max_length`
200
200
  Defines the (inclusive) maximum required string length
201
201
  * `pattern`
202
- Defines a (ruby) regex pattern the value will be matched against. Must be a
203
- string and should generally start with `^` and end with `$` so as to evaluate
204
- the entire string. It should not be enclosed in `/` characters.
202
+ Defines a (ruby) regex pattern the value will be matched against. Must be either
203
+ a string which should not be enclosed in `/` characters, or a Ruby Regexp.
204
+ The pattern should generally start with `^` and end with `$` so as to evaluate
205
+ the entire string.
205
206
  * `format`
206
207
  The `format` option allows for basic semantic validation on certain kinds of
207
208
  string values that are commonly used. See section *formats* for more
208
209
  information on the available formats. Note that strings with a format are also
209
210
  **casted** into that format.
211
+ * `allow_blank`
212
+ By default, blank strings are allowed and left as they are when casted (e.g.
213
+ the string `''` is valid). If you want to disallow blank strings, set this
214
+ option to `false`.
210
215
 
211
216
  #### Formats
212
217
 
@@ -246,6 +251,38 @@ transformed into various types.
246
251
 
247
252
  #### Examples
248
253
 
254
+ ```ruby
255
+ # Basic example
256
+ schema = Schemacop::Schema3.new :string
257
+ schema.validate!(nil) # => nil
258
+ schema.validate!('') # => ""
259
+ schema.validate!('foo') # => "foo"
260
+ schema.validate!("\n") # => "\n"
261
+ ```
262
+
263
+ With the `required` option:
264
+
265
+ ```ruby
266
+ # Basic example
267
+ schema = Schemacop::Schema3.new :string, required: true
268
+ schema.validate!(nil) # => Schemacop::Exceptions::ValidationError: /: Value must be given.
269
+ schema.validate!('') # => ""
270
+ schema.validate!('foo') # => "foo"
271
+ schema.validate!("\n") # => "\n"
272
+ ```
273
+
274
+ With the `allow_blank` option:
275
+
276
+ ```ruby
277
+ # Basic example
278
+ schema = Schemacop::Schema3.new :string, allow_blank: false
279
+ schema.validate!(nil) # => Schemacop::Exceptions::ValidationError: /: String is blank but must not be blank!
280
+ schema.validate!('') # => Schemacop::Exceptions::ValidationError: /: String is blank but must not be blank!
281
+ schema.validate!('foo') # => "foo"
282
+ schema.validate!("\n") # => Schemacop::Exceptions::ValidationError: /: String is blank but must not be blank!
283
+ ```
284
+ Example of using a `format` option:
285
+
249
286
  ```ruby
250
287
  # By using a format, string values are casted to that respective format
251
288
  schema = Schemacop::Schema3.new(:string, format: :date)
@@ -277,6 +314,10 @@ integer can be done.
277
314
  * `multiple_of`
278
315
  The received number has to be a multiple of the given number for the validation to
279
316
  pass.
317
+ * `cast_str`
318
+ When set to `true`, this node also accepts strings that can be casted to an integer, e.g.
319
+ the values `'-5'` or `'42'`. Please note that you can only validate numbers which
320
+ are in the `Integer` format.
280
321
 
281
322
  #### Examples
282
323
 
@@ -293,6 +334,20 @@ schema.validate!((4 + 0i)) # => Schemacop::Exceptions::ValidationError: /:
293
334
  schema.validate!(BigDecimal(5)) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "integer".
294
335
  ```
295
336
 
337
+ With `cast_str` enabled:
338
+
339
+ ```ruby
340
+ # Validates that the input is an even number between 0 and 100 (inclusive)
341
+ schema = Schemacop::Schema3.new(:integer, minimum: 0, maximum: 100, multiple_of: 2, cast_str: true)
342
+ schema.validate!('42') # => 42
343
+ schema.validate!('43') # => Schemacop::Exceptions::ValidationError: /: Matches 0 definitions but should match exactly 1.
344
+ schema.validate!('-2') # => Schemacop::Exceptions::ValidationError: /: Matches 0 definitions but should match exactly 1.
345
+ schema.validate!('102') # => Schemacop::Exceptions::ValidationError: /: Matches 0 definitions but should match exactly 1.
346
+ schema.validate!('42.1') # => Schemacop::Exceptions::ValidationError: /: Matches 0 definitions but should match exactly 1.
347
+ schema.validate!('4r') # => Schemacop::Exceptions::ValidationError: /: Matches 0 definitions but should match exactly 1.
348
+ schema.validate!('(4 + 0i)') # => Schemacop::Exceptions::ValidationError: /: Matches 0 definitions but should match exactly 1.
349
+ ```
350
+
296
351
  ### Number
297
352
 
298
353
  Type: `:number`\
@@ -327,6 +382,11 @@ With the various available options, validations on the value of the number can b
327
382
  * `multiple_of`
328
383
  The received number has to be a multiple of the given number for the validation to
329
384
  pass.
385
+ * `cast_str`
386
+ When set to `true`, this node also accepts strings that can be casted to a number, e.g.
387
+ the values `'0.1'` or `'3.1415'`. Please note that you can only validate numbers which
388
+ are in the `Integer` or `Float` format, i.e. values like `'1.5r'` or `'(4 + 0i)'` will
389
+ not work.
330
390
 
331
391
  #### Examples
332
392
 
@@ -343,6 +403,19 @@ schema.validate!(BigDecimal(5)) # => 0.5e1
343
403
  schema.validate!((4 + 0i)) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "big_decimal" or "float" or "integer" or "rational".
344
404
  ```
345
405
 
406
+ With `cast_str` enabled:
407
+
408
+ ```ruby
409
+ schema = Schemacop::Schema3.new(:number, cast_str: true, minimum: 0.0, maximum: (50r), multiple_of: BigDecimal('0.5'))
410
+ schema.validate!('42') # => 42
411
+ schema.validate!('42.2') # => Schemacop::Exceptions::ValidationError: /: Matches 0 definitions but should match exactly 1.
412
+ schema.validate!('-2') # => Schemacop::Exceptions::ValidationError: /: Matches 0 definitions but should match exactly 1.
413
+ schema.validate!('51') # => Schemacop::Exceptions::ValidationError: /: Matches 0 definitions but should match exactly 1.
414
+ schema.validate!('42.5') # => 42.5
415
+ schema.validate!('1.5r') # => Schemacop::Exceptions::ValidationError: /: Matches 0 definitions but should match exactly 1.
416
+ schema.validate!('(4 + 0i)') # => Schemacop::Exceptions::ValidationError: /: Matches 0 definitions but should match exactly 1.
417
+ ```
418
+
346
419
  ### Symbol
347
420
 
348
421
  Type: `:symbol`\
@@ -350,15 +423,33 @@ DSL: `sym`
350
423
 
351
424
  The symbol type is used to validate elements for the Ruby `Symbol` class.
352
425
 
426
+ #### Options
427
+
428
+ * `cast_str`
429
+ When set to `true`, this node also accepts strings that can be casted to a symbol.
430
+
353
431
  #### Examples
354
432
 
355
433
  ```ruby
356
434
  # Validates that the input is a symbol
357
435
  schema = Schemacop::Schema3.new(:symbol)
358
- schema.validate!(:foo) # => :foo
359
- schema.validate!('foo') # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "Symbol".
360
- schema.validate!(123) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "Symbol".
361
- schema.validate!(false) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "Symbol".
436
+ schema.validate!(:foo) # => :foo
437
+ schema.validate!('foo') # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "Symbol".
438
+ schema.validate!(123) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "Symbol".
439
+ schema.validate!(false) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "Symbol".
440
+ schema.validate!(:false) # => :false
441
+ ```
442
+
443
+ With `cast_str` enabled:
444
+
445
+ ```ruby
446
+ # Validates that the input is a symbol
447
+ schema = Schemacop::Schema3.new(:symbol, cast_str: true)
448
+ schema.validate!(':foo') # => :":foo"
449
+ schema.validate!('foo') # => :foo
450
+ schema.validate!('123') # => :"123"
451
+ schema.validate!('false') # => :false
452
+ schema.validate!(':false') # => :":false"
362
453
  ```
363
454
 
364
455
  ### Boolean
@@ -368,6 +459,12 @@ DSL: `boo`
368
459
 
369
460
  The boolean type is used to validate Ruby booleans, i.e. the `TrueClass` and `FalseClass`
370
461
 
462
+ #### Options
463
+
464
+ * `cast_str`
465
+ When set to `true`, this node also accepts strings that can be casted to a boolean, i.e.
466
+ the values `'true'` and `'false'`
467
+
371
468
  #### Examples
372
469
 
373
470
  ```ruby
@@ -380,6 +477,17 @@ schema.validate!('false') # => Schemacop::Exceptions::ValidationError: /: Invali
380
477
  schema.validate!(1234) # => Schemacop::Exceptions::ValidationError: /: Invalid type, expected "boolean".
381
478
  ```
382
479
 
480
+ With `cast_str` enabled:
481
+
482
+ ```ruby
483
+ schema = Schemacop::Schema3.new(:boolean, cast_str: true)
484
+ schema.validate!(true) # => true
485
+ schema.validate!(false) # => false
486
+ schema.validate!(:false) # => Schemacop::Exceptions::ValidationError: /: Matches 0 definitions but should match exactly 1.
487
+ schema.validate!('false') # => false
488
+ schema.validate!(1234) # => Schemacop::Exceptions::ValidationError: /: Matches 0 definitions but should match exactly 1.
489
+ ```
490
+
383
491
  ### Array
384
492
 
385
493
  Type: `:array`\
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.0.4
1
+ 3.0.9
data/lib/schemacop/v3.rb CHANGED
@@ -3,6 +3,24 @@ module Schemacop
3
3
  def self.register(*args)
4
4
  NodeRegistry.register(*args)
5
5
  end
6
+
7
+ # @private
8
+ def self.sanitize_exp(exp)
9
+ return exp if exp.is_a?(String)
10
+
11
+ _start_slash, caret, exp, dollar, _end_slash, flags = exp.inspect.match(%r{^(/?)(\^)?(.*?)(\$)?(/?)([ixm]*)?$}).captures
12
+ flags = flags.split('')
13
+
14
+ if flags.delete('i')
15
+ exp = "(?i)(#{exp})"
16
+ end
17
+
18
+ if flags.any?
19
+ fail "Flags #{flags.inspect} are not supported by Schemacop."
20
+ end
21
+
22
+ return "#{caret}#{exp}#{dollar}"
23
+ end
6
24
  end
7
25
  end
8
26
 
@@ -19,6 +19,10 @@ module Schemacop
19
19
  default
20
20
  end
21
21
  end
22
+
23
+ def self.allowed_options
24
+ super + %i[cast_str]
25
+ end
22
26
  end
23
27
  end
24
28
  end
@@ -2,13 +2,13 @@ module Schemacop
2
2
  module V3
3
3
  class Context
4
4
  attr_accessor :schemas
5
- attr_accessor :examples_keyword
5
+ attr_accessor :json_format
6
6
 
7
- DEFAULT_EXAMPLES_KEYWORD = :examples
7
+ DEFAULT_JSON_FORMAT = :default
8
8
 
9
- def initialize
9
+ def initialize(json_format: DEFAULT_JSON_FORMAT)
10
10
  @schemas = {}.with_indifferent_access.freeze
11
- @examples_keyword = DEFAULT_EXAMPLES_KEYWORD
11
+ @json_format = json_format
12
12
  end
13
13
 
14
14
  def schema(name, type = :hash, **options, &block)
@@ -16,6 +16,18 @@ module Schemacop
16
16
  name => Node.create(type, **options, &block)
17
17
  ).freeze
18
18
  end
19
+
20
+ def with_json_format(json_format)
21
+ prev_json_format = @json_format
22
+ @json_format = json_format
23
+ return yield
24
+ ensure
25
+ @json_format = prev_json_format
26
+ end
27
+
28
+ def swagger_json?
29
+ @json_format == :swagger
30
+ end
19
31
  end
20
32
  end
21
33
  end
@@ -21,14 +21,6 @@ module Schemacop
21
21
  super + NodeRegistry.dsl_methods(true) + %i[dsl_dep dsl_add]
22
22
  end
23
23
 
24
- def self.sanitize_exp(exp)
25
- exp = exp.to_s
26
- if exp.start_with?('(?-mix:')
27
- exp = exp.to_s.gsub(/^\(\?-mix:/, '').gsub(/\)$/, '')
28
- end
29
- return exp
30
- end
31
-
32
24
  def add_child(node)
33
25
  unless node.name
34
26
  fail Exceptions::InvalidSchemaError, 'Child nodes must have a name.'
@@ -64,7 +56,7 @@ module Schemacop
64
56
 
65
57
  json = {}
66
58
  json[:properties] = Hash[properties.values.map { |p| [p.name, p.as_json] }] if properties.any?
67
- json[:patternProperties] = Hash[pattern_properties.values.map { |p| [self.class.sanitize_exp(p.name), p.as_json] }] if pattern_properties.any?
59
+ json[:patternProperties] = Hash[pattern_properties.values.map { |p| [V3.sanitize_exp(p.name), p.as_json] }] if pattern_properties.any?
68
60
 
69
61
  # In schemacop, by default, additional properties are not allowed,
70
62
  # the users explicitly need to enable additional properties
@@ -48,7 +48,7 @@ module Schemacop
48
48
  end
49
49
 
50
50
  def self.allowed_options
51
- %i[name required default description examples enum parent options cast_str title as]
51
+ %i[name required default description examples enum parent options title as]
52
52
  end
53
53
 
54
54
  def self.dsl_methods
@@ -150,6 +150,10 @@ module Schemacop
150
150
 
151
151
  protected
152
152
 
153
+ def context
154
+ Schemacop.context
155
+ end
156
+
153
157
  def item_matches?(item, data)
154
158
  item_result = Result.new(self)
155
159
  item._validate(data, result: item_result)
@@ -157,12 +161,13 @@ module Schemacop
157
161
  end
158
162
 
159
163
  def process_json(attrs, json)
160
- if @schemas.any?
164
+ if !context.swagger_json? && @schemas.any?
161
165
  json[:definitions] = {}
162
166
  @schemas.each do |name, subschema|
163
167
  json[:definitions][name] = subschema.as_json
164
168
  end
165
169
  end
170
+
166
171
  attrs.each do |attr|
167
172
  if options.include?(attr)
168
173
  json[attr.to_s.camelize(:lower).to_sym] = options[attr]
@@ -170,7 +175,7 @@ module Schemacop
170
175
  end
171
176
 
172
177
  json[:title] = @title if @title
173
- json[Schemacop.context.examples_keyword] = @examples if @examples
178
+ json[context.swagger_json? ? :example : :examples] = @examples if @examples
174
179
  json[:description] = @description if @description
175
180
  json[:default] = @default if @default
176
181
  json[:enum] = @enum.to_a if @enum
@@ -4,14 +4,32 @@ module Schemacop
4
4
  class NumericNode < Node
5
5
  ATTRIBUTES = %i[
6
6
  minimum
7
- exclusive_minimum
8
7
  maximum
9
- exclusive_maximum
10
8
  multiple_of
9
+ exclusive_minimum
10
+ exclusive_maximum
11
11
  ].freeze
12
12
 
13
13
  def self.allowed_options
14
- super + ATTRIBUTES
14
+ super + ATTRIBUTES + %i[cast_str]
15
+ end
16
+
17
+ def process_json(attrs, json)
18
+ if context.swagger_json?
19
+ if options[:exclusive_minimum]
20
+ json[:minimum] = options[:exclusive_minimum]
21
+ json[:exclusiveMinimum] = true
22
+ end
23
+
24
+ if options[:exclusive_maximum]
25
+ json[:maximum] = options[:exclusive_maximum]
26
+ json[:exclusiveMaximum] = true
27
+ end
28
+
29
+ attrs -= %i[exclusive_minimum exclusive_maximum]
30
+ end
31
+
32
+ super attrs, json
15
33
  end
16
34
 
17
35
  def _validate(data, result:)
@@ -5,11 +5,6 @@ module Schemacop
5
5
  super + %i[classes strict]
6
6
  end
7
7
 
8
- def self.create(classes, **options, &block)
9
- options[:classes] = classes
10
- super(**options, &block)
11
- end
12
-
13
8
  def as_json
14
9
  {} # Not supported by Json Schema
15
10
  end
@@ -11,7 +11,11 @@ module Schemacop
11
11
  end
12
12
 
13
13
  def as_json
14
- process_json([], '$ref': "#/definitions/#{@path}")
14
+ if context.swagger_json?
15
+ process_json([], '$ref': "#/components/schemas/#{@path}")
16
+ else
17
+ process_json([], '$ref': "#/definitions/#{@path}")
18
+ end
15
19
  end
16
20
 
17
21
  def _validate(data, result:)
@@ -4,7 +4,6 @@ module Schemacop
4
4
  ATTRIBUTES = %i[
5
5
  min_length
6
6
  max_length
7
- pattern
8
7
  format
9
8
  ].freeze
10
9
 
@@ -23,7 +22,7 @@ module Schemacop
23
22
  # rubocop:enable Layout/LineLength
24
23
 
25
24
  def self.allowed_options
26
- super + ATTRIBUTES - %i[cast_str] + %i[format_options]
25
+ super + ATTRIBUTES + %i[format_options pattern allow_blank]
27
26
  end
28
27
 
29
28
  def allowed_types
@@ -31,11 +30,21 @@ module Schemacop
31
30
  end
32
31
 
33
32
  def as_json
34
- process_json(ATTRIBUTES, type: :string)
33
+ json = { type: :string }
34
+ if options[:pattern]
35
+ json[:pattern] = V3.sanitize_exp(Regexp.compile(options[:pattern]))
36
+ end
37
+ process_json(ATTRIBUTES, json)
35
38
  end
36
39
 
37
40
  def _validate(data, result:)
38
41
  super_data = super
42
+
43
+ # Validate blank #
44
+ if options[:allow_blank].is_a?(FalseClass) && super_data.blank?
45
+ result.error 'String is blank but must not be blank!'
46
+ end
47
+
39
48
  return if super_data.nil?
40
49
 
41
50
  # Validate length #
@@ -50,8 +59,14 @@ module Schemacop
50
59
  end
51
60
 
52
61
  # Validate pattern #
53
- if options[:pattern] && !super_data.match?(Regexp.compile(options[:pattern]))
54
- result.error "String does not match pattern #{options[:pattern].inspect}."
62
+ if (pattern = options[:pattern])
63
+ unless options[:pattern].is_a?(Regexp)
64
+ pattern = Regexp.compile(pattern)
65
+ end
66
+
67
+ unless super_data.match?(pattern)
68
+ result.error "String does not match pattern #{V3.sanitize_exp(pattern).inspect}."
69
+ end
55
70
  end
56
71
 
57
72
  # Validate format #
@@ -67,7 +82,7 @@ module Schemacop
67
82
  end
68
83
 
69
84
  def cast(value)
70
- if value.present?
85
+ if !value.nil?
71
86
  to_cast = value
72
87
  elsif default.present?
73
88
  to_cast = default
@@ -121,7 +136,9 @@ module Schemacop
121
136
  end
122
137
 
123
138
  if options[:pattern]
124
- fail 'Option "pattern" must be a string.' unless options[:pattern].is_a?(String)
139
+ unless options[:pattern].is_a?(String) || options[:pattern].is_a?(Regexp)
140
+ fail 'Option "pattern" must be a string or Regexp.'
141
+ end
125
142
 
126
143
  begin
127
144
  Regexp.compile(options[:pattern])
@@ -8,6 +8,10 @@ module Schemacop
8
8
  def allowed_types
9
9
  { Symbol => 'Symbol' }
10
10
  end
11
+
12
+ def self.allowed_options
13
+ super + %i[cast_str]
14
+ end
11
15
  end
12
16
  end
13
17
  end
data/schemacop.gemspec CHANGED
@@ -1,14 +1,14 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: schemacop 3.0.4 ruby lib
2
+ # stub: schemacop 3.0.9 ruby lib
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "schemacop".freeze
6
- s.version = "3.0.4"
6
+ s.version = "3.0.9"
7
7
 
8
8
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
9
9
  s.require_paths = ["lib".freeze]
10
10
  s.authors = ["Sitrox".freeze]
11
- s.date = "2021-02-15"
11
+ s.date = "2021-02-26"
12
12
  s.files = [".gitignore".freeze, ".releaser_config".freeze, ".rubocop.yml".freeze, ".travis.yml".freeze, ".yardopts".freeze, "CHANGELOG.md".freeze, "Gemfile".freeze, "LICENSE".freeze, "README.md".freeze, "README_V2.md".freeze, "README_V3.md".freeze, "RUBY_VERSION".freeze, "Rakefile".freeze, "VERSION".freeze, "lib/schemacop.rb".freeze, "lib/schemacop/base_schema.rb".freeze, "lib/schemacop/exceptions.rb".freeze, "lib/schemacop/railtie.rb".freeze, "lib/schemacop/schema.rb".freeze, "lib/schemacop/schema2.rb".freeze, "lib/schemacop/schema3.rb".freeze, "lib/schemacop/scoped_env.rb".freeze, "lib/schemacop/v2.rb".freeze, "lib/schemacop/v2/caster.rb".freeze, "lib/schemacop/v2/collector.rb".freeze, "lib/schemacop/v2/dupper.rb".freeze, "lib/schemacop/v2/field_node.rb".freeze, "lib/schemacop/v2/node.rb".freeze, "lib/schemacop/v2/node_resolver.rb".freeze, "lib/schemacop/v2/node_supporting_field.rb".freeze, "lib/schemacop/v2/node_supporting_type.rb".freeze, "lib/schemacop/v2/node_with_block.rb".freeze, "lib/schemacop/v2/validator/array_validator.rb".freeze, "lib/schemacop/v2/validator/boolean_validator.rb".freeze, "lib/schemacop/v2/validator/float_validator.rb".freeze, "lib/schemacop/v2/validator/hash_validator.rb".freeze, "lib/schemacop/v2/validator/integer_validator.rb".freeze, "lib/schemacop/v2/validator/nil_validator.rb".freeze, "lib/schemacop/v2/validator/number_validator.rb".freeze, "lib/schemacop/v2/validator/object_validator.rb".freeze, "lib/schemacop/v2/validator/string_validator.rb".freeze, "lib/schemacop/v2/validator/symbol_validator.rb".freeze, "lib/schemacop/v3.rb".freeze, "lib/schemacop/v3/all_of_node.rb".freeze, "lib/schemacop/v3/any_of_node.rb".freeze, "lib/schemacop/v3/array_node.rb".freeze, "lib/schemacop/v3/boolean_node.rb".freeze, "lib/schemacop/v3/combination_node.rb".freeze, "lib/schemacop/v3/context.rb".freeze, "lib/schemacop/v3/dsl_scope.rb".freeze, "lib/schemacop/v3/global_context.rb".freeze, "lib/schemacop/v3/hash_node.rb".freeze, "lib/schemacop/v3/integer_node.rb".freeze, "lib/schemacop/v3/is_not_node.rb".freeze, "lib/schemacop/v3/node.rb".freeze, "lib/schemacop/v3/node_registry.rb".freeze, "lib/schemacop/v3/number_node.rb".freeze, "lib/schemacop/v3/numeric_node.rb".freeze, "lib/schemacop/v3/object_node.rb".freeze, "lib/schemacop/v3/one_of_node.rb".freeze, "lib/schemacop/v3/reference_node.rb".freeze, "lib/schemacop/v3/result.rb".freeze, "lib/schemacop/v3/string_node.rb".freeze, "lib/schemacop/v3/symbol_node.rb".freeze, "schemacop.gemspec".freeze, "test/lib/test_helper.rb".freeze, "test/schemas/nested/group.rb".freeze, "test/schemas/user.rb".freeze, "test/unit/schemacop/v2/casting_test.rb".freeze, "test/unit/schemacop/v2/collector_test.rb".freeze, "test/unit/schemacop/v2/custom_check_test.rb".freeze, "test/unit/schemacop/v2/custom_if_test.rb".freeze, "test/unit/schemacop/v2/defaults_test.rb".freeze, "test/unit/schemacop/v2/empty_test.rb".freeze, "test/unit/schemacop/v2/nil_dis_allow_test.rb".freeze, "test/unit/schemacop/v2/node_resolver_test.rb".freeze, "test/unit/schemacop/v2/short_forms_test.rb".freeze, "test/unit/schemacop/v2/types_test.rb".freeze, "test/unit/schemacop/v2/validator_array_test.rb".freeze, "test/unit/schemacop/v2/validator_boolean_test.rb".freeze, "test/unit/schemacop/v2/validator_float_test.rb".freeze, "test/unit/schemacop/v2/validator_hash_test.rb".freeze, "test/unit/schemacop/v2/validator_integer_test.rb".freeze, "test/unit/schemacop/v2/validator_nil_test.rb".freeze, "test/unit/schemacop/v2/validator_number_test.rb".freeze, "test/unit/schemacop/v2/validator_object_test.rb".freeze, "test/unit/schemacop/v2/validator_string_test.rb".freeze, "test/unit/schemacop/v2/validator_symbol_test.rb".freeze, "test/unit/schemacop/v3/all_of_node_test.rb".freeze, "test/unit/schemacop/v3/any_of_node_test.rb".freeze, "test/unit/schemacop/v3/array_node_test.rb".freeze, "test/unit/schemacop/v3/boolean_node_test.rb".freeze, "test/unit/schemacop/v3/global_context_test.rb".freeze, "test/unit/schemacop/v3/hash_node_test.rb".freeze, "test/unit/schemacop/v3/integer_node_test.rb".freeze, "test/unit/schemacop/v3/is_not_node_test.rb".freeze, "test/unit/schemacop/v3/node_test.rb".freeze, "test/unit/schemacop/v3/number_node_test.rb".freeze, "test/unit/schemacop/v3/object_node_test.rb".freeze, "test/unit/schemacop/v3/one_of_node_test.rb".freeze, "test/unit/schemacop/v3/reference_node_test.rb".freeze, "test/unit/schemacop/v3/string_node_test.rb".freeze, "test/unit/schemacop/v3/symbol_node_test.rb".freeze]
13
13
  s.homepage = "https://github.com/sitrox/schemacop".freeze
14
14
  s.licenses = ["MIT".freeze]
@@ -142,6 +142,13 @@ class V3Test < SchemacopTest
142
142
  assert_equal expected_json.as_json, @schema.as_json.as_json
143
143
  end
144
144
 
145
+ def assert_swagger_json(expected_json)
146
+ # TODO: Double "as_json" should not be necessary
147
+ Schemacop.context.with_json_format(:swagger) do
148
+ assert_equal expected_json.as_json, @schema.as_json.as_json
149
+ end
150
+ end
151
+
145
152
  def assert_match_any(array, exp)
146
153
  assert array.any? { |element| element.match?(exp) },
147
154
  "Expected any of #{array.inspect} to match #{exp}."
@@ -73,8 +73,8 @@ module Schemacop
73
73
  hsh { str! :foo }
74
74
  end
75
75
  any_of do
76
- obj(Date)
77
- obj(Time)
76
+ obj classes: Date
77
+ obj classes: Time
78
78
  end
79
79
  ary
80
80
  boo
@@ -136,6 +136,20 @@ module Schemacop
136
136
  assert_cast(false, false)
137
137
  assert_cast(nil, true)
138
138
  end
139
+
140
+ def test_cast_str
141
+ schema :boolean, cast_str: true
142
+
143
+ assert_cast('true', true)
144
+ assert_cast('false', false)
145
+
146
+ assert_cast(true, true)
147
+ assert_cast(false, false)
148
+
149
+ assert_validation('1') do
150
+ error '/', 'Matches 0 definitions but should match exactly 1.'
151
+ end
152
+ end
139
153
  end
140
154
  end
141
155
  end
@@ -125,6 +125,12 @@ module Schemacop
125
125
  exclusiveMinimum: 0
126
126
  )
127
127
 
128
+ assert_swagger_json(
129
+ type: :integer,
130
+ minimum: 0,
131
+ exclusiveMinimum: true
132
+ )
133
+
128
134
  assert_validation 5
129
135
  assert_validation 1
130
136
  assert_validation(0) do
@@ -158,6 +164,12 @@ module Schemacop
158
164
  exclusiveMaximum: 5
159
165
  )
160
166
 
167
+ assert_swagger_json(
168
+ type: :integer,
169
+ maximum: 5,
170
+ exclusiveMaximum: true
171
+ )
172
+
161
173
  assert_validation 4
162
174
  assert_validation 1
163
175
  assert_validation(5) do
@@ -318,6 +330,17 @@ module Schemacop
318
330
  ]
319
331
  })
320
332
  end
333
+
334
+ def test_cast_str
335
+ schema :integer, cast_str: true
336
+
337
+ assert_cast('1', 1)
338
+ assert_cast(1, 1)
339
+
340
+ assert_validation('true') do
341
+ error '/', 'Matches 0 definitions but should match exactly 1.'
342
+ end
343
+ end
321
344
  end
322
345
  end
323
346
  end
@@ -63,6 +63,20 @@ module Schemacop
63
63
  })
64
64
  end
65
65
 
66
+ def test_swagger_example
67
+ schema :string, examples: ['Foo', 'Foo bar']
68
+
69
+ assert_json(
70
+ type: :string,
71
+ examples: ['Foo', 'Foo bar']
72
+ )
73
+
74
+ assert_swagger_json(
75
+ type: :string,
76
+ example: ['Foo', 'Foo bar']
77
+ )
78
+ end
79
+
66
80
  def test_cast_in_root
67
81
  schema :integer, cast_str: true, required: true
68
82
 
@@ -101,6 +101,12 @@ module Schemacop
101
101
  exclusiveMinimum: 0
102
102
  )
103
103
 
104
+ assert_swagger_json(
105
+ type: :number,
106
+ minimum: 0,
107
+ exclusiveMinimum: true
108
+ )
109
+
104
110
  assert_validation 5
105
111
  assert_validation 1
106
112
  assert_validation(0) do
@@ -134,6 +140,12 @@ module Schemacop
134
140
  exclusiveMaximum: 5
135
141
  )
136
142
 
143
+ assert_swagger_json(
144
+ type: :number,
145
+ maximum: 5,
146
+ exclusiveMaximum: true
147
+ )
148
+
137
149
  assert_validation 4
138
150
  assert_validation 1
139
151
  assert_validation(5) do
@@ -287,6 +299,35 @@ module Schemacop
287
299
  ]
288
300
  })
289
301
  end
302
+
303
+ def test_cast_str
304
+ schema :number, cast_str: true, minimum: 0.0, maximum: 50r, multiple_of: BigDecimal('0.5')
305
+
306
+ assert_cast('1', 1)
307
+ assert_cast(1, 1)
308
+
309
+ assert_cast('1.0', 1.0)
310
+ assert_cast(1.0, 1.0)
311
+
312
+ assert_validation('42')
313
+ assert_validation('0.5')
314
+
315
+ assert_validation('true') do
316
+ error '/', 'Matches 0 definitions but should match exactly 1.'
317
+ end
318
+
319
+ assert_validation('51') do
320
+ error '/', 'Matches 0 definitions but should match exactly 1.'
321
+ end
322
+
323
+ assert_validation('-2') do
324
+ error '/', 'Matches 0 definitions but should match exactly 1.'
325
+ end
326
+
327
+ assert_validation('3.1415') do
328
+ error '/', 'Matches 0 definitions but should match exactly 1.'
329
+ end
330
+ end
290
331
  end
291
332
  end
292
333
  end
@@ -59,7 +59,10 @@ module Schemacop
59
59
  end
60
60
 
61
61
  def test_hash
62
- schema { obj! :myobj, String }
62
+ schema do
63
+ obj! :myobj, classes: String
64
+ end
65
+
63
66
  assert_json(
64
67
  type: :object,
65
68
  properties: {
@@ -78,6 +81,16 @@ module Schemacop
78
81
  end
79
82
  end
80
83
 
84
+ def test_hash_no_class
85
+ schema do
86
+ obj! :myobj
87
+ end
88
+
89
+ assert_validation(myobj: 'str')
90
+ assert_validation(myobj: 123)
91
+ assert_validation(myobj: Object.new)
92
+ end
93
+
81
94
  def test_enum_schema
82
95
  schema :object, enum: [true, 'foo', :baz, [], { qux: '123' }]
83
96
 
@@ -80,7 +80,7 @@ module Schemacop
80
80
  end
81
81
  end
82
82
 
83
- def test_pattern
83
+ def test_pattern_as_string
84
84
  schema :string, pattern: '^a_.*_z$'
85
85
 
86
86
  assert_json(type: :string, pattern: '^a_.*_z$')
@@ -95,6 +95,22 @@ module Schemacop
95
95
  end
96
96
  end
97
97
 
98
+ def test_pattern_as_regexp
99
+ schema :string, pattern: /^a_.*_z$/i
100
+
101
+ assert_json(type: :string, pattern: '^(?i)(a_.*_z)$')
102
+
103
+ assert_validation 'a__z'
104
+ assert_validation 'a__Z'
105
+ assert_validation 'a_ foo bar _Z'
106
+ assert_validation '' do
107
+ error '/', 'String does not match pattern "^(?i)(a_.*_z)$".'
108
+ end
109
+ assert_validation 'a_ _zfoo' do
110
+ error '/', 'String does not match pattern "^(?i)(a_.*_z)$".'
111
+ end
112
+ end
113
+
98
114
  def test_format_date
99
115
  schema :string, format: :date
100
116
 
@@ -314,8 +330,8 @@ module Schemacop
314
330
  end
315
331
 
316
332
  assert_raises_with_message Exceptions::InvalidSchemaError,
317
- 'Option "pattern" must be a string.' do
318
- schema :string, pattern: //
333
+ 'Option "pattern" must be a string or Regexp.' do
334
+ schema :string, pattern: 42
319
335
  end
320
336
 
321
337
  assert_raises_with_message Exceptions::InvalidSchemaError,
@@ -384,6 +400,53 @@ module Schemacop
384
400
  ]
385
401
  })
386
402
  end
403
+
404
+ def test_cast_empty_or_whitespace_string
405
+ schema :string
406
+
407
+ assert_cast(nil, nil)
408
+ assert_cast('', '')
409
+ assert_cast(' ', ' ')
410
+ assert_cast("\n", "\n")
411
+ assert_cast("\t", "\t")
412
+ end
413
+
414
+ def test_cast_empty_or_whitespace_string_required
415
+ schema :string, required: true
416
+
417
+ assert_validation(nil) do
418
+ error '/', 'Value must be given.'
419
+ end
420
+
421
+ assert_cast('', '')
422
+ assert_cast(' ', ' ')
423
+ assert_cast("\n", "\n")
424
+ assert_cast("\t", "\t")
425
+ end
426
+
427
+ def test_empty_or_whitespace_string_blank_not_allowed
428
+ schema :string, allow_blank: false
429
+
430
+ assert_validation(nil) do
431
+ error '/', 'String is blank but must not be blank!'
432
+ end
433
+
434
+ assert_validation('') do
435
+ error '/', 'String is blank but must not be blank!'
436
+ end
437
+
438
+ assert_validation(' ') do
439
+ error '/', 'String is blank but must not be blank!'
440
+ end
441
+
442
+ assert_validation("\n") do
443
+ error '/', 'String is blank but must not be blank!'
444
+ end
445
+
446
+ assert_validation("\t") do
447
+ error '/', 'String is blank but must not be blank!'
448
+ end
449
+ end
387
450
  end
388
451
  end
389
452
  end
@@ -70,6 +70,20 @@ module Schemacop
70
70
  error '/', 'Value not included in enum [1, 2, "foo", :bar, {:qux=>42}].'
71
71
  end
72
72
  end
73
+
74
+ # rubocop:disable Lint/BooleanSymbol
75
+ def test_cast_str
76
+ schema :symbol, cast_str: true
77
+
78
+ assert_cast('true', :true)
79
+ assert_cast('foo', :foo)
80
+ assert_cast('1', :'1')
81
+
82
+ assert_cast(:true, :true)
83
+ assert_cast(:foo, :foo)
84
+ assert_cast(:'1', :'1')
85
+ end
86
+ # rubocop:enable Lint/BooleanSymbol
73
87
  end
74
88
  end
75
89
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: schemacop
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.4
4
+ version: 3.0.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sitrox
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-02-15 00:00:00.000000000 Z
11
+ date: 2021-02-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -164,8 +164,8 @@ dependencies:
164
164
  - - '='
165
165
  - !ruby/object:Gem::Version
166
166
  version: 0.21.2
167
- description:
168
- email:
167
+ description:
168
+ email:
169
169
  executables: []
170
170
  extensions: []
171
171
  extra_rdoc_files: []
@@ -277,7 +277,7 @@ homepage: https://github.com/sitrox/schemacop
277
277
  licenses:
278
278
  - MIT
279
279
  metadata: {}
280
- post_install_message:
280
+ post_install_message:
281
281
  rdoc_options: []
282
282
  require_paths:
283
283
  - lib
@@ -292,8 +292,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
292
292
  - !ruby/object:Gem::Version
293
293
  version: '0'
294
294
  requirements: []
295
- rubygems_version: 3.1.4
296
- signing_key:
295
+ rubygems_version: 3.0.3
296
+ signing_key:
297
297
  specification_version: 4
298
298
  summary: Schemacop validates ruby structures consisting of nested hashes and arrays
299
299
  against simple schema definitions.