schemacop 3.0.34 → 3.0.35
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/CHANGELOG.md +5 -0
- data/README_V3.md +19 -0
- data/VERSION +1 -1
- data/lib/schemacop/v3/numeric_node.rb +28 -0
- data/schemacop.gemspec +3 -3
- data/test/unit/schemacop/v3/number_node_test.rb +111 -0
- metadata +8 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 80f588e0e3422fa0fea90f6725f48cb7eab64b7b2d6158e93e05496bc51f7565
|
4
|
+
data.tar.gz: 18b0514093843e7b1065e3b2c102263cbb4afa708cf8fa13f7c404cbec037abe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f54e94a656fa9df82c4adde9f81d60a5f93b673018a8f9d50a80c755aa2b67ec362eeace426a1b4fcd9cbd4a0af8100b845b876afc37a2de5dac82a8b14acd6f
|
7
|
+
data.tar.gz: 8e362b61caf5ba6f885cef3014e53677e3d76775f31945fc70b0a7400cf465d5c28a0a6c053288e38a4642f8dd13b60cd35047dfe58a5fb8c88eecafc65c9499
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
# Change log
|
2
2
|
|
3
|
+
## 3.0.35 (2025-07-31)
|
4
|
+
|
5
|
+
* Add option `max_precision` to `number` nodes for Schemacop V3 schemas to validate
|
6
|
+
the maximum number of decimal places for `Float` and `BigDecimal` values
|
7
|
+
|
3
8
|
## 3.0.34 (2025-05-27)
|
4
9
|
|
5
10
|
* Adapt string format `number` to cast to `Integer` instead of to a `Float` if
|
data/README_V3.md
CHANGED
@@ -445,6 +445,10 @@ With the various available options, validations on the value of the number can b
|
|
445
445
|
* `multiple_of`
|
446
446
|
The received number has to be a multiple of the given number for the validation to
|
447
447
|
pass.
|
448
|
+
* `max_precision`
|
449
|
+
Defines the maximum number of digits after the decimal point for `Float` and
|
450
|
+
`BigDecimal` values. Must be a non-negative integer. When `nil` (default), no
|
451
|
+
precision validation is performed. Trailing zeros are ignored in the validation.
|
448
452
|
* `cast_str`
|
449
453
|
When set to `true`, this node also accepts strings that can be casted to a number, e.g.
|
450
454
|
the values `'0.1'` or `'3.1415'`. Please note that you can only validate numbers which
|
@@ -483,6 +487,21 @@ schema.validate!(nil) # => nil
|
|
483
487
|
schema.validate!('') # => nil
|
484
488
|
```
|
485
489
|
|
490
|
+
With `max_precision`:
|
491
|
+
|
492
|
+
```ruby
|
493
|
+
# Validates that the input is a number with maximum 2 decimal places
|
494
|
+
schema = Schemacop::Schema3.new(:number, max_precision: 2)
|
495
|
+
schema.validate!(42) # => 42
|
496
|
+
schema.validate!(42.5) # => 42.5
|
497
|
+
schema.validate!(42.52) # => 42.52
|
498
|
+
schema.validate!(42.523) # => Schemacop::Exceptions::ValidationError: /: Value must have a maximum precision of 2 digits after the decimal point.
|
499
|
+
schema.validate!(BigDecimal('3.14')) # => 0.314e1
|
500
|
+
schema.validate!(BigDecimal('3.141')) # => Schemacop::Exceptions::ValidationError: /: Value must have a maximum precision of 2 digits after the decimal point.
|
501
|
+
schema.validate!(BigDecimal('3.140')) # => 0.314e1 (trailing zeros are ignored)
|
502
|
+
schema.validate!(1r) # => (1/1) (rational numbers are not affected)
|
503
|
+
```
|
504
|
+
|
486
505
|
Please note, that `nil` and blank strings are treated equally when using the `cast_str` option,
|
487
506
|
and validating a blank string will return `nil`.
|
488
507
|
If you need a value, use the `required` option:
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.0.
|
1
|
+
3.0.35
|
@@ -8,6 +8,7 @@ module Schemacop
|
|
8
8
|
multiple_of
|
9
9
|
exclusive_minimum
|
10
10
|
exclusive_maximum
|
11
|
+
max_precision
|
11
12
|
].freeze
|
12
13
|
|
13
14
|
def self.allowed_options
|
@@ -58,12 +59,39 @@ module Schemacop
|
|
58
59
|
if options[:multiple_of] && !compare_float((super_data % options[:multiple_of]), 0.0)
|
59
60
|
result.error "Value must be a multiple of #{options[:multiple_of]}."
|
60
61
|
end
|
62
|
+
|
63
|
+
# Validate max precision #
|
64
|
+
if options[:max_precision] && (super_data.is_a?(Float) || super_data.is_a?(BigDecimal))
|
65
|
+
# Convert to string and check decimal places
|
66
|
+
str = super_data.to_s
|
67
|
+
# For BigDecimal, remove the trailing '0' from the scientific notation if present
|
68
|
+
str = str.sub(/\.0e[+-]?\d+\z/, '.0') if super_data.is_a?(BigDecimal)
|
69
|
+
|
70
|
+
if (match = str.match(/\.(\d+)$/))
|
71
|
+
# Remove trailing zeros for more accurate precision check
|
72
|
+
decimal_part = match[1].sub(/0+\z/, '')
|
73
|
+
decimal_places = decimal_part.empty? ? 0 : decimal_part.length
|
74
|
+
if decimal_places > options[:max_precision]
|
75
|
+
result.error "Value must have a maximum precision of #{options[:max_precision]} digits after the decimal point."
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
61
79
|
end
|
62
80
|
|
63
81
|
def validate_self
|
64
82
|
# Check that the options have the correct type
|
65
83
|
ATTRIBUTES.each do |attribute|
|
66
84
|
next if options[attribute].nil?
|
85
|
+
|
86
|
+
# Special handling for max_precision - must be an integer
|
87
|
+
if attribute == :max_precision
|
88
|
+
unless options[attribute].is_a?(Integer) && options[attribute] >= 0
|
89
|
+
fail 'Option "max_precision" must be a non-negative integer.'
|
90
|
+
end
|
91
|
+
|
92
|
+
next
|
93
|
+
end
|
94
|
+
|
67
95
|
next unless allowed_types.keys.none? { |c| options[attribute].send(type_assertion_method, c) }
|
68
96
|
|
69
97
|
collection = allowed_types.values.map { |t| "\"#{t}\"" }.uniq.sort.join(' or ')
|
data/schemacop.gemspec
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
# stub: schemacop 3.0.
|
2
|
+
# stub: schemacop 3.0.35 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.0.35".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 = "2025-
|
11
|
+
s.date = "2025-07-31"
|
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/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]
|
@@ -193,6 +193,75 @@ module Schemacop
|
|
193
193
|
end
|
194
194
|
end
|
195
195
|
|
196
|
+
def test_max_precision
|
197
|
+
schema :number, max_precision: 2
|
198
|
+
|
199
|
+
assert_json(
|
200
|
+
type: :number,
|
201
|
+
maxPrecision: 2
|
202
|
+
)
|
203
|
+
|
204
|
+
# Valid cases
|
205
|
+
assert_validation(42) # Integer
|
206
|
+
assert_validation(3.14) # 2 decimal places
|
207
|
+
assert_validation(3.1) # 1 decimal place
|
208
|
+
assert_validation(3.0) # 0 decimal places
|
209
|
+
assert_validation(0.12) # 2 decimal places
|
210
|
+
assert_validation(1r) # Rational should not be affected
|
211
|
+
|
212
|
+
# BigDecimal valid cases
|
213
|
+
assert_validation(BigDecimal('3.14')) # 2 decimal places
|
214
|
+
assert_validation(BigDecimal('3.1')) # 1 decimal place
|
215
|
+
assert_validation(BigDecimal('3.0')) # 0 decimal places (with trailing zero)
|
216
|
+
assert_validation(BigDecimal('3')) # 0 decimal places
|
217
|
+
assert_validation(BigDecimal('3.00')) # trailing zeros should be ignored
|
218
|
+
|
219
|
+
# Invalid cases - Float
|
220
|
+
assert_validation(3.141) do
|
221
|
+
error '/', 'Value must have a maximum precision of 2 digits after the decimal point.'
|
222
|
+
end
|
223
|
+
|
224
|
+
assert_validation(0.123) do
|
225
|
+
error '/', 'Value must have a maximum precision of 2 digits after the decimal point.'
|
226
|
+
end
|
227
|
+
|
228
|
+
assert_validation(123.456789) do
|
229
|
+
error '/', 'Value must have a maximum precision of 2 digits after the decimal point.'
|
230
|
+
end
|
231
|
+
|
232
|
+
# Invalid cases - BigDecimal
|
233
|
+
assert_validation(BigDecimal('3.141')) do
|
234
|
+
error '/', 'Value must have a maximum precision of 2 digits after the decimal point.'
|
235
|
+
end
|
236
|
+
|
237
|
+
assert_validation(BigDecimal('0.123')) do
|
238
|
+
error '/', 'Value must have a maximum precision of 2 digits after the decimal point.'
|
239
|
+
end
|
240
|
+
|
241
|
+
# Test with max_precision: 0
|
242
|
+
schema :number, max_precision: 0
|
243
|
+
|
244
|
+
assert_json(
|
245
|
+
type: :number,
|
246
|
+
maxPrecision: 0
|
247
|
+
)
|
248
|
+
|
249
|
+
assert_validation(42)
|
250
|
+
assert_validation(3.0)
|
251
|
+
assert_validation(0)
|
252
|
+
assert_validation(BigDecimal('3'))
|
253
|
+
assert_validation(BigDecimal('3.0'))
|
254
|
+
assert_validation(BigDecimal('3.00'))
|
255
|
+
|
256
|
+
assert_validation(3.1) do
|
257
|
+
error '/', 'Value must have a maximum precision of 0 digits after the decimal point.'
|
258
|
+
end
|
259
|
+
|
260
|
+
assert_validation(BigDecimal('3.1')) do
|
261
|
+
error '/', 'Value must have a maximum precision of 0 digits after the decimal point.'
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
196
265
|
# Helper function that checks for all the options if the option is
|
197
266
|
# an allowed class or something else, in which case it needs to raise
|
198
267
|
def validate_self_should_error(value_to_check)
|
@@ -239,6 +308,21 @@ module Schemacop
|
|
239
308
|
schema :number, multiple_of: 0
|
240
309
|
end
|
241
310
|
|
311
|
+
assert_raises_with_message Exceptions::InvalidSchemaError,
|
312
|
+
'Option "max_precision" must be a non-negative integer.' do
|
313
|
+
schema :number, max_precision: -1
|
314
|
+
end
|
315
|
+
|
316
|
+
assert_raises_with_message Exceptions::InvalidSchemaError,
|
317
|
+
'Option "max_precision" must be a non-negative integer.' do
|
318
|
+
schema :number, max_precision: 2.5
|
319
|
+
end
|
320
|
+
|
321
|
+
assert_raises_with_message Exceptions::InvalidSchemaError,
|
322
|
+
'Option "max_precision" must be a non-negative integer.' do
|
323
|
+
schema :number, max_precision: '2'
|
324
|
+
end
|
325
|
+
|
242
326
|
validate_self_should_error(1 + 0i) # Complex
|
243
327
|
validate_self_should_error(Object.new)
|
244
328
|
validate_self_should_error(true)
|
@@ -310,6 +394,33 @@ module Schemacop
|
|
310
394
|
})
|
311
395
|
end
|
312
396
|
|
397
|
+
def test_combined_validations_with_max_precision
|
398
|
+
schema :number, minimum: 0, maximum: 100, max_precision: 1
|
399
|
+
|
400
|
+
assert_json(
|
401
|
+
type: :number,
|
402
|
+
minimum: 0,
|
403
|
+
maximum: 100,
|
404
|
+
maxPrecision: 1
|
405
|
+
)
|
406
|
+
|
407
|
+
assert_validation(50.5)
|
408
|
+
assert_validation(0.1)
|
409
|
+
assert_validation(100.0)
|
410
|
+
|
411
|
+
assert_validation(50.55) do
|
412
|
+
error '/', 'Value must have a maximum precision of 1 digits after the decimal point.'
|
413
|
+
end
|
414
|
+
|
415
|
+
assert_validation(-0.1) do
|
416
|
+
error '/', 'Value must have a minimum of 0.'
|
417
|
+
end
|
418
|
+
|
419
|
+
assert_validation(100.1) do
|
420
|
+
error '/', 'Value must have a maximum of 100.'
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
313
424
|
def test_cast_str
|
314
425
|
schema :number, cast_str: true, minimum: 0.0, maximum: 50r, multiple_of: BigDecimal('0.5')
|
315
426
|
|
metadata
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: schemacop
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.
|
4
|
+
version: 3.0.35
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sitrox
|
8
|
+
autorequire:
|
8
9
|
bindir: bin
|
9
10
|
cert_chain: []
|
10
|
-
date: 2025-
|
11
|
+
date: 2025-07-31 00:00:00.000000000 Z
|
11
12
|
dependencies:
|
12
13
|
- !ruby/object:Gem::Dependency
|
13
14
|
name: activesupport
|
@@ -37,6 +38,8 @@ dependencies:
|
|
37
38
|
- - ">="
|
38
39
|
- !ruby/object:Gem::Version
|
39
40
|
version: 0.0.4
|
41
|
+
description:
|
42
|
+
email:
|
40
43
|
executables: []
|
41
44
|
extensions: []
|
42
45
|
extra_rdoc_files: []
|
@@ -148,6 +151,7 @@ homepage: https://github.com/sitrox/schemacop
|
|
148
151
|
licenses:
|
149
152
|
- MIT
|
150
153
|
metadata: {}
|
154
|
+
post_install_message:
|
151
155
|
rdoc_options: []
|
152
156
|
require_paths:
|
153
157
|
- lib
|
@@ -162,7 +166,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
162
166
|
- !ruby/object:Gem::Version
|
163
167
|
version: '0'
|
164
168
|
requirements: []
|
165
|
-
rubygems_version: 3.6
|
169
|
+
rubygems_version: 3.4.6
|
170
|
+
signing_key:
|
166
171
|
specification_version: 4
|
167
172
|
summary: Schemacop validates ruby structures consisting of nested hashes and arrays
|
168
173
|
against simple schema definitions.
|