delayed 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6690016ceeb6764ea0d848c7b8f065f1c5c7587b1a6f519f81f90131c2a77c30
4
- data.tar.gz: 83dc7b900584296c9c383ca5171f087da2c71cbbc44e5dca1898e03e2716ab6f
3
+ metadata.gz: ec0bc05440d0ea5392480d83c4a3295aecfcad71a47fc48b16de19d356387e8d
4
+ data.tar.gz: 2a9b09c4a77f058e8fe322f9b80da8aed4b909b20e3ebbcc33da1277435407d0
5
5
  SHA512:
6
- metadata.gz: d39684e6f3d1fafff2ee632ec243ca5e387f30fa970125f16c456410c5a69ea25fcf906b61d7015e53e40743f9d76133f3054c3fcb781c381c46ea9454d44d42
7
- data.tar.gz: '08f057ac20053b5e7a04c98a5b14214ebf43ff0056798a456229482303057c7a68eb08d84d4724d0c3058348ec04d9a3f08db9f1bc5f2dd4528de9161e405ddb'
6
+ metadata.gz: 183b8ad1b96cbe2ce987a6a2faa449b90963a898ae2ffccb7f99eacbeb94be5cedae452bc2ed884414e6cfe2a8169481192d9269e2111b80b3be2d805a5cac83
7
+ data.tar.gz: 75ad14cf19505ee4b0152797cbee705dbe712939a5e5a0f3bd960a9ddad2c25756e0aedbc8745e70e40d5b634bdf96ef5d175c81f88a86308b524a1f97b2356c
@@ -8,8 +8,8 @@ module Delayed
8
8
  @options = options
9
9
  end
10
10
 
11
- def method_missing(method, *args)
12
- Job.enqueue({ payload_object: @payload_class.new(@target, method.to_sym, args) }.merge(@options))
11
+ def method_missing(method, *args, **kwargs)
12
+ Job.enqueue({ payload_object: @payload_class.new(@target, method.to_sym, args, kwargs) }.merge(@options))
13
13
  end
14
14
  end
15
15
 
@@ -18,16 +18,6 @@ module Delayed
18
18
  DelayProxy.new(PerformableMethod, self, options)
19
19
  end
20
20
  alias __delay__ delay
21
-
22
- def send_later(method, *args)
23
- warn '[DEPRECATION] `object.send_later(:method)` is deprecated. Use `object.delay.method'
24
- __delay__.__send__(method, *args)
25
- end
26
-
27
- def send_at(time, method, *args)
28
- warn '[DEPRECATION] `object.send_at(time, :method)` is deprecated. Use `object.delay(:run_at => time).method'
29
- __delay__(run_at: time).__send__(method, *args)
30
- end
31
21
  end
32
22
 
33
23
  module MessageSendingClassMethods
@@ -36,7 +26,7 @@ module Delayed
36
26
  punctuation = $1 # rubocop:disable Style/PerlBackrefs
37
27
  with_method = "#{aliased_method}_with_delay#{punctuation}"
38
28
  without_method = "#{aliased_method}_without_delay#{punctuation}"
39
- define_method(with_method) do |*args|
29
+ define_method(with_method) do |*args, **kwargs|
40
30
  curr_opts = opts.clone
41
31
  curr_opts.each_key do |key|
42
32
  next unless (val = curr_opts[key]).is_a?(Proc)
@@ -47,7 +37,7 @@ module Delayed
47
37
  val.call
48
38
  end
49
39
  end
50
- delay(curr_opts).__send__(without_method, *args)
40
+ delay(curr_opts).__send__(without_method, *args, **kwargs)
51
41
  end
52
42
 
53
43
  alias_method without_method, method
@@ -3,7 +3,7 @@ require 'mail'
3
3
  module Delayed
4
4
  class PerformableMailer < PerformableMethod
5
5
  def perform
6
- mailer = object.send(method_name, *args)
6
+ mailer = super
7
7
  mailer.respond_to?(:deliver_now) ? mailer.deliver_now : mailer.deliver
8
8
  end
9
9
  end
@@ -1,8 +1,8 @@
1
1
  module Delayed
2
2
  class PerformableMethod
3
- attr_accessor :object, :method_name, :args
3
+ attr_accessor :object, :method_name, :args, :kwargs
4
4
 
5
- def initialize(object, method_name, args)
5
+ def initialize(object, method_name, args, kwargs)
6
6
  raise NoMethodError, "undefined method `#{method_name}' for #{object.inspect}" unless object.respond_to?(method_name, true)
7
7
 
8
8
  if !her_model?(object) && object.respond_to?(:persisted?) && !object.persisted?
@@ -11,6 +11,7 @@ module Delayed
11
11
 
12
12
  self.object = object
13
13
  self.args = args
14
+ self.kwargs = kwargs
14
15
  self.method_name = method_name.to_sym
15
16
  end
16
17
 
@@ -23,7 +24,13 @@ module Delayed
23
24
  end
24
25
 
25
26
  def perform
26
- object.send(method_name, *args) if object
27
+ return unless object
28
+
29
+ if kwargs.nil? || (RUBY_VERSION < '2.7' && kwargs.empty?)
30
+ object.send(method_name, *args)
31
+ else
32
+ object.send(method_name, *args, **kwargs)
33
+ end
27
34
  end
28
35
 
29
36
  def method(sym)
@@ -6,6 +6,7 @@ module Delayed
6
6
  'object' => object,
7
7
  'method_name' => method_name,
8
8
  'args' => args,
9
+ 'kwargs' => kwargs,
9
10
  }
10
11
  end
11
12
  end
@@ -223,6 +223,25 @@ RSpec.describe Delayed::ActiveJobAdapter do
223
223
  end
224
224
  end
225
225
 
226
+ context 'when ActiveJob has both positional and keyword arguments' do
227
+ let(:job_class) do
228
+ Class.new(ActiveJob::Base) do # rubocop:disable Rails/ApplicationJob
229
+ cattr_accessor(:result)
230
+
231
+ def perform(arg, kwarg:)
232
+ self.class.result = [arg, kwarg]
233
+ end
234
+ end
235
+ end
236
+
237
+ it 'passes arguments through to the perform method' do
238
+ JobClass.perform_later('foo', kwarg: 'bar')
239
+
240
+ Delayed::Worker.new.work_off
241
+ expect(JobClass.result).to eq %w(foo bar)
242
+ end
243
+ end
244
+
226
245
  context 'when using the ActiveJob test adapter' do
227
246
  let(:queue_adapter) { :test }
228
247
 
@@ -352,7 +352,7 @@ describe Delayed::Job do
352
352
  context 'large handler' do
353
353
  before do
354
354
  text = 'Lorem ipsum dolor sit amet. ' * 1000
355
- @job = described_class.enqueue Delayed::PerformableMethod.new(text, :length, {})
355
+ @job = described_class.enqueue Delayed::PerformableMethod.new(text, :length, [], {})
356
356
  end
357
357
 
358
358
  it 'has an id' do
@@ -7,24 +7,27 @@ describe Delayed::MessageSending do
7
7
  end
8
8
 
9
9
  describe 'handle_asynchronously' do
10
- class Story
11
- def tell!(_arg); end
12
- handle_asynchronously :tell!
10
+ let(:test_class) do
11
+ Class.new do
12
+ def tell!(_arg, _kwarg:); end
13
+ handle_asynchronously :tell!
14
+ end
13
15
  end
14
16
 
15
17
  it 'aliases original method' do
16
- expect(Story.new).to respond_to(:tell_without_delay!)
17
- expect(Story.new).to respond_to(:tell_with_delay!)
18
+ expect(test_class.new).to respond_to(:tell_without_delay!)
19
+ expect(test_class.new).to respond_to(:tell_with_delay!)
18
20
  end
19
21
 
20
22
  it 'creates a PerformableMethod' do
21
- story = Story.create
23
+ obj = test_class.new
22
24
  expect {
23
- job = story.tell!(1)
25
+ job = obj.tell!('a', kwarg: 'b')
24
26
  expect(job.payload_object.class).to eq(Delayed::PerformableMethod)
25
27
  expect(job.payload_object.method_name).to eq(:tell_without_delay!)
26
- expect(job.payload_object.args).to eq([1])
27
- }.to(change { Delayed::Job.count })
28
+ expect(job.payload_object.args).to eq(['a'])
29
+ expect(job.payload_object.kwargs).to eq(kwarg: 'b')
30
+ }.to change { Delayed::Job.count }.by(1)
28
31
  end
29
32
 
30
33
  describe 'with options' do
@@ -64,26 +67,33 @@ describe Delayed::MessageSending do
64
67
  end
65
68
 
66
69
  context 'delay' do
67
- class FairyTail
68
- attr_accessor :happy_ending
70
+ let(:fairy_tail_class) do
71
+ Class.new do
72
+ attr_accessor :happy_ending
69
73
 
70
- def self.princesses; end
74
+ def self.princesses; end
71
75
 
72
- def tell
73
- @happy_ending = true
76
+ def tell(arg, kwarg:)
77
+ @happy_ending = [arg, kwarg]
78
+ end
74
79
  end
75
80
  end
76
81
 
82
+ before do
83
+ stub_const('FairyTail', fairy_tail_class)
84
+ end
85
+
77
86
  after do
78
87
  Delayed::Worker.default_queue_name = nil
79
88
  end
80
89
 
81
90
  it 'creates a new PerformableMethod job' do
82
91
  expect {
83
- job = 'hello'.delay.count('l')
92
+ job = FairyTail.new.delay.tell('arg', kwarg: 'kwarg')
84
93
  expect(job.payload_object.class).to eq(Delayed::PerformableMethod)
85
- expect(job.payload_object.method_name).to eq(:count)
86
- expect(job.payload_object.args).to eq(['l'])
94
+ expect(job.payload_object.method_name).to eq(:tell)
95
+ expect(job.payload_object.args).to eq(['arg'])
96
+ expect(job.payload_object.kwargs).to eq(kwarg: 'kwarg')
87
97
  }.to change { Delayed::Job.count }.by(1)
88
98
  end
89
99
 
@@ -111,8 +121,8 @@ describe Delayed::MessageSending do
111
121
  fairy_tail = FairyTail.new
112
122
  expect {
113
123
  expect {
114
- fairy_tail.delay.tell
115
- }.to change { fairy_tail.happy_ending }.from(nil).to(true)
124
+ fairy_tail.delay.tell('a', kwarg: 'b')
125
+ }.to change { fairy_tail.happy_ending }.from(nil).to %w(a b)
116
126
  }.not_to(change { Delayed::Job.count })
117
127
  end
118
128
 
@@ -121,7 +131,7 @@ describe Delayed::MessageSending do
121
131
  fairy_tail = FairyTail.new
122
132
  expect {
123
133
  expect {
124
- fairy_tail.delay.tell
134
+ fairy_tail.delay.tell('a', kwarg: 'b')
125
135
  }.not_to change { fairy_tail.happy_ending }
126
136
  }.to change { Delayed::Job.count }.by(1)
127
137
  end
@@ -131,7 +141,7 @@ describe Delayed::MessageSending do
131
141
  fairy_tail = FairyTail.new
132
142
  expect {
133
143
  expect {
134
- fairy_tail.delay.tell
144
+ fairy_tail.delay.tell('a', kwarg: 'b')
135
145
  }.not_to change { fairy_tail.happy_ending }
136
146
  }.to change { Delayed::Job.count }.by(1)
137
147
  end
@@ -141,8 +151,8 @@ describe Delayed::MessageSending do
141
151
  fairy_tail = FairyTail.new
142
152
  expect {
143
153
  expect {
144
- fairy_tail.delay.tell
145
- }.to change { fairy_tail.happy_ending }.from(nil).to(true)
154
+ fairy_tail.delay.tell('a', kwarg: 'b')
155
+ }.to change { fairy_tail.happy_ending }.from(nil).to %w(a b)
146
156
  }.not_to(change { Delayed::Job.count })
147
157
  end
148
158
  end
@@ -1,58 +1,41 @@
1
1
  require 'helper'
2
2
 
3
- class MyMailer < ActionMailer::Base
4
- def signup(email)
5
- mail to: email, subject: 'Delaying Emails', from: 'delayedjob@example.com', body: 'Delaying Emails Body'
6
- end
7
- end
3
+ describe Delayed::PerformableMailer do
4
+ let(:mailer_class) do
5
+ Class.new(ActionMailer::Base) do
6
+ cattr_accessor(:emails) { [] }
8
7
 
9
- describe ActionMailer::Base do
10
- describe 'delay' do
11
- it 'enqueues a PerformableEmail job' do
12
- expect {
13
- job = MyMailer.delay.signup('john@example.com')
14
- expect(job.payload_object.class).to eq(Delayed::PerformableMailer)
15
- expect(job.payload_object.method_name).to eq(:signup)
16
- expect(job.payload_object.args).to eq(['john@example.com'])
17
- }.to change { Delayed::Job.count }.by(1)
8
+ def signup(email, beta_tester: false)
9
+ mail to: email, subject: "Delaying Emails (beta: #{beta_tester})", from: 'delayedjob@example.com', body: 'Delaying Emails Body'
10
+ end
18
11
  end
19
12
  end
20
13
 
21
- describe 'delay on a mail object' do
22
- it 'raises an exception' do
23
- expect {
24
- MyMailer.signup('john@example.com').delay
25
- }.to raise_error(RuntimeError)
26
- end
14
+ before do
15
+ stub_const('MyMailer', mailer_class)
27
16
  end
28
17
 
29
- describe Delayed::PerformableMailer do
30
- describe 'perform' do
31
- it 'calls the method and #deliver on the mailer' do
32
- email = double('email', deliver: true)
33
- mailer_class = double('MailerClass', signup: email)
34
- mailer = described_class.new(mailer_class, :signup, ['john@example.com'])
18
+ describe 'perform' do
19
+ it 'calls the method and #deliver on the mailer' do
20
+ mailer = MyMailer.new
21
+ email = double('email', deliver: true)
22
+ allow(mailer).to receive(:mail).and_return(email)
23
+ mailer_job = described_class.new(mailer, :signup, ['john@example.com'], {})
35
24
 
36
- expect(mailer_class).to receive(:signup).with('john@example.com')
37
- expect(email).to receive(:deliver)
38
- mailer.perform
39
- end
25
+ expect(email).to receive(:deliver)
26
+ mailer_job.perform
40
27
  end
41
28
  end
42
- end
43
29
 
44
- if defined?(ActionMailer::Parameterized::Mailer)
45
- describe ActionMailer::Parameterized::Mailer do
30
+ describe ActionMailer::Base do
46
31
  describe 'delay' do
47
32
  it 'enqueues a PerformableEmail job' do
48
33
  expect {
49
- job = MyMailer.with(foo: 1, bar: 2).delay.signup('john@example.com')
34
+ job = MyMailer.delay.signup('john@example.com', beta_tester: true)
50
35
  expect(job.payload_object.class).to eq(Delayed::PerformableMailer)
51
- expect(job.payload_object.object.class).to eq(described_class)
52
- expect(job.payload_object.object.instance_variable_get('@mailer')).to eq(MyMailer)
53
- expect(job.payload_object.object.instance_variable_get('@params')).to eq(foo: 1, bar: 2)
54
36
  expect(job.payload_object.method_name).to eq(:signup)
55
37
  expect(job.payload_object.args).to eq(['john@example.com'])
38
+ expect(job.payload_object.kwargs).to eq(beta_tester: true)
56
39
  }.to change { Delayed::Job.count }.by(1)
57
40
  end
58
41
  end
@@ -60,9 +43,36 @@ if defined?(ActionMailer::Parameterized::Mailer)
60
43
  describe 'delay on a mail object' do
61
44
  it 'raises an exception' do
62
45
  expect {
63
- MyMailer.with(foo: 1, bar: 2).signup('john@example.com').delay
46
+ MyMailer.signup('john@example.com').delay
64
47
  }.to raise_error(RuntimeError)
65
48
  end
66
49
  end
67
50
  end
51
+
52
+ if defined?(ActionMailer::Parameterized::Mailer)
53
+ describe ActionMailer::Parameterized::Mailer do
54
+ describe 'delay' do
55
+ it 'enqueues a PerformableEmail job' do
56
+ expect {
57
+ job = MyMailer.with(foo: 1, bar: 2).delay.signup('john@example.com', beta_tester: false)
58
+ expect(job.payload_object.class).to eq(Delayed::PerformableMailer)
59
+ expect(job.payload_object.object.class).to eq(described_class)
60
+ expect(job.payload_object.object.instance_variable_get('@mailer')).to eq(MyMailer)
61
+ expect(job.payload_object.object.instance_variable_get('@params')).to eq(foo: 1, bar: 2)
62
+ expect(job.payload_object.method_name).to eq(:signup)
63
+ expect(job.payload_object.args).to eq(['john@example.com'])
64
+ expect(job.payload_object.kwargs).to eq(beta_tester: false)
65
+ }.to change { Delayed::Job.count }.by(1)
66
+ end
67
+ end
68
+
69
+ describe 'delay on a mail object' do
70
+ it 'raises an exception' do
71
+ expect {
72
+ MyMailer.with(foo: 1, bar: 2).signup('john@example.com').delay
73
+ }.to raise_error(RuntimeError)
74
+ end
75
+ end
76
+ end
77
+ end
68
78
  end
@@ -2,8 +2,18 @@ require 'helper'
2
2
 
3
3
  describe Delayed::PerformableMethod do
4
4
  describe 'perform' do
5
+ let(:test_class) do
6
+ Class.new do
7
+ cattr_accessor :result
8
+
9
+ def foo(arg, kwarg:)
10
+ self.class.result = [arg, kwarg]
11
+ end
12
+ end
13
+ end
14
+
5
15
  before do
6
- @method = described_class.new('foo', :count, ['o'])
16
+ @method = described_class.new(test_class.new, :foo, ['a'], { kwarg: 'b' })
7
17
  end
8
18
 
9
19
  context 'with the persisted record cannot be found' do
@@ -17,14 +27,29 @@ describe Delayed::PerformableMethod do
17
27
  end
18
28
 
19
29
  it 'calls the method on the object' do
20
- expect(@method.object).to receive(:count).with('o')
21
- @method.perform
30
+ expect { @method.perform }
31
+ .to change { test_class.result }
32
+ .from(nil).to %w(a b)
33
+ end
34
+
35
+ if RUBY_VERSION < '3.0'
36
+ context 'when kwargs are nil (job was delayed via prior gem version)' do
37
+ before do
38
+ @method = described_class.new(test_class.new, :foo, ['a', { kwarg: 'b' }], nil)
39
+ end
40
+
41
+ it 'calls the method on the object' do
42
+ expect { @method.perform }
43
+ .to change { test_class.result }
44
+ .from(nil).to %w(a b)
45
+ end
46
+ end
22
47
  end
23
48
  end
24
49
 
25
50
  it "raises a NoMethodError if target method doesn't exist" do
26
51
  expect {
27
- described_class.new(Object, :method_that_does_not_exist, [])
52
+ described_class.new(Object, :method_that_does_not_exist, [], {})
28
53
  }.to raise_error(NoMethodError)
29
54
  end
30
55
 
@@ -33,29 +58,29 @@ describe Delayed::PerformableMethod do
33
58
  def private_method; end
34
59
  private :private_method
35
60
  end
36
- expect { described_class.new(clazz.new, :private_method, []) }.not_to raise_error
61
+ expect { described_class.new(clazz.new, :private_method, [], {}) }.not_to raise_error
37
62
  end
38
63
 
39
64
  context 'when it receives an object that is not persisted' do
40
65
  let(:object) { double(persisted?: false, expensive_operation: true) }
41
66
 
42
67
  it 'raises an ArgumentError' do
43
- expect { described_class.new(object, :expensive_operation, []) }.to raise_error ArgumentError
68
+ expect { described_class.new(object, :expensive_operation, [], {}) }.to raise_error ArgumentError
44
69
  end
45
70
 
46
71
  it 'does not raise ArgumentError if the object acts like a Her model' do
47
72
  allow(object.class).to receive(:save_existing).and_return(true)
48
- expect { described_class.new(object, :expensive_operation, []) }.not_to raise_error
73
+ expect { described_class.new(object, :expensive_operation, [], {}) }.not_to raise_error
49
74
  end
50
75
  end
51
76
 
52
77
  describe 'display_name' do
53
78
  it 'returns class_name#method_name for instance methods' do
54
- expect(described_class.new('foo', :count, ['o']).display_name).to eq('String#count')
79
+ expect(described_class.new('foo', :count, ['o'], {}).display_name).to eq('String#count')
55
80
  end
56
81
 
57
82
  it 'returns class_name.method_name for class methods' do
58
- expect(described_class.new(Class, :inspect, []).display_name).to eq('Class.inspect')
83
+ expect(described_class.new(Class, :inspect, [], {}).display_name).to eq('Class.inspect')
59
84
  end
60
85
  end
61
86
 
@@ -84,7 +109,7 @@ describe Delayed::PerformableMethod do
84
109
  end
85
110
 
86
111
  it 'delegates failure hook to object' do
87
- method = described_class.new('object', :size, [])
112
+ method = described_class.new('object', :size, [], {})
88
113
  expect(method.object).to receive(:failure)
89
114
  method.failure
90
115
  end
@@ -114,7 +139,7 @@ describe Delayed::PerformableMethod do
114
139
  end
115
140
 
116
141
  it 'delegates failure hook to object' do
117
- method = described_class.new('object', :size, [])
142
+ method = described_class.new('object', :size, [], {})
118
143
  expect(method.object).to receive(:failure)
119
144
  method.failure
120
145
  end
@@ -11,6 +11,30 @@ describe 'Psych::Visitors::ToRuby', if: defined?(Psych::Visitors::ToRuby) do
11
11
  end
12
12
  end
13
13
 
14
+ context Delayed::PerformableMethod do
15
+ it 'serializes with object, method_name, args, and kwargs' do
16
+ Delayed::PerformableMethod.new(String, :new, ['hello'], capacity: 20).tap do |pm|
17
+ serialized = YAML.dump_dj(pm)
18
+ expect(serialized).to eq <<~YAML
19
+ --- !ruby/object:Delayed::PerformableMethod
20
+ object: !ruby/class 'String'
21
+ method_name: :new
22
+ args:
23
+ - hello
24
+ kwargs:
25
+ :capacity: 20
26
+ YAML
27
+
28
+ deserialized = YAML.load_dj(serialized)
29
+ expect(deserialized).to be_an_instance_of(Delayed::PerformableMethod)
30
+ expect(deserialized.object).to eq String
31
+ expect(deserialized.method_name).to eq :new
32
+ expect(deserialized.args).to eq ['hello']
33
+ expect(deserialized.kwargs).to eq(capacity: 20)
34
+ end
35
+ end
36
+ end
37
+
14
38
  context ActiveRecord::Base do
15
39
  it 'serializes and deserializes in a version-independent way' do
16
40
  Story.create.tap do |story|
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: delayed
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathan Griffith
@@ -19,7 +19,7 @@ authors:
19
19
  autorequire:
20
20
  bindir: bin
21
21
  cert_chain: []
22
- date: 2021-10-26 00:00:00.000000000 Z
22
+ date: 2021-11-30 00:00:00.000000000 Z
23
23
  dependencies:
24
24
  - !ruby/object:Gem::Dependency
25
25
  name: activerecord
@@ -290,6 +290,7 @@ metadata:
290
290
  changelog_uri: https://github.com/betterment/delayed/blob/main/CHANGELOG.md
291
291
  bug_tracker_uri: https://github.com/betterment/delayed/issues
292
292
  source_code_uri: https://github.com/betterment/delayed
293
+ rubygems_mfa_required: 'true'
293
294
  post_install_message:
294
295
  rdoc_options: []
295
296
  require_paths: