Skip to content

Hand a Proc passed as the last middleware argument to the middleware - #2967

Merged
ericproulx merged 1 commit into
masterfrom
fix/middleware-proc-argument
Sep 19, 2026
Merged

ericproulx merged 1 commit into
masterfrom
fix/middleware-proc-argument

Conversation

@ericproulx

Copy link
Copy Markdown
Contributor

Summary

A callable passed as the last argument to use, insert_before or insert_after was taken for the middleware's block:

class Reporter
  def initialize(app, on_error)
    @app = app
    @on_error = on_error
  end
  # ...
end

use Reporter, ->(e) { Sentry.capture_exception(e) }
# => ArgumentError: wrong number of arguments (given 1, expected 2)

The DSL recorded a registration as [operation, *args] and appended the block only when one was given, so Stack#merge_with told the block apart by its class and popped whatever Proc came last. The middleware was then built without its callable. Reproduced on 2.1.3, 3.0.1, 3.3.5 and 4.0.1; the pop dates from c2e41f7 (2016).

Every registration now ends in its block, nil when none was given, and merge_with always takes the last element as the block.

Contract change

API.middleware returns those entries, so each one now ends in nil when no block was given:

use SomeMiddleware, 'abc'
API.middleware # before: [[:use, SomeMiddleware, 'abc']]
               # after:  [[:use, SomeMiddleware, 'abc', nil]]

Covered in UPGRADING. grape-swagger's own check (middleware.flatten.include?(auth_wrapper)) is unaffected.

Test plan

  • New specs: a Proc argument reaches the middleware through use and insert_before, and a block given alongside it stays the block; the two argument cases fail without the fix.
  • Updated the specs that pinned the old entry shape.
  • Full RSpec suite passes locally (2945 examples, 0 failures); RuboCop clean.
  • CI green.

🤖 Generated with Claude Code

@ericproulx
ericproulx force-pushed the fix/middleware-proc-argument branch from 6b11f86 to 2a048d4 Compare September 19, 2026 19:19
The middleware DSL recorded a registration as [operation, *args] and
appended the block only when one was given, so Stack#merge_with told the
block apart by its class: whatever Proc came last was popped and passed
as the block. A callable handed to a middleware as its last argument,
as in `use SomeMiddleware, ->(env) { ... }`, was taken for the block
instead, and the middleware was built without it, raising ArgumentError
when the stack was built. `insert_before` and `insert_after` did the
same.

Every registration now ends in its block, nil when none was given, and
merge_with always takes the last element as the block. That changes the
entries `API.middleware` returns, hence the UPGRADING note.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ericproulx
ericproulx force-pushed the fix/middleware-proc-argument branch from 2a048d4 to 9d85969 Compare September 19, 2026 19:37
@github-actions

Copy link
Copy Markdown

Danger Report

No issues found.

View run

@ericproulx
ericproulx merged commit fb26e8f into master Sep 19, 2026
69 checks passed
@ericproulx
ericproulx deleted the fix/middleware-proc-argument branch September 20, 2026 21:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant