Skip to content
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

Support Ruby 3.0 kwarg-handling within .delay API #7

Merged
merged 8 commits into from
Nov 30, 2021

Conversation

smudge
Copy link
Member

@smudge smudge commented Nov 29, 2021

/domain @effron @samandmoore
/platform @effron @samandmoore

Resolves #6. While we already fully support Ruby 3.0 kwargs via ActiveJob, the legacy delay and handle_asynchronously APIs did not support the new separation of positional and keyword arguments introduced in 2.7 and enforced in 3.0.

This means that methods accepting both positional and keyword arguments (e.g. def foo(a, b:)) would fail with a wrong number of arguments (given 2, expected 1; required keyword:) error on Ruby 3. In order to resolve this, this PR changes Delayed::PerformableMethod to handle kwargs separately from args, with a backwards compatibility check for any Ruby 2.6 methods that do not accept keyword arguments. It should also support jobs that were enqueued by the prior delayed gem version (where the new kwargs accessor would be nil, and args contains the kwargs as its last item).

@nanda-prbot
Copy link

Needs somebody from @effron and @samandmoore to claim domain review
Needs somebody from @effron and @samandmoore to claim platform review

Use the shovel operator to claim, e.g.:

@myname << domain && platform

HOW TO: Claim a Review

def send_at(time, method, *args)
warn '[DEPRECATION] `object.send_at(time, :method)` is deprecated. Use `object.delay(:run_at => time).method'
__delay__(run_at: time).__send__(method, *args)
end
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing these -- they were already deprecated prior to our fork.

@effron
Copy link
Contributor

effron commented Nov 29, 2021

<<domainLGTM

@nanda-prbot
Copy link

Needs somebody from @effron and @samandmoore to claim platform review

Use the shovel operator to claim, e.g.:

@myname << domain && platform

HOW TO: Claim a Review


if defined?(ActionMailer::Parameterized::Mailer)
describe ActionMailer::Parameterized::Mailer do
describe 'delay' do
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't actually add this section -- I just reorganized the file a little.

@samandmoore
Copy link
Member

<<platformlgtm

nanda-prbot
nanda-prbot previously approved these changes Nov 29, 2021
Copy link

@nanda-prbot nanda-prbot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved! 👯 😻 🙆‍♀️

object.send(method_name, *args) if object
return unless object

if kwargs.nil? || (RUBY_VERSION < '2.7' && kwargs.empty?)
Copy link
Member Author

@smudge smudge Nov 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a proposed PR in the one of the ancestor repos, but for this PR I took a very different strategy, because we want to both maintain Ruby 2.6 support and support the Ruby 3 case where both positional and keyword arguments are present. Holding onto kwargs separately resolves edge cases around argument auto-merging behaviors, but then on Ruby 2.6 we need to fall back in the **{} case (since on 2.6 and below that causes {} to get passed as a positional argument).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I think that this should resolve collectiveidea/delayed_job#1134 and might be worth upstreaming, if someone gets a chance.

@smudge smudge changed the title Support Ruby 3.0 kwarg-handling via legacy .delay API Support Ruby 3.0 kwarg-handling via .delay API Nov 29, 2021
@nanda-prbot
Copy link

Approved! 🍩 👍 ✨

@smudge smudge changed the title Support Ruby 3.0 kwarg-handling via .delay API Support Ruby 3.0 kwarg-handling within .delay API Nov 29, 2021
@nanda-prbot
Copy link

Approved! 🎏 🎁 🌯

@nanda-prbot
Copy link

This PR requires additional review because of new changes

Please get another domain review from @effron, or another reviewer with write access if unavailable.

@samandmoore
Copy link
Member

nice. glad you tested it :)

domainltm

@samandmoore
Copy link
Member

ugh domainlgtm

curse this keyboard!

nanda-prbot
nanda-prbot previously approved these changes Nov 29, 2021
Copy link

@nanda-prbot nanda-prbot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved! 🙏 ⭐ 🙆‍♀️

@nanda-prbot
Copy link

This PR requires additional review because of new changes

Please get another domain review from one of @effron, @samandmoore, or another reviewer with write access if unavailable.

@smudge
Copy link
Member Author

smudge commented Nov 30, 2021

Sorry for the review churn! I've been testing this in the context of our largest Rails monorepo, and seem to have ironed out the kinks, so I'd like to cut a new version all in one go.

@samandmoore
Copy link
Member

domainlgtm

Copy link

@nanda-prbot nanda-prbot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved! 👏 🍕 💥

object.send(method_name, *args) if object
return unless object

if kwargs.nil? || (RUBY_VERSION < '2.7' && kwargs.empty?)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can check if the callee uses kwargs. First get the method object with object.method(method_name) and check its parameter list for :key, :keyreq or :keyrest. (From experience, Rails Mailer magical methods end up with a single :rest argument that you'd need to account for as well.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, I don't think we want it to actually care about the arity of the method it's about to call -- only the arity that was provided to it by the enqueuing call. (For example, if I do obj.delay.something(test: 123) on a method that does not accept kwargs, I want to ensure that the job raises an error and tells me that my arguments are wrong.)

So the fix here is really just to prevent **{} from breaking on Ruby 2.6 (because trying to pass that as a kwarg results in it passing {} as a positional arg). This quirk was fixed in 2.7.

@effron
Copy link
Contributor

effron commented Nov 30, 2021

domainLGTM

@nanda-prbot
Copy link

Approved! 💯 🍕 💅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Ruby 3 support
5 participants