Skip to content

Block arity detection #389

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jun 5, 2025
Merged

Conversation

numbata
Copy link
Collaborator

@numbata numbata commented Jun 1, 2025

Ruby 3’s stricter Proc arity rules could slip unwanted arguments into &:method_name exposures, causing confusing errors. This change detects pure symbol-to-proc wrappers up front, looks up the actual method’s arity on the entity object, and raises an error if it’s not zero. Regular blocks still use their own arity to decide whether to pass options. In practice, this makes it impossible to accidentally expose a one- or two-argument method with &:foo, ensuring only truly zero-argument methods are used in that shorthand.

Possible sollution #376

Chau Hong Linh and others added 6 commits June 1, 2025 03:47
Addresses `ArgumentError`s when using `&:method_name` with `expose`,
a scenario particularly affected by Ruby 3.0's stricter argument handling
for procs. The previous arity check, including the condition
`block.parameters == [[:req], [:rest]]`, was not consistently reliable
for these cases.

This change introduces `determine_block_arity` which:
1. Attempts to parse the original method name from `Proc#to_s` for
   blocks likely created via `&:method_name`.
2. If the method name is found and the object responds to it, this
   logic uses the *actual arity of the original method* to correctly
   determine how `instance_exec` should be called.
3. If the original method name can't be determined, it falls back
   to using `block.parameters.size`.

This ensures methods exposed via `&:method_name` are called with the
correct arguments (i.e., with or without `options`), resolving the
`ArgumentError`s and removing the need for the previous `rescue` logic.
Ruby 3.0’s stricter arity rules meant `&:method_name` blocks could be called with wrong args,
causing errors. The old check (`parameters == [[:req],[:rest]]` + `parameters.size`) was unreliable.

This update:

- Adds `symbol_to_proc_wrapper?` to detect pure `&:method_name` Procs (checks `lambda?`,
  `source_location.nil?`, and `parameters == [[:req],[:rest]]`).
- Introduces `determine_block_arity`, which parses the method name from `block.to_s`; if
  the object responds to it, it uses `object.method(name).arity`, otherwise falls back
  to `block.arity`.
- In `exec_with_object`, symbol-to-proc wrappers are required to have zero arity (raising
  `ArgumentError` otherwise), while regular Procs use `block.arity` to decide between
  `instance_exec(object)` and `instance_exec(object, options)`.

This removes rescue logic and ensures `&:method_name` is only used for zero-argument methods.
Ensure symbol-to-proc exposures only work for zero-argument methods:
- Raise if the method is undefined on the object
- Raise if the method expects one or more arguments
- Fall back to `Proc#arity` for regular blocks
@numbata
Copy link
Collaborator Author

numbata commented Jun 2, 2025

More details in #376 (comment)

@dblock dblock merged commit 4d71e0e into ruby-grape:master Jun 5, 2025
6 checks passed
@dblock
Copy link
Member

dblock commented Jun 5, 2025

I merged this following the "didn't break existing specs and added new ones" rule ;)

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.

2 participants