Skip to content

Commit 97f2413

Browse files
authored
Fix middleware with keywords (#2546)
* Refactor Stack and Middleware closer to ActionDispatch::MiddlewareStack Flag hash arg with `ruby2_keywords_hash` Add spec * Add CHANGELOG.md * Change 1 liner
1 parent de10be9 commit 97f2413

File tree

3 files changed

+55
-23
lines changed

3 files changed

+55
-23
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
* [#2538](https://github.com/ruby-grape/grape/pull/2538): Fix validating nested json array params - [@mohammednasser-32](https://github.com/mohammednasser-32).
1515
* [#2543](https://github.com/ruby-grape/grape/pull/2543): Fix array allocation on mount - [@ericproulx](https://github.com/ericproulx).
16+
* [#2546](https://github.com/ruby-grape/grape/pull/2546): Fix middleware with keywords - [@ericproulx](https://github.com/ericproulx).
1617
* Your contribution here.
1718

1819
### 2.3.0 (2025-02-08)

lib/grape/middleware/stack.rb

+26-23
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,18 @@ module Middleware
77
class Stack
88
extend Forwardable
99
class Middleware
10-
extend Forwardable
11-
1210
attr_reader :args, :block, :klass
1311

14-
def_delegators :klass, :name
15-
16-
def initialize(klass, *args, &block)
12+
def initialize(klass, args, block)
1713
@klass = klass
1814
@args = args
1915
@block = block
2016
end
2117

18+
def name
19+
klass.name
20+
end
21+
2222
def ==(other)
2323
case other
2424
when Middleware
@@ -32,7 +32,11 @@ def inspect
3232
klass.to_s
3333
end
3434

35-
def use_in(builder)
35+
def build(builder)
36+
# we need to force the ruby2_keywords_hash for middlewares that initialize contains keywords
37+
# like ActionDispatch::RequestId since middleware arguments are serialized
38+
# https://rubyapi.org/3.4/o/hash#method-c-ruby2_keywords_hash
39+
args[-1] = Hash.ruby2_keywords_hash(args[-1]) if args.last.is_a?(Hash) && Hash.respond_to?(:ruby2_keywords_hash)
3640
builder.use(klass, *args, &block)
3741
end
3842
end
@@ -48,51 +52,50 @@ def initialize
4852
@others = []
4953
end
5054

51-
def insert(index, *args, &block)
55+
def insert(index, klass, *args, &block)
5256
index = assert_index(index, :before)
53-
middleware = self.class::Middleware.new(*args, &block)
54-
middlewares.insert(index, middleware)
57+
middlewares.insert(index, self.class::Middleware.new(klass, args, block))
5558
end
56-
ruby2_keywords :insert if respond_to?(:ruby2_keywords, true)
5759

5860
alias insert_before insert
5961

6062
def insert_after(index, *args, &block)
6163
index = assert_index(index, :after)
6264
insert(index + 1, *args, &block)
6365
end
64-
ruby2_keywords :insert_after if respond_to?(:ruby2_keywords, true)
6566

66-
def use(...)
67-
middleware = self.class::Middleware.new(...)
67+
def use(klass, *args, &block)
68+
middleware = self.class::Middleware.new(klass, args, block)
6869
middlewares.push(middleware)
6970
end
7071

7172
def merge_with(middleware_specs)
72-
middleware_specs.each do |operation, *args|
73+
middleware_specs.each do |operation, klass, *args|
7374
if args.last.is_a?(Proc)
7475
last_proc = args.pop
75-
public_send(operation, *args, &last_proc)
76+
public_send(operation, klass, *args, &last_proc)
7677
else
77-
public_send(operation, *args)
78+
public_send(operation, klass, *args)
7879
end
7980
end
8081
end
8182

8283
# @return [Rack::Builder] the builder object with our middlewares applied
83-
def build(builder = Rack::Builder.new)
84-
others.shift(others.size).each { |m| merge_with(m) }
85-
middlewares.each do |m|
86-
m.use_in(builder)
84+
def build
85+
Rack::Builder.new.tap do |builder|
86+
others.shift(others.size).each { |m| merge_with(m) }
87+
middlewares.each do |m|
88+
m.build(builder)
89+
end
8790
end
88-
builder
8991
end
9092

9193
# @description Add middlewares with :use operation to the stack. Store others with :insert_* operation for later
9294
# @param [Array] other_specs An array of middleware specifications (e.g. [[:use, klass], [:insert_before, *args]])
9395
def concat(other_specs)
94-
@others << Array(other_specs).reject { |o| o.first == :use }
95-
merge_with(Array(other_specs).select { |o| o.first == :use })
96+
use, not_use = other_specs.partition { |o| o.first == :use }
97+
others << not_use
98+
merge_with(use)
9699
end
97100

98101
protected

spec/grape/api_spec.rb

+28
Original file line numberDiff line numberDiff line change
@@ -1525,6 +1525,34 @@ def before
15251525
expect(last_response).to be_bad_request
15261526
expect(last_response.body).to eq('Caught in the Net')
15271527
end
1528+
1529+
context 'when middleware initialize as keywords' do
1530+
let(:middleware_with_keywords) do
1531+
Class.new do
1532+
def initialize(app, keyword:)
1533+
@app = app
1534+
@keyword = keyword
1535+
end
1536+
1537+
def call(env)
1538+
env['middleware_with_keywords'] = @keyword
1539+
@app.call(env)
1540+
end
1541+
end
1542+
end
1543+
1544+
before do
1545+
subject.use middleware_with_keywords, keyword: 'hello'
1546+
subject.get '/' do
1547+
env['middleware_with_keywords']
1548+
end
1549+
get '/'
1550+
end
1551+
1552+
it 'returns the middleware value' do
1553+
expect(last_response.body).to eq('hello')
1554+
end
1555+
end
15281556
end
15291557

15301558
describe '.insert_before' do

0 commit comments

Comments
 (0)