Skip to content

Commit 2457d42

Browse files
committed
Bump to 2.2.0; document breaking changes and tighten review nits
Per maintainer feedback on PR #978, this release carries breaking changes (Grape floor bump, `type: 'Object'` migration, custom-type `parse` requirement) and therefore lands as 2.2.0, with a matching UPGRADING entry and README compatibility-matrix row. Docs - README: add a `>= 2.2.0` compatibility row and cap the prior row at `< 2.2.0`. Document the new `:description` alias / string-keyed `api_documentation` affordance and the `:desc` precedence rule. - UPGRADING: new "Upgrading to >= 2.2.0" section covering all three user-facing breaking changes (Grape floor, `type: 'Object'`, custom types needing `.parse`). - CHANGELOG: split bullets into Breaking changes / Features / Fixes with accurate wording. Drop the misleading "matching the pre-keyword -args behaviour" framing — `:description` and string keys are new affordances. Add the custom-type `parse` requirement. Note that the variant-type recovery is a no-op on Grape < 3.3. Code - `pop_desc` is now private; it was an internal helper to `setup` but was leaking onto every `Grape::API` that mixed in `DocMethods`. - `route.rb`: `flatten(1)` instead of unbounded `.flatten`, and skip the merge when the recovered coercer `@types` is empty so we don't overwrite the legacy serialisation with `[]`. Tests - Add regression coverage for the parts of `pop_desc` that the v2 fix was actually for: explicit `desc: nil` must not fall through to `:description`, and `specific_api_documentation: { description: ... }` goes through the second `pop_desc` call site too. Public-API audit: `@converter` / `@types` (on VariantCollectionCoercer) and `@scope` (on Validators::Base) have no public reader on the supported Grape range (`scope` is private on HEAD and absent on 3.2.1), so the `instance_variable_get` reaches remain — there is no stable public alternative across the matrix.
1 parent 64ebd4c commit 2457d42

7 files changed

Lines changed: 70 additions & 15 deletions

File tree

CHANGELOG.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
1-
### 2.1.5 (Next)
1+
### 2.2.0 (Next)
22

3-
#### Features
3+
#### Breaking changes
44

5-
* Your contribution here.
5+
* [#978](https://github.com/ruby-grape/grape-swagger/pull/978): Bump minimum Grape to `>= 2.1` (was `>= 1.7`); drop Grape 1.8/2.0 CI rows. See [UPGRADING](UPGRADING.md) - [@numbata](https://github.com/numbata).
6+
* [#978](https://github.com/ruby-grape/grape-swagger/pull/978): On Grape 3.2+, declare swagger-only types under `documentation: { type: 'Object' }` instead of `params { type: 'Object' }` - [@numbata](https://github.com/numbata).
7+
* [#978](https://github.com/ruby-grape/grape-swagger/pull/978): On Grape 3.2+, custom type classes used via `type: MyClass` must implement `MyClass.parse(value)` - [@numbata](https://github.com/numbata).
68

7-
#### Breaking changes
9+
#### Features
810

9-
* Minimum required Grape version is now `>= 2.1` (was `>= 1.7`). Grape 1.8.0 and 2.0.0 do not work on Ruby 3.3+ due to a Mustermann private-method incompatibility (the existing CI rows for those versions had been failing on master). Their CI rows are removed.
10-
* On Grape 3.2+ the `params` block rejects string type names. If you used `params { optional :foo, type: 'Object' }` to declare a swagger-only documentation hint, move the type into the `documentation:` hash: `optional :foo, documentation: { type: 'Object' }`. grape-swagger picks the type up from the merged settings unchanged.
11+
* [#978](https://github.com/ruby-grape/grape-swagger/pull/978): Accept string-keyed `api_documentation` / `specific_api_documentation` and a `:description` alias for `:desc` - [@numbata](https://github.com/numbata).
1112

1213
#### Fixes
1314

14-
* [#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).
15-
* Accept string-keyed `api_documentation` / `specific_api_documentation` and the `:description` key as an alias for `:desc`, matching the pre-keyword-args behaviour of `desc`.
16-
* Grape 3.3+: recover the real type list for multi-type params (`type: [A, B]`). Grape now wraps these in a `VariantCollectionCoercer` and serialises it via `#to_s` in `route.params`, which lost the original types. Swagger output now reflects the first declared type (e.g. `type: [Integer, Float]` produces `"integer"`) instead of the coercer's `#inspect` string.
15+
* [#978](https://github.com/ruby-grape/grape-swagger/pull/978): Pass keyword arguments to `desc` to fix Grape 3.2 deprecation - [@numbata](https://github.com/numbata).
16+
* [#978](https://github.com/ruby-grape/grape-swagger/pull/978): On Grape 3.3+, recover multi-type params (`type: [A, B]`) from `VariantCollectionCoercer` so swagger reflects the first declared type instead of the coercer's `#inspect` string - [@numbata](https://github.com/numbata).
1717

1818
### 2.1.4 (2026-02-02)
1919

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,8 @@ The following versions of grape, grape-entity and grape-swagger can currently be
125125
| >= 1.0.0 | 2.0 | >= 1.3.0 | >= 0.5.0 | >= 2.4.1 |
126126
| >= 2.0.0 | 2.0 | >= 1.7.0 | >= 0.5.0 | >= 2.4.1 |
127127
| >= 2.0.0 ... <= 2.1.2 | 2.0 | >= 1.8.0 ... < 2.3.0 | >= 0.5.0 | >= 2.4.1 |
128-
| > 2.1.2 | 2.0 | >= 1.8.0 ... < 4.0 | >= 0.5.0 | >= 2.4.1 |
128+
| >= 2.1.3 ... < 2.2.0 | 2.0 | >= 1.8.0 ... < 4.0 | >= 0.5.0 | >= 2.4.1 |
129+
| >= 2.2.0 | 2.0 | >= 2.1 ... < 4.0 | >= 0.5.0 | >= 2.4.1 |
129130

130131

131132
## Swagger-Spec <a name="swagger-spec"></a>
@@ -498,6 +499,8 @@ add_swagger_documentation \
498499
api_documentation: { desc: 'Reticulated splines API swagger-compatible documentation.' }
499500
```
500501

502+
`:description` is accepted as an alias for `:desc` (when both are supplied, `:desc` wins; an explicit `desc: nil` is respected and does not fall through). String keys (e.g. when loading from YAML/JSON) are accepted too.
503+
501504
#### specific_api_documentation
502505

503506
Customize the Swagger API specific documentation route, typically contains a `desc` field. The default description is "Swagger compatible API description for specific API".

UPGRADING.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,26 @@
11
## Upgrading Grape-swagger
22

3+
### Upgrading to >= 2.2.0
4+
5+
- **Minimum Grape version is now `>= 2.1`** (was `>= 1.7`). Grape 1.8.0 and 2.0.0 cannot be used on Ruby 3.3+ because of an upstream Mustermann/forwardable incompatibility; the CI rows for those combinations were already failing on `master` and have been removed.
6+
- **`type: 'Object'` (and other string type names) in `params` blocks**: Grape 3.2+ rejects string type names. If you previously declared a swagger-only documentation hint via `params { optional :foo, type: 'Object' }`, move the type under `documentation:`:
7+
8+
```ruby
9+
optional :foo, documentation: { type: 'Object' }
10+
```
11+
12+
grape-swagger picks the type up from the merged settings unchanged, so the swagger output is identical.
13+
- **Custom type classes** used via `type: MyClass` must implement `MyClass.parse(value)` (arity 1) on Grape 3.2+; otherwise Grape's dry-types lookup raises `ArgumentError`. `Grape::Entity` already provides `parse`; `Representable::Decorator` and plain Ruby classes need to define it explicitly:
14+
15+
```ruby
16+
class MyType
17+
def self.parse(value) = new(value)
18+
# ...
19+
end
20+
```
21+
22+
- **Multi-type params (`type: [A, B]`) on Grape 3.3+**: swagger output now reflects the first declared type (e.g. `type: [Integer, Float]` produces `"integer"`). Previously, Grape 3.3 serialized the `VariantCollectionCoercer` wrapper via `#to_s`, leaking `"#<Grape::Validations::Types::VariantCollectionCoercer:0x...>"` into the documentation. No action required, but if you were programmatically post-processing that string, the fix will change the output.
23+
324
### Upgrading to >= x.y.z
425

526
- Grape-swagger now documents array parameters within an object schema in Swagger. This aligns with grape's JSON structure requirements and ensures the documentation is correct.

lib/grape-swagger/doc_methods.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,11 @@ def setup_formatter(formatter)
137137
FORMATTER_METHOD.each { |method| send(method, formatter) }
138138
end
139139

140+
private
141+
142+
# :desc takes precedence over :description; explicit nil under :desc wins
143+
# (don't fall through on nil — that would silently substitute :description).
140144
def pop_desc(doc)
141-
# :desc takes precedence over :description; explicit nil under :desc wins
142-
# (don't fall through on nil — that would silently substitute :description).
143145
doc.key?(:desc) ? doc.delete(:desc) : doc.delete(:description)
144146
end
145147
end

lib/grape-swagger/request_param_parsers/route.rb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,10 @@ def collect_variant_types(stackable_values)
6464
defined?(Grape::Validations::Validators::CoerceValidator) &&
6565
stackable_values.respond_to?(:[])
6666

67-
Array(stackable_values[:validations]).flatten.each do |validator|
67+
# `flatten(1)` only collapses the inheritance-stack levels (StackableValues
68+
# stores `[[...level1...], [...level2...]]`); avoids descending into
69+
# validator internals if any ever implements `to_ary`.
70+
Array(stackable_values[:validations]).flatten(1).each do |validator|
6871
next unless validator.is_a?(Grape::Validations::Validators::CoerceValidator)
6972

7073
converter = validator.instance_variable_get(:@converter)
@@ -73,6 +76,8 @@ def collect_variant_types(stackable_values)
7376
# `.to_a` preserves the user-declared order for both Array and Set
7477
# storage shapes; `DataType.parse_multi_type` uses `.first` downstream.
7578
types = converter.instance_variable_get(:@types).to_a
79+
next if types.empty?
80+
7681
scope = validator.instance_variable_get(:@scope)
7782
next unless scope.respond_to?(:full_name)
7883

lib/grape-swagger/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# frozen_string_literal: true
22

33
module GrapeSwagger
4-
VERSION = '2.1.5'
4+
VERSION = '2.2.0'
55
end

spec/swagger_v2/api_documentation_spec.rb

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,31 @@
5656
end
5757

5858
it ':desc takes precedence' do
59-
expect(subject.first[:description]).to eq('Desc wins')
59+
expect(subject.pluck(:description)).to include('Desc wins')
60+
end
61+
end
62+
63+
context 'when :desc is explicitly nil' do
64+
let(:api) do
65+
Class.new(Grape::API) do
66+
add_swagger_documentation api_documentation: { desc: nil, description: 'fallback' }
67+
end
68+
end
69+
70+
it 'respects the explicit nil and does not fall through to :description' do
71+
expect(subject.pluck(:description)).not_to include('fallback')
72+
end
73+
end
74+
75+
context 'when specific_api_documentation uses :description' do
76+
let(:api) do
77+
Class.new(Grape::API) do
78+
add_swagger_documentation specific_api_documentation: { description: 'Specific via description' }
79+
end
80+
end
81+
82+
it 'accepts :description on the specific endpoint too' do
83+
expect(subject.pluck(:description)).to include('Specific via description')
6084
end
6185
end
6286
end

0 commit comments

Comments
 (0)