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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 818a611a08d135cb5fb9c691a101472133c2313e5536675b62af795b01a56fbb
4
- data.tar.gz: bea474ef2808294b83597bab07b076103b5ddddbe0d4aa2164bbed779f2cc130
3
+ metadata.gz: 80f588e0e3422fa0fea90f6725f48cb7eab64b7b2d6158e93e05496bc51f7565
4
+ data.tar.gz: 18b0514093843e7b1065e3b2c102263cbb4afa708cf8fa13f7c404cbec037abe
5
5
  SHA512:
6
- metadata.gz: f815986a0a382961c35445d710217a7af8bf9937fe6e137852e4c54c95bc9550b6796aa610f29e2b06aacd277e99eb2e19039bde2d8e1b6c0c5d27578db6b2a0
7
- data.tar.gz: 0db3deb0605ae6a1315e91a58ca1beebfd74aabe7be9488df320736d1077c54ba993c4705b4a8f4b63c8adda3daf57c5a3e6b46639127daf9435a8e1b45544a1
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.34
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.34 ruby lib
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.34".freeze
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-05-27"
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.34
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-05-27 00:00:00.000000000 Z
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.8
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.