Skip to content

Commit 0641a11

Browse files
ericproulxclaude
andcommitted
Pass path to Grape::Endpoint as a keyword instead of via options
`path` was pulled from the endpoint's `**options` Hash and normalized in place (`@options[:path] = Array(...)`), duplicating the normalization `Options` already does. Expose it as a required `path:` keyword on `Grape::Endpoint#initialize` and pass it straight into `Options`, which stays the single normalized source of truth. `path` remains in `config` because it is load-bearing for route building (`config.path`) and for endpoint equality (`config == other.config`, which keeps same-path/different-verb endpoints distinct). The endpoint's public `options` Hash no longer carries `:path`. Its only reader was an internal test; `route.path` (the compiled pattern) is the public path API and is what grape-swagger already uses. Documented in UPGRADING. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 2e7890c commit 0641a11

4 files changed

Lines changed: 8 additions & 9 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* [#2768](https://github.com/ruby-grape/grape/pull/2768): Remove Guard - [@ericproulx](https://github.com/ericproulx).
66
* [#2774](https://github.com/ruby-grape/grape/pull/2774): Make `forward_match` an internal kwarg instead of a route option - [@ericproulx](https://github.com/ericproulx).
77
* [#2775](https://github.com/ruby-grape/grape/pull/2775): Pass `http_methods` to `Grape::Endpoint` as a keyword instead of via options - [@ericproulx](https://github.com/ericproulx).
8+
* [#2776](https://github.com/ruby-grape/grape/pull/2776): Pass `path` to `Grape::Endpoint` as a keyword instead of via options - [@ericproulx](https://github.com/ericproulx).
89
* Your contribution here.
910

1011
#### Fixes

UPGRADING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Grape::Endpoint.new(settings, method: :get, path: '/foo', for: self)
2121
Grape::Endpoint.new(settings, http_methods: :get, path: '/foo', for: self)
2222
```
2323

24-
Relatedly, an endpoint's public `options` Hash no longer carries `:method` (`endpoint.options[:method]`). Nothing in Grape read it, and downstream gems such as grape-swagger already read the verb from `route.request_method`, which is unchanged.
24+
Relatedly, an endpoint's public `options` Hash no longer carries `:method` or `:path`. Nothing in Grape read `:method`, and the only reader of `:path` was an internal test; both values are available from the route instead — `route.request_method` for the verb and `route.path` for the (compiled) path, which is what grape-swagger and other introspection already use. The raw definition-time path array is no longer exposed on the endpoint.
2525

2626
### Upgrading to >= 3.3
2727

lib/grape/endpoint.rb

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,15 @@ def block_to_unbound_method(block)
4040
# validations, and other properties from.
4141
# @param http_methods [String or Array] which HTTP method(s) can be used to
4242
# reach this endpoint.
43+
# @param path [String or Array] the path to this endpoint, within the
44+
# current scope.
4345
# @param options [Hash] attributes of this endpoint, normalized into a
4446
# +Grape::Endpoint::Options+ value object.
45-
# @option options path [String or Array] the path to this endpoint, within
46-
# the current scope.
4747
# @option options route_options [Hash]
4848
# @note This happens at the time of API definition, so in this context the
4949
# endpoint does not know if it will be mounted under a different endpoint.
5050
# @yield a block defining what your API should do when this endpoint is hit
51-
def initialize(new_settings, http_methods:, **options, &block)
51+
def initialize(new_settings, http_methods:, path:, **options, &block)
5252
self.inheritable_setting = new_settings.point_in_time_copy
5353

5454
# now +namespace_stackable(:declared_params)+ contains all params defined for
@@ -61,9 +61,7 @@ def initialize(new_settings, http_methods:, **options, &block)
6161
inheritable_setting.namespace_inheritable[:default_error_status] ||= 500
6262

6363
@options = options
64-
@options[:path] = Array(@options[:path])
65-
@options[:path] << '/' if @options[:path].empty?
66-
@config = Options.new(http_methods:, **options)
64+
@config = Options.new(http_methods:, path:, **options)
6765

6866
@status = nil
6967
@stream = nil

spec/grape/api_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3982,7 +3982,7 @@ def my_method
39823982
subject.format :json
39833983
subject.get '/endpoint/options' do
39843984
{
3985-
path: options[:path],
3985+
path: route.path,
39863986
source_location: source.source_location
39873987
}
39883988
end
@@ -3991,7 +3991,7 @@ def my_method
39913991
it 'path' do
39923992
get '/endpoint/options'
39933993
options = Grape::Json.parse(last_response.body)
3994-
expect(options['path']).to eq(['/endpoint/options'])
3994+
expect(options['path']).to eq('/endpoint/options(.json)')
39953995
expect(options['source_location'][0]).to include 'api_spec.rb'
39963996
expect(options['source_location'][1].to_i).to be > 0
39973997
end

0 commit comments

Comments
 (0)