Skip to content

Preserve keyword arguments across options and builders - #26

Open
OskarEichler wants to merge 1 commit into
apotonick:masterfrom
OskarEichler:codex/preserve-keyword-forwarding
Open

Preserve keyword arguments across options and builders#26
OskarEichler wants to merge 1 commit into
apotonick:masterfrom
OskarEichler:codex/preserve-keyword-forwarding

Conversation

@OskarEichler

Copy link
Copy Markdown

Summary

Preserve keyword arguments through Uber's option and builder forwarding layers on Ruby 3 and 4. This covers generated method/instance-exec wrappers, Options#evaluate/#eval, Builder#build!/Builders#call, and the still-shipped deprecated Options::Value helpers and evaluate alias.

The guarded Ruby ruby2_keywords mechanism retains the existing *args signatures and positional-hash behavior without introducing runtime syntax newer than the documented Ruby floor. User-provided direct Procs and Callable objects retain their identity.

Reproduction

require 'uber/options'
context = Object.new
def context.pick(required:); required; end
Uber::Option[:pick].call(context, required: 7)
Uber::Options.new(value: :pick).evaluate(context, required: 7)

Both calls previously raise ArgumentError on Ruby 3.2/4.0. They now return 7 and {value: 7}. Explicit positional hashes are not promoted to keywords; missing required keywords still raise. Keyword-only instance-exec lambdas and builder declarations fail similarly before the patch.

Verification

  • Existing master baseline and this isolated branch: bundle exec rake test, 45 tests / 50 assertions, zero failures/errors/skips, Ruby 4.0.6 / Minitest 4.7.5.
  • 4,202 focused forwarding checks on Ruby 3.2.11 and 4.0.6: required/empty/string-key keywords, positional hashes, Proc/Callable identity, legacy helpers and alias, nil execution context, static values and builder non-inheritance.
  • Release-based combination with the separate unused-reference cleanup: existing suite passes on both Rubies; 2,750 additional inheritance/delegation boundary checks pass. Package retains all 25 paths and compared metadata; eight runtime files compile on both Rubies. Two pre-existing Lint offenses remain unchanged.
  • Downstream Representable 3.2.0, baseline/fixed: 544 tests / 700 assertions, zero failures/errors, three existing skips on Ruby 3.2.11. Ruby 4 retains the same eight legacy inspection-format failures, zero errors and three skips. Existing tests are unchanged; external tooling supplies its old MiniTest alias and ruby-prof 1.7 API.
  • Three alternating local YJIT process pairs show no observed call-path regression in this bounded positional workload (100,000 Proc calls ~0.0099→0.0097s; method calls ~0.0084→0.0083s). This is not a production throughput claim.

Compatibility / limitations

No intended breaking changes to documented APIs. Calls that previously lost explicit keywords now deliver them to their target. Positional arguments, explicit positional Hashes, empty keywords, static returns, private-method dispatch and unwrapped callable identity retain their behavior.

No new or modified checked-in tests. Ruby 1.9.3–3.1, JRuby, Windows and Linux were not executed locally. The keyword-marker calls are guarded for Ruby versions where the methods do not exist. Main's unrelated test/CI changes are not included in the 0.1.0 consumer backport.

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