schemacop 3.0.39 → 3.1.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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +1 -1
- data/.rubocop.yml +4 -1
- data/CHANGELOG.md +27 -0
- data/Gemfile +1 -1
- data/README.md +8 -7
- data/README_V3.md +9 -0
- data/VERSION +1 -1
- data/lib/schemacop/schema3.rb +2 -2
- data/lib/schemacop/v2/node.rb +1 -1
- data/lib/schemacop/v2/validator/string_validator.rb +1 -1
- data/lib/schemacop/v3/any_of_node.rb +13 -0
- data/lib/schemacop/v3/array_node.rb +2 -3
- data/lib/schemacop/v3/global_context.rb +1 -1
- data/lib/schemacop/v3/hash_node.rb +5 -9
- data/lib/schemacop/v3/node.rb +5 -0
- data/lib/schemacop/v3/numeric_node.rb +2 -2
- data/lib/schemacop/v3/object_node.rb +1 -1
- data/lib/schemacop/v3/one_of_node.rb +14 -1
- data/lib/schemacop/v3/string_node.rb +26 -1
- data/lib/schemacop.rb +1 -1
- data/schemacop.gemspec +3 -3
- data/test/unit/schemacop/v2/casting_test.rb +1 -1
- data/test/unit/schemacop/v2/custom_check_test.rb +2 -2
- data/test/unit/schemacop/v2/custom_if_test.rb +2 -2
- data/test/unit/schemacop/v3/any_of_node_test.rb +24 -0
- data/test/unit/schemacop/v3/array_node_test.rb +40 -1
- data/test/unit/schemacop/v3/hash_node_test.rb +1 -1
- data/test/unit/schemacop/v3/integer_node_test.rb +1 -1
- data/test/unit/schemacop/v3/number_node_test.rb +1 -1
- data/test/unit/schemacop/v3/one_of_node_test.rb +23 -0
- data/test/unit/schemacop/v3/string_node_test.rb +69 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9ef422cfe57686302457be2282f2b8e5f45ccdc79d6cd62597e8308bbb94026e
|
|
4
|
+
data.tar.gz: d45307834a863e1fdfdf616acbb9727fd78db428ce88436c27a67db88a3e802f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1253508e8f7270b6fa5f8f54563e79fa034a97fa8f89334f416d0e4549a5b374a2cdc0b3b4777e15202a7759a275d389a8390d67efd7acbb9114de38e21aceb1
|
|
7
|
+
data.tar.gz: 5aa472bf5a7d1aa37f946cf298b76349ad4faee611491c0ecf1bd377baa1127cfaae668f4a71ad00381a0afa88b5c23d073a3b84018ee5387808ad645766b37a
|
data/.github/workflows/ruby.yml
CHANGED
data/.rubocop.yml
CHANGED
|
@@ -10,7 +10,7 @@ AllCops:
|
|
|
10
10
|
- 'locale/translations.rb'
|
|
11
11
|
- 'lib/scratch.rb'
|
|
12
12
|
- 'schemacop.gemspec'
|
|
13
|
-
|
|
13
|
+
SuggestExtensions: false
|
|
14
14
|
DisplayCopNames: true
|
|
15
15
|
|
|
16
16
|
Style/SignalException:
|
|
@@ -108,4 +108,7 @@ Style/CaseLikeIf:
|
|
|
108
108
|
Enabled: false
|
|
109
109
|
|
|
110
110
|
Lint/EmptyClass:
|
|
111
|
+
Enabled: false
|
|
112
|
+
|
|
113
|
+
Lint/RedundantRequireStatement:
|
|
111
114
|
Enabled: false
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# Change log
|
|
2
2
|
|
|
3
|
+
## 3.1.0 (2026-05-09)
|
|
4
|
+
|
|
5
|
+
* Add option `encoding` to `str` node for Schemacop V3 schemas to validate that
|
|
6
|
+
a string has one of the specified encodings. Accepts a single encoding name or
|
|
7
|
+
an array of encoding names (e.g. `encoding: 'UTF-8'` or
|
|
8
|
+
`encoding: %w[UTF-8 US-ASCII]`).
|
|
9
|
+
|
|
10
|
+
Internal reference: `#143271`.
|
|
11
|
+
|
|
12
|
+
* All strings are now validated for valid encoding (via `valid_encoding?`),
|
|
13
|
+
regardless of whether the `encoding` option is set. Strings with invalid byte
|
|
14
|
+
sequences for their declared encoding will now produce a validation error.
|
|
15
|
+
|
|
16
|
+
Internal reference: `#143271`.
|
|
17
|
+
|
|
18
|
+
* Update RuboCop from 1.24.1 to 1.69.2.
|
|
19
|
+
|
|
20
|
+
* Drop support for all EOL Rubies (2.6.2, 2.7.1, 3.0.1, 3.1.0)
|
|
21
|
+
|
|
22
|
+
* Fix `cast_str` default option causing incorrect casting and validation errors
|
|
23
|
+
when used with combination nodes and array tuples. Numeric-looking strings
|
|
24
|
+
(e.g. `"1"`) were incorrectly cast to integers in schemas where a string type
|
|
25
|
+
was also valid. With this fix, `cast_str` only activates when the value cannot
|
|
26
|
+
be matched natively by another sibling schema.
|
|
27
|
+
|
|
28
|
+
Internal reference: `#149255`.
|
|
29
|
+
|
|
3
30
|
## 3.0.39 (2026-03-26)
|
|
4
31
|
|
|
5
32
|
* Add `BinaryNode` for validating binary data fields such as file uploads.
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -12,13 +12,14 @@ use in conjunction with [OpenAPI](https://swagger.io/specification/).
|
|
|
12
12
|
|
|
13
13
|
Schemacop is tested with the following ruby versions:
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
| Schemacop | Ruby |
|
|
16
|
+
|:----------|:------------------------------------------------|
|
|
17
|
+
| >= 3.1.0 | 3.2.0, 3.3.0, 3.4.0, 4.0.0 |
|
|
18
|
+
| >= 3.0.31 | 2.6.2, 2.7.1, 3.0.1, 3.1.0, 3.2.0, 3.3.0, 3.4.0 |
|
|
19
|
+
| >= 3.0.29 | 2.6.2, 2.7.1, 3.0.1, 3.1.0, 3.2.0, 3.3.0 |
|
|
20
|
+
| >= 3.0.23 | 2.6.2, 2.7.1, 3.0.1, 3.1.0, 3.2.0 |
|
|
21
|
+
| >= 3.0.17 | 2.6.2, 2.7.1, 3.0.1, 3.1.0 |
|
|
22
|
+
| <= 3.0.16 | 2.6.2, 2.7.1, 3.0.1 |
|
|
22
23
|
|
|
23
24
|
Other ruby versions might work but are not covered by our automated tests.
|
|
24
25
|
|
data/README_V3.md
CHANGED
|
@@ -224,6 +224,15 @@ transformed into various types.
|
|
|
224
224
|
By default, blank strings are allowed and left as they are when casted (e.g.
|
|
225
225
|
the string `''` is valid). If you want to disallow blank strings, set this
|
|
226
226
|
option to `false`.
|
|
227
|
+
* `encoding`
|
|
228
|
+
Validates the encoding of the string. Accepts a single encoding name or an
|
|
229
|
+
array of encoding names (e.g. `encoding: 'UTF-8'` or
|
|
230
|
+
`encoding: %w[UTF-8 US-ASCII]`). See `Encoding.name_list` for all available
|
|
231
|
+
encoding names.
|
|
232
|
+
|
|
233
|
+
Note that regardless of the `encoding` option, all strings are validated for
|
|
234
|
+
valid encoding using `valid_encoding?`. Strings with invalid byte sequences for
|
|
235
|
+
their declared encoding will always produce a validation error.
|
|
227
236
|
|
|
228
237
|
#### Formats
|
|
229
238
|
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.0
|
|
1
|
+
3.1.0
|
data/lib/schemacop/schema3.rb
CHANGED
data/lib/schemacop/v2/node.rb
CHANGED
|
@@ -88,7 +88,7 @@ module Schemacop
|
|
|
88
88
|
|
|
89
89
|
if option?(:cast) && self.class.klasses.size > 1
|
|
90
90
|
fail Exceptions::InvalidSchemaError,
|
|
91
|
-
"Casting is only allowed for single-value datatypes, but type #{self.class.inspect} has classes "\
|
|
91
|
+
"Casting is only allowed for single-value datatypes, but type #{self.class.inspect} has classes " \
|
|
92
92
|
"#{self.class.klasses.map(&:inspect)}."
|
|
93
93
|
end
|
|
94
94
|
end
|
|
@@ -5,6 +5,19 @@ module Schemacop
|
|
|
5
5
|
:anyOf
|
|
6
6
|
end
|
|
7
7
|
|
|
8
|
+
protected
|
|
9
|
+
|
|
10
|
+
def matches(data)
|
|
11
|
+
all_matches = super
|
|
12
|
+
if all_matches.size > 1
|
|
13
|
+
non_wrappers = all_matches.reject(&:cast_str_wrapper?)
|
|
14
|
+
return non_wrappers if non_wrappers.any?
|
|
15
|
+
end
|
|
16
|
+
all_matches
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
public
|
|
20
|
+
|
|
8
21
|
def _validate(data, result:)
|
|
9
22
|
super_data = super
|
|
10
23
|
return if super_data.nil?
|
|
@@ -156,7 +156,7 @@ module Schemacop
|
|
|
156
156
|
result = []
|
|
157
157
|
|
|
158
158
|
value.each_with_index do |value_item, index|
|
|
159
|
-
if cont_item.present? && item_matches?(cont_item, value_item)
|
|
159
|
+
if cont_item.present? && !cont_item.cast_str_wrapper? && item_matches?(cont_item, value_item)
|
|
160
160
|
result << cont_item.cast(value_item)
|
|
161
161
|
elsif list?
|
|
162
162
|
result << list_item.cast(value_item)
|
|
@@ -168,8 +168,7 @@ module Schemacop
|
|
|
168
168
|
result << value_item
|
|
169
169
|
end
|
|
170
170
|
else
|
|
171
|
-
|
|
172
|
-
result << item.cast(value_item)
|
|
171
|
+
result << items[index].cast(value_item)
|
|
173
172
|
end
|
|
174
173
|
else
|
|
175
174
|
result << value_item
|
|
@@ -97,7 +97,7 @@ module Schemacop
|
|
|
97
97
|
fail "Schema #{path.inspect} does not define any schema."
|
|
98
98
|
when 1
|
|
99
99
|
if @schemas.include?(virtual_path)
|
|
100
|
-
fail "Schema #{virtual_path.to_s.inspect} is defined in both load paths "\
|
|
100
|
+
fail "Schema #{virtual_path.to_s.inspect} is defined in both load paths " \
|
|
101
101
|
"#{@load_paths_by_schemas[virtual_path].inspect} and #{load_path.inspect}."
|
|
102
102
|
end
|
|
103
103
|
|
|
@@ -65,8 +65,8 @@ module Schemacop
|
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
json = {}
|
|
68
|
-
json[:properties] = properties.values.
|
|
69
|
-
json[:patternProperties] = pattern_properties.values.
|
|
68
|
+
json[:properties] = properties.values.to_h { |p| [p.name, p.as_json] } if properties.any?
|
|
69
|
+
json[:patternProperties] = pattern_properties.values.to_h { |p| [V3.sanitize_exp(p.name), p.as_json] } if pattern_properties.any?
|
|
70
70
|
|
|
71
71
|
# In schemacop, by default, additional properties are not allowed,
|
|
72
72
|
# the users explicitly need to enable additional properties
|
|
@@ -300,19 +300,15 @@ module Schemacop
|
|
|
300
300
|
protected
|
|
301
301
|
|
|
302
302
|
def as_json_with_inline_refs(properties, pattern_properties)
|
|
303
|
-
all_of = []
|
|
304
|
-
|
|
305
303
|
# Add each inline ref
|
|
306
|
-
@inline_refs.
|
|
307
|
-
all_of << inline_ref.as_json
|
|
308
|
-
end
|
|
304
|
+
all_of = @inline_refs.map(&:as_json)
|
|
309
305
|
|
|
310
306
|
# Add own properties schema if any direct properties exist
|
|
311
307
|
if properties.any? || pattern_properties.any?
|
|
312
308
|
own_schema = {}
|
|
313
309
|
own_schema[:type] = :object
|
|
314
|
-
own_schema[:properties] = properties.values.
|
|
315
|
-
own_schema[:patternProperties] = pattern_properties.values.
|
|
310
|
+
own_schema[:properties] = properties.values.to_h { |p| [p.name, p.as_json] } if properties.any?
|
|
311
|
+
own_schema[:patternProperties] = pattern_properties.values.to_h { |p| [V3.sanitize_exp(p.name), p.as_json] } if pattern_properties.any?
|
|
316
312
|
|
|
317
313
|
if options[:additional_properties].is_a?(TrueClass)
|
|
318
314
|
own_schema[:additionalProperties] = true
|
data/lib/schemacop/v3/node.rb
CHANGED
|
@@ -45,6 +45,7 @@ module Schemacop
|
|
|
45
45
|
self.node node
|
|
46
46
|
str format: format, format_options: options
|
|
47
47
|
end
|
|
48
|
+
node.instance_variable_set(:@cast_str_wrapper, true)
|
|
48
49
|
end
|
|
49
50
|
|
|
50
51
|
return node
|
|
@@ -136,6 +137,10 @@ module Schemacop
|
|
|
136
137
|
(parent&.schemas || {}).merge(@schemas)
|
|
137
138
|
end
|
|
138
139
|
|
|
140
|
+
def cast_str_wrapper?
|
|
141
|
+
!!@cast_str_wrapper
|
|
142
|
+
end
|
|
143
|
+
|
|
139
144
|
def required?
|
|
140
145
|
@required
|
|
141
146
|
end
|
|
@@ -30,7 +30,7 @@ module Schemacop
|
|
|
30
30
|
attrs -= %i[exclusive_minimum exclusive_maximum]
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
-
super
|
|
33
|
+
super
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def _validate(data, result:)
|
|
@@ -102,7 +102,7 @@ module Schemacop
|
|
|
102
102
|
fail 'Option "minimum" can\'t be greater than "maximum".'
|
|
103
103
|
end
|
|
104
104
|
|
|
105
|
-
if options[:exclusive_minimum] && options[:exclusive_maximum]\
|
|
105
|
+
if options[:exclusive_minimum] && options[:exclusive_maximum] \
|
|
106
106
|
&& options[:exclusive_minimum] > options[:exclusive_maximum]
|
|
107
107
|
fail 'Option "exclusive_minimum" can\'t be greater than "exclusive_maximum".'
|
|
108
108
|
end
|
|
@@ -23,12 +23,25 @@ module Schemacop
|
|
|
23
23
|
return item.cast(value)
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
+
protected
|
|
27
|
+
|
|
28
|
+
def matches(data)
|
|
29
|
+
all_matches = super
|
|
30
|
+
if all_matches.size > 1
|
|
31
|
+
non_wrappers = all_matches.reject(&:cast_str_wrapper?)
|
|
32
|
+
return non_wrappers if non_wrappers.any?
|
|
33
|
+
end
|
|
34
|
+
all_matches
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
public
|
|
38
|
+
|
|
26
39
|
def _validate(data, result:)
|
|
27
40
|
if options[:treat_blank_as_nil] && data.blank? && !data.is_a?(FalseClass)
|
|
28
41
|
data = nil
|
|
29
42
|
end
|
|
30
43
|
|
|
31
|
-
super_data = super
|
|
44
|
+
super_data = super
|
|
32
45
|
return if super_data.nil?
|
|
33
46
|
|
|
34
47
|
matches = matches(super_data)
|
|
@@ -8,7 +8,7 @@ module Schemacop
|
|
|
8
8
|
].freeze
|
|
9
9
|
|
|
10
10
|
def self.allowed_options
|
|
11
|
-
super + ATTRIBUTES + %i[format_options pattern allow_blank]
|
|
11
|
+
super + ATTRIBUTES + %i[format_options pattern allow_blank encoding]
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def allowed_types
|
|
@@ -55,6 +55,19 @@ module Schemacop
|
|
|
55
55
|
end
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
+
# Validate encoding matches #
|
|
59
|
+
if options[:encoding]
|
|
60
|
+
allowed_encodings = Array(options[:encoding])
|
|
61
|
+
unless allowed_encodings.include?(super_data.encoding.name)
|
|
62
|
+
result.error "String has encoding #{super_data.encoding.name.inspect} but must be #{allowed_encodings.map(&:inspect).join(' or ')}."
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Validate encoding #
|
|
67
|
+
unless super_data.valid_encoding?
|
|
68
|
+
result.error "String has invalid #{super_data.encoding.name.inspect} encoding."
|
|
69
|
+
end
|
|
70
|
+
|
|
58
71
|
# Validate format #
|
|
59
72
|
if options[:format] && Schemacop.string_formatters.include?(options[:format])
|
|
60
73
|
pattern = Schemacop.string_formatters[options[:format]][:pattern]
|
|
@@ -109,6 +122,18 @@ module Schemacop
|
|
|
109
122
|
fail 'Option "min_length" can\'t be greater than "max_length".'
|
|
110
123
|
end
|
|
111
124
|
|
|
125
|
+
if options[:encoding]
|
|
126
|
+
unless options[:encoding].is_a?(String) || (options[:encoding].is_a?(Array) && options[:encoding].all? { |e| e.is_a?(String) })
|
|
127
|
+
fail 'Option "encoding" must be a string or an array of strings.'
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
Array(options[:encoding]).each do |encoding|
|
|
131
|
+
Encoding.find(encoding)
|
|
132
|
+
rescue ArgumentError
|
|
133
|
+
fail "Option \"encoding\" contains unknown encoding #{encoding.inspect}."
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
112
137
|
if options[:pattern]
|
|
113
138
|
unless options[:pattern].is_a?(String) || options[:pattern].is_a?(Regexp)
|
|
114
139
|
fail 'Option "pattern" must be a string or Regexp.'
|
data/lib/schemacop.rb
CHANGED
data/schemacop.gemspec
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
|
-
# stub: schemacop 3.0
|
|
2
|
+
# stub: schemacop 3.1.0 ruby lib
|
|
3
3
|
|
|
4
4
|
Gem::Specification.new do |s|
|
|
5
5
|
s.name = "schemacop".freeze
|
|
6
|
-
s.version = "3.0
|
|
6
|
+
s.version = "3.1.0".freeze
|
|
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 = "2026-
|
|
11
|
+
s.date = "2026-05-12"
|
|
12
12
|
s.files = [".github/workflows/ruby.yml".freeze, ".gitignore".freeze, ".releaser_config".freeze, ".rubocop.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/binary_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/binary_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]
|
|
@@ -75,7 +75,7 @@ module Schemacop
|
|
|
75
75
|
end
|
|
76
76
|
end
|
|
77
77
|
|
|
78
|
-
assert_equal 'Casting is only allowed for single-value datatypes, '\
|
|
78
|
+
assert_equal 'Casting is only allowed for single-value datatypes, ' \
|
|
79
79
|
'but type Schemacop::V2::NumberValidator has classes ["Integer", "Float"].',
|
|
80
80
|
e.message
|
|
81
81
|
end
|
|
@@ -4,7 +4,7 @@ module Schemacop
|
|
|
4
4
|
module V2
|
|
5
5
|
class CustomCheckTest < V2Test
|
|
6
6
|
def test_integer_check_short_form
|
|
7
|
-
s = Schema.new :integer, check: proc
|
|
7
|
+
s = Schema.new :integer, check: proc(&:even?)
|
|
8
8
|
assert_nothing_raised { s.validate!(2) }
|
|
9
9
|
assert_nothing_raised { s.validate!(-8) }
|
|
10
10
|
assert_nothing_raised { s.validate!(0) }
|
|
@@ -22,7 +22,7 @@ module Schemacop
|
|
|
22
22
|
|
|
23
23
|
def test_integer_check_with_lambda
|
|
24
24
|
s = Schema.new do
|
|
25
|
-
type :integer, check:
|
|
25
|
+
type :integer, check: lambda(&:even?)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
assert_nothing_raised { s.validate!(2) }
|
|
@@ -5,7 +5,7 @@ module Schemacop
|
|
|
5
5
|
class CustomIfTest < V2Test
|
|
6
6
|
def test_allowed_subset_only
|
|
7
7
|
s = Schema.new do
|
|
8
|
-
type :integer, if: proc
|
|
8
|
+
type :integer, if: proc(&:odd?)
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
assert_nothing_raised { s.validate! 5 }
|
|
@@ -15,7 +15,7 @@ module Schemacop
|
|
|
15
15
|
|
|
16
16
|
def test_if_with_multiple_types
|
|
17
17
|
s = Schema.new do
|
|
18
|
-
type :integer, if: proc
|
|
18
|
+
type :integer, if: proc(&:odd?)
|
|
19
19
|
type :string
|
|
20
20
|
end
|
|
21
21
|
|
|
@@ -362,6 +362,30 @@ module Schemacop
|
|
|
362
362
|
schema :any_of
|
|
363
363
|
end
|
|
364
364
|
end
|
|
365
|
+
|
|
366
|
+
# With cast_str as default option, int gets wrapped in a OneOfNode containing
|
|
367
|
+
# [IntegerNode, StringNode(format: :integer)]. In any_of, the first matching
|
|
368
|
+
# schema is used for casting. Since the wrapped int node matches numeric-looking
|
|
369
|
+
# strings (via StringNode(format: :integer)), a string like "1" gets cast to
|
|
370
|
+
# integer 1, even though the plain str branch also matches and the value is
|
|
371
|
+
# already a valid string.
|
|
372
|
+
def test_default_cast_str_with_int_and_str
|
|
373
|
+
Schemacop.v3_default_options = { cast_str: true }.freeze
|
|
374
|
+
|
|
375
|
+
schema :any_of do
|
|
376
|
+
int
|
|
377
|
+
str
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
assert_validation(42)
|
|
381
|
+
assert_validation('hello')
|
|
382
|
+
assert_validation('1')
|
|
383
|
+
assert_cast(42, 42)
|
|
384
|
+
assert_cast('hello', 'hello')
|
|
385
|
+
assert_cast('1', '1')
|
|
386
|
+
ensure
|
|
387
|
+
Schemacop.v3_default_options = {}
|
|
388
|
+
end
|
|
365
389
|
end
|
|
366
390
|
end
|
|
367
391
|
end
|
|
@@ -988,13 +988,52 @@ module Schemacop
|
|
|
988
988
|
end
|
|
989
989
|
|
|
990
990
|
assert_validation('{42]') do
|
|
991
|
-
error '/', /JSON parse error: "((\d+: )?unexpected token at '{42]'|expected object key, got '42]' at line \d+ column \d+)"\./
|
|
991
|
+
error '/', /JSON parse error: "((\d+: )?unexpected token at '{42\]'|expected object key, got '42\]' at line \d+ column \d+)"\./
|
|
992
992
|
end
|
|
993
993
|
|
|
994
994
|
assert_validation('"foo"') do
|
|
995
995
|
error '/', 'Invalid type, got type "String", expected "array".'
|
|
996
996
|
end
|
|
997
997
|
end
|
|
998
|
+
|
|
999
|
+
def test_tuple_cast_with_default_cast_str
|
|
1000
|
+
Schemacop.v3_default_options = { cast_str: true }.freeze
|
|
1001
|
+
|
|
1002
|
+
schema :array do
|
|
1003
|
+
list :array do
|
|
1004
|
+
int
|
|
1005
|
+
str
|
|
1006
|
+
end
|
|
1007
|
+
end
|
|
1008
|
+
|
|
1009
|
+
# String "1" at position 1 (str) must not be cast to integer
|
|
1010
|
+
assert_validation([[1, '1']])
|
|
1011
|
+
assert_cast([[1, '1']], [[1, '1']])
|
|
1012
|
+
|
|
1013
|
+
# String "1" at position 0 (int with cast_str) should be cast to integer
|
|
1014
|
+
assert_validation([%w[1 foo]])
|
|
1015
|
+
assert_cast([%w[1 foo]], [[1, 'foo']])
|
|
1016
|
+
ensure
|
|
1017
|
+
Schemacop.v3_default_options = {}
|
|
1018
|
+
end
|
|
1019
|
+
|
|
1020
|
+
# With cast_str as default option, cont :integer creates a OneOfNode containing
|
|
1021
|
+
# [IntegerNode, StringNode(format: :integer)]. The cont_item takes priority in
|
|
1022
|
+
# casting over the list_item, so numeric-looking strings that match the cont
|
|
1023
|
+
# schema get cast to integers even though the list schema says they are strings.
|
|
1024
|
+
def test_cont_cast_with_default_cast_str
|
|
1025
|
+
Schemacop.v3_default_options = { cast_str: true }.freeze
|
|
1026
|
+
|
|
1027
|
+
schema :array do
|
|
1028
|
+
list :string
|
|
1029
|
+
cont :integer
|
|
1030
|
+
end
|
|
1031
|
+
|
|
1032
|
+
assert_validation(%w[hello 42 world])
|
|
1033
|
+
assert_cast(%w[hello 42 world], %w[hello 42 world])
|
|
1034
|
+
ensure
|
|
1035
|
+
Schemacop.v3_default_options = {}
|
|
1036
|
+
end
|
|
998
1037
|
end
|
|
999
1038
|
end
|
|
1000
1039
|
end
|
|
@@ -32,7 +32,7 @@ module Schemacop
|
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
assert_validation('{42]') do
|
|
35
|
-
error '/', /JSON parse error: "((\d+: )?unexpected token at '{42]'|expected object key, got '42]' at line \d+ column \d+)"\./
|
|
35
|
+
error '/', /JSON parse error: "((\d+: )?unexpected token at '{42\]'|expected object key, got '42\]' at line \d+ column \d+)"\./
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
assert_validation('"foo"') do
|
|
@@ -256,7 +256,7 @@ module Schemacop
|
|
|
256
256
|
end
|
|
257
257
|
|
|
258
258
|
assert_raises_with_message Exceptions::InvalidSchemaError,
|
|
259
|
-
'Option "exclusive_minimum" can\'t be '\
|
|
259
|
+
'Option "exclusive_minimum" can\'t be ' \
|
|
260
260
|
'greater than "exclusive_maximum".' do
|
|
261
261
|
schema :integer, exclusive_minimum: 5, exclusive_maximum: 4
|
|
262
262
|
end
|
|
@@ -298,7 +298,7 @@ module Schemacop
|
|
|
298
298
|
end
|
|
299
299
|
|
|
300
300
|
assert_raises_with_message Exceptions::InvalidSchemaError,
|
|
301
|
-
'Option "exclusive_minimum" can\'t be '\
|
|
301
|
+
'Option "exclusive_minimum" can\'t be ' \
|
|
302
302
|
'greater than "exclusive_maximum".' do
|
|
303
303
|
schema :number, exclusive_minimum: 5, exclusive_maximum: 4
|
|
304
304
|
end
|
|
@@ -259,6 +259,29 @@ module Schemacop
|
|
|
259
259
|
assert_validation('true')
|
|
260
260
|
assert_validation(true)
|
|
261
261
|
end
|
|
262
|
+
|
|
263
|
+
# With cast_str as default option, int gets wrapped in a OneOfNode containing
|
|
264
|
+
# [IntegerNode, StringNode(format: :integer)]. When combined with a sibling str
|
|
265
|
+
# node in one_of, numeric-looking strings like "1" match both the wrapped int
|
|
266
|
+
# (via StringNode(format: :integer)) and the plain str, causing a "matches 2"
|
|
267
|
+
# validation error.
|
|
268
|
+
def test_default_cast_str_with_int_and_str
|
|
269
|
+
Schemacop.v3_default_options = { cast_str: true }.freeze
|
|
270
|
+
|
|
271
|
+
schema :one_of do
|
|
272
|
+
int
|
|
273
|
+
str
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
assert_validation(42)
|
|
277
|
+
assert_validation('hello')
|
|
278
|
+
assert_validation('1')
|
|
279
|
+
assert_cast(42, 42)
|
|
280
|
+
assert_cast('hello', 'hello')
|
|
281
|
+
assert_cast('1', '1')
|
|
282
|
+
ensure
|
|
283
|
+
Schemacop.v3_default_options = {}
|
|
284
|
+
end
|
|
262
285
|
end
|
|
263
286
|
end
|
|
264
287
|
end
|
|
@@ -653,7 +653,7 @@ module Schemacop
|
|
|
653
653
|
end
|
|
654
654
|
|
|
655
655
|
assert_raises_with_message Exceptions::InvalidSchemaError,
|
|
656
|
-
'Option "pattern" can\'t be parsed: end pattern '\
|
|
656
|
+
'Option "pattern" can\'t be parsed: end pattern ' \
|
|
657
657
|
'with unmatched parenthesis: /(abcde/.' do
|
|
658
658
|
schema :string, pattern: '(abcde'
|
|
659
659
|
end
|
|
@@ -750,6 +750,74 @@ module Schemacop
|
|
|
750
750
|
assert_cast("\t", "\t")
|
|
751
751
|
end
|
|
752
752
|
|
|
753
|
+
def test_encoding_single
|
|
754
|
+
schema :string, encoding: 'UTF-8'
|
|
755
|
+
|
|
756
|
+
assert_validation 'Hello World'
|
|
757
|
+
assert_validation ''
|
|
758
|
+
|
|
759
|
+
assert_validation 'Hello World'.encode('ASCII') do
|
|
760
|
+
error '/', 'String has encoding "US-ASCII" but must be "UTF-8".'
|
|
761
|
+
end
|
|
762
|
+
end
|
|
763
|
+
|
|
764
|
+
def test_encoding_multiple
|
|
765
|
+
schema :string, encoding: %w[UTF-8 US-ASCII]
|
|
766
|
+
|
|
767
|
+
assert_validation 'Hello World'
|
|
768
|
+
assert_validation 'Hello World'.encode('ASCII')
|
|
769
|
+
|
|
770
|
+
assert_validation 'Hello World'.encode('ISO-8859-1') do
|
|
771
|
+
error '/', 'String has encoding "ISO-8859-1" but must be "UTF-8" or "US-ASCII".'
|
|
772
|
+
end
|
|
773
|
+
end
|
|
774
|
+
|
|
775
|
+
def test_encoding_with_nil
|
|
776
|
+
schema :string, encoding: 'UTF-8'
|
|
777
|
+
|
|
778
|
+
assert_validation nil
|
|
779
|
+
end
|
|
780
|
+
|
|
781
|
+
def test_encoding_invalid_bytes
|
|
782
|
+
schema :string, encoding: 'UTF-8'
|
|
783
|
+
|
|
784
|
+
invalid_string = "abc\x80def".force_encoding('UTF-8')
|
|
785
|
+
assert_validation invalid_string do
|
|
786
|
+
error '/', 'String has invalid "UTF-8" encoding.'
|
|
787
|
+
end
|
|
788
|
+
end
|
|
789
|
+
|
|
790
|
+
def test_encoding_invalid_bytes_without_specific_encoding
|
|
791
|
+
schema :string
|
|
792
|
+
|
|
793
|
+
invalid_string = "abc\x80def".force_encoding('UTF-8')
|
|
794
|
+
assert_validation invalid_string do
|
|
795
|
+
error '/', 'String has invalid "UTF-8" encoding.'
|
|
796
|
+
end
|
|
797
|
+
end
|
|
798
|
+
|
|
799
|
+
def test_encoding_validate_self
|
|
800
|
+
assert_raises_with_message Exceptions::InvalidSchemaError,
|
|
801
|
+
'Option "encoding" must be a string or an array of strings.' do
|
|
802
|
+
schema :string, encoding: 123
|
|
803
|
+
end
|
|
804
|
+
|
|
805
|
+
assert_raises_with_message Exceptions::InvalidSchemaError,
|
|
806
|
+
'Option "encoding" must be a string or an array of strings.' do
|
|
807
|
+
schema :string, encoding: [123]
|
|
808
|
+
end
|
|
809
|
+
|
|
810
|
+
assert_raises_with_message Exceptions::InvalidSchemaError,
|
|
811
|
+
'Option "encoding" contains unknown encoding "UNKNOWN-FOO".' do
|
|
812
|
+
schema :string, encoding: 'UNKNOWN-FOO'
|
|
813
|
+
end
|
|
814
|
+
|
|
815
|
+
assert_raises_with_message Exceptions::InvalidSchemaError,
|
|
816
|
+
'Option "encoding" contains unknown encoding "UNKNOWN-FOO".' do
|
|
817
|
+
schema :string, encoding: %w[UTF-8 UNKNOWN-FOO]
|
|
818
|
+
end
|
|
819
|
+
end
|
|
820
|
+
|
|
753
821
|
def test_empty_or_whitespace_string_blank_not_allowed
|
|
754
822
|
schema :string, allow_blank: false
|
|
755
823
|
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: schemacop
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.0
|
|
4
|
+
version: 3.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sitrox
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-05-12 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: activesupport
|