Skip to content
Open
Show file tree
Hide file tree
Changes from all 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