Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,6 @@ jobs:
strategy:
matrix:
entry:
- { ruby: '3.1', grape: '1.8.0' }
- { ruby: '3.2', grape: '1.8.0' }
- { ruby: '3.3', grape: '1.8.0' }
- { ruby: '3.4', grape: '1.8.0' }
- { ruby: '3.1', grape: '2.0.0' }
- { ruby: '3.2', grape: '2.0.0' }
- { ruby: '3.3', grape: '2.0.0' }
- { ruby: '3.4', grape: '2.0.0' }
- { ruby: '3.1', grape: '2.1.3' }
- { ruby: '3.2', grape: '2.1.3' }
- { ruby: '3.3', grape: '2.1.3' }
Expand Down
8 changes: 8 additions & 0 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.

# Offense count: 4
# Configuration parameters: Severity.
Style/OneClassPerFile:
Exclude:
- 'lib/grape-swagger.rb'
- 'spec/support/empty_model_parser.rb'
- 'spec/swagger_v2/guarded_endpoint_spec.rb'

# Offense count: 1
# Configuration parameters: Severity, Include.
# Include: **/*.gemspec
Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

#### Fixes

* Your contribution here.
* [#977](https://github.com/ruby-grape/grape-swagger/issues/977): Pass keyword arguments to `desc` to fix deprecation warning from Grape - [@numbata](https://github.com/numbata).

### 2.1.4 (2026-02-02)

Expand Down
2 changes: 1 addition & 1 deletion grape-swagger.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Gem::Specification.new do |s|
s.metadata['rubygems_mfa_required'] = 'true'

s.required_ruby_version = '>= 3.1'
s.add_dependency 'grape', '>= 1.7', '< 4.0'
s.add_dependency 'grape', '>= 2.1', '< 4.0'

s.files = Dir['lib/**/*', '*.md', 'LICENSE.txt', 'grape-swagger.gemspec']
s.require_paths = ['lib']
Expand Down
4 changes: 2 additions & 2 deletions lib/grape-swagger/doc_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def setup(options)

setup_formatter(options[:format])

desc api_doc.delete(:desc), api_doc
desc api_doc.delete(:desc), **api_doc

instance_eval(guard) unless guard.nil?

Expand All @@ -105,7 +105,7 @@ def setup(options)
.output_path_definitions(target_class.combined_namespace_routes, self, target_class, options)
end

desc specific_api_doc.delete(:desc), { params: specific_api_doc.delete(:params) || {}, **specific_api_doc }
desc specific_api_doc.delete(:desc), params: specific_api_doc.delete(:params) || {}, **specific_api_doc

params do
requires :name, type: String, desc: 'Resource name of mounted API'
Expand Down
39 changes: 37 additions & 2 deletions lib/grape-swagger/request_param_parsers/route.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ def parse
stackable_values = route.app&.inheritable_setting&.namespace_stackable

path_params = build_path_params(stackable_values)
variant_types = collect_variant_types(stackable_values)

fulfill_params(path_params)
fulfill_params(path_params, variant_types)
end

private
Expand All @@ -47,7 +48,33 @@ def fetch_inherited_params(stackable_values)
end
end

def fulfill_params(path_params)
# In Grape >= 3.3 `type: [A, B]` is wrapped in VariantCollectionCoercer and
# Grape's documentation pipeline serialises it via `#to_s`, so the original
# type list is lost in route.params. The live coercer is still reachable
# through the CoerceValidator's @converter, so we rebuild a name => types
# map and restore it in fulfill_params.
def collect_variant_types(stackable_values)
variant_types = {}
return variant_types unless defined?(Grape::Validations::Types::VariantCollectionCoercer) &&
defined?(Grape::Validations::Validators::CoerceValidator) &&
stackable_values.respond_to?(:[])

Array(stackable_values[:validations]).each do |validator|
next unless validator.is_a?(Grape::Validations::Validators::CoerceValidator)

converter = validator.instance_variable_get(:@converter)
next unless converter.is_a?(Grape::Validations::Types::VariantCollectionCoercer)

types = converter.instance_variable_get(:@types)
Array(validator.instance_variable_get(:@attrs)).each do |attr|
variant_types[attr.to_s] = types
end
end

variant_types
end

def fulfill_params(path_params, variant_types)
# Merge path params options into route params
route.params.each_with_object({}) do |(param, definition), accum|
# The route.params hash includes both parametrized params (with a string as a key)
Expand All @@ -57,10 +84,18 @@ def fulfill_params(path_params)
next if param.is_a?(String) && accum.key?(key)

defined_options = definition.is_a?(Hash) ? definition : {}
defined_options = restore_variant_type(defined_options, param, variant_types)
value = (path_params[param] || {}).merge(defined_options)
accum[key] = value.empty? ? DEFAULT_PARAM_TYPE : value
end
end

def restore_variant_type(defined_options, param, variant_types)
types = variant_types[param.to_s]
return defined_options unless types

defined_options.merge(type: types)
end
end
end
end
8 changes: 7 additions & 1 deletion spec/support/model_parsers/mock_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,13 @@ class RecursiveModel < OpenStruct; end
class DocumentedHashAndArrayModel < OpenStruct; end

module NestedModule
class ApiResponse < OpenStruct; end
class ApiResponse < OpenStruct
# Grape 3.2+ requires unknown types to implement .parse (arity 1) to pass the
# Types.custom? check and avoid a dry-types lookup that would raise ArgumentError.
# The implementation is minimal because these tests exercise swagger doc generation
# only, not actual request coercion.
def self.parse(val) = val
end
end
end
end
Expand Down
5 changes: 5 additions & 0 deletions spec/support/model_parsers/representable_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@ class ApiResponse < Representable::Decorator

property :status, documentation: { type: String }
property :error, documentation: { type: ::Entities::ApiError }

# Grape 3.2+ requires unknown types to implement .parse (arity 1) to pass the
# Types.custom? check. Representable::Decorator does not define parse, so we add
# a minimal pass-through sufficient for documentation-generation tests.
def self.parse(val) = val
end
end

Expand Down
5 changes: 4 additions & 1 deletion spec/swagger_v2/params_example_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ def app
params :common_params do
requires :id, type: Integer, documentation: { example: 123 }
optional :name, type: String, documentation: { example: 'Person' }
optional :obj, type: 'Object', documentation: { example: { 'foo' => 'bar' } }
# 'Object' is not a valid Grape coercion type; it is a swagger documentation-only hint.
# Grape 3.2+ rejects string type names in params blocks, so the type lives in
# documentation: where grape-swagger picks it up via ParseParams#call settings merge.
optional :obj, documentation: { type: 'Object', example: { 'foo' => 'bar' } }
end
end

Expand Down
Loading