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

Add keyword arguments support in extensions #5120

Merged
merged 4 commits into from Jan 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions Changes.md
Expand Up @@ -26,6 +26,7 @@ bin/rails generate sidekiq:job ProcessOrderJob
```
- Fix job retries losing CurrentAttributes [#5090]
- Tweak shutdown to give long-running threads time to cleanup [#5095]
- Add keyword arguments support in extensions

6.3.1
---------
Expand Down
4 changes: 2 additions & 2 deletions lib/sidekiq/extensions/action_mailer.rb
Expand Up @@ -16,8 +16,8 @@ class DelayedMailer
include Sidekiq::Worker

def perform(yml)
(target, method_name, args) = YAML.load(yml)
msg = target.public_send(method_name, *args)
(target, method_name, args, kwargs) = YAML.load(yml)
msg = kwargs.empty? ? target.public_send(method_name, *args) : target.public_send(method_name, *args, **kwargs)
# The email method can return nil, which causes ActionMailer to return
# an undeliverable empty message.
if msg
Expand Down
4 changes: 2 additions & 2 deletions lib/sidekiq/extensions/active_record.rb
Expand Up @@ -18,8 +18,8 @@ class DelayedModel
include Sidekiq::Worker

def perform(yml)
(target, method_name, args) = YAML.load(yml)
target.__send__(method_name, *args)
(target, method_name, args, kwargs) = YAML.load(yml)
kwargs.empty? ? target.__send__(method_name, *args) : target.__send__(method_name, *args, **kwargs)
end
end

Expand Down
4 changes: 2 additions & 2 deletions lib/sidekiq/extensions/class_methods.rb
Expand Up @@ -16,8 +16,8 @@ class DelayedClass
include Sidekiq::Worker

def perform(yml)
(target, method_name, args) = YAML.load(yml)
target.__send__(method_name, *args)
(target, method_name, args, kwargs) = YAML.load(yml)
kwargs.empty? ? target.__send__(method_name, *args) : target.__send__(method_name, *args, **kwargs)
end
end

Expand Down
4 changes: 2 additions & 2 deletions lib/sidekiq/extensions/generic_proxy.rb
Expand Up @@ -13,13 +13,13 @@ def initialize(performable, target, options = {})
@opts = options
end

def method_missing(name, *args)
def method_missing(name, *args, **kwargs)
# Sidekiq has a limitation in that its message must be JSON.
# JSON can't round trip real Ruby objects so we use YAML to
# serialize the objects to a String. The YAML will be converted
# to JSON and then deserialized on the other side back into a
# Ruby object.
obj = [@target, name, args]
obj = [@target, name, args, kwargs]
marshalled = ::YAML.dump(obj)
if marshalled.size > SIZE_LIMIT
::Sidekiq.logger.warn { "#{@target}.#{name} job argument is #{marshalled.bytesize} bytes, you should refactor it to reduce the size" }
Expand Down
72 changes: 72 additions & 0 deletions test/test_extensions.rb
Expand Up @@ -14,6 +14,10 @@ class MyModel < ActiveRecord::Base
def self.long_class_method
raise "Should not be called!"
end

def self.long_class_method_with_optional_args(*arg, **kwargs)
kwargs
end
end

it 'allows delayed execution of ActiveRecord class methods' do
Expand All @@ -25,6 +29,23 @@ def self.long_class_method
assert_equal 1, q.size
end

it 'allows delayed execution of ActiveRecord class methods with optional arguments' do
assert_equal [], Sidekiq::Queue.all.map(&:name)
q = Sidekiq::Queue.new
assert_equal 0, q.size
MyModel.delay.long_class_method_with_optional_args(with: :keywords)
assert_equal ['default'], Sidekiq::Queue.all.map(&:name)
assert_equal 1, q.size
obj = YAML.load q.first['args'].first
assert_equal({ with: :keywords }, obj.last)
end

it 'forwards the keyword arguments to perform' do
yml = "---\n- !ruby/class 'MyModel'\n- :long_class_method_with_optional_args\n- []\n- :with: :keywords\n"
result = Sidekiq::Extensions::DelayedClass.new.perform(yml)
assert_equal({ with: :keywords }, result)
end

it 'uses and stringifies specified options' do
assert_equal [], Sidekiq::Queue.all.map(&:name)
q = Sidekiq::Queue.new('notdefault')
Expand Down Expand Up @@ -53,6 +74,9 @@ class UserMailer < ActionMailer::Base
def greetings(a, b)
raise "Should not be called!"
end

def greetings_with_optional_args(*arg, **kwargs)
end
end

it 'allows delayed delivery of ActionMailer mails' do
Expand All @@ -64,6 +88,17 @@ def greetings(a, b)
assert_equal 1, q.size
end

it 'allows delayed delivery of ActionMailer mails with optional arguments' do
assert_equal [], Sidekiq::Queue.all.map(&:name)
q = Sidekiq::Queue.new
assert_equal 0, q.size
UserMailer.delay.greetings_with_optional_args(with: :keywords)
assert_equal ['default'], Sidekiq::Queue.all.map(&:name)
assert_equal 1, q.size
obj = YAML.load q.first['args'].first
assert_equal({ with: :keywords }, obj.last)
end

it 'allows delayed scheduling of AM mails' do
ss = Sidekiq::ScheduledSet.new
assert_equal 0, ss.size
Expand All @@ -81,6 +116,10 @@ def greetings(a, b)
class SomeClass
def self.doit(arg)
end

def self.doit_with_optional_args(*arg, **kwargs)
kwargs
end
end

it 'allows delay of any ole class method' do
Expand All @@ -90,9 +129,28 @@ def self.doit(arg)
assert_equal 1, q.size
end

it 'allows delay of any ole class method with optional arguments' do
q = Sidekiq::Queue.new
assert_equal 0, q.size
SomeClass.delay.doit_with_optional_args(with: :keywords)
assert_equal 1, q.size
obj = YAML.load q.first['args'].first
assert_equal({ with: :keywords }, obj.last)
end

it 'forwards the keyword arguments to perform' do
yml = "---\n- !ruby/class 'SomeClass'\n- :doit_with_optional_args\n- []\n- :with: :keywords\n"
result = Sidekiq::Extensions::DelayedClass.new.perform(yml)
assert_equal({ with: :keywords }, result)
end

module SomeModule
def self.doit(arg)
end

def self.doit_with_optional_args(*arg, **kwargs)
kwargs
end
end

it 'logs large payloads' do
Expand All @@ -109,4 +167,18 @@ def self.doit(arg)
assert_equal 1, q.size
end

it 'allows delay of any module class method with optional arguments' do
q = Sidekiq::Queue.new
assert_equal 0, q.size
SomeModule.delay.doit_with_optional_args(with: :keywords)
assert_equal 1, q.size
obj = YAML.load q.first['args'].first
assert_equal({ with: :keywords }, obj.last)
end

it 'forwards the keyword arguments to perform' do
yml = "---\n- !ruby/class 'SomeModule'\n- :doit_with_optional_args\n- []\n- :with: :keywords\n"
result = Sidekiq::Extensions::DelayedClass.new.perform(yml)
assert_equal({ with: :keywords }, result)
end
end