schemacop 3.0.4 → 3.0.9

Sign up to get free protection for your applications and to get access to all the features.
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.