rspec-openapi 0.24.0 → 0.25.1

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: df4214dc6ec5b88e9418ccc5e0c7cc86b69fbe90ec960faba1c48db8c5a5885d
4
- data.tar.gz: f4de4de889d2e8df45173ce55c3e010e7838fa32b7f4af20435df7ba01b03868
3
+ metadata.gz: fdb09465b214320cfd32bf020363827d17c3346d48cb7e724effc99df6bdf722
4
+ data.tar.gz: 9947b0df4b2af301473ff89cd52cd035d8a4cb129bd83f8aacef2fc4df107cd5
5
5
  SHA512:
6
- metadata.gz: 908be0ef51bad71044644c4e3abaf6dccf54d712f489963afd4b7e05a4cdbfdb6848536ed1aa9004e38bf1b85f0ebe79da365368a7f0559f291aa29416e54040
7
- data.tar.gz: 542c5ed74819337e5cddcd244792aab84084afc9a43e81cb2b73e3bbb8eff9056ed3b62f5354d4421a6c3490e8356b16366b22da30c317989efcce757e77d714
6
+ metadata.gz: f17329553b8d2a02b196faba4ac592826557acb79648f364994614759e762608241d1310be6080873703af3b7f45d111db3509302f9ec22270c6c3c849bbd180
7
+ data.tar.gz: dd616bc6570e99d2b6439d4003bcadcdc9881613c6f4ba0428191ceef2c7c73dd185c5e385ea6c8af271f98d5c67d3e597062052d2c3bd7c59d1c0f6e75f0deb
@@ -4,7 +4,7 @@ on:
4
4
  workflow_dispatch:
5
5
  inputs:
6
6
  version:
7
- description: 'Version to release (e.g. 0.24.1 or 0.25.0)'
7
+ description: 'Version to release (e.g. 0.25.2 or 0.26.0)'
8
8
  required: true
9
9
 
10
10
  jobs:
@@ -42,7 +42,7 @@ jobs:
42
42
  - run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
43
43
  name: codecov-action@v4 workaround
44
44
  - name: Upload coverage reports
45
- uses: codecov/codecov-action@v5
45
+ uses: codecov/codecov-action@v6
46
46
  if: matrix.coverage == 'coverage'
47
47
  with:
48
48
  fail_ci_if_error: true
data/Gemfile CHANGED
@@ -24,7 +24,7 @@ gem 'concurrent-ruby', '1.3.4'
24
24
  gem 'roda'
25
25
 
26
26
  gem 'rails-dom-testing', '~> 2.2'
27
- gem 'rspec-rails'
27
+ gem 'rspec-rails', '>= 5.0'
28
28
 
29
29
  group :test do
30
30
  gem 'simplecov', git: 'https://github.com/exoego/simplecov.git', branch: 'branch-fix'
data/README.md CHANGED
@@ -366,6 +366,87 @@ Some examples' attributes can be overwritten via RSpec metadata options. Example
366
366
 
367
367
  **NOTE**: `description` key will override also the one provided by `RSpec::OpenAPI.description_builder` method.
368
368
 
369
+ ### Enum Support
370
+
371
+ You can specify enum values for string properties that should have a fixed set of allowed values. Since enums cannot be reliably inferred from test data, you can define them via the `enum` metadata option:
372
+
373
+ ```rb
374
+ it 'returns user status', openapi: {
375
+ enum: {
376
+ 'status' => %w[active inactive suspended],
377
+ },
378
+ } do
379
+ get '/users/1'
380
+ expect(response.status).to eq(200)
381
+ end
382
+ ```
383
+
384
+ This generates:
385
+
386
+ ```yaml
387
+ schema:
388
+ type: object
389
+ properties:
390
+ status:
391
+ type: string
392
+ enum:
393
+ - active
394
+ - inactive
395
+ - suspended
396
+ ```
397
+
398
+ #### Nested Paths
399
+
400
+ For nested objects, use dot notation to specify the path:
401
+
402
+ ```rb
403
+ it 'returns user with role', openapi: {
404
+ enum: {
405
+ 'status' => %w[active inactive],
406
+ 'user.role' => %w[admin user guest],
407
+ },
408
+ } do
409
+ get '/teams/1'
410
+ # Response: { "status": "active", "user": { "name": "John", "role": "admin" } }
411
+ expect(response.status).to eq(200)
412
+ end
413
+ ```
414
+
415
+ #### Array Items
416
+
417
+ For properties inside array items, use the array property name followed by the item property:
418
+
419
+ ```rb
420
+ it 'returns items with status', openapi: {
421
+ enum: {
422
+ 'items.status' => %w[pending completed failed],
423
+ 'items.priority' => %w[high medium low],
424
+ },
425
+ } do
426
+ get '/tasks'
427
+ # Response: { "items": [{ "id": 1, "status": "pending", "priority": "high" }] }
428
+ expect(response.status).to eq(200)
429
+ end
430
+ ```
431
+
432
+ #### Request vs Response Enums
433
+
434
+ By default, `enum` applies to both request and response bodies. If you need different enum values for request and response, use `request_enum` and `response_enum`:
435
+
436
+ ```rb
437
+ it 'creates a task', openapi: {
438
+ request_enum: {
439
+ 'action' => %w[create update delete],
440
+ },
441
+ response_enum: {
442
+ 'status' => %w[pending processing completed],
443
+ },
444
+ } do
445
+ post '/tasks', params: { action: 'create', name: 'New Task' }
446
+ expect(response.status).to eq(201)
447
+ end
448
+ ```
449
+
369
450
  ### Multiple Examples Mode
370
451
 
371
452
  You can generate multiple named examples for the same endpoint using `example_mode`:
@@ -3,6 +3,8 @@
3
3
  require_relative 'hash_helper'
4
4
 
5
5
  class << RSpec::OpenAPI::ComponentsUpdater = Object.new
6
+ SCHEMA_REF_PREFIX = '#/components/schemas/'
7
+
6
8
  # @param [Hash] base
7
9
  # @param [Hash] fresh
8
10
  def update!(base, fresh)
@@ -30,7 +32,7 @@ class << RSpec::OpenAPI::ComponentsUpdater = Object.new
30
32
  # Skip if the property using $ref is not found in the parent schema. The property may be removed.
31
33
  next if nested_schema.nil?
32
34
 
33
- schema_name = base.dig(*paths)&.gsub('#/components/schemas/', '')&.to_sym
35
+ schema_name = extract_schema_name(base.dig(*paths))&.to_sym
34
36
  fresh_schemas[schema_name] ||= {}
35
37
  RSpec::OpenAPI::SchemaMerger.merge!(fresh_schemas[schema_name], nested_schema)
36
38
  end
@@ -44,8 +46,8 @@ class << RSpec::OpenAPI::ComponentsUpdater = Object.new
44
46
  def build_fresh_schemas(references, base, fresh)
45
47
  references.inject({}) do |acc, paths|
46
48
  ref_link = dig_schema(base, paths)[:$ref]
47
- schema_name = ref_link.to_s.gsub('#/components/schemas/', '')
48
- schema_body = dig_schema(fresh, paths.reject { |path| path.is_a?(Integer) })
49
+ schema_name = extract_schema_name(ref_link)
50
+ schema_body = dig_schema(fresh, paths.grep_v(Integer))
49
51
 
50
52
  RSpec::OpenAPI::SchemaMerger.merge!(acc, { schema_name => schema_body })
51
53
  end
@@ -81,18 +83,29 @@ class << RSpec::OpenAPI::ComponentsUpdater = Object.new
81
83
  # Reject already-generated schemas to reduce unnecessary loop
82
84
  nested_refs.reject do |paths|
83
85
  ref_link = base.dig(*paths)
84
- schema_name = ref_link.gsub('#/components/schemas/', '')
86
+ schema_name = extract_schema_name(ref_link)
85
87
  generated_names.include?(schema_name)
86
88
  end
87
89
  end
88
90
 
89
91
  def find_one_of_refs(base, paths)
90
- dig_schema(base, paths)&.dig(:oneOf)&.map&.with_index do |schema, index|
91
- paths + [index] if schema&.dig(:$ref)&.start_with?('#/components/schemas/')
92
- end&.compact
92
+ one_of = dig_schema(base, paths)&.dig(:oneOf)
93
+ return unless one_of
94
+
95
+ one_of.each_with_index.filter_map do |schema, index|
96
+ paths + [index] if schema_ref?(schema&.dig(:$ref))
97
+ end
93
98
  end
94
99
 
95
100
  def find_object_refs(base, paths)
96
- [paths] if dig_schema(base, paths)&.dig(:$ref)&.start_with?('#/components/schemas/')
101
+ [paths] if schema_ref?(dig_schema(base, paths)&.dig(:$ref))
102
+ end
103
+
104
+ def extract_schema_name(ref_link)
105
+ ref_link&.delete_prefix(SCHEMA_REF_PREFIX)
106
+ end
107
+
108
+ def schema_ref?(ref_link)
109
+ ref_link&.start_with?(SCHEMA_REF_PREFIX)
97
110
  end
98
111
  end
@@ -38,13 +38,11 @@ end
38
38
  InspectorAnalyzer = Inspector.new
39
39
 
40
40
  # Add default parameter to load inspector before test cases run
41
- module InspectorAnalyzerPrepender
41
+ Hanami::Slice::ClassMethods.prepend(Module.new do
42
42
  def router(inspector: InspectorAnalyzer)
43
43
  super
44
44
  end
45
- end
46
-
47
- Hanami::Slice::ClassMethods.prepend(InspectorAnalyzerPrepender)
45
+ end)
48
46
 
49
47
  # Extractor for hanami
50
48
  class << RSpec::OpenAPI::Extractors::Hanami = Object.new
@@ -57,17 +55,17 @@ class << RSpec::OpenAPI::Extractors::Hanami = Object.new
57
55
  return RSpec::OpenAPI::Extractors::Rack.request_attributes(request, example) unless route.routable?
58
56
 
59
57
  summary, tags, formats, operation_id, required_request_params, security, description, deprecated, example_mode,
60
- example_key, example_name = SharedExtractor.attributes(example)
58
+ example_key, example_name, response_enum, request_enum = SharedExtractor.attributes(example)
61
59
 
62
60
  path = request.path
63
61
 
64
62
  raw_path_params = route.params
65
63
 
66
- result = InspectorAnalyzer.call(request.method, add_id(path, route))
64
+ result = InspectorAnalyzer.call(request.method, replace_path_params(path, route, '/:%{key}'))
67
65
 
68
66
  summary ||= result[:summary]
69
67
  tags ||= result[:tags]
70
- path = add_openapi_id(path, route)
68
+ path = replace_path_params(path, route, '/{%{key}}')
71
69
 
72
70
  raw_path_params = raw_path_params.slice(*(raw_path_params.keys - RSpec::OpenAPI.ignored_path_params))
73
71
 
@@ -85,6 +83,8 @@ class << RSpec::OpenAPI::Extractors::Hanami = Object.new
85
83
  example_mode,
86
84
  example_key,
87
85
  example_name,
86
+ response_enum,
87
+ request_enum,
88
88
  ]
89
89
  end
90
90
 
@@ -99,21 +99,11 @@ class << RSpec::OpenAPI::Extractors::Hanami = Object.new
99
99
 
100
100
  private
101
101
 
102
- def add_id(path, route)
103
- return path if route.params.empty?
104
-
105
- route.params.each_pair do |key, value|
106
- path = path.sub("/#{value}", "/:#{key}")
107
- end
108
-
109
- path
110
- end
111
-
112
- def add_openapi_id(path, route)
102
+ def replace_path_params(path, route, format)
113
103
  return path if route.params.empty?
114
104
 
115
105
  route.params.each_pair do |key, value|
116
- path = path.sub("/#{value}", "/{#{key}}")
106
+ path = path.sub("/#{value}", format % { key: key })
117
107
  end
118
108
 
119
109
  path
@@ -7,7 +7,7 @@ class << RSpec::OpenAPI::Extractors::Rack = Object.new
7
7
  # @return Array
8
8
  def request_attributes(request, example)
9
9
  summary, tags, formats, operation_id, required_request_params, security, description, deprecated, example_mode,
10
- example_key, example_name = SharedExtractor.attributes(example)
10
+ example_key, example_name, response_enum, request_enum = SharedExtractor.attributes(example)
11
11
 
12
12
  raw_path_params = request.path_parameters
13
13
  path = request.path
@@ -27,6 +27,8 @@ class << RSpec::OpenAPI::Extractors::Rack = Object.new
27
27
  example_mode,
28
28
  example_key,
29
29
  example_name,
30
+ response_enum,
31
+ request_enum,
30
32
  ]
31
33
  end
32
34
 
@@ -17,7 +17,7 @@ class << RSpec::OpenAPI::Extractors::Rails = Object.new
17
17
  raise "No route matched for #{fixed_request.request_method} #{fixed_request.path_info}" if route.nil?
18
18
 
19
19
  summary, tags, formats, operation_id, required_request_params, security, description, deprecated, example_mode,
20
- example_key, example_name = SharedExtractor.attributes(example)
20
+ example_key, example_name, response_enum, request_enum = SharedExtractor.attributes(example)
21
21
 
22
22
  raw_path_params = request.path_parameters
23
23
 
@@ -43,6 +43,8 @@ class << RSpec::OpenAPI::Extractors::Rails = Object.new
43
43
  example_mode,
44
44
  example_key,
45
45
  example_name,
46
+ response_enum,
47
+ request_enum,
46
48
  ]
47
49
  end
48
50
 
@@ -20,8 +20,20 @@ class SharedExtractor
20
20
  example_key = RSpec::OpenAPI::ExampleKey.normalize(raw_example_key)
21
21
  example_key = 'default' if example_key.nil? || example_key.empty?
22
22
 
23
+ # Enum support: response_enum and request_enum can override the general enum
24
+ base_enum = normalize_enum(metadata[:enum])
25
+ response_enum = normalize_enum(metadata[:response_enum]) || base_enum
26
+ request_enum = normalize_enum(metadata[:request_enum]) || base_enum
27
+
23
28
  [summary, tags, formats, operation_id, required_request_params, security, description, deprecated, example_mode,
24
- example_key, example_name,]
29
+ example_key, example_name, response_enum, request_enum,]
30
+ end
31
+
32
+ def self.normalize_enum(enum_hash)
33
+ return nil if enum_hash.nil? || enum_hash.empty?
34
+
35
+ # Convert all keys to strings for consistent lookup
36
+ enum_hash.transform_keys(&:to_s)
25
37
  end
26
38
 
27
39
  def self.merge_openapi_metadata(metadata)
@@ -26,5 +26,7 @@ RSpec::OpenAPI::Record = Struct.new(
26
26
  :response_headers, # @param [Array] - [["header_key1", "header_value1"], ["header_key2", "header_value2"]]
27
27
  :response_content_type, # @param [String] - "application/json"
28
28
  :response_content_disposition, # @param [String] - "inline"
29
+ :response_enum, # @param [Hash] - {"status" => ["active", "inactive"], "user.role" => ["admin", "user"]}
30
+ :request_enum, # @param [Hash] - {"type" => ["create", "update"]}
29
31
  keyword_init: true,
30
32
  )
@@ -12,8 +12,9 @@ class << RSpec::OpenAPI::RecordBuilder = Object.new
12
12
  return if request.nil?
13
13
 
14
14
  title = RSpec::OpenAPI.title.then { |t| t.is_a?(Proc) ? t.call(example) : t }
15
- path, summary, tags, operation_id, required_request_params, raw_path_params, description, security, deprecated,
16
- formats, example_mode, example_key, example_name = extractor.request_attributes(request, example)
15
+ path, summary, tags, operation_id, required_request_params, raw_path_params,
16
+ description, security, deprecated, formats, example_mode, example_key,
17
+ example_name, response_enum, request_enum = extractor.request_attributes(request, example)
17
18
 
18
19
  return if RSpec::OpenAPI.ignored_paths.any? { |ignored_path| path.match?(ignored_path) }
19
20
 
@@ -45,6 +46,8 @@ class << RSpec::OpenAPI::RecordBuilder = Object.new
45
46
  example_mode: example_mode,
46
47
  example_key: example_key,
47
48
  example_name: example_name,
49
+ response_enum: response_enum,
50
+ request_enum: request_enum,
48
51
  ).freeze
49
52
  end
50
53
 
@@ -47,7 +47,7 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
47
47
 
48
48
  def build_content(disposition, record)
49
49
  content_type = normalize_content_type(record.response_content_type)
50
- schema = build_property(record.response_body, disposition: disposition, record: record)
50
+ schema = build_property(record.response_body, disposition: disposition, record: record, context: :response)
51
51
 
52
52
  # If examples are globally disabled, always return schema-only content.
53
53
  return { content_type => { schema: schema }.compact } unless example_enabled?(record)
@@ -121,7 +121,7 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
121
121
  name: build_parameter_name(key, value),
122
122
  in: 'path',
123
123
  required: true,
124
- schema: build_property(try_cast(value), key: key, record: record),
124
+ schema: build_property(try_cast(value), key: key, record: record, path: key.to_s, context: :request),
125
125
  example: (try_cast(value) if example_enabled?(record)),
126
126
  }.compact
127
127
  end
@@ -131,7 +131,7 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
131
131
  name: key,
132
132
  in: 'query',
133
133
  required: record.required_request_params.include?(key),
134
- schema: build_property(try_cast(value), key: key, record: record),
134
+ schema: build_property(try_cast(value), key: key, record: record, path: key.to_s, context: :request),
135
135
  example: (try_cast(value) if example_enabled?(record)),
136
136
  }.compact
137
137
  end
@@ -141,7 +141,7 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
141
141
  name: build_parameter_name(key, value),
142
142
  in: 'header',
143
143
  required: true,
144
- schema: build_property(try_cast(value), key: key, record: record),
144
+ schema: build_property(try_cast(value), key: key, record: record, path: key.to_s, context: :request),
145
145
  example: (try_cast(value) if example_enabled?(record)),
146
146
  }.compact
147
147
  end
@@ -158,7 +158,7 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
158
158
 
159
159
  record.response_headers.each do |key, value|
160
160
  headers[key] = {
161
- schema: build_property(try_cast(value), key: key, record: record),
161
+ schema: build_property(try_cast(value), key: key, record: record, path: key.to_s, context: :response),
162
162
  }.compact
163
163
  end
164
164
 
@@ -196,29 +196,31 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
196
196
  {
197
197
  content: {
198
198
  normalize_content_type(record.request_content_type) => {
199
- schema: build_property(record.request_params, record: record),
199
+ schema: build_property(record.request_params, record: record, context: :request),
200
200
  example: (build_example(record.request_params) if example_enabled?(record)),
201
201
  }.compact,
202
202
  },
203
203
  }
204
204
  end
205
205
 
206
- def build_property(value, disposition: nil, key: nil, record: nil)
206
+ def build_property(value, disposition: nil, key: nil, record: nil, path: nil, context: nil)
207
207
  format = disposition ? 'binary' : infer_format(key, record)
208
+ enum = infer_enum(path, record, context)
208
209
 
209
- property = build_type(value, format: format)
210
+ property = build_type(value, format: format, enum: enum)
210
211
 
211
212
  case value
212
213
  when Array
213
214
  property[:items] = if value.empty?
214
215
  {} # unknown
215
216
  else
216
- build_array_items_schema(value, record: record)
217
+ build_array_items_schema(value, record: record, path: path, context: context)
217
218
  end
218
219
  when Hash
219
220
  property[:properties] = {}.tap do |properties|
220
- value.each do |key, v|
221
- properties[key] = build_property(v, record: record, key: key)
221
+ value.each do |k, v|
222
+ child_path = path ? "#{path}.#{k}" : k.to_s
223
+ properties[k] = build_property(v, record: record, key: k, path: child_path, context: context)
222
224
  end
223
225
  end
224
226
  property = enrich_with_required_keys(property)
@@ -226,29 +228,34 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
226
228
  property
227
229
  end
228
230
 
229
- def build_type(value, format: nil)
230
- return { type: 'string', format: format } if format
231
-
232
- case value
233
- when String
234
- { type: 'string' }
235
- when Integer
236
- { type: 'integer' }
237
- when Float
238
- { type: 'number', format: 'float' }
239
- when TrueClass, FalseClass
240
- { type: 'boolean' }
241
- when Array
242
- { type: 'array' }
243
- when Hash
244
- { type: 'object' }
245
- when ActionDispatch::Http::UploadedFile
246
- { type: 'string', format: 'binary' }
247
- when NilClass
248
- { nullable: true }
249
- else
250
- raise NotImplementedError, "type detection is not implemented for: #{value.inspect}"
251
- end
231
+ def build_type(value, format: nil, enum: nil)
232
+ result = if format
233
+ { type: 'string', format: format }
234
+ else
235
+ case value
236
+ when String
237
+ { type: 'string' }
238
+ when Integer
239
+ { type: 'integer' }
240
+ when Float
241
+ { type: 'number', format: 'float' }
242
+ when TrueClass, FalseClass
243
+ { type: 'boolean' }
244
+ when Array
245
+ { type: 'array' }
246
+ when Hash
247
+ { type: 'object' }
248
+ when ActionDispatch::Http::UploadedFile
249
+ { type: 'string', format: 'binary' }
250
+ when NilClass
251
+ { nullable: true }
252
+ else
253
+ raise NotImplementedError, "type detection is not implemented for: #{value.inspect}"
254
+ end
255
+ end
256
+
257
+ result[:enum] = enum if enum
258
+ result
252
259
  end
253
260
 
254
261
  def infer_format(key, record)
@@ -257,6 +264,16 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
257
264
  record.formats[key]
258
265
  end
259
266
 
267
+ def infer_enum(path, record, context)
268
+ return nil if !path || !record
269
+
270
+ enum_hash = context == :request ? record.request_enum : record.response_enum
271
+ return nil unless enum_hash
272
+
273
+ # Keys are already normalized to strings by SharedExtractor.normalize_enum
274
+ enum_hash[path.to_s]
275
+ end
276
+
260
277
  # Convert an always-String param to an appropriate type
261
278
  def try_cast(value)
262
279
  Integer(value)
@@ -302,16 +319,15 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
302
319
  content_type&.sub(/;.+\z/, '')
303
320
  end
304
321
 
305
- def normalize_content_disposition(content_disposition)
306
- content_disposition&.sub(/;.+\z/, '')
307
- end
322
+ # Same logic as normalize_content_type – strips header parameters after ';'
323
+ alias normalize_content_disposition normalize_content_type
308
324
 
309
- def build_array_items_schema(array, record: nil)
325
+ def build_array_items_schema(array, record: nil, path: nil, context: nil)
310
326
  return {} if array.empty?
311
- return build_property(array.first, record: record) if array.size == 1
312
- return build_property(array.first, record: record) unless array.all? { |item| item.is_a?(Hash) }
327
+ return build_property(array.first, record: record, path: path, context: context) if array.size == 1
328
+ return build_property(array.first, record: record, path: path, context: context) unless array.all?(Hash)
313
329
 
314
- all_schemas = array.map { |item| build_property(item, record: record) }
330
+ all_schemas = array.map { |item| build_property(item, record: record, path: path, context: context) }
315
331
  merged_schema = all_schemas.first.dup
316
332
  merged_schema[:properties] = {}
317
333
 
@@ -70,6 +70,8 @@ class << RSpec::OpenAPI::SchemaCleaner = Object.new
70
70
  ]
71
71
  paths_to_objects.each do |path|
72
72
  parent = base.dig(*path.take(path.length - 1))
73
+ next unless parent
74
+
73
75
  # "required" array must not be present if empty
74
76
  parent.delete(:required) if parent[:required] && parent[:required].empty?
75
77
  end
@@ -9,6 +9,8 @@ class << RSpec::OpenAPI::SchemaMerger = Object.new
9
9
  merge_schema!(base, spec)
10
10
  end
11
11
 
12
+ SIMILARITY_THRESHOLD = 0.5
13
+
12
14
  private
13
15
 
14
16
  # Not doing `base.replace(deep_merge(base, spec))` to preserve key orders.
@@ -121,13 +123,11 @@ class << RSpec::OpenAPI::SchemaMerger = Object.new
121
123
  end
122
124
 
123
125
  def build_unique_params(base, key)
124
- base[key].each_with_object({}) do |parameter, hash|
125
- hash[[parameter[:name], parameter[:in]]] = parameter
126
+ base[key].to_h do |parameter|
127
+ [[parameter[:name], parameter[:in]], parameter]
126
128
  end
127
129
  end
128
130
 
129
- SIMILARITY_THRESHOLD = 0.5
130
-
131
131
  # Normalize example/examples fields when there's a conflict
132
132
  # OpenAPI spec doesn't allow both example and examples in the same object
133
133
  def normalize_example_fields!(base, spec)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RSpec
4
4
  module OpenAPI
5
- VERSION = '0.24.0'
5
+ VERSION = '0.25.1'
6
6
  end
7
7
  end
data/lib/rspec/openapi.rb CHANGED
@@ -33,7 +33,7 @@ module RSpec::OpenAPI
33
33
  @comment = nil
34
34
  @enable_example = true
35
35
  @enable_example_summary = true
36
- @description_builder = ->(example) { example.description }
36
+ @description_builder = :description.to_proc
37
37
  @example_name_builder = :description.to_proc
38
38
  @summary_builder = ->(example) { example.metadata[:summary] }
39
39
  @tags_builder = ->(example) { example.metadata[:tags] }
@@ -31,5 +31,4 @@ Gem::Specification.new do |spec|
31
31
  spec.add_dependency 'actionpack', '>= 5.2.0'
32
32
  spec.add_dependency 'rails-dom-testing'
33
33
  spec.add_dependency 'rspec-core'
34
- spec.metadata['rubygems_mfa_required'] = 'true'
35
34
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-openapi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.24.0
4
+ version: 0.25.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Takashi Kokubun
@@ -112,7 +112,7 @@ licenses:
112
112
  metadata:
113
113
  homepage_uri: https://github.com/exoego/rspec-openapi
114
114
  source_code_uri: https://github.com/exoego/rspec-openapi
115
- changelog_uri: https://github.com/exoego/rspec-openapi/releases/tag/v0.24.0
115
+ changelog_uri: https://github.com/exoego/rspec-openapi/releases/tag/v0.25.1
116
116
  rubygems_mfa_required: 'true'
117
117
  rdoc_options: []
118
118
  require_paths:
@@ -128,7 +128,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
128
128
  - !ruby/object:Gem::Version
129
129
  version: '0'
130
130
  requirements: []
131
- rubygems_version: 4.0.3
131
+ rubygems_version: 4.0.6
132
132
  specification_version: 4
133
133
  summary: Generate OpenAPI schema from RSpec request specs
134
134
  test_files: []