Skip to content

Commit 6167847

Browse files
committed
Merge pull request #1142 from jrforrest/bugfix/declared_scope
Makes #declared unavailable to before filters
2 parents 6c70bbd + ef2948f commit 6167847

File tree

8 files changed

+81
-33
lines changed

8 files changed

+81
-33
lines changed

.rubocop_todo.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ Metrics/BlockNesting:
2222
# Offense count: 5
2323
# Configuration parameters: CountComments.
2424
Metrics/ClassLength:
25-
Max: 249
25+
Max: 250
2626

2727
# Offense count: 23
2828
Metrics/CyclomaticComplexity:
@@ -41,7 +41,7 @@ Metrics/MethodLength:
4141
# Offense count: 8
4242
# Configuration parameters: CountComments.
4343
Metrics/ModuleLength:
44-
Max: 271
44+
Max: 272
4545

4646
# Offense count: 17
4747
Metrics/PerceivedComplexity:

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#### Fixes
1111

12+
* [#1142](https://github.com/ruby-grape/grape/pull/1114): Makes #declared unavailable to before filters - [@jrforrest](https://github.com/jrforrest)
1213
* [#1114](https://github.com/ruby-grape/grape/pull/1114): Fix regression which broke identical endpoints with different versions - [@suan](https://github.com/suan).
1314
* [#1109](https://github.com/ruby-grape/grape/pull/1109): Memoize Virtus attribute and fix memory leak - [@marshall-lee](https://github.com/marshall-lee).
1415
* [#1101](https://github.com/intridea/grape/pull/1101): Fix: Incorrect media-type `Accept` header now correctly returns 406 with `strict: true` - [@elliotlarson](https://github.com/elliotlarson).

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,10 @@ The returned hash is a `Hashie::Mash` instance, allowing you to access parameter
534534
declared(params).user == declared(params)["user"]
535535
```
536536

537+
538+
The `#declared` method is not available to `before` filters, as those are evaluated prior
539+
to parameter coercion.
540+
537541
### Include missing
538542

539543
By default `declared(params)` includes parameters that have `nil` values. If you want to return only the parameters that are not `nil`, you can use the `include_missing` option. By default, `include_missing` is set to `true`. Consider the following API:

UPGRADING.md

+6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ Upgrading Grape
33

44
### Upgrading to >= 0.13.1
55

6+
#### Changes to availability of DSL methods in filters
7+
8+
The `#declared` method of the route DSL is no longer available in the `before` filter. Using `declared` in a `before` filter will now raise `Grape::DSL::InsideRoute::MethodNotYetAvailable`.
9+
10+
See [#1074](https://github.com/ruby-grape/grape/issues/1074) for discussion of the issue.
11+
612
#### Changes to header versioning and invalid header version handling
713

814
Identical endpoints with different versions now work correctly. A regression introduced in Grape 0.11.0 caused all but the first-mounted version for such an endpoint to wrongly throw an `InvalidAcceptHeader`. As a side effect, requests with a correct vendor but invalid version can no longer be rescued from a `rescue_from` block.

lib/grape/dsl/inside_route.rb

+54-29
Original file line numberDiff line numberDiff line change
@@ -6,46 +6,71 @@ module InsideRoute
66
extend ActiveSupport::Concern
77
include Grape::DSL::Settings
88

9-
# A filtering method that will return a hash
10-
# consisting only of keys that have been declared by a
11-
# `params` statement against the current/target endpoint or parent
12-
# namespaces.
13-
#
14-
# @param params [Hash] The initial hash to filter. Usually this will just be `params`
15-
# @param options [Hash] Can pass `:include_missing`, `:stringify` and `:include_parent_namespaces`
16-
# options. `:include_parent_namespaces` defaults to true, hence must be set to false if
17-
# you want only to return params declared against the current/target endpoint.
18-
def declared(params, options = {}, declared_params = nil)
19-
options = options.reverse_merge(include_missing: true, include_parent_namespaces: true)
9+
# Denotes a situation where a DSL method has been invoked in a
10+
# filter which it should not yet be available in
11+
class MethodNotYetAvailable < StandardError; end
2012

21-
declared_params ||= (!options[:include_parent_namespaces] ? route_setting(:declared_params) : (route_setting(:saved_declared_params) || [])).flatten(1) || []
13+
# @param type [Symbol] The type of filter for which evaluation has been
14+
# completed
15+
# @return [Module] A module containing method overrides suitable for the
16+
# position in the filter evaluation sequence denoted by +type+. This
17+
# defaults to an empty module if no overrides are defined for the given
18+
# filter +type+.
19+
def self.post_filter_methods(type)
20+
@post_filter_modules ||= { before: PostBeforeFilter }
21+
@post_filter_modules[type] || Module.new
22+
end
2223

23-
fail ArgumentError, 'Tried to filter for declared parameters but none exist.' unless declared_params
24+
# Methods which should not be available in filters until the before filter
25+
# has completed
26+
module PostBeforeFilter
27+
def declared(params, options = {}, declared_params = nil)
28+
options = options.reverse_merge(include_missing: true, include_parent_namespaces: true)
2429

25-
if params.is_a? Array
26-
params.map do |param|
27-
declared(param || {}, options, declared_params)
28-
end
29-
else
30-
declared_params.each_with_object(Hashie::Mash.new) do |key, hash|
31-
key = { key => nil } unless key.is_a? Hash
30+
declared_params ||= (!options[:include_parent_namespaces] ? route_setting(:declared_params) : (route_setting(:saved_declared_params) || [])).flatten(1) || []
31+
32+
fail ArgumentError, 'Tried to filter for declared parameters but none exist.' unless declared_params
33+
34+
if params.is_a? Array
35+
params.map do |param|
36+
declared(param || {}, options, declared_params)
37+
end
38+
else
39+
declared_params.each_with_object(Hashie::Mash.new) do |key, hash|
40+
key = { key => nil } unless key.is_a? Hash
3241

33-
key.each_pair do |parent, children|
34-
output_key = options[:stringify] ? parent.to_s : parent.to_sym
42+
key.each_pair do |parent, children|
43+
output_key = options[:stringify] ? parent.to_s : parent.to_sym
3544

36-
next unless options[:include_missing] || params.key?(parent)
45+
next unless options[:include_missing] || params.key?(parent)
3746

38-
hash[output_key] = if children
39-
children_params = params[parent] || (children.is_a?(Array) ? [] : {})
40-
declared(children_params, options, Array(children))
41-
else
42-
params[parent]
43-
end
47+
hash[output_key] = if children
48+
children_params = params[parent] || (children.is_a?(Array) ? [] : {})
49+
declared(children_params, options, Array(children))
50+
else
51+
params[parent]
52+
end
53+
end
4454
end
4555
end
4656
end
4757
end
4858

59+
# A filtering method that will return a hash
60+
# consisting only of keys that have been declared by a
61+
# `params` statement against the current/target endpoint or parent
62+
# namespaces.
63+
#
64+
# @see +PostBeforeFilter#declared+
65+
#
66+
# @param params [Hash] The initial hash to filter. Usually this will just be `params`
67+
# @param options [Hash] Can pass `:include_missing`, `:stringify` and `:include_parent_namespaces`
68+
# options. `:include_parent_namespaces` defaults to true, hence must be set to false if
69+
# you want only to return params declared against the current/target endpoint.
70+
def declared(*)
71+
fail MethodNotYetAvailable, '#declared is not available prior to parameter validation.'
72+
end
73+
4974
# The API version as specified in the URL.
5075
def version
5176
env[Grape::Env::API_VERSION]

lib/grape/endpoint.rb

+1
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ def run_filters(filters, type = :other)
317317
instance_eval(&filter)
318318
end
319319
end
320+
extend DSL::InsideRoute.post_filter_methods(type)
320321
end
321322

322323
def befores

spec/grape/dsl/inside_route_spec.rb

+3-2
Original file line numberDiff line numberDiff line change
@@ -343,8 +343,9 @@ def initialize
343343
describe '#declared' do
344344
# see endpoint_spec.rb#declared for spec coverage
345345

346-
it 'returns an empty hash' do
347-
expect(subject.declared({})).to eq({})
346+
it 'is not available by default' do
347+
expect { subject.declared({}) }.to raise_error(
348+
Grape::DSL::InsideRoute::MethodNotYetAvailable)
348349
end
349350
end
350351
end

spec/grape/endpoint_spec.rb

+10
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,16 @@ def app
239239
end
240240
end
241241

242+
it 'does not work in a before filter' do
243+
subject.before do
244+
declared(params)
245+
end
246+
subject.get('/declared') { declared(params) }
247+
248+
expect { get('/declared') }.to raise_error(
249+
Grape::DSL::InsideRoute::MethodNotYetAvailable)
250+
end
251+
242252
it 'has as many keys as there are declared params' do
243253
inner_params = nil
244254
subject.get '/declared' do

0 commit comments

Comments
 (0)