activejob 5.2.3
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 +7 -0
- data/CHANGELOG.md +80 -0
- data/MIT-LICENSE +21 -0
- data/README.md +126 -0
- data/lib/active_job.rb +39 -0
- data/lib/active_job/arguments.rb +165 -0
- data/lib/active_job/base.rb +74 -0
- data/lib/active_job/callbacks.rb +155 -0
- data/lib/active_job/configured_job.rb +18 -0
- data/lib/active_job/core.rb +158 -0
- data/lib/active_job/enqueuing.rb +59 -0
- data/lib/active_job/exceptions.rb +134 -0
- data/lib/active_job/execution.rb +49 -0
- data/lib/active_job/gem_version.rb +17 -0
- data/lib/active_job/logging.rb +130 -0
- data/lib/active_job/queue_adapter.rb +60 -0
- data/lib/active_job/queue_adapters.rb +139 -0
- data/lib/active_job/queue_adapters/async_adapter.rb +116 -0
- data/lib/active_job/queue_adapters/backburner_adapter.rb +36 -0
- data/lib/active_job/queue_adapters/delayed_job_adapter.rb +47 -0
- data/lib/active_job/queue_adapters/inline_adapter.rb +23 -0
- data/lib/active_job/queue_adapters/qu_adapter.rb +46 -0
- data/lib/active_job/queue_adapters/que_adapter.rb +39 -0
- data/lib/active_job/queue_adapters/queue_classic_adapter.rb +58 -0
- data/lib/active_job/queue_adapters/resque_adapter.rb +53 -0
- data/lib/active_job/queue_adapters/sidekiq_adapter.rb +47 -0
- data/lib/active_job/queue_adapters/sneakers_adapter.rb +48 -0
- data/lib/active_job/queue_adapters/sucker_punch_adapter.rb +49 -0
- data/lib/active_job/queue_adapters/test_adapter.rb +67 -0
- data/lib/active_job/queue_name.rb +49 -0
- data/lib/active_job/queue_priority.rb +43 -0
- data/lib/active_job/railtie.rb +34 -0
- data/lib/active_job/test_case.rb +11 -0
- data/lib/active_job/test_helper.rb +456 -0
- data/lib/active_job/translation.rb +13 -0
- data/lib/active_job/version.rb +10 -0
- data/lib/rails/generators/job/job_generator.rb +40 -0
- data/lib/rails/generators/job/templates/application_job.rb.tt +9 -0
- data/lib/rails/generators/job/templates/job.rb.tt +9 -0
- metadata +110 -0
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveJob
|
4
|
+
module QueueAdapters
|
5
|
+
# == Test adapter for Active Job
|
6
|
+
#
|
7
|
+
# The test adapter should be used only in testing. Along with
|
8
|
+
# <tt>ActiveJob::TestCase</tt> and <tt>ActiveJob::TestHelper</tt>
|
9
|
+
# it makes a great tool to test your Rails application.
|
10
|
+
#
|
11
|
+
# To use the test adapter set queue_adapter config to +:test+.
|
12
|
+
#
|
13
|
+
# Rails.application.config.active_job.queue_adapter = :test
|
14
|
+
class TestAdapter
|
15
|
+
attr_accessor(:perform_enqueued_jobs, :perform_enqueued_at_jobs, :filter, :reject)
|
16
|
+
attr_writer(:enqueued_jobs, :performed_jobs)
|
17
|
+
|
18
|
+
# Provides a store of all the enqueued jobs with the TestAdapter so you can check them.
|
19
|
+
def enqueued_jobs
|
20
|
+
@enqueued_jobs ||= []
|
21
|
+
end
|
22
|
+
|
23
|
+
# Provides a store of all the performed jobs with the TestAdapter so you can check them.
|
24
|
+
def performed_jobs
|
25
|
+
@performed_jobs ||= []
|
26
|
+
end
|
27
|
+
|
28
|
+
def enqueue(job) #:nodoc:
|
29
|
+
return if filtered?(job)
|
30
|
+
|
31
|
+
job_data = job_to_hash(job)
|
32
|
+
enqueue_or_perform(perform_enqueued_jobs, job, job_data)
|
33
|
+
end
|
34
|
+
|
35
|
+
def enqueue_at(job, timestamp) #:nodoc:
|
36
|
+
return if filtered?(job)
|
37
|
+
|
38
|
+
job_data = job_to_hash(job, at: timestamp)
|
39
|
+
enqueue_or_perform(perform_enqueued_at_jobs, job, job_data)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
def job_to_hash(job, extras = {})
|
44
|
+
{ job: job.class, args: job.serialize.fetch("arguments"), queue: job.queue_name }.merge!(extras)
|
45
|
+
end
|
46
|
+
|
47
|
+
def enqueue_or_perform(perform, job, job_data)
|
48
|
+
if perform
|
49
|
+
performed_jobs << job_data
|
50
|
+
Base.execute job.serialize
|
51
|
+
else
|
52
|
+
enqueued_jobs << job_data
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def filtered?(job)
|
57
|
+
if filter
|
58
|
+
!Array(filter).include?(job.class)
|
59
|
+
elsif reject
|
60
|
+
Array(reject).include?(job.class)
|
61
|
+
else
|
62
|
+
false
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveJob
|
4
|
+
module QueueName
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
# Includes the ability to override the default queue name and prefix.
|
8
|
+
module ClassMethods
|
9
|
+
mattr_accessor :queue_name_prefix
|
10
|
+
mattr_accessor :default_queue_name, default: "default"
|
11
|
+
|
12
|
+
# Specifies the name of the queue to process the job on.
|
13
|
+
#
|
14
|
+
# class PublishToFeedJob < ActiveJob::Base
|
15
|
+
# queue_as :feeds
|
16
|
+
#
|
17
|
+
# def perform(post)
|
18
|
+
# post.to_feed!
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
def queue_as(part_name = nil, &block)
|
22
|
+
if block_given?
|
23
|
+
self.queue_name = block
|
24
|
+
else
|
25
|
+
self.queue_name = queue_name_from_part(part_name)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def queue_name_from_part(part_name) #:nodoc:
|
30
|
+
queue_name = part_name || default_queue_name
|
31
|
+
name_parts = [queue_name_prefix.presence, queue_name]
|
32
|
+
name_parts.compact.join(queue_name_delimiter)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
included do
|
37
|
+
class_attribute :queue_name, instance_accessor: false, default: default_queue_name
|
38
|
+
class_attribute :queue_name_delimiter, instance_accessor: false, default: "_"
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns the name of the queue the job will be run on.
|
42
|
+
def queue_name
|
43
|
+
if @queue_name.is_a?(Proc)
|
44
|
+
@queue_name = self.class.queue_name_from_part(instance_exec(&@queue_name))
|
45
|
+
end
|
46
|
+
@queue_name
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveJob
|
4
|
+
module QueuePriority
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
# Includes the ability to override the default queue priority.
|
8
|
+
module ClassMethods
|
9
|
+
mattr_accessor :default_priority
|
10
|
+
|
11
|
+
# Specifies the priority of the queue to create the job with.
|
12
|
+
#
|
13
|
+
# class PublishToFeedJob < ActiveJob::Base
|
14
|
+
# queue_with_priority 50
|
15
|
+
#
|
16
|
+
# def perform(post)
|
17
|
+
# post.to_feed!
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# Specify either an argument or a block.
|
22
|
+
def queue_with_priority(priority = nil, &block)
|
23
|
+
if block_given?
|
24
|
+
self.priority = block
|
25
|
+
else
|
26
|
+
self.priority = priority
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
included do
|
32
|
+
class_attribute :priority, instance_accessor: false, default: default_priority
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns the priority that the job will be created with
|
36
|
+
def priority
|
37
|
+
if @priority.is_a?(Proc)
|
38
|
+
@priority = instance_exec(&@priority)
|
39
|
+
end
|
40
|
+
@priority
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "global_id/railtie"
|
4
|
+
require "active_job"
|
5
|
+
|
6
|
+
module ActiveJob
|
7
|
+
# = Active Job Railtie
|
8
|
+
class Railtie < Rails::Railtie # :nodoc:
|
9
|
+
config.active_job = ActiveSupport::OrderedOptions.new
|
10
|
+
|
11
|
+
initializer "active_job.logger" do
|
12
|
+
ActiveSupport.on_load(:active_job) { self.logger = ::Rails.logger }
|
13
|
+
end
|
14
|
+
|
15
|
+
initializer "active_job.set_configs" do |app|
|
16
|
+
options = app.config.active_job
|
17
|
+
options.queue_adapter ||= :async
|
18
|
+
|
19
|
+
ActiveSupport.on_load(:active_job) do
|
20
|
+
options.each { |k, v| send("#{k}=", v) }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
initializer "active_job.set_reloader_hook" do |app|
|
25
|
+
ActiveSupport.on_load(:active_job) do
|
26
|
+
ActiveJob::Callbacks.singleton_class.set_callback(:execute, :around, prepend: true) do |_, inner|
|
27
|
+
app.reloader.wrap do
|
28
|
+
inner.call
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,456 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/class/subclasses"
|
4
|
+
require "active_support/core_ext/hash/keys"
|
5
|
+
|
6
|
+
module ActiveJob
|
7
|
+
# Provides helper methods for testing Active Job
|
8
|
+
module TestHelper
|
9
|
+
delegate :enqueued_jobs, :enqueued_jobs=,
|
10
|
+
:performed_jobs, :performed_jobs=,
|
11
|
+
to: :queue_adapter
|
12
|
+
|
13
|
+
module TestQueueAdapter
|
14
|
+
extend ActiveSupport::Concern
|
15
|
+
|
16
|
+
included do
|
17
|
+
class_attribute :_test_adapter, instance_accessor: false, instance_predicate: false
|
18
|
+
end
|
19
|
+
|
20
|
+
module ClassMethods
|
21
|
+
def queue_adapter
|
22
|
+
self._test_adapter.nil? ? super : self._test_adapter
|
23
|
+
end
|
24
|
+
|
25
|
+
def disable_test_adapter
|
26
|
+
self._test_adapter = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def enable_test_adapter(test_adapter)
|
30
|
+
self._test_adapter = test_adapter
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
ActiveJob::Base.include(TestQueueAdapter)
|
36
|
+
|
37
|
+
def before_setup # :nodoc:
|
38
|
+
test_adapter = queue_adapter_for_test
|
39
|
+
|
40
|
+
queue_adapter_changed_jobs.each do |klass|
|
41
|
+
klass.enable_test_adapter(test_adapter)
|
42
|
+
end
|
43
|
+
|
44
|
+
clear_enqueued_jobs
|
45
|
+
clear_performed_jobs
|
46
|
+
super
|
47
|
+
end
|
48
|
+
|
49
|
+
def after_teardown # :nodoc:
|
50
|
+
super
|
51
|
+
|
52
|
+
queue_adapter_changed_jobs.each { |klass| klass.disable_test_adapter }
|
53
|
+
end
|
54
|
+
|
55
|
+
# Specifies the queue adapter to use with all active job test helpers.
|
56
|
+
#
|
57
|
+
# Returns an instance of the queue adapter and defaults to
|
58
|
+
# <tt>ActiveJob::QueueAdapters::TestAdapter</tt>.
|
59
|
+
#
|
60
|
+
# Note: The adapter provided by this method must provide some additional
|
61
|
+
# methods from those expected of a standard <tt>ActiveJob::QueueAdapter</tt>
|
62
|
+
# in order to be used with the active job test helpers. Refer to
|
63
|
+
# <tt>ActiveJob::QueueAdapters::TestAdapter</tt>.
|
64
|
+
def queue_adapter_for_test
|
65
|
+
ActiveJob::QueueAdapters::TestAdapter.new
|
66
|
+
end
|
67
|
+
|
68
|
+
# Asserts that the number of enqueued jobs matches the given number.
|
69
|
+
#
|
70
|
+
# def test_jobs
|
71
|
+
# assert_enqueued_jobs 0
|
72
|
+
# HelloJob.perform_later('david')
|
73
|
+
# assert_enqueued_jobs 1
|
74
|
+
# HelloJob.perform_later('abdelkader')
|
75
|
+
# assert_enqueued_jobs 2
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# If a block is passed, that block will cause the specified number of
|
79
|
+
# jobs to be enqueued.
|
80
|
+
#
|
81
|
+
# def test_jobs_again
|
82
|
+
# assert_enqueued_jobs 1 do
|
83
|
+
# HelloJob.perform_later('cristian')
|
84
|
+
# end
|
85
|
+
#
|
86
|
+
# assert_enqueued_jobs 2 do
|
87
|
+
# HelloJob.perform_later('aaron')
|
88
|
+
# HelloJob.perform_later('rafael')
|
89
|
+
# end
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
# The number of times a specific job was enqueued can be asserted.
|
93
|
+
#
|
94
|
+
# def test_logging_job
|
95
|
+
# assert_enqueued_jobs 1, only: LoggingJob do
|
96
|
+
# LoggingJob.perform_later
|
97
|
+
# HelloJob.perform_later('jeremy')
|
98
|
+
# end
|
99
|
+
# end
|
100
|
+
#
|
101
|
+
# The number of times a job except specific class was enqueued can be asserted.
|
102
|
+
#
|
103
|
+
# def test_logging_job
|
104
|
+
# assert_enqueued_jobs 1, except: HelloJob do
|
105
|
+
# LoggingJob.perform_later
|
106
|
+
# HelloJob.perform_later('jeremy')
|
107
|
+
# end
|
108
|
+
# end
|
109
|
+
#
|
110
|
+
# The number of times a job is enqueued to a specific queue can also be asserted.
|
111
|
+
#
|
112
|
+
# def test_logging_job
|
113
|
+
# assert_enqueued_jobs 2, queue: 'default' do
|
114
|
+
# LoggingJob.perform_later
|
115
|
+
# HelloJob.perform_later('elfassy')
|
116
|
+
# end
|
117
|
+
# end
|
118
|
+
def assert_enqueued_jobs(number, only: nil, except: nil, queue: nil)
|
119
|
+
if block_given?
|
120
|
+
original_count = enqueued_jobs_size(only: only, except: except, queue: queue)
|
121
|
+
yield
|
122
|
+
new_count = enqueued_jobs_size(only: only, except: except, queue: queue)
|
123
|
+
assert_equal number, new_count - original_count, "#{number} jobs expected, but #{new_count - original_count} were enqueued"
|
124
|
+
else
|
125
|
+
actual_count = enqueued_jobs_size(only: only, except: except, queue: queue)
|
126
|
+
assert_equal number, actual_count, "#{number} jobs expected, but #{actual_count} were enqueued"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Asserts that no jobs have been enqueued.
|
131
|
+
#
|
132
|
+
# def test_jobs
|
133
|
+
# assert_no_enqueued_jobs
|
134
|
+
# HelloJob.perform_later('jeremy')
|
135
|
+
# assert_enqueued_jobs 1
|
136
|
+
# end
|
137
|
+
#
|
138
|
+
# If a block is passed, that block should not cause any job to be enqueued.
|
139
|
+
#
|
140
|
+
# def test_jobs_again
|
141
|
+
# assert_no_enqueued_jobs do
|
142
|
+
# # No job should be enqueued from this block
|
143
|
+
# end
|
144
|
+
# end
|
145
|
+
#
|
146
|
+
# It can be asserted that no jobs of a specific kind are enqueued:
|
147
|
+
#
|
148
|
+
# def test_no_logging
|
149
|
+
# assert_no_enqueued_jobs only: LoggingJob do
|
150
|
+
# HelloJob.perform_later('jeremy')
|
151
|
+
# end
|
152
|
+
# end
|
153
|
+
#
|
154
|
+
# It can be asserted that no jobs except specific class are enqueued:
|
155
|
+
#
|
156
|
+
# def test_no_logging
|
157
|
+
# assert_no_enqueued_jobs except: HelloJob do
|
158
|
+
# HelloJob.perform_later('jeremy')
|
159
|
+
# end
|
160
|
+
# end
|
161
|
+
#
|
162
|
+
# Note: This assertion is simply a shortcut for:
|
163
|
+
#
|
164
|
+
# assert_enqueued_jobs 0, &block
|
165
|
+
def assert_no_enqueued_jobs(only: nil, except: nil, &block)
|
166
|
+
assert_enqueued_jobs 0, only: only, except: except, &block
|
167
|
+
end
|
168
|
+
|
169
|
+
# Asserts that the number of performed jobs matches the given number.
|
170
|
+
# If no block is passed, <tt>perform_enqueued_jobs</tt>
|
171
|
+
# must be called around the job call.
|
172
|
+
#
|
173
|
+
# def test_jobs
|
174
|
+
# assert_performed_jobs 0
|
175
|
+
#
|
176
|
+
# perform_enqueued_jobs do
|
177
|
+
# HelloJob.perform_later('xavier')
|
178
|
+
# end
|
179
|
+
# assert_performed_jobs 1
|
180
|
+
#
|
181
|
+
# perform_enqueued_jobs do
|
182
|
+
# HelloJob.perform_later('yves')
|
183
|
+
# assert_performed_jobs 2
|
184
|
+
# end
|
185
|
+
# end
|
186
|
+
#
|
187
|
+
# If a block is passed, that block should cause the specified number of
|
188
|
+
# jobs to be performed.
|
189
|
+
#
|
190
|
+
# def test_jobs_again
|
191
|
+
# assert_performed_jobs 1 do
|
192
|
+
# HelloJob.perform_later('robin')
|
193
|
+
# end
|
194
|
+
#
|
195
|
+
# assert_performed_jobs 2 do
|
196
|
+
# HelloJob.perform_later('carlos')
|
197
|
+
# HelloJob.perform_later('sean')
|
198
|
+
# end
|
199
|
+
# end
|
200
|
+
#
|
201
|
+
# The block form supports filtering. If the :only option is specified,
|
202
|
+
# then only the listed job(s) will be performed.
|
203
|
+
#
|
204
|
+
# def test_hello_job
|
205
|
+
# assert_performed_jobs 1, only: HelloJob do
|
206
|
+
# HelloJob.perform_later('jeremy')
|
207
|
+
# LoggingJob.perform_later
|
208
|
+
# end
|
209
|
+
# end
|
210
|
+
#
|
211
|
+
# Also if the :except option is specified,
|
212
|
+
# then the job(s) except specific class will be performed.
|
213
|
+
#
|
214
|
+
# def test_hello_job
|
215
|
+
# assert_performed_jobs 1, except: LoggingJob do
|
216
|
+
# HelloJob.perform_later('jeremy')
|
217
|
+
# LoggingJob.perform_later
|
218
|
+
# end
|
219
|
+
# end
|
220
|
+
#
|
221
|
+
# An array may also be specified, to support testing multiple jobs.
|
222
|
+
#
|
223
|
+
# def test_hello_and_logging_jobs
|
224
|
+
# assert_nothing_raised do
|
225
|
+
# assert_performed_jobs 2, only: [HelloJob, LoggingJob] do
|
226
|
+
# HelloJob.perform_later('jeremy')
|
227
|
+
# LoggingJob.perform_later('stewie')
|
228
|
+
# RescueJob.perform_later('david')
|
229
|
+
# end
|
230
|
+
# end
|
231
|
+
# end
|
232
|
+
def assert_performed_jobs(number, only: nil, except: nil)
|
233
|
+
if block_given?
|
234
|
+
original_count = performed_jobs.size
|
235
|
+
perform_enqueued_jobs(only: only, except: except) { yield }
|
236
|
+
new_count = performed_jobs.size
|
237
|
+
assert_equal number, new_count - original_count,
|
238
|
+
"#{number} jobs expected, but #{new_count - original_count} were performed"
|
239
|
+
else
|
240
|
+
performed_jobs_size = performed_jobs.size
|
241
|
+
assert_equal number, performed_jobs_size, "#{number} jobs expected, but #{performed_jobs_size} were performed"
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
# Asserts that no jobs have been performed.
|
246
|
+
#
|
247
|
+
# def test_jobs
|
248
|
+
# assert_no_performed_jobs
|
249
|
+
#
|
250
|
+
# perform_enqueued_jobs do
|
251
|
+
# HelloJob.perform_later('matthew')
|
252
|
+
# assert_performed_jobs 1
|
253
|
+
# end
|
254
|
+
# end
|
255
|
+
#
|
256
|
+
# If a block is passed, that block should not cause any job to be performed.
|
257
|
+
#
|
258
|
+
# def test_jobs_again
|
259
|
+
# assert_no_performed_jobs do
|
260
|
+
# # No job should be performed from this block
|
261
|
+
# end
|
262
|
+
# end
|
263
|
+
#
|
264
|
+
# The block form supports filtering. If the :only option is specified,
|
265
|
+
# then only the listed job(s) will not be performed.
|
266
|
+
#
|
267
|
+
# def test_no_logging
|
268
|
+
# assert_no_performed_jobs only: LoggingJob do
|
269
|
+
# HelloJob.perform_later('jeremy')
|
270
|
+
# end
|
271
|
+
# end
|
272
|
+
#
|
273
|
+
# Also if the :except option is specified,
|
274
|
+
# then the job(s) except specific class will not be performed.
|
275
|
+
#
|
276
|
+
# def test_no_logging
|
277
|
+
# assert_no_performed_jobs except: HelloJob do
|
278
|
+
# HelloJob.perform_later('jeremy')
|
279
|
+
# end
|
280
|
+
# end
|
281
|
+
#
|
282
|
+
# Note: This assertion is simply a shortcut for:
|
283
|
+
#
|
284
|
+
# assert_performed_jobs 0, &block
|
285
|
+
def assert_no_performed_jobs(only: nil, except: nil, &block)
|
286
|
+
assert_performed_jobs 0, only: only, except: except, &block
|
287
|
+
end
|
288
|
+
|
289
|
+
# Asserts that the job passed in the block has been enqueued with the given arguments.
|
290
|
+
#
|
291
|
+
# def test_assert_enqueued_with
|
292
|
+
# assert_enqueued_with(job: MyJob, args: [1,2,3], queue: 'low') do
|
293
|
+
# MyJob.perform_later(1,2,3)
|
294
|
+
# end
|
295
|
+
#
|
296
|
+
# assert_enqueued_with(job: MyJob, at: Date.tomorrow.noon) do
|
297
|
+
# MyJob.set(wait_until: Date.tomorrow.noon).perform_later
|
298
|
+
# end
|
299
|
+
# end
|
300
|
+
def assert_enqueued_with(job: nil, args: nil, at: nil, queue: nil)
|
301
|
+
original_enqueued_jobs_count = enqueued_jobs.count
|
302
|
+
expected = { job: job, args: args, at: at, queue: queue }.compact
|
303
|
+
expected_args = prepare_args_for_assertion(expected)
|
304
|
+
yield
|
305
|
+
in_block_jobs = enqueued_jobs.drop(original_enqueued_jobs_count)
|
306
|
+
matching_job = in_block_jobs.find do |in_block_job|
|
307
|
+
deserialized_job = deserialize_args_for_assertion(in_block_job)
|
308
|
+
expected_args.all? { |key, value| value == deserialized_job[key] }
|
309
|
+
end
|
310
|
+
assert matching_job, "No enqueued job found with #{expected}"
|
311
|
+
instantiate_job(matching_job)
|
312
|
+
end
|
313
|
+
|
314
|
+
# Asserts that the job passed in the block has been performed with the given arguments.
|
315
|
+
#
|
316
|
+
# def test_assert_performed_with
|
317
|
+
# assert_performed_with(job: MyJob, args: [1,2,3], queue: 'high') do
|
318
|
+
# MyJob.perform_later(1,2,3)
|
319
|
+
# end
|
320
|
+
#
|
321
|
+
# assert_performed_with(job: MyJob, at: Date.tomorrow.noon) do
|
322
|
+
# MyJob.set(wait_until: Date.tomorrow.noon).perform_later
|
323
|
+
# end
|
324
|
+
# end
|
325
|
+
def assert_performed_with(job: nil, args: nil, at: nil, queue: nil)
|
326
|
+
original_performed_jobs_count = performed_jobs.count
|
327
|
+
expected = { job: job, args: args, at: at, queue: queue }.compact
|
328
|
+
expected_args = prepare_args_for_assertion(expected)
|
329
|
+
perform_enqueued_jobs { yield }
|
330
|
+
in_block_jobs = performed_jobs.drop(original_performed_jobs_count)
|
331
|
+
matching_job = in_block_jobs.find do |in_block_job|
|
332
|
+
deserialized_job = deserialize_args_for_assertion(in_block_job)
|
333
|
+
expected_args.all? { |key, value| value == deserialized_job[key] }
|
334
|
+
end
|
335
|
+
assert matching_job, "No performed job found with #{expected}"
|
336
|
+
instantiate_job(matching_job)
|
337
|
+
end
|
338
|
+
|
339
|
+
# Performs all enqueued jobs in the duration of the block.
|
340
|
+
#
|
341
|
+
# def test_perform_enqueued_jobs
|
342
|
+
# perform_enqueued_jobs do
|
343
|
+
# MyJob.perform_later(1, 2, 3)
|
344
|
+
# end
|
345
|
+
# assert_performed_jobs 1
|
346
|
+
# end
|
347
|
+
#
|
348
|
+
# This method also supports filtering. If the +:only+ option is specified,
|
349
|
+
# then only the listed job(s) will be performed.
|
350
|
+
#
|
351
|
+
# def test_perform_enqueued_jobs_with_only
|
352
|
+
# perform_enqueued_jobs(only: MyJob) do
|
353
|
+
# MyJob.perform_later(1, 2, 3) # will be performed
|
354
|
+
# HelloJob.perform_later(1, 2, 3) # will not be performed
|
355
|
+
# end
|
356
|
+
# assert_performed_jobs 1
|
357
|
+
# end
|
358
|
+
#
|
359
|
+
# Also if the +:except+ option is specified,
|
360
|
+
# then the job(s) except specific class will be performed.
|
361
|
+
#
|
362
|
+
# def test_perform_enqueued_jobs_with_except
|
363
|
+
# perform_enqueued_jobs(except: HelloJob) do
|
364
|
+
# MyJob.perform_later(1, 2, 3) # will be performed
|
365
|
+
# HelloJob.perform_later(1, 2, 3) # will not be performed
|
366
|
+
# end
|
367
|
+
# assert_performed_jobs 1
|
368
|
+
# end
|
369
|
+
#
|
370
|
+
def perform_enqueued_jobs(only: nil, except: nil)
|
371
|
+
validate_option(only: only, except: except)
|
372
|
+
old_perform_enqueued_jobs = queue_adapter.perform_enqueued_jobs
|
373
|
+
old_perform_enqueued_at_jobs = queue_adapter.perform_enqueued_at_jobs
|
374
|
+
old_filter = queue_adapter.filter
|
375
|
+
old_reject = queue_adapter.reject
|
376
|
+
|
377
|
+
begin
|
378
|
+
queue_adapter.perform_enqueued_jobs = true
|
379
|
+
queue_adapter.perform_enqueued_at_jobs = true
|
380
|
+
queue_adapter.filter = only
|
381
|
+
queue_adapter.reject = except
|
382
|
+
yield
|
383
|
+
ensure
|
384
|
+
queue_adapter.perform_enqueued_jobs = old_perform_enqueued_jobs
|
385
|
+
queue_adapter.perform_enqueued_at_jobs = old_perform_enqueued_at_jobs
|
386
|
+
queue_adapter.filter = old_filter
|
387
|
+
queue_adapter.reject = old_reject
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
# Accesses the queue_adapter set by ActiveJob::Base.
|
392
|
+
#
|
393
|
+
# def test_assert_job_has_custom_queue_adapter_set
|
394
|
+
# assert_instance_of CustomQueueAdapter, HelloJob.queue_adapter
|
395
|
+
# end
|
396
|
+
def queue_adapter
|
397
|
+
ActiveJob::Base.queue_adapter
|
398
|
+
end
|
399
|
+
|
400
|
+
private
|
401
|
+
def clear_enqueued_jobs
|
402
|
+
enqueued_jobs.clear
|
403
|
+
end
|
404
|
+
|
405
|
+
def clear_performed_jobs
|
406
|
+
performed_jobs.clear
|
407
|
+
end
|
408
|
+
|
409
|
+
def enqueued_jobs_size(only: nil, except: nil, queue: nil)
|
410
|
+
validate_option(only: only, except: except)
|
411
|
+
enqueued_jobs.count do |job|
|
412
|
+
job_class = job.fetch(:job)
|
413
|
+
if only
|
414
|
+
next false unless Array(only).include?(job_class)
|
415
|
+
elsif except
|
416
|
+
next false if Array(except).include?(job_class)
|
417
|
+
end
|
418
|
+
if queue
|
419
|
+
next false unless queue.to_s == job.fetch(:queue, job_class.queue_name)
|
420
|
+
end
|
421
|
+
true
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
def prepare_args_for_assertion(args)
|
426
|
+
args.dup.tap do |arguments|
|
427
|
+
arguments[:at] = arguments[:at].to_f if arguments[:at]
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
def deserialize_args_for_assertion(job)
|
432
|
+
job.dup.tap do |new_job|
|
433
|
+
new_job[:args] = ActiveJob::Arguments.deserialize(new_job[:args]) if new_job[:args]
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
def instantiate_job(payload)
|
438
|
+
args = ActiveJob::Arguments.deserialize(payload[:args])
|
439
|
+
job = payload[:job].new(*args)
|
440
|
+
job.scheduled_at = Time.at(payload[:at]) if payload.key?(:at)
|
441
|
+
job.queue_name = payload[:queue]
|
442
|
+
job
|
443
|
+
end
|
444
|
+
|
445
|
+
def queue_adapter_changed_jobs
|
446
|
+
(ActiveJob::Base.descendants << ActiveJob::Base).select do |klass|
|
447
|
+
# only override explicitly set adapters, a quirk of `class_attribute`
|
448
|
+
klass.singleton_class.public_instance_methods(false).include?(:_queue_adapter)
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
def validate_option(only: nil, except: nil)
|
453
|
+
raise ArgumentError, "Cannot specify both `:only` and `:except` options." if only && except
|
454
|
+
end
|
455
|
+
end
|
456
|
+
end
|