Skip to content

Require Ruby 2.7.3 and replace ruby2_keywords - #2499

Merged
samuel-williams-shopify merged 3 commits into
rack:mainfrom
socketry:drop-ruby2-keywords
Aug 14, 2026
Merged

samuel-williams-shopify merged 3 commits into
rack:mainfrom
socketry:drop-ruby2-keywords

Conversation

@samuel-williams-shopify

@samuel-williams-shopify samuel-williams-shopify commented Aug 13, 2026 •

Copy link
Copy Markdown
Collaborator

Ruby Feature #22205 records agreement to deprecate ruby2_keywords, with warnings beginning in Ruby 4.2 and removal in Ruby 4.4. Therefore, we should remove our usage of ruby2_keywords, otherwise Rack 3.3 (tentatively) would break on Ruby 4.4. As it stands, all released versions of Rack will break on Ruby 4.4.

  • require Ruby 2.7.3 or newer
  • replace ruby2_keywords with ... argument forwarding in Rack::Builder and Rack's delegators
  • test both Ruby 2.7.3 exactly and the latest Ruby 2.7 patch release
  • remove compatibility branches for older Ruby versions
  • add coverage for forwarding positional hashes and keyword arguments together

Why Ruby 2.7.3?

Ruby 2.7 introduced ... argument forwarding. Support for leading arguments such as def method_missing(name, ...), which Rack needs, was backported in Ruby 2.7.3. Ruby 2.7.0 through 2.7.2 therefore cannot parse all of the forwarding definitions used here.

Using 2.7.3 as the minimum preserves compatibility with the final Ruby 2.7 release line while allowing Rack to remove every ruby2_keywords call without version-gated forwarding implementations. CI includes both exact Ruby 2.7.3 and 2.7, which resolves to the latest patch release.

A Ruby 3.0 minimum would provide a cleaner keyword-separation boundary, but is not technically required when ... is used. The trade-off is retaining support for Ruby 2.7's transitional keyword semantics; the forwarding sites in this PR avoid that ambiguity by forwarding the original arguments unchanged.

Rack::Headers#except remains conditional using feature detection because Ruby before 3.0 does not provide Hash#except.

Related work

This is stacked on #2482 so Rack::Multipart::UploadedFile is migrated directly to ... instead of introducing another ruby2_keywords dependency. The original commit and authorship from @SeanLF are preserved; if #2482 merges first, that commit will drop out of this PR.

This takes the minimum-version approach discussed in #2484 instead of retaining older Ruby support through version-gated method definitions.

Verification

  • bundle exec rake
  • targeted forwarding tests for Rack::Builder, Rack::BodyProxy, Rack::MockResponse::Cookie, and Rack::Multipart::UploadedFile
  • built the gem and verified its required_ruby_version

SeanLF and others added 2 commits August 14, 2026 10:14
Rack::Multipart::UploadedFile forwards missing methods to the wrapped
tempfile, but the delegator was never flagged with ruby2_keywords. On Ruby
3.0+, where keywords are separated from a trailing Hash, the keywords are
rebuilt as a positional Hash before reaching the tempfile:

  uploaded_file.readlines(chomp: true)
  # => TypeError: no implicit conversion of Hash into Integer
  uploaded_file.gets(chomp: true)
  # => TypeError: no implicit conversion of Hash into Integer

Ruby 2.4 through 2.6 are unaffected, since they do not distinguish a
trailing Hash from keywords, which is why this went unnoticed.

Flag the delegator, matching BodyProxy#method_missing and
MockResponse::Cookie#method_missing.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Use argument forwarding for middleware and delegators instead of ruby2_keywords. Remove obsolete compatibility branches and test only supported Ruby versions.

Assisted-By: devx/9a5ced6c-36f2-46e3-a662-1765c7341b0e
Comment thread lib/rack/mock_response.rb Outdated
Comment thread .github/workflows/test.yaml
Comment thread lib/rack/multipart/uploaded_file.rb Outdated
@jeremyevans

Copy link
Copy Markdown
Contributor

Note that Matz agreed to the deprecation (4.2) and removal (4.4) of ruby2_keywords, so it's beyond just a proposal.

Keep the latest Ruby 2.7 release line by using the leading-argument forwarding syntax available since Ruby 2.7.3. Test both the minimum version and the latest 2.7 patch release.

Assisted-By: devx/4adb4993-e502-416b-8fe7-efc94c16ae7e
@samuel-williams-shopify samuel-williams-shopify changed the title Require Ruby 3.0 and replace ruby2_keywords Require Ruby 2.7.3 and replace ruby2_keywords Aug 14, 2026
@samuel-williams-shopify
samuel-williams-shopify merged commit ad27aed into rack:main Aug 14, 2026
16 checks passed
@samuel-williams-shopify
samuel-williams-shopify deleted the drop-ruby2-keywords branch August 14, 2026 00:25
@eregon

eregon commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Why Ruby 2.7.3?

Indeed def foo(name, ...) is 2.7.3+, I got the version wrong in https://bugs.ruby-lang.org/issues/22205#note-14 and now updated that :)
(I checked with docker run -it ghcr.io/ruby/all-ruby /all-ruby/all-ruby -e 'class A; def method_missing(name, ...); name; end; end')

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.

5 participants