grape-oas 1.0.2 → 1.0.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: e0520e8f719a37c5e33b330a681a9e8da8b1c3a9b921aa245ef328d716bf2bd9
4
- data.tar.gz: aa612915b60e40933256692bb9ad33b2dcf77bc1f68c52d2a9c32cd0befc0bff
3
+ metadata.gz: 683920ecd4af815a8dd0a58b8e36b5feb4be21030566842474f33a1c804fa008
4
+ data.tar.gz: c5c3035f9038878338b836cb09b096164723e012af2066b120b63535f062f5ed
5
5
  SHA512:
6
- metadata.gz: 312f2f0c9dfae0735187b4b7e6ab1969c619e36d58b74e094d990354fdc827a809b49a2ab7ec910bb1e67ac586b9fa4da54742b2f2cd449cca289832e88e5ae3
7
- data.tar.gz: 0e94d7856bc9e94c354b56196f8a77a1689f17281298dc56810de7792fc4d91988fe38f4943a98c9dbf855dae644a36ca01df376a3176210c6d1ef5764ff1db0
6
+ metadata.gz: 820d58e88ffe10735ef72a72c41b4feeaedbcfb6d7a5cb3a3f5f5a50d8c91c6d7db5203fce40289511d9adc57ca605ec1ca3a2f6158ce043e58baf89c30a9d3a
7
+ data.tar.gz: ac0862ed30858730c19d7afc1592a53ca2aef1f14c789c407e535ab482f4ea497ab342b2967730aed4ffdf542497f445766def91d22e19c8810c0dded172b80d
data/CHANGELOG.md CHANGED
@@ -5,7 +5,14 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
-
8
+ ## [1.0.3] - 2025-12-23
9
+
10
+ * Your contribution here
11
+ - [#21](https://github.com/numbata/grape-oas/pull/21): Remove unnecessary require_relative in favor of Zeitwerk autoloadin [@numbata](https://github.com/numbata)
12
+ - [#17](https://github.com/numbata/grape-oas/pull/17): Support for nested rules and predicates in dry-schema introspection [@slbug](https://github.com/slbug)
13
+ - [#20](https://github.com/numbata/grape-oas/pull/20): Use annotation for coverage report [@numbata](https://github.com/numbata)
14
+ - [#18](https://github.com/numbata/grape-oas/pull/18): Support for range in size? predicate `required(:tags).value(:array, size?: 1..10).each(:string)` [@slbug](https://github.com/slbug)
15
+ - [#19](https://github.com/numbata/grape-oas/pull/19): Temporary disable memory profiler workflow for PRs [@numbata](https://github.com/numbata).
9
16
 
10
17
  ## [1.0.2] - 2025-12-15
11
18
 
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "bigdecimal"
4
- require_relative "concerns/type_resolver"
5
4
 
6
5
  module GrapeOAS
7
6
  module ApiModelBuilders
@@ -1,10 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "request_params_support/param_location_resolver"
4
- require_relative "request_params_support/param_schema_builder"
5
- require_relative "request_params_support/schema_enhancer"
6
- require_relative "request_params_support/nested_params_builder"
7
-
8
3
  module GrapeOAS
9
4
  module ApiModelBuilders
10
5
  class RequestParams
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "concerns/tag_builder"
4
- require_relative "concerns/schema_indexer"
5
-
6
3
  module GrapeOAS
7
4
  module Exporter
8
5
  class OAS2Schema
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "concerns/tag_builder"
4
- require_relative "concerns/schema_indexer"
5
-
6
3
  module GrapeOAS
7
4
  module Exporter
8
5
  class OAS3Schema
@@ -1,10 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base"
4
- require_relative "dry_introspector_support/contract_resolver"
5
- require_relative "dry_introspector_support/inheritance_handler"
6
- require_relative "dry_introspector_support/type_schema_builder"
7
-
8
3
  module GrapeOAS
9
4
  module Introspectors
10
5
  # Introspector for Dry::Validation contracts and Dry::Schema.
@@ -92,16 +87,30 @@ module GrapeOAS
92
87
  end
93
88
 
94
89
  def build_flat_schema
95
- rule_constraints = DryIntrospectorSupport::ConstraintExtractor.extract(contract_resolver.contract_schema)
90
+ contract_schema = contract_resolver.contract_schema
91
+
92
+ constraints_by_path, required_by_object_path =
93
+ DryIntrospectorSupport::RuleIndex.build(contract_schema)
94
+
95
+ type_schema_builder.configure_path_aware_mode(constraints_by_path, required_by_object_path)
96
+
96
97
  schema = ApiModel::Schema.new(
97
98
  type: Constants::SchemaTypes::OBJECT,
98
99
  canonical_name: contract_resolver.canonical_name,
99
100
  )
100
101
 
101
- contract_resolver.contract_schema.types.each do |name, dry_type|
102
- constraints = rule_constraints[name]
103
- prop_schema = type_schema_builder.build_schema_for_type(dry_type, constraints)
104
- schema.add_property(name, prop_schema, required: type_schema_builder.required?(dry_type, constraints))
102
+ root_required = required_by_object_path.fetch("", [])
103
+
104
+ contract_schema.types.each do |name, dry_type|
105
+ name_s = name.to_s
106
+ prop_schema = nil
107
+
108
+ type_schema_builder.with_path(name_s) do
109
+ prop_schema = type_schema_builder.build_schema_for_type(dry_type,
110
+ type_schema_builder.constraints_for_current_path,)
111
+ end
112
+
113
+ schema.add_property(name, prop_schema, required: root_required.include?(name_s))
105
114
  end
106
115
 
107
116
  # Use canonical_name as registry key for schema objects (they don't have unique classes),
@@ -17,6 +17,7 @@ module GrapeOAS
17
17
  def extract_range(arg)
18
18
  return arg if arg.is_a?(Range)
19
19
  return arg[1] if arg.is_a?(Array) && arg.first == :range
20
+ return arg[1] if arg.is_a?(Array) && arg.first == :size && arg[1].is_a?(Range)
20
21
 
21
22
  nil
22
23
  end
@@ -68,15 +68,27 @@ module GrapeOAS
68
68
  def build_child_only_schema(parent_contract, type_schema_builder)
69
69
  child_schema = ApiModel::Schema.new(type: Constants::SchemaTypes::OBJECT)
70
70
  parent_keys = parent_contract_types(parent_contract)
71
- rule_constraints = ConstraintExtractor.extract(@contract_resolver.contract_schema)
71
+ contract_schema = @contract_resolver.contract_schema
72
72
 
73
- @contract_resolver.contract_schema.types.each do |name, dry_type|
73
+ constraints_by_path, required_by_object_path =
74
+ RuleIndex.build(contract_schema)
75
+
76
+ type_schema_builder.configure_path_aware_mode(constraints_by_path, required_by_object_path)
77
+ root_required = required_by_object_path.fetch("", [])
78
+
79
+ contract_schema.types.each do |name, dry_type|
74
80
  # Skip inherited properties
75
81
  next if parent_keys.include?(name.to_s)
76
82
 
77
- constraints = rule_constraints[name]
78
- prop_schema = type_schema_builder.build_schema_for_type(dry_type, constraints)
79
- child_schema.add_property(name, prop_schema, required: type_schema_builder.required?(dry_type, constraints))
83
+ name_s = name.to_s
84
+ prop_schema = nil
85
+
86
+ type_schema_builder.with_path(name_s) do
87
+ prop_schema = type_schema_builder.build_schema_for_type(dry_type,
88
+ type_schema_builder.constraints_for_current_path,)
89
+ end
90
+
91
+ child_schema.add_property(name, prop_schema, required: root_required.include?(name_s))
80
92
  end
81
93
 
82
94
  child_schema
@@ -97,10 +97,17 @@ module GrapeOAS
97
97
  end
98
98
 
99
99
  def handle_size(name, args)
100
- min_val = ArgumentExtractor.extract_numeric(args[0])
101
- max_val = ArgumentExtractor.extract_numeric(args[1]) if name == :size?
102
- constraints.min_size = min_val if min_val
103
- constraints.max_size = max_val if max_val
100
+ rng = ArgumentExtractor.extract_range(args.first)
101
+
102
+ if rng
103
+ constraints.min_size = rng.begin if rng.begin
104
+ constraints.max_size = rng.max if rng.end
105
+ else
106
+ min_val = ArgumentExtractor.extract_numeric(args[0])
107
+ max_val = ArgumentExtractor.extract_numeric(args[1]) if name == :size?
108
+ constraints.min_size = min_val if min_val
109
+ constraints.max_size = max_val if max_val
110
+ end
104
111
  end
105
112
 
106
113
  def handle_range(args)
@@ -0,0 +1,196 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "ast_walker"
4
+ require_relative "constraint_extractor"
5
+ require_relative "constraint_merger"
6
+
7
+ module GrapeOAS
8
+ module Introspectors
9
+ module DryIntrospectorSupport
10
+ # Builds path-aware constraint and required field indexes from dry-schema AST
11
+ class RuleIndex
12
+ def initialize(contract_schema)
13
+ @walker = AstWalker.new(ConstraintExtractor::ConstraintSet)
14
+ @merger = ConstraintMerger
15
+ @constraints_by_path = {}
16
+ @required_by_object_path = Hash.new { |h, k| h[k] = {} }
17
+
18
+ build_indexes(contract_schema)
19
+ end
20
+
21
+ def self.build(contract_schema)
22
+ new(contract_schema).to_a
23
+ end
24
+
25
+ def to_a
26
+ [@constraints_by_path, @required_by_object_path.transform_values(&:keys)]
27
+ end
28
+
29
+ private
30
+
31
+ def build_indexes(contract_schema)
32
+ rules = contract_schema.respond_to?(:rules) ? contract_schema.rules : {}
33
+ rules.each_value do |rule|
34
+ ast = rule.respond_to?(:to_ast) ? rule.to_ast : rule
35
+ collect_constraints(ast, [])
36
+ collect_required(ast, [], in_implication_condition: false)
37
+ end
38
+ end
39
+
40
+ def collect_constraints(ast, path)
41
+ return unless ast.is_a?(Array)
42
+
43
+ case ast[0]
44
+ when :key
45
+ key_name, value_ast = parse_key_node(ast)
46
+ return unless key_name && value_ast.is_a?(Array)
47
+
48
+ new_path = path + [key_name]
49
+ apply_node_constraints(value_ast, new_path)
50
+ collect_constraints(value_ast, new_path)
51
+
52
+ when :each
53
+ child = ast[1]
54
+ return unless child.is_a?(Array)
55
+
56
+ item_path = path + ["[]"]
57
+
58
+ # Index constraints that apply to the item schema itself
59
+ apply_node_constraints(child, item_path)
60
+
61
+ # Recurse so nested keys inside the item get their own paths
62
+ collect_constraints(child, item_path)
63
+ else
64
+ ast.each { |child| collect_constraints(child, path) if child.is_a?(Array) }
65
+ end
66
+ end
67
+
68
+ def collect_required(ast, object_path, in_implication_condition:)
69
+ return unless ast.is_a?(Array)
70
+
71
+ case ast[0]
72
+ when :implication
73
+ left, right = ast[1].is_a?(Array) ? ast[1] : [nil, nil]
74
+ collect_required(left, object_path, in_implication_condition: true) if left
75
+ collect_required(right, object_path, in_implication_condition: false) if right
76
+
77
+ when :predicate
78
+ mark_required_if_key_predicate(ast[1], object_path) unless in_implication_condition
79
+
80
+ when :key, :each
81
+ if ast[0] == :key
82
+ key_name, value_ast = parse_key_node(ast)
83
+ if key_name && value_ast.is_a?(Array)
84
+ collect_required(value_ast, object_path + [key_name],
85
+ in_implication_condition: in_implication_condition,)
86
+ end
87
+ elsif ast[1] # :each
88
+ collect_required(ast[1], object_path + ["[]"],
89
+ in_implication_condition: in_implication_condition,)
90
+ end
91
+
92
+ else
93
+ ast.each do |child|
94
+ collect_required(child, object_path, in_implication_condition: in_implication_condition) if child.is_a?(Array)
95
+ end
96
+ end
97
+ end
98
+
99
+ def parse_key_node(ast)
100
+ info = ast[1]
101
+ return [nil, nil] unless info.is_a?(Array) && info.any?
102
+
103
+ key_name = info[0]
104
+ value_ast = info[1] || info[-1]
105
+ [key_name&.to_s, value_ast]
106
+ end
107
+
108
+ def apply_node_constraints(value_ast, path)
109
+ pruned = prune_nested_validations(value_ast)
110
+ return unless pruned
111
+
112
+ constraints = @walker.walk(pruned)
113
+ constraints.required = nil if constraints.respond_to?(:required=)
114
+
115
+ path_key = path.join("/")
116
+ if @constraints_by_path.key?(path_key)
117
+ @merger.merge(@constraints_by_path[path_key], constraints)
118
+ else
119
+ @constraints_by_path[path_key] = constraints
120
+ end
121
+ end
122
+
123
+ def prune_nested_validations(ast)
124
+ return ast unless ast.is_a?(Array)
125
+
126
+ tag = ast[0]
127
+ return ast unless tag.is_a?(Symbol)
128
+
129
+ case tag
130
+ when :each, :key
131
+ nil
132
+
133
+ when :set
134
+ children, wrapped = extract_children(ast)
135
+ pruned = children.filter_map { |c| c.is_a?(Array) ? prune_nested_validations(c) : c }
136
+ return nil if pruned.empty?
137
+
138
+ # rewrite set -> and, preserve wrapper style
139
+ wrapped ? [:and, pruned] : [:and, *pruned]
140
+
141
+ when :and, :or, :rule
142
+ children, wrapped = extract_children(ast)
143
+ pruned = children.filter_map { |c| c.is_a?(Array) ? prune_nested_validations(c) : c }
144
+ return nil if pruned.empty?
145
+
146
+ wrapped ? [tag, pruned] : [tag, *pruned]
147
+
148
+ when :implication
149
+ pair = ast[1]
150
+ return ast unless pair.is_a?(Array) && pair.size >= 2
151
+
152
+ left = pair[0].is_a?(Array) ? prune_nested_validations(pair[0]) : pair[0]
153
+ right = pair[1].is_a?(Array) ? prune_nested_validations(pair[1]) : pair[1]
154
+ [:implication, [left, right]]
155
+
156
+ when :not
157
+ child = ast[1]
158
+ child = prune_nested_validations(child) if child.is_a?(Array)
159
+ [:not, child]
160
+
161
+ else
162
+ ast
163
+ end
164
+ end
165
+
166
+ def extract_children(ast)
167
+ # handles both shapes:
168
+ # [:and, [node1, node2]]
169
+ # [:and, node1, node2]
170
+ payload = ast[1]
171
+
172
+ if payload.is_a?(Array) && !payload.empty? && payload.all? { |x| x.is_a?(Array) && x[0].is_a?(Symbol) }
173
+ [payload, true] # wrapped list
174
+ else
175
+ [ast[1..], false] # splatted
176
+ end
177
+ end
178
+
179
+ def mark_required_if_key_predicate(pred, object_path)
180
+ return unless pred.is_a?(Array) && pred[0] == :key?
181
+
182
+ name = extract_key_name(pred)
183
+ @required_by_object_path[object_path.join("/")][name] = true if name
184
+ end
185
+
186
+ def extract_key_name(pred_node)
187
+ args = pred_node[1]
188
+ return nil unless args.is_a?(Array)
189
+
190
+ name_pair = args.find { |x| x.is_a?(Array) && x[0] == :name }
191
+ name_pair&.dig(1)&.to_s
192
+ end
193
+ end
194
+ end
195
+ end
196
+ end
@@ -11,7 +11,39 @@ module GrapeOAS
11
11
  ConstraintSet = ConstraintExtractor::ConstraintSet
12
12
 
13
13
  def initialize
14
- # Stateless builder - no initialization needed
14
+ @path_stack = []
15
+ @constraints_by_path = nil
16
+ @required_by_object_path = nil
17
+ end
18
+
19
+ def configure_path_aware_mode(constraints_by_path, required_by_object_path)
20
+ @path_stack = []
21
+ @constraints_by_path = constraints_by_path
22
+ @required_by_object_path = required_by_object_path
23
+ end
24
+
25
+ def with_path(part)
26
+ @path_stack << part
27
+ yield
28
+ ensure
29
+ @path_stack.pop
30
+ end
31
+
32
+ def current_object_path
33
+ @path_stack.join("/")
34
+ end
35
+
36
+ def constraints_for_current_path
37
+ return nil unless @constraints_by_path
38
+
39
+ @constraints_by_path[current_object_path]
40
+ end
41
+
42
+ def required_keys_for_current_object
43
+ return nil unless @required_by_object_path
44
+
45
+ # In path-aware mode we rely entirely on rule-index requiredness
46
+ @required_by_object_path[current_object_path] || []
15
47
  end
16
48
 
17
49
  # Builds a schema for a Dry type.
@@ -20,7 +52,7 @@ module GrapeOAS
20
52
  # @param constraints [ConstraintSet, nil] extracted constraints
21
53
  # @return [ApiModel::Schema] the built schema
22
54
  def build_schema_for_type(dry_type, constraints = nil)
23
- constraints ||= ConstraintSet.new(unhandled_predicates: [])
55
+ constraints ||= constraints_for_current_path || ConstraintSet.new(unhandled_predicates: [])
24
56
  meta = dry_type.respond_to?(:meta) ? dry_type.meta : {}
25
57
 
26
58
  # Check for Sum type first (TypeA | TypeB) -> anyOf
@@ -29,6 +61,10 @@ module GrapeOAS
29
61
  # Check for Hash schema type (nested schemas like .hash(SomeSchema))
30
62
  return build_hash_schema(dry_type) if hash_schema_type?(dry_type)
31
63
 
64
+ # Check for object schema (unwrapped hash with keys)
65
+ unwrapped = TypeUnwrapper.unwrap(dry_type)
66
+ return build_object_schema(unwrapped) if unwrapped.respond_to?(:keys)
67
+
32
68
  primitive, member = TypeUnwrapper.derive_primitive_and_member(dry_type)
33
69
  enum_vals = extract_enum_from_type(dry_type)
34
70
 
@@ -59,6 +95,36 @@ module GrapeOAS
59
95
 
60
96
  private
61
97
 
98
+ def build_object_schema(unwrapped_schema_type)
99
+ schema = ApiModel::Schema.new(type: Constants::SchemaTypes::OBJECT)
100
+ required_keys = required_keys_for_current_object
101
+
102
+ # Dry::Types::Schema does not have each_key, so we disable the cop here
103
+ unwrapped_schema_type.keys.each do |key| # rubocop:disable Style/HashEachMethods
104
+ key_name = key.respond_to?(:name) ? key.name.to_s : key.to_s
105
+ key_type = key.respond_to?(:type) ? key.type : nil
106
+
107
+ prop_schema = nil
108
+ with_path(key_name) do
109
+ prop_schema = if key_type
110
+ build_schema_for_type(key_type, constraints_for_current_path)
111
+ else
112
+ default_string_schema
113
+ end
114
+ end
115
+
116
+ is_required = if required_keys
117
+ required_keys.include?(key_name)
118
+ else
119
+ key.respond_to?(:required?) ? key.required? : false
120
+ end
121
+
122
+ schema.add_property(key_name, prop_schema, required: is_required)
123
+ end
124
+
125
+ schema
126
+ end
127
+
62
128
  def build_any_of_schema(sum_type)
63
129
  types = TypeUnwrapper.extract_sum_types(sum_type)
64
130
 
@@ -86,29 +152,35 @@ module GrapeOAS
86
152
  end
87
153
 
88
154
  def build_hash_schema(dry_type)
89
- schema = ApiModel::Schema.new(type: Constants::SchemaTypes::OBJECT)
90
155
  unwrapped = TypeUnwrapper.unwrap(dry_type)
91
156
 
92
- return schema unless unwrapped.respond_to?(:keys)
157
+ # Delegate to the same path-aware logic as regular object schemas.
158
+ # This ensures nested rule constraints (e.g. max_size?, gteq?, format?) are applied
159
+ # to properties inside `.hash do ... end` blocks.
160
+ return ApiModel::Schema.new(type: Constants::SchemaTypes::OBJECT) unless unwrapped.respond_to?(:keys)
93
161
 
94
- # Dry::Schema keys method returns an array of Key objects, not a Hash
95
- schema_keys = unwrapped.keys
96
- schema_keys.each do |key|
97
- key_name = key.respond_to?(:name) ? key.name.to_s : key.to_s
98
- key_type = key.respond_to?(:type) ? key.type : nil
99
-
100
- prop_schema = key_type ? build_schema_for_type(key_type) : default_string_schema
101
- req = key.respond_to?(:required?) ? key.required? : true
102
- schema.add_property(key_name, prop_schema, required: req)
103
- end
104
-
105
- schema
162
+ build_object_schema(unwrapped)
106
163
  end
107
164
 
108
165
  def build_base_schema(primitive, member)
109
166
  if primitive == Array
110
- items_schema = member ? build_schema_for_type(member) : default_string_schema
167
+ items_schema = nil
168
+
169
+ with_path("[]") do
170
+ if member
171
+ unwrapped = TypeUnwrapper.unwrap(member)
172
+ items_schema = if unwrapped.respond_to?(:keys)
173
+ build_object_schema(unwrapped)
174
+ else
175
+ build_schema_for_type(member, constraints_for_current_path)
176
+ end
177
+ else
178
+ items_schema = default_string_schema
179
+ end
180
+ end
181
+
111
182
  ApiModel::Schema.new(type: Constants::SchemaTypes::ARRAY, items: items_schema)
183
+
112
184
  else
113
185
  build_schema_for_primitive(primitive)
114
186
  end
@@ -1,13 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base"
4
- require_relative "../api_model_builders/concerns/type_resolver"
5
- require_relative "entity_introspector_support/cycle_tracker"
6
- require_relative "entity_introspector_support/discriminator_handler"
7
- require_relative "entity_introspector_support/inheritance_builder"
8
- require_relative "entity_introspector_support/property_extractor"
9
- require_relative "entity_introspector_support/exposure_processor"
10
-
11
3
  module GrapeOAS
12
4
  module Introspectors
13
5
  # Introspector for Grape::Entity classes.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GrapeOAS
4
- VERSION = "1.0.2"
4
+ VERSION = "1.0.3"
5
5
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grape-oas
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrei Subbota
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2025-12-15 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: grape
@@ -114,6 +113,7 @@ files:
114
113
  - lib/grape_oas/introspectors/dry_introspector_support/contract_resolver.rb
115
114
  - lib/grape_oas/introspectors/dry_introspector_support/inheritance_handler.rb
116
115
  - lib/grape_oas/introspectors/dry_introspector_support/predicate_handler.rb
116
+ - lib/grape_oas/introspectors/dry_introspector_support/rule_index.rb
117
117
  - lib/grape_oas/introspectors/dry_introspector_support/type_schema_builder.rb
118
118
  - lib/grape_oas/introspectors/dry_introspector_support/type_unwrapper.rb
119
119
  - lib/grape_oas/introspectors/entity_introspector.rb
@@ -136,7 +136,6 @@ metadata:
136
136
  documentation_uri: https://github.com/numbata/grape-oas#readme
137
137
  bug_tracker_uri: https://github.com/numbata/grape-oas/issues
138
138
  rubygems_mfa_required: 'true'
139
- post_install_message:
140
139
  rdoc_options: []
141
140
  require_paths:
142
141
  - lib
@@ -151,8 +150,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
151
150
  - !ruby/object:Gem::Version
152
151
  version: '0'
153
152
  requirements: []
154
- rubygems_version: 3.5.22
155
- signing_key:
153
+ rubygems_version: 3.6.8
156
154
  specification_version: 4
157
155
  summary: OpenAPI (Swagger) v2 and v3 documentation for Grape APIs
158
156
  test_files: []