verquest 0.6.1 → 0.6.3

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: 48e1067531cf426f709ebe2dfffe4548cfd161b19f9481ca87599a0152e1fcab
4
- data.tar.gz: d4387ad9de93bceebb05e94b69560f43f7b374cad28588a3ff0599d9f8d17667
3
+ metadata.gz: f02a2f54e2736694e9f26f4c4b2fd2d2939fb246b3bd9031733e6bf606fac3f0
4
+ data.tar.gz: 0ca7bd1d066426f72a8507cb25dcda7f45df491dedfd8ee4b0be9e425df5c528
5
5
  SHA512:
6
- metadata.gz: bc95249593570b003e7dc49881fe4a3a34a60efc535be6f1803c17db7cd6475317eaacb0c9ff5c1f11ba24bb18ff5065d86d070be92e24f7ff4d1e1fa88f14e9
7
- data.tar.gz: 8d915f5cbd2f3fdfea67a1d94f13803d9a1125db6a8a21fe3cec81233693017391e40f69feee0fd9a60a8bb6926764cc0d7de1c77a3746c7ef87e2672acdd978
6
+ metadata.gz: 68bb809fe32619ff5bf02f43787cee0d36f6462c1fb84114b246e8165c6c8725d1fd05048cd788d5c9eb97d89b60cc9dc91c008b81ad1240d80dd3dc62cf1843
7
+ data.tar.gz: 59471fdfdfb8aaca008467e772d925520fd1b491ce9e303b987bc96f4c2d119f37517cc819c5506de17b2839c3f386827fc5408cfa9fa694b4250642dbd1d6a2
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.6.3] - 2026-03-17
4
+
5
+ ### Fixed
6
+ - Transformer fixes: fix null propagation, unknown discriminator, and null variant path.
7
+
8
+ ## [0.6.2] - 2025-12-09
9
+
10
+ ### Fixed
11
+ - The `item_schema_options` is now properly passed to the `Array` property.
12
+
3
13
  ## [0.6.1] - 2025-10-13
4
14
 
5
15
  ### Fixed
data/Rakefile CHANGED
@@ -1,12 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "bundler/gem_tasks"
4
- require "minitest/test_task"
4
+ require "rake/testtask"
5
5
 
6
- Minitest::TestTask.create
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/*_test.rb"]
10
+ t.warning = true
11
+ end
7
12
 
8
13
  require "rubocop/rake_task"
9
14
 
10
15
  RuboCop::RakeTask.new
11
16
 
12
- task default: %i[test rubocop]
17
+ desc "Check YARD documentation coverage (must be 100%)"
18
+ task :yard do
19
+ require "yard"
20
+
21
+ # Capture the stats output
22
+ stats = YARD::CLI::Stats.new
23
+ stats.run("--list-undoc")
24
+
25
+ # Check if there are any undocumented objects
26
+ undocumented = stats.instance_variable_get(:@undoc_list) || []
27
+
28
+ unless undocumented.empty?
29
+ abort "\nYARD documentation check failed: #{undocumented.size} undocumented objects found."
30
+ end
31
+
32
+ puts "\nYARD documentation: 100% documented"
33
+ end
34
+
35
+ task default: %i[test rubocop yard]
@@ -265,9 +265,10 @@ module Verquest
265
265
  # @param required [Boolean, Array<Symbol>] Whether the array property is required
266
266
  # @param nullable [Boolean] Whether this array can be null
267
267
  # @param map [String, nil] An optional mapping to another array property
268
+ # @param item_schema_options [Hash] Additional JSON schema options for the array items (merged with custom type options)
268
269
  # @param schema_options [Hash] Additional schema options for the array property
269
270
  # @return [void]
270
- def array(name, type:, required: nil, nullable: nil, map: nil, **schema_options)
271
+ def array(name, type:, required: nil, nullable: nil, map: nil, item_schema_options: {}, **schema_options)
271
272
  camelize(schema_options)
272
273
 
273
274
  type = default_options.fetch(:type, type)
@@ -275,10 +276,40 @@ module Verquest
275
276
  nullable = default_options.fetch(:nullable, false) if nullable.nil?
276
277
  schema_options = default_options.except(:type, :required, :nullable).merge(schema_options)
277
278
 
278
- array = Properties::Array.new(name:, type:, required:, nullable:, map:, **schema_options)
279
+ array = Properties::Array.new(name:, type:, required:, nullable:, map:, item_schema_options:, **schema_options)
279
280
  current_scope.add(array)
280
281
  end
281
282
 
283
+ # Defines a oneOf property for polymorphic schemas
284
+ #
285
+ # Creates a JSON Schema oneOf structure where exactly one of the defined
286
+ # schemas must match. When used at the root level (without a name), it creates
287
+ # a "combination schema" where the entire request body matches one of the options.
288
+ #
289
+ # @param name [Symbol, nil] The property name, or nil for root-level oneOf
290
+ # @param discriminator [Symbol, nil] The property used to discriminate between schemas
291
+ # @param required [Boolean, Array<Symbol>, nil] Whether this property is required
292
+ # @param nullable [Boolean] Whether this property can be null
293
+ # @param map [String, nil] An optional mapping to another property
294
+ # @yield Block defining the schema options using reference declarations
295
+ # @return [void]
296
+ def one_of(name: nil, discriminator: nil, required: nil, nullable: nil, map: nil, &block)
297
+ required = default_options.fetch(:required, false) if required.nil?
298
+ nullable = default_options.fetch(:nullable, false) if nullable.nil?
299
+
300
+ one_of = Properties::OneOf.new(name:, discriminator:, required:, nullable:, map:)
301
+ current_scope.add(one_of)
302
+
303
+ if block_given?
304
+ previous_scope = current_scope
305
+ @current_scope = one_of
306
+
307
+ instance_exec(&block)
308
+ end
309
+ ensure
310
+ @current_scope = previous_scope if block_given?
311
+ end
312
+
282
313
  # Excludes specified properties from the current scope by removing them
283
314
  # from the version's property set
284
315
  #
@@ -12,7 +12,7 @@ module Verquest
12
12
  # @param version [String, nil] Specific version to use, defaults to configuration setting
13
13
  # @param validate [Boolean, nil] Whether to validate the params, defaults to configuration setting
14
14
  # @param remove_extra_root_keys [Boolean, nil] Whether to remove extra keys at the root level, defaults to configuration setting
15
- # @return [Verquest::Result, Hash, Exception] When validation_error_handling is :result, returns a Success result with mapped params or Failure result with validation errors.
15
+ # @return [Verquest::Result, Hash] When validation_error_handling is :result, returns a Success result with mapped params or Failure result with validation errors.
16
16
  # When validation_error_handling is :raise, returns mapped params directly or raises InvalidParamsError with validation errors.
17
17
  def process(params, version: nil, validate: nil, remove_extra_root_keys: nil)
18
18
  validate = Verquest.configuration.validate_params if validate.nil?
@@ -22,7 +22,7 @@ module Verquest
22
22
 
23
23
  params = params.dup
24
24
  params = params.to_unsafe_h if params.respond_to?(:to_unsafe_h)
25
- params = params.slice(*version_class.properties.keys) if remove_extra_root_keys
25
+ params = params.slice(*version_class.properties.keys) if remove_extra_root_keys && !version_class.combination?
26
26
 
27
27
  if validate && (validation_result = version_class.validate_params(params: params)) && validation_result.any?
28
28
  case Verquest.configuration.validation_error_handling
@@ -32,13 +32,22 @@ module Verquest
32
32
  Result.failure(validation_result)
33
33
  end
34
34
  else
35
- mapped_params = version_class.map_params(params)
36
-
37
- case Verquest.configuration.validation_error_handling
38
- when :raise
39
- mapped_params
40
- when :result
41
- Result.success(mapped_params)
35
+ begin
36
+ mapped_params = version_class.map_params(params)
37
+
38
+ case Verquest.configuration.validation_error_handling
39
+ when :raise
40
+ mapped_params
41
+ when :result
42
+ Result.success(mapped_params)
43
+ end
44
+ rescue MappingError => e
45
+ case Verquest.configuration.validation_error_handling
46
+ when :raise
47
+ raise
48
+ when :result
49
+ Result.failure([{message: e.message, type: "mapping_error"}])
50
+ end
42
51
  end
43
52
  end
44
53
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Verquest
2
4
  # Configuration for the Verquest gem
3
5
  #
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Verquest
4
- GEM_VERSION = "0.6.1"
4
+ GEM_VERSION = "0.6.3"
5
5
  end
@@ -20,7 +20,7 @@ module Verquest
20
20
  # by selecting those with their required attribute set to true (boolean).
21
21
  # Results are memoized to avoid recalculating on subsequent calls.
22
22
  #
23
- # @return [Array<Symbol>] Names of properties marked as unconditionally required (required == true)
23
+ # @return [Array<String>] Names of properties marked as unconditionally required (required == true)
24
24
  def required_properties
25
25
  @_required_properties ||= properties.values.select { _1.required == true }.map(&:name)
26
26
  end
@@ -72,8 +72,8 @@ module Verquest
72
72
  # @param key_prefix [Array<String>] Prefix for the source key
73
73
  # @param value_prefix [Array<String>] Prefix for the target value
74
74
  # @param mapping [Hash] The mapping hash to be updated
75
- # @param version [String, nil] The version to create mapping for, defaults to configuration setting
76
- # @return [Hash] The updated mapping hash
75
+ # @param version [String, nil] The version to create mapping for
76
+ # @return [void]
77
77
  def mapping(key_prefix:, value_prefix:, mapping:, version: nil)
78
78
  mapping[(key_prefix + [name]).join("/")] = mapping_value_key(value_prefix:)
79
79
  end
@@ -49,7 +49,7 @@ module Verquest
49
49
  # @param value_prefix [Array<String>] Prefix for the target value
50
50
  # @param mapping [Hash] The mapping hash to be updated
51
51
  # @param version [String, nil] The version to create mapping for
52
- # @return [Hash] The updated mapping hash
52
+ # @return [void]
53
53
  # @raise [NoMethodError] This is an abstract method that must be overridden
54
54
  def mapping(key_prefix:, value_prefix:, mapping:, version:)
55
55
  raise NoMethodError
@@ -61,6 +61,22 @@ module Verquest
61
61
  !item.nil?
62
62
  end
63
63
 
64
+ # Check if this collection contains a oneOf property as its item type
65
+ #
66
+ # @return [Boolean] True if the collection contains a oneOf property
67
+ def has_one_of?
68
+ properties.values.size == 1 && properties.values.first.is_a?(Verquest::Properties::OneOf)
69
+ end
70
+
71
+ # Returns the oneOf property if this collection contains one
72
+ #
73
+ # @return [Verquest::Properties::OneOf, nil] The oneOf property or nil
74
+ def one_of_property
75
+ return nil unless has_one_of?
76
+
77
+ properties.values.first
78
+ end
79
+
64
80
  # Generate JSON schema definition for this collection property
65
81
  #
66
82
  # @return [Hash] The schema definition for this collection property
@@ -74,6 +90,13 @@ module Verquest
74
90
  }
75
91
  }.merge(schema_options)
76
92
  }
93
+ elsif has_one_of?
94
+ {
95
+ name => {
96
+ "type" => type,
97
+ "items" => one_of_property.to_schema[one_of_property.name] || one_of_property.to_schema
98
+ }.merge(schema_options)
99
+ }
77
100
  else
78
101
  {
79
102
  name => {
@@ -103,6 +126,14 @@ module Verquest
103
126
  "items" => item.to_validation_schema(version: version)
104
127
  }.merge(schema_options)
105
128
  }
129
+ elsif has_one_of?
130
+ one_of_schema = one_of_property.to_validation_schema(version: version)
131
+ {
132
+ name => {
133
+ "type" => type,
134
+ "items" => one_of_schema[one_of_property.name] || one_of_schema
135
+ }.merge(schema_options)
136
+ }
106
137
  else
107
138
  {
108
139
  name => {
@@ -122,13 +153,17 @@ module Verquest
122
153
 
123
154
  # Create mapping for this collection property and all its children
124
155
  #
125
- # This method handles two different scenarios:
156
+ # This method handles three different scenarios:
126
157
  # 1. When the collection references an external item schema (`has_item?` returns true)
127
158
  # - Creates mappings by transforming keys from the referenced item schema
128
159
  # - Adds array notation ([]) to indicate this is a collection
129
160
  # - Prefixes all keys and values with the appropriate paths
130
161
  #
131
- # 2. When the collection has inline item properties
162
+ # 2. When the collection contains a oneOf property (`has_one_of?` returns true)
163
+ # - Creates variant-keyed mappings for discriminator-less oneOf support
164
+ # - Each variant gets array notation applied to its paths
165
+ #
166
+ # 3. When the collection has inline item properties
132
167
  # - Creates mappings for each property in the collection items
133
168
  # - Each property gets mapped with array notation and appropriate prefixes
134
169
  #
@@ -136,16 +171,20 @@ module Verquest
136
171
  # @param value_prefix [Array<String>] Prefix for the target value
137
172
  # @param mapping [Hash] The mapping hash to be updated
138
173
  # @param version [String, nil] The version to create mapping for
139
- # @return [Hash] The updated mapping hash
174
+ # @return [void]
140
175
  def mapping(key_prefix:, value_prefix:, mapping:, version:)
141
176
  if has_item?
142
177
  value_key_prefix = mapping_value_key(value_prefix: value_prefix, collection: true)
143
178
 
144
179
  reference_mapping = item.mapping(version:).dup
145
- reference_mapping.transform_keys! { "#{(key_prefix + [name]).join("/")}[]/#{_1}" }
146
- reference_mapping.transform_values! { "#{value_key_prefix}/#{_1}" }
180
+ reference_mapping.transform_keys! { |k| "#{(key_prefix + [name]).join("/")}[]/#{k}" }
181
+ reference_mapping.transform_values! { |v| "#{value_key_prefix}/#{v}" }
147
182
 
148
183
  mapping.merge!(reference_mapping)
184
+ elsif has_one_of?
185
+ one_of_mapping = {}
186
+ one_of_property.mapping(key_prefix: key_prefix + ["#{name}[]"], value_prefix: mapping_value_prefix(value_prefix: value_prefix, collection: true), mapping: one_of_mapping, version:)
187
+ mapping.merge!(one_of_mapping)
149
188
  else
150
189
  properties.values.each do |property|
151
190
  property.mapping(key_prefix: key_prefix + ["#{name}[]"], value_prefix: mapping_value_prefix(value_prefix: value_prefix, collection: true), mapping:, version:)
@@ -36,11 +36,11 @@ module Verquest
36
36
 
37
37
  # Create mapping for this const property
38
38
  #
39
- # @param key_prefix [Array<Symbol>] Prefix for the source key
39
+ # @param key_prefix [Array<String>] Prefix for the source key
40
40
  # @param value_prefix [Array<String>] Prefix for the target value
41
41
  # @param mapping [Hash] The mapping hash to be updated
42
42
  # @param version [String, nil] The version to create mapping for
43
- # @return [Hash] The updated mapping hash
43
+ # @return [void]
44
44
  def mapping(key_prefix:, value_prefix:, mapping:, version: nil)
45
45
  mapping[(key_prefix + [name]).join("/")] = mapping_value_key(value_prefix:)
46
46
  end
@@ -48,11 +48,11 @@ module Verquest
48
48
 
49
49
  # Create mapping for this enum property
50
50
  #
51
- # @param key_prefix [Array<Symbol>] Prefix for the source key
51
+ # @param key_prefix [Array<String>] Prefix for the source key
52
52
  # @param value_prefix [Array<String>] Prefix for the target value
53
53
  # @param mapping [Hash] The mapping hash to be updated
54
54
  # @param version [String, nil] The version to create mapping for
55
- # @return [Hash] The updated mapping hash
55
+ # @return [void]
56
56
  def mapping(key_prefix:, value_prefix:, mapping:, version: nil)
57
57
  mapping[(key_prefix + [name]).join("/")] = mapping_value_key(value_prefix:)
58
58
  end
@@ -68,11 +68,11 @@ module Verquest
68
68
 
69
69
  # Create mapping for this field property
70
70
  #
71
- # @param key_prefix [Array<Symbol>] Prefix for the source key
71
+ # @param key_prefix [Array<String>] Prefix for the source key
72
72
  # @param value_prefix [Array<String>] Prefix for the target value
73
73
  # @param mapping [Hash] The mapping hash to be updated
74
74
  # @param version [String, nil] The version to create mapping for
75
- # @return [Hash] The updated mapping hash
75
+ # @return [void]
76
76
  def mapping(key_prefix:, value_prefix:, mapping:, version: nil)
77
77
  mapping[(key_prefix + [name]).join("/")] = mapping_value_key(value_prefix:)
78
78
  end
@@ -84,7 +84,7 @@ module Verquest
84
84
  # @param value_prefix [Array<String>] Prefix for the target value
85
85
  # @param mapping [Hash] The mapping hash to be updated
86
86
  # @param version [String, nil] The version to create mapping for
87
- # @return [Hash] The updated mapping hash
87
+ # @return [void]
88
88
  def mapping(key_prefix:, value_prefix:, mapping:, version: nil)
89
89
  properties.values.each do |property|
90
90
  property.mapping(key_prefix: key_prefix + [name], value_prefix: mapping_value_prefix(value_prefix:), mapping:, version:)