verquest 0.6.3 → 0.6.4

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: f02a2f54e2736694e9f26f4c4b2fd2d2939fb246b3bd9031733e6bf606fac3f0
4
- data.tar.gz: 0ca7bd1d066426f72a8507cb25dcda7f45df491dedfd8ee4b0be9e425df5c528
3
+ metadata.gz: 5be764c988f4ea26a04112ed68684fcc7d292e25c37c6c2105f1c62ba7c92bc8
4
+ data.tar.gz: 15cead4ea24fc972317e15157fd14194098f2f543ae7d2402ae81a443ad198d9
5
5
  SHA512:
6
- metadata.gz: 68bb809fe32619ff5bf02f43787cee0d36f6462c1fb84114b246e8165c6c8725d1fd05048cd788d5c9eb97d89b60cc9dc91c008b81ad1240d80dd3dc62cf1843
7
- data.tar.gz: 59471fdfdfb8aaca008467e772d925520fd1b491ce9e303b987bc96f4c2d119f37517cc819c5506de17b2839c3f386827fc5408cfa9fa694b4250642dbd1d6a2
6
+ metadata.gz: 898d20e10c9ea20ccacd10fb8711778444f7cc096c09152e6ba48a167e363a19e60a2d8ed481205d2331b3b5a076ccc888a279ba44121342e698ade944ff9c8b
7
+ data.tar.gz: 23dba503f13f33a571f65008eaa13f2f94347dd974cfa45e9e8bb2f76e9e2599e0ab19fed299b2ea43144641e642c54a30d636ffe0d332967cb7ec84c521f3d6
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.6.3] - 2026-09-23
4
+
5
+ ### Fixed
6
+ - Handling `nullable: true` ([#18](https://github.com/verquest/verquest/pull/18), [@CiTroNaK](https://github.com/CiTroNaK))
7
+
3
8
  ## [0.6.3] - 2026-03-17
4
9
 
5
10
  ### Fixed
data/README.md CHANGED
@@ -319,6 +319,20 @@ Will produce this validation schema:
319
319
 
320
320
  You can define nullable properties in your request schema by setting the `nullable` option to `true`. This feature is based on the latest JSON Schema specification, which is also used in OpenAPI 3.1.
321
321
 
322
+ Nullable enums include Ruby `nil` in their allowed values, which exports as JSON `null`, not the string `"null"`. This also applies to fields with an `enum:` constraint (including custom field types). The supplied values array is not modified.
323
+
324
+ ```ruby
325
+ enum :role, values: %w[member admin], nullable: true
326
+ # => {"role" => {"enum" => ["member", "admin", nil]}}
327
+
328
+ const :kind, value: "user", nullable: true
329
+ # => {"kind" => {"anyOf" => [{"const" => "user"}, {"type" => "null"}]}}
330
+ ```
331
+
332
+ Nullable constants and references use `anyOf` with a null alternative in both exported and validation schemas. Nullable `one_of` uses an outer `anyOf` around the original `oneOf` and the null alternative. This allows null even when a referenced schema or multiple variants already accept it, while preserving all restrictions on non-null values. A discriminator, when present, stays alongside the inner `oneOf`.
333
+
334
+ `required: true` still requires the key to be present; `nullable: true` only permits its value to be null. Defaults stay on the outer nullable schema so missing properties still receive them when default insertion is enabled. Explicit null values are not replaced by defaults.
335
+
322
336
  ```ruby
323
337
  class NullableRequest < Verquest::Base
324
338
  description "This is a simple request with nullable properties for testing purposes."
@@ -365,13 +379,18 @@ Will produce this validation schema:
365
379
  "additionalProperties" => false
366
380
  },
367
381
  "referenced_object" => {
368
- "type" => %w[object null],
369
- "description" => "This is an another example for testing purposes.",
370
- "required" => %w[simple_field nested],
371
- "properties" => {"simple_field" => {"type" => "string", "description" => "The simple field"}, "nested" => {"type" => "object", "required" => %w[nested_field_1 nested_field_2], "properties" => {"nested_field_1" => {"type" => "string", "description" => "This is a nested field"}, "nested_field_2" => {"type" => "string", "description" => "This is another nested field"}}, "additionalProperties" => false}},
372
- "additionalProperties" => false
382
+ "anyOf" => [
383
+ {
384
+ "type" => "object",
385
+ "description" => "This is an another example for testing purposes.",
386
+ "required" => %w[simple_field nested],
387
+ "properties" => {"simple_field" => {"type" => "string", "description" => "The simple field"}, "nested" => {"type" => "object", "required" => %w[nested_field_1 nested_field_2], "properties" => {"nested_field_1" => {"type" => "string", "description" => "This is a nested field"}, "nested_field_2" => {"type" => "string", "description" => "This is another nested field"}}, "additionalProperties" => false}},
388
+ "additionalProperties" => false
389
+ },
390
+ {"type" => "null"}
391
+ ]
373
392
  },
374
- "referenced_field" => {"type" => %w[string null], "description" => "The simple field"}
393
+ "referenced_field" => {"anyOf" => [{"type" => "string", "description" => "The simple field"}, {"type" => "null"}]}
375
394
  },
376
395
  "additionalProperties" => false
377
396
  }
@@ -164,13 +164,15 @@ module Verquest
164
164
  # @param value [Object] The value of the constant
165
165
  # @param map [String, nil] An optional mapping to another constant
166
166
  # @param required [Boolean, Array<Symbol>] Whether the constant is required
167
+ # @param nullable [Boolean] Whether the constant can be null
167
168
  # @param schema_options [Hash] Additional schema options for the constant
168
169
  # @return [void]
169
- def const(name, value:, map: nil, required: nil, **schema_options)
170
+ def const(name, value:, map: nil, required: nil, nullable: nil, **schema_options)
170
171
  camelize(schema_options)
171
172
  required = default_options.fetch(:required, false) if required.nil?
173
+ nullable = default_options.fetch(:nullable, false) if nullable.nil?
172
174
 
173
- const = Properties::Const.new(name:, value:, map:, required:, **schema_options)
175
+ const = Properties::Const.new(name:, value:, map:, required:, nullable:, **schema_options)
174
176
  current_scope.add(const)
175
177
  end
176
178
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Verquest
4
- GEM_VERSION = "0.6.3"
4
+ GEM_VERSION = "0.6.4"
5
5
  end
@@ -12,6 +12,9 @@ module Verquest
12
12
  class Base
13
13
  include HelperMethods::RequiredProperties
14
14
 
15
+ # JSON Schema for null type, used when nullable is true
16
+ NULL_TYPE_SCHEMA = {"type" => "null"}.freeze
17
+
15
18
  # @!attribute [rw] name
16
19
  # @return [String] The name of the property
17
20
  # @!attribute [rw] required
@@ -61,6 +64,15 @@ module Verquest
61
64
  # @return [Boolean] Whether this property can be null
62
65
  attr_reader :nullable
63
66
 
67
+ # Allows null without weakening constraints, keeping defaults available for insertion
68
+ # @param schema [Hash] The original property schema
69
+ # @return [Hash] The schema, optionally wrapped in a nullable union
70
+ def nullable_schema(schema)
71
+ return schema unless nullable
72
+
73
+ schema.slice("default").merge("anyOf" => [schema.except("default"), NULL_TYPE_SCHEMA])
74
+ end
75
+
64
76
  # Determines the mapping target key based on mapping configuration
65
77
  # @param value_prefix [Array<String>] Prefix for the target value
66
78
  # @param collection [Boolean] Whether this is a collection mapping
@@ -14,12 +14,14 @@ module Verquest
14
14
  # @param value [Object] The fixed value of the constant (can be any scalar value)
15
15
  # @param map [Object, nil] Optional mapping information
16
16
  # @param required [Boolean, Array<Symbol>] Whether this property is required, or array of dependency names (can be overridden by custom type)
17
+ # @param nullable [Boolean] Whether this property can be null
17
18
  # @param schema_options [Hash] Additional JSON schema options for this property
18
- def initialize(name:, value:, map: nil, required: false, **schema_options)
19
+ def initialize(name:, value:, map: nil, required: false, nullable: false, **schema_options)
19
20
  @name = name.to_s
20
21
  @value = value
21
22
  @map = map
22
23
  @required = required
24
+ @nullable = nullable
23
25
  @schema_options = schema_options&.transform_keys(&:to_s)
24
26
  end
25
27
 
@@ -28,9 +30,7 @@ module Verquest
28
30
  # @return [Hash] The schema definition for this constant
29
31
  def to_schema
30
32
  {
31
- name => {
32
- "const" => value
33
- }.merge(schema_options)
33
+ name => nullable_schema({"const" => value}.merge(schema_options))
34
34
  }
35
35
  end
36
36
 
@@ -26,15 +26,11 @@ module Verquest
26
26
  raise ArgumentError, "Use const for a single value" if values.length == 1
27
27
 
28
28
  @name = name.to_s
29
- @values = values
29
+ @values = nullable ? values | [nil] : values
30
30
  @required = required
31
31
  @nullable = nullable
32
32
  @map = map
33
33
  @schema_options = schema_options&.transform_keys(&:to_s)
34
-
35
- if nullable && !values.include?("null")
36
- values << "null"
37
- end
38
34
  end
39
35
 
40
36
  # Generate JSON schema definition for this enum
@@ -54,6 +54,7 @@ module Verquest
54
54
 
55
55
  if nullable
56
56
  @type = [@type, "null"]
57
+ @schema_options["enum"] = @schema_options["enum"] | [nil] if @schema_options.key?("enum")
57
58
  end
58
59
  end
59
60
 
@@ -31,9 +31,6 @@ module Verquest
31
31
  # one_of = Verquest::Properties::OneOf.new(name: :value)
32
32
  # # Validates that exactly one schema matches
33
33
  class OneOf < Base
34
- # JSON Schema for null type, used when nullable is true
35
- NULL_TYPE_SCHEMA = {"type" => "null"}.freeze
36
-
37
34
  # @return [String, nil] The discriminator property name for schema selection
38
35
  attr_reader :discriminator
39
36
 
@@ -75,7 +72,7 @@ module Verquest
75
72
  # @return [Hash] The schema definition with oneOf array and optional discriminator
76
73
  def to_schema
77
74
  freeze_schemas
78
- wrap_schema(build_schema_with_refs)
75
+ wrap_schema(nullable_schema(build_schema_with_refs))
79
76
  end
80
77
 
81
78
  # Generate validation schema for this oneOf property
@@ -87,7 +84,7 @@ module Verquest
87
84
  # @return [Hash] The validation schema with inline schema definitions
88
85
  def to_validation_schema(version: nil)
89
86
  freeze_schemas
90
- wrap_schema(build_validation_schema(version: version))
87
+ wrap_schema(nullable_schema(build_validation_schema(version: version)))
91
88
  end
92
89
 
93
90
  # Create mapping for this oneOf property
@@ -386,9 +383,7 @@ module Verquest
386
383
  #
387
384
  # @return [Array<Hash>] Array of schema references
388
385
  def collect_schema_refs
389
- refs = schemas.values.map { |schema| schema.to_schema[schema.name] }
390
- refs << NULL_TYPE_SCHEMA if nullable
391
- refs
386
+ schemas.values.map { |schema| schema.to_schema[schema.name] }
392
387
  end
393
388
 
394
389
  # Collects inline schema definitions for all variants
@@ -396,9 +391,7 @@ module Verquest
396
391
  # @param version [String, nil] The version for schema resolution
397
392
  # @return [Array<Hash>] Array of inline schema definitions
398
393
  def collect_inline_schemas(version)
399
- inline_schemas = schemas.values.map { |schema| schema.to_validation_schema(version: version)[schema.name] }
400
- inline_schemas << NULL_TYPE_SCHEMA if nullable
401
- inline_schemas
394
+ schemas.values.map { |schema| schema.to_validation_schema(version: version)[schema.name] }
402
395
  end
403
396
 
404
397
  # Adds discriminator information to the schema if present
@@ -42,20 +42,7 @@ module Verquest
42
42
  #
43
43
  # @return [Hash] The schema definition with a $ref pointer
44
44
  def to_schema
45
- if nullable
46
- {
47
- name => {
48
- "oneOf" => [
49
- {"$ref" => from.to_ref(property: property)},
50
- {"type" => "null"}
51
- ]
52
- }
53
- }
54
- else
55
- {
56
- name => {"$ref" => from.to_ref(property: property)}
57
- }
58
- end
45
+ {name => nullable_schema({"$ref" => from.to_ref(property: property)})}
59
46
  end
60
47
 
61
48
  # Generate validation schema for this reference property
@@ -65,13 +52,7 @@ module Verquest
65
52
  def to_validation_schema(version: nil)
66
53
  schema = from.to_validation_schema(version:, property: property).dup
67
54
 
68
- if nullable
69
- schema["type"] = [schema["type"], "null"] unless schema["type"].include?("null")
70
- end
71
-
72
- {
73
- name => schema
74
- }
55
+ {name => nullable_schema(schema)}
75
56
  end
76
57
 
77
58
  # Create mapping for this reference property
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: verquest
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.3
4
+ version: 0.6.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Petr Hlavicka
@@ -98,7 +98,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
98
98
  - !ruby/object:Gem::Version
99
99
  version: '0'
100
100
  requirements: []
101
- rubygems_version: 3.6.7
101
+ rubygems_version: 3.6.9
102
102
  specification_version: 4
103
103
  summary: Verquest is a Ruby gem that offers an elegant solution for versioning API
104
104
  requests