Skip to content

Commit 0a4de94

Browse files
authored
Add keyword arguments support in extensions (#5120)
* Add keyword arguments support in extensions * Check that keywords arguments are passed * Test perform calls with keyword arguments * Fix kwargs forwarding on Ruby 2.6
1 parent b3c99ca commit 0a4de94

File tree

6 files changed

+81
-8
lines changed

6 files changed

+81
-8
lines changed

Changes.md

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ bin/rails generate sidekiq:job ProcessOrderJob
2626
```
2727
- Fix job retries losing CurrentAttributes [#5090]
2828
- Tweak shutdown to give long-running threads time to cleanup [#5095]
29+
- Add keyword arguments support in extensions
2930

3031
6.3.1
3132
---------

lib/sidekiq/extensions/action_mailer.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ class DelayedMailer
1616
include Sidekiq::Worker
1717

1818
def perform(yml)
19-
(target, method_name, args) = YAML.load(yml)
20-
msg = target.public_send(method_name, *args)
19+
(target, method_name, args, kwargs) = YAML.load(yml)
20+
msg = kwargs.empty? ? target.public_send(method_name, *args) : target.public_send(method_name, *args, **kwargs)
2121
# The email method can return nil, which causes ActionMailer to return
2222
# an undeliverable empty message.
2323
if msg

lib/sidekiq/extensions/active_record.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ class DelayedModel
1818
include Sidekiq::Worker
1919

2020
def perform(yml)
21-
(target, method_name, args) = YAML.load(yml)
22-
target.__send__(method_name, *args)
21+
(target, method_name, args, kwargs) = YAML.load(yml)
22+
kwargs.empty? ? target.__send__(method_name, *args) : target.__send__(method_name, *args, **kwargs)
2323
end
2424
end
2525

lib/sidekiq/extensions/class_methods.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ class DelayedClass
1616
include Sidekiq::Worker
1717

1818
def perform(yml)
19-
(target, method_name, args) = YAML.load(yml)
20-
target.__send__(method_name, *args)
19+
(target, method_name, args, kwargs) = YAML.load(yml)
20+
kwargs.empty? ? target.__send__(method_name, *args) : target.__send__(method_name, *args, **kwargs)
2121
end
2222
end
2323

lib/sidekiq/extensions/generic_proxy.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ def initialize(performable, target, options = {})
1313
@opts = options
1414
end
1515

16-
def method_missing(name, *args)
16+
def method_missing(name, *args, **kwargs)
1717
# Sidekiq has a limitation in that its message must be JSON.
1818
# JSON can't round trip real Ruby objects so we use YAML to
1919
# serialize the objects to a String. The YAML will be converted
2020
# to JSON and then deserialized on the other side back into a
2121
# Ruby object.
22-
obj = [@target, name, args]
22+
obj = [@target, name, args, kwargs]
2323
marshalled = ::YAML.dump(obj)
2424
if marshalled.size > SIZE_LIMIT
2525
::Sidekiq.logger.warn { "#{@target}.#{name} job argument is #{marshalled.bytesize} bytes, you should refactor it to reduce the size" }

test/test_extensions.rb

+72
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ class MyModel < ActiveRecord::Base
1414
def self.long_class_method
1515
raise "Should not be called!"
1616
end
17+
18+
def self.long_class_method_with_optional_args(*arg, **kwargs)
19+
kwargs
20+
end
1721
end
1822

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

32+
it 'allows delayed execution of ActiveRecord class methods with optional arguments' do
33+
assert_equal [], Sidekiq::Queue.all.map(&:name)
34+
q = Sidekiq::Queue.new
35+
assert_equal 0, q.size
36+
MyModel.delay.long_class_method_with_optional_args(with: :keywords)
37+
assert_equal ['default'], Sidekiq::Queue.all.map(&:name)
38+
assert_equal 1, q.size
39+
obj = YAML.load q.first['args'].first
40+
assert_equal({ with: :keywords }, obj.last)
41+
end
42+
43+
it 'forwards the keyword arguments to perform' do
44+
yml = "---\n- !ruby/class 'MyModel'\n- :long_class_method_with_optional_args\n- []\n- :with: :keywords\n"
45+
result = Sidekiq::Extensions::DelayedClass.new.perform(yml)
46+
assert_equal({ with: :keywords }, result)
47+
end
48+
2849
it 'uses and stringifies specified options' do
2950
assert_equal [], Sidekiq::Queue.all.map(&:name)
3051
q = Sidekiq::Queue.new('notdefault')
@@ -53,6 +74,9 @@ class UserMailer < ActionMailer::Base
5374
def greetings(a, b)
5475
raise "Should not be called!"
5576
end
77+
78+
def greetings_with_optional_args(*arg, **kwargs)
79+
end
5680
end
5781

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

91+
it 'allows delayed delivery of ActionMailer mails with optional arguments' do
92+
assert_equal [], Sidekiq::Queue.all.map(&:name)
93+
q = Sidekiq::Queue.new
94+
assert_equal 0, q.size
95+
UserMailer.delay.greetings_with_optional_args(with: :keywords)
96+
assert_equal ['default'], Sidekiq::Queue.all.map(&:name)
97+
assert_equal 1, q.size
98+
obj = YAML.load q.first['args'].first
99+
assert_equal({ with: :keywords }, obj.last)
100+
end
101+
67102
it 'allows delayed scheduling of AM mails' do
68103
ss = Sidekiq::ScheduledSet.new
69104
assert_equal 0, ss.size
@@ -81,6 +116,10 @@ def greetings(a, b)
81116
class SomeClass
82117
def self.doit(arg)
83118
end
119+
120+
def self.doit_with_optional_args(*arg, **kwargs)
121+
kwargs
122+
end
84123
end
85124

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

132+
it 'allows delay of any ole class method with optional arguments' do
133+
q = Sidekiq::Queue.new
134+
assert_equal 0, q.size
135+
SomeClass.delay.doit_with_optional_args(with: :keywords)
136+
assert_equal 1, q.size
137+
obj = YAML.load q.first['args'].first
138+
assert_equal({ with: :keywords }, obj.last)
139+
end
140+
141+
it 'forwards the keyword arguments to perform' do
142+
yml = "---\n- !ruby/class 'SomeClass'\n- :doit_with_optional_args\n- []\n- :with: :keywords\n"
143+
result = Sidekiq::Extensions::DelayedClass.new.perform(yml)
144+
assert_equal({ with: :keywords }, result)
145+
end
146+
93147
module SomeModule
94148
def self.doit(arg)
95149
end
150+
151+
def self.doit_with_optional_args(*arg, **kwargs)
152+
kwargs
153+
end
96154
end
97155

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

170+
it 'allows delay of any module class method with optional arguments' do
171+
q = Sidekiq::Queue.new
172+
assert_equal 0, q.size
173+
SomeModule.delay.doit_with_optional_args(with: :keywords)
174+
assert_equal 1, q.size
175+
obj = YAML.load q.first['args'].first
176+
assert_equal({ with: :keywords }, obj.last)
177+
end
178+
179+
it 'forwards the keyword arguments to perform' do
180+
yml = "---\n- !ruby/class 'SomeModule'\n- :doit_with_optional_args\n- []\n- :with: :keywords\n"
181+
result = Sidekiq::Extensions::DelayedClass.new.perform(yml)
182+
assert_equal({ with: :keywords }, result)
183+
end
112184
end

0 commit comments

Comments
 (0)