activejob 5.2.0 → 6.0.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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +145 -14
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +17 -10
  5. data/lib/active_job/arguments.rb +54 -33
  6. data/lib/active_job/base.rb +3 -1
  7. data/lib/active_job/callbacks.rb +4 -1
  8. data/lib/active_job/core.rb +54 -25
  9. data/lib/active_job/enqueuing.rb +26 -5
  10. data/lib/active_job/exceptions.rb +44 -21
  11. data/lib/active_job/execution.rb +4 -4
  12. data/lib/active_job/gem_version.rb +2 -2
  13. data/lib/active_job/logging.rb +40 -9
  14. data/lib/active_job/queue_adapter.rb +2 -0
  15. data/lib/active_job/queue_adapters/async_adapter.rb +1 -1
  16. data/lib/active_job/queue_adapters/backburner_adapter.rb +2 -2
  17. data/lib/active_job/queue_adapters/inline_adapter.rb +1 -1
  18. data/lib/active_job/queue_adapters/test_adapter.rb +22 -8
  19. data/lib/active_job/queue_adapters.rb +8 -10
  20. data/lib/active_job/queue_name.rb +21 -1
  21. data/lib/active_job/railtie.rb +16 -1
  22. data/lib/active_job/serializers/date_serializer.rb +21 -0
  23. data/lib/active_job/serializers/date_time_serializer.rb +21 -0
  24. data/lib/active_job/serializers/duration_serializer.rb +24 -0
  25. data/lib/active_job/serializers/object_serializer.rb +54 -0
  26. data/lib/active_job/serializers/symbol_serializer.rb +21 -0
  27. data/lib/active_job/serializers/time_serializer.rb +21 -0
  28. data/lib/active_job/serializers/time_with_zone_serializer.rb +21 -0
  29. data/lib/active_job/serializers.rb +63 -0
  30. data/lib/active_job/test_helper.rb +290 -61
  31. data/lib/active_job/timezones.rb +13 -0
  32. data/lib/active_job/translation.rb +1 -1
  33. data/lib/active_job.rb +2 -1
  34. data/lib/rails/generators/job/job_generator.rb +4 -0
  35. metadata +19 -12
  36. data/lib/active_job/queue_adapters/qu_adapter.rb +0 -46
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+
5
+ module ActiveJob
6
+ # The <tt>ActiveJob::Serializers</tt> module is used to store a list of known serializers
7
+ # and to add new ones. It also has helpers to serialize/deserialize objects.
8
+ module Serializers # :nodoc:
9
+ extend ActiveSupport::Autoload
10
+
11
+ autoload :ObjectSerializer
12
+ autoload :SymbolSerializer
13
+ autoload :DurationSerializer
14
+ autoload :DateTimeSerializer
15
+ autoload :DateSerializer
16
+ autoload :TimeWithZoneSerializer
17
+ autoload :TimeSerializer
18
+
19
+ mattr_accessor :_additional_serializers
20
+ self._additional_serializers = Set.new
21
+
22
+ class << self
23
+ # Returns serialized representative of the passed object.
24
+ # Will look up through all known serializers.
25
+ # Raises <tt>ActiveJob::SerializationError</tt> if it can't find a proper serializer.
26
+ def serialize(argument)
27
+ serializer = serializers.detect { |s| s.serialize?(argument) }
28
+ raise SerializationError.new("Unsupported argument type: #{argument.class.name}") unless serializer
29
+ serializer.serialize(argument)
30
+ end
31
+
32
+ # Returns deserialized object.
33
+ # Will look up through all known serializers.
34
+ # If no serializer found will raise <tt>ArgumentError</tt>.
35
+ def deserialize(argument)
36
+ serializer_name = argument[Arguments::OBJECT_SERIALIZER_KEY]
37
+ raise ArgumentError, "Serializer name is not present in the argument: #{argument.inspect}" unless serializer_name
38
+
39
+ serializer = serializer_name.safe_constantize
40
+ raise ArgumentError, "Serializer #{serializer_name} is not known" unless serializer
41
+
42
+ serializer.deserialize(argument)
43
+ end
44
+
45
+ # Returns list of known serializers.
46
+ def serializers
47
+ self._additional_serializers
48
+ end
49
+
50
+ # Adds new serializers to a list of known serializers.
51
+ def add_serializers(*new_serializers)
52
+ self._additional_serializers += new_serializers.flatten
53
+ end
54
+ end
55
+
56
+ add_serializers SymbolSerializer,
57
+ DurationSerializer,
58
+ DateTimeSerializer,
59
+ DateSerializer,
60
+ TimeWithZoneSerializer,
61
+ TimeSerializer
62
+ end
63
+ end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/class/subclasses"
4
- require "active_support/core_ext/hash/keys"
5
4
 
6
5
  module ActiveJob
7
6
  # Provides helper methods for testing Active Job
@@ -52,7 +51,7 @@ module ActiveJob
52
51
  queue_adapter_changed_jobs.each { |klass| klass.disable_test_adapter }
53
52
  end
54
53
 
55
- # Specifies the queue adapter to use with all active job test helpers.
54
+ # Specifies the queue adapter to use with all Active Job test helpers.
56
55
  #
57
56
  # Returns an instance of the queue adapter and defaults to
58
57
  # <tt>ActiveJob::QueueAdapters::TestAdapter</tt>.
@@ -75,7 +74,7 @@ module ActiveJob
75
74
  # assert_enqueued_jobs 2
76
75
  # end
77
76
  #
78
- # If a block is passed, that block will cause the specified number of
77
+ # If a block is passed, asserts that the block will cause the specified number of
79
78
  # jobs to be enqueued.
80
79
  #
81
80
  # def test_jobs_again
@@ -89,7 +88,7 @@ module ActiveJob
89
88
  # end
90
89
  # end
91
90
  #
92
- # The number of times a specific job was enqueued can be asserted.
91
+ # Asserts the number of times a specific job was enqueued by passing +:only+ option.
93
92
  #
94
93
  # def test_logging_job
95
94
  # assert_enqueued_jobs 1, only: LoggingJob do
@@ -98,7 +97,7 @@ module ActiveJob
98
97
  # end
99
98
  # end
100
99
  #
101
- # The number of times a job except specific class was enqueued can be asserted.
100
+ # Asserts the number of times a job except specific class was enqueued by passing +:except+ option.
102
101
  #
103
102
  # def test_logging_job
104
103
  # assert_enqueued_jobs 1, except: HelloJob do
@@ -107,7 +106,10 @@ module ActiveJob
107
106
  # end
108
107
  # end
109
108
  #
110
- # The number of times a job is enqueued to a specific queue can also be asserted.
109
+ # +:only+ and +:except+ options accepts Class, Array of Class or Proc. When passed a Proc,
110
+ # a hash containing the job's class and it's argument are passed as argument.
111
+ #
112
+ # Asserts the number of times a job is enqueued to a specific queue by passing +:queue+ option.
111
113
  #
112
114
  # def test_logging_job
113
115
  # assert_enqueued_jobs 2, queue: 'default' do
@@ -117,14 +119,18 @@ module ActiveJob
117
119
  # end
118
120
  def assert_enqueued_jobs(number, only: nil, except: nil, queue: nil)
119
121
  if block_given?
120
- original_count = enqueued_jobs_size(only: only, except: except, queue: queue)
122
+ original_count = enqueued_jobs_with(only: only, except: except, queue: queue)
123
+
121
124
  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"
125
+
126
+ new_count = enqueued_jobs_with(only: only, except: except, queue: queue)
127
+
128
+ actual_count = new_count - original_count
124
129
  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"
130
+ actual_count = enqueued_jobs_with(only: only, except: except, queue: queue)
127
131
  end
132
+
133
+ assert_equal number, actual_count, "#{number} jobs expected, but #{actual_count} were enqueued"
128
134
  end
129
135
 
130
136
  # Asserts that no jobs have been enqueued.
@@ -135,7 +141,7 @@ module ActiveJob
135
141
  # assert_enqueued_jobs 1
136
142
  # end
137
143
  #
138
- # If a block is passed, that block should not cause any job to be enqueued.
144
+ # If a block is passed, asserts that the block will not cause any job to be enqueued.
139
145
  #
140
146
  # def test_jobs_again
141
147
  # assert_no_enqueued_jobs do
@@ -143,7 +149,7 @@ module ActiveJob
143
149
  # end
144
150
  # end
145
151
  #
146
- # It can be asserted that no jobs of a specific kind are enqueued:
152
+ # Asserts that no jobs of a specific kind are enqueued by passing +:only+ option.
147
153
  #
148
154
  # def test_no_logging
149
155
  # assert_no_enqueued_jobs only: LoggingJob do
@@ -151,7 +157,7 @@ module ActiveJob
151
157
  # end
152
158
  # end
153
159
  #
154
- # It can be asserted that no jobs except specific class are enqueued:
160
+ # Asserts that no jobs except specific class are enqueued by passing +:except+ option.
155
161
  #
156
162
  # def test_no_logging
157
163
  # assert_no_enqueued_jobs except: HelloJob do
@@ -159,16 +165,27 @@ module ActiveJob
159
165
  # end
160
166
  # end
161
167
  #
168
+ # +:only+ and +:except+ options accepts Class, Array of Class or Proc. When passed a Proc,
169
+ # a hash containing the job's class and it's argument are passed as argument.
170
+ #
171
+ # Asserts that no jobs are enqueued to a specific queue by passing +:queue+ option
172
+ #
173
+ # def test_no_logging
174
+ # assert_no_enqueued_jobs queue: 'default' do
175
+ # LoggingJob.set(queue: :some_queue).perform_later
176
+ # end
177
+ # end
178
+ #
162
179
  # Note: This assertion is simply a shortcut for:
163
180
  #
164
181
  # 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
182
+ def assert_no_enqueued_jobs(only: nil, except: nil, queue: nil, &block)
183
+ assert_enqueued_jobs 0, only: only, except: except, queue: queue, &block
167
184
  end
168
185
 
169
186
  # Asserts that the number of performed jobs matches the given number.
170
187
  # If no block is passed, <tt>perform_enqueued_jobs</tt>
171
- # must be called around the job call.
188
+ # must be called around or after the job call.
172
189
  #
173
190
  # def test_jobs
174
191
  # assert_performed_jobs 0
@@ -178,13 +195,14 @@ module ActiveJob
178
195
  # end
179
196
  # assert_performed_jobs 1
180
197
  #
181
- # perform_enqueued_jobs do
182
- # HelloJob.perform_later('yves')
183
- # assert_performed_jobs 2
184
- # end
198
+ # HelloJob.perform_later('yves')
199
+ #
200
+ # perform_enqueued_jobs
201
+ #
202
+ # assert_performed_jobs 2
185
203
  # end
186
204
  #
187
- # If a block is passed, that block should cause the specified number of
205
+ # If a block is passed, asserts that the block will cause the specified number of
188
206
  # jobs to be performed.
189
207
  #
190
208
  # def test_jobs_again
@@ -198,7 +216,7 @@ module ActiveJob
198
216
  # end
199
217
  # end
200
218
  #
201
- # The block form supports filtering. If the :only option is specified,
219
+ # This method also supports filtering. If the +:only+ option is specified,
202
220
  # then only the listed job(s) will be performed.
203
221
  #
204
222
  # def test_hello_job
@@ -208,7 +226,7 @@ module ActiveJob
208
226
  # end
209
227
  # end
210
228
  #
211
- # Also if the :except option is specified,
229
+ # Also if the +:except+ option is specified,
212
230
  # then the job(s) except specific class will be performed.
213
231
  #
214
232
  # def test_hello_job
@@ -229,17 +247,42 @@ module ActiveJob
229
247
  # end
230
248
  # end
231
249
  # end
232
- def assert_performed_jobs(number, only: nil, except: nil)
250
+ #
251
+ # A proc may also be specified. When passed a Proc, the job's instance will be passed as argument.
252
+ #
253
+ # def test_hello_and_logging_jobs
254
+ # assert_nothing_raised do
255
+ # assert_performed_jobs(1, only: ->(job) { job.is_a?(HelloJob) }) do
256
+ # HelloJob.perform_later('jeremy')
257
+ # LoggingJob.perform_later('stewie')
258
+ # RescueJob.perform_later('david')
259
+ # end
260
+ # end
261
+ # end
262
+ #
263
+ # If the +:queue+ option is specified,
264
+ # then only the job(s) enqueued to a specific queue will be performed.
265
+ #
266
+ # def test_assert_performed_jobs_with_queue_option
267
+ # assert_performed_jobs 1, queue: :some_queue do
268
+ # HelloJob.set(queue: :some_queue).perform_later("jeremy")
269
+ # HelloJob.set(queue: :other_queue).perform_later("bogdan")
270
+ # end
271
+ # end
272
+ def assert_performed_jobs(number, only: nil, except: nil, queue: nil, &block)
233
273
  if block_given?
234
274
  original_count = performed_jobs.size
235
- perform_enqueued_jobs(only: only, except: except) { yield }
275
+
276
+ perform_enqueued_jobs(only: only, except: except, queue: queue, &block)
277
+
236
278
  new_count = performed_jobs.size
237
- assert_equal number, new_count - original_count,
238
- "#{number} jobs expected, but #{new_count - original_count} were performed"
279
+
280
+ performed_jobs_size = new_count - original_count
239
281
  else
240
- performed_jobs_size = performed_jobs.size
241
- assert_equal number, performed_jobs_size, "#{number} jobs expected, but #{performed_jobs_size} were performed"
282
+ performed_jobs_size = performed_jobs_with(only: only, except: except, queue: queue)
242
283
  end
284
+
285
+ assert_equal number, performed_jobs_size, "#{number} jobs expected, but #{performed_jobs_size} were performed"
243
286
  end
244
287
 
245
288
  # Asserts that no jobs have been performed.
@@ -253,7 +296,7 @@ module ActiveJob
253
296
  # end
254
297
  # end
255
298
  #
256
- # If a block is passed, that block should not cause any job to be performed.
299
+ # If a block is passed, asserts that the block will not cause any job to be performed.
257
300
  #
258
301
  # def test_jobs_again
259
302
  # assert_no_performed_jobs do
@@ -261,7 +304,7 @@ module ActiveJob
261
304
  # end
262
305
  # end
263
306
  #
264
- # The block form supports filtering. If the :only option is specified,
307
+ # The block form supports filtering. If the +:only+ option is specified,
265
308
  # then only the listed job(s) will not be performed.
266
309
  #
267
310
  # def test_no_logging
@@ -270,7 +313,7 @@ module ActiveJob
270
313
  # end
271
314
  # end
272
315
  #
273
- # Also if the :except option is specified,
316
+ # Also if the +:except+ option is specified,
274
317
  # then the job(s) except specific class will not be performed.
275
318
  #
276
319
  # def test_no_logging
@@ -279,14 +322,53 @@ module ActiveJob
279
322
  # end
280
323
  # end
281
324
  #
325
+ # +:only+ and +:except+ options accepts Class, Array of Class or Proc. When passed a Proc,
326
+ # an instance of the job will be passed as argument.
327
+ #
328
+ # If the +:queue+ option is specified,
329
+ # then only the job(s) enqueued to a specific queue will not be performed.
330
+ #
331
+ # def test_assert_no_performed_jobs_with_queue_option
332
+ # assert_no_performed_jobs queue: :some_queue do
333
+ # HelloJob.set(queue: :other_queue).perform_later("jeremy")
334
+ # end
335
+ # end
336
+ #
282
337
  # Note: This assertion is simply a shortcut for:
283
338
  #
284
339
  # 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
340
+ def assert_no_performed_jobs(only: nil, except: nil, queue: nil, &block)
341
+ assert_performed_jobs 0, only: only, except: except, queue: queue, &block
287
342
  end
288
343
 
289
- # Asserts that the job passed in the block has been enqueued with the given arguments.
344
+ # Asserts that the job has been enqueued with the given arguments.
345
+ #
346
+ # def test_assert_enqueued_with
347
+ # MyJob.perform_later(1,2,3)
348
+ # assert_enqueued_with(job: MyJob, args: [1,2,3], queue: 'low')
349
+ #
350
+ # MyJob.set(wait_until: Date.tomorrow.noon).perform_later
351
+ # assert_enqueued_with(job: MyJob, at: Date.tomorrow.noon)
352
+ # end
353
+ #
354
+ #
355
+ # The +args+ argument also accepts a proc which will get passed the actual
356
+ # job's arguments. Your proc needs to return a boolean value determining if
357
+ # the job's arguments matches your expectation. This is useful to check only
358
+ # for a subset of arguments.
359
+ #
360
+ # def test_assert_enqueued_with
361
+ # expected_args = ->(job_args) do
362
+ # assert job_args.first.key?(:foo)
363
+ # end
364
+ #
365
+ # MyJob.perform_later(foo: 'bar', other_arg: 'No need to check in the test')
366
+ # assert_enqueued_with(job: MyJob, args: expected_args, queue: 'low')
367
+ # end
368
+ #
369
+ #
370
+ # If a block is passed, asserts that the block will cause the job to be
371
+ # enqueued with the given arguments.
290
372
  #
291
373
  # def test_assert_enqueued_with
292
374
  # assert_enqueued_with(job: MyJob, args: [1,2,3], queue: 'low') do
@@ -298,19 +380,70 @@ module ActiveJob
298
380
  # end
299
381
  # end
300
382
  def assert_enqueued_with(job: nil, args: nil, at: nil, queue: nil)
301
- original_enqueued_jobs_count = enqueued_jobs.count
302
383
  expected = { job: job, args: args, at: at, queue: queue }.compact
303
- serialized_args = serialize_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
- serialized_args.all? { |key, value| value == in_block_job[key] }
384
+ expected_args = prepare_args_for_assertion(expected)
385
+
386
+ if block_given?
387
+ original_enqueued_jobs_count = enqueued_jobs.count
388
+
389
+ yield
390
+
391
+ jobs = enqueued_jobs.drop(original_enqueued_jobs_count)
392
+ else
393
+ jobs = enqueued_jobs
394
+ end
395
+
396
+ matching_job = jobs.find do |enqueued_job|
397
+ deserialized_job = deserialize_args_for_assertion(enqueued_job)
398
+
399
+ expected_args.all? do |key, value|
400
+ if value.respond_to?(:call)
401
+ value.call(deserialized_job[key])
402
+ else
403
+ value == deserialized_job[key]
404
+ end
405
+ end
308
406
  end
407
+
309
408
  assert matching_job, "No enqueued job found with #{expected}"
310
409
  instantiate_job(matching_job)
311
410
  end
312
411
 
313
- # Asserts that the job passed in the block has been performed with the given arguments.
412
+ # Asserts that the job has been performed with the given arguments.
413
+ #
414
+ # def test_assert_performed_with
415
+ # MyJob.perform_later(1,2,3)
416
+ #
417
+ # perform_enqueued_jobs
418
+ #
419
+ # assert_performed_with(job: MyJob, args: [1,2,3], queue: 'high')
420
+ #
421
+ # MyJob.set(wait_until: Date.tomorrow.noon).perform_later
422
+ #
423
+ # perform_enqueued_jobs
424
+ #
425
+ # assert_performed_with(job: MyJob, at: Date.tomorrow.noon)
426
+ # end
427
+ #
428
+ # The +args+ argument also accepts a proc which will get passed the actual
429
+ # job's arguments. Your proc needs to return a boolean value determining if
430
+ # the job's arguments matches your expectation. This is useful to check only
431
+ # for a subset of arguments.
432
+ #
433
+ # def test_assert_performed_with
434
+ # expected_args = ->(job_args) do
435
+ # assert job_args.first.key?(:foo)
436
+ # end
437
+ # MyJob.perform_later(foo: 'bar', other_arg: 'No need to check in the test')
438
+ #
439
+ # perform_enqueued_jobs
440
+ #
441
+ # assert_performed_with(job: MyJob, args: expected_args, queue: 'high')
442
+ # end
443
+ #
444
+ # If a block is passed, that block performs all of the jobs that were
445
+ # enqueued throughout the duration of the block and asserts that
446
+ # the job has been performed with the given arguments in the block.
314
447
  #
315
448
  # def test_assert_performed_with
316
449
  # assert_performed_with(job: MyJob, args: [1,2,3], queue: 'high') do
@@ -321,20 +454,39 @@ module ActiveJob
321
454
  # MyJob.set(wait_until: Date.tomorrow.noon).perform_later
322
455
  # end
323
456
  # end
324
- def assert_performed_with(job: nil, args: nil, at: nil, queue: nil)
325
- original_performed_jobs_count = performed_jobs.count
457
+ def assert_performed_with(job: nil, args: nil, at: nil, queue: nil, &block)
326
458
  expected = { job: job, args: args, at: at, queue: queue }.compact
327
- serialized_args = serialize_args_for_assertion(expected)
328
- perform_enqueued_jobs { yield }
329
- in_block_jobs = performed_jobs.drop(original_performed_jobs_count)
330
- matching_job = in_block_jobs.find do |in_block_job|
331
- serialized_args.all? { |key, value| value == in_block_job[key] }
459
+ expected_args = prepare_args_for_assertion(expected)
460
+
461
+ if block_given?
462
+ original_performed_jobs_count = performed_jobs.count
463
+
464
+ perform_enqueued_jobs(&block)
465
+
466
+ jobs = performed_jobs.drop(original_performed_jobs_count)
467
+ else
468
+ jobs = performed_jobs
469
+ end
470
+
471
+ matching_job = jobs.find do |enqueued_job|
472
+ deserialized_job = deserialize_args_for_assertion(enqueued_job)
473
+
474
+ expected_args.all? do |key, value|
475
+ if value.respond_to?(:call)
476
+ value.call(deserialized_job[key])
477
+ else
478
+ value == deserialized_job[key]
479
+ end
480
+ end
332
481
  end
482
+
333
483
  assert matching_job, "No performed job found with #{expected}"
334
484
  instantiate_job(matching_job)
335
485
  end
336
486
 
337
- # Performs all enqueued jobs in the duration of the block.
487
+ # Performs all enqueued jobs. If a block is given, performs all of the jobs
488
+ # that were enqueued throughout the duration of the block. If a block is
489
+ # not given, performs all of the enqueued jobs up to this point in the test.
338
490
  #
339
491
  # def test_perform_enqueued_jobs
340
492
  # perform_enqueued_jobs do
@@ -343,6 +495,14 @@ module ActiveJob
343
495
  # assert_performed_jobs 1
344
496
  # end
345
497
  #
498
+ # def test_perform_enqueued_jobs_without_block
499
+ # MyJob.perform_later(1, 2, 3)
500
+ #
501
+ # perform_enqueued_jobs
502
+ #
503
+ # assert_performed_jobs 1
504
+ # end
505
+ #
346
506
  # This method also supports filtering. If the +:only+ option is specified,
347
507
  # then only the listed job(s) will be performed.
348
508
  #
@@ -365,24 +525,45 @@ module ActiveJob
365
525
  # assert_performed_jobs 1
366
526
  # end
367
527
  #
368
- def perform_enqueued_jobs(only: nil, except: nil)
528
+ # +:only+ and +:except+ options accepts Class, Array of Class or Proc. When passed a Proc,
529
+ # an instance of the job will be passed as argument.
530
+ #
531
+ # If the +:queue+ option is specified,
532
+ # then only the job(s) enqueued to a specific queue will be performed.
533
+ #
534
+ # def test_perform_enqueued_jobs_with_queue
535
+ # perform_enqueued_jobs queue: :some_queue do
536
+ # MyJob.set(queue: :some_queue).perform_later(1, 2, 3) # will be performed
537
+ # HelloJob.set(queue: :other_queue).perform_later(1, 2, 3) # will not be performed
538
+ # end
539
+ # assert_performed_jobs 1
540
+ # end
541
+ #
542
+ def perform_enqueued_jobs(only: nil, except: nil, queue: nil)
543
+ return flush_enqueued_jobs(only: only, except: except, queue: queue) unless block_given?
544
+
369
545
  validate_option(only: only, except: except)
546
+
370
547
  old_perform_enqueued_jobs = queue_adapter.perform_enqueued_jobs
371
548
  old_perform_enqueued_at_jobs = queue_adapter.perform_enqueued_at_jobs
372
549
  old_filter = queue_adapter.filter
373
550
  old_reject = queue_adapter.reject
551
+ old_queue = queue_adapter.queue
374
552
 
375
553
  begin
376
554
  queue_adapter.perform_enqueued_jobs = true
377
555
  queue_adapter.perform_enqueued_at_jobs = true
378
556
  queue_adapter.filter = only
379
557
  queue_adapter.reject = except
558
+ queue_adapter.queue = queue
559
+
380
560
  yield
381
561
  ensure
382
562
  queue_adapter.perform_enqueued_jobs = old_perform_enqueued_jobs
383
563
  queue_adapter.perform_enqueued_at_jobs = old_perform_enqueued_at_jobs
384
564
  queue_adapter.filter = old_filter
385
565
  queue_adapter.reject = old_reject
566
+ queue_adapter.queue = old_queue
386
567
  end
387
568
  end
388
569
 
@@ -404,31 +585,79 @@ module ActiveJob
404
585
  performed_jobs.clear
405
586
  end
406
587
 
407
- def enqueued_jobs_size(only: nil, except: nil, queue: nil)
588
+ def jobs_with(jobs, only: nil, except: nil, queue: nil)
408
589
  validate_option(only: only, except: except)
409
- enqueued_jobs.count do |job|
590
+
591
+ jobs.count do |job|
410
592
  job_class = job.fetch(:job)
593
+
411
594
  if only
412
- next false unless Array(only).include?(job_class)
595
+ next false unless filter_as_proc(only).call(job)
413
596
  elsif except
414
- next false if Array(except).include?(job_class)
597
+ next false if filter_as_proc(except).call(job)
415
598
  end
599
+
416
600
  if queue
417
601
  next false unless queue.to_s == job.fetch(:queue, job_class.queue_name)
418
602
  end
603
+
604
+ yield job if block_given?
605
+
419
606
  true
420
607
  end
421
608
  end
422
609
 
423
- def serialize_args_for_assertion(args)
424
- args.dup.tap do |serialized_args|
425
- serialized_args[:args] = ActiveJob::Arguments.serialize(serialized_args[:args]) if serialized_args[:args]
426
- serialized_args[:at] = serialized_args[:at].to_f if serialized_args[:at]
610
+ def filter_as_proc(filter)
611
+ return filter if filter.is_a?(Proc)
612
+
613
+ ->(job) { Array(filter).include?(job.fetch(:job)) }
614
+ end
615
+
616
+ def enqueued_jobs_with(only: nil, except: nil, queue: nil, &block)
617
+ jobs_with(enqueued_jobs, only: only, except: except, queue: queue, &block)
618
+ end
619
+
620
+ def performed_jobs_with(only: nil, except: nil, queue: nil, &block)
621
+ jobs_with(performed_jobs, only: only, except: except, queue: queue, &block)
622
+ end
623
+
624
+ def flush_enqueued_jobs(only: nil, except: nil, queue: nil)
625
+ enqueued_jobs_with(only: only, except: except, queue: queue) do |payload|
626
+ instantiate_job(payload).perform_now
627
+ queue_adapter.performed_jobs << payload
628
+ end
629
+ end
630
+
631
+ def prepare_args_for_assertion(args)
632
+ args.dup.tap do |arguments|
633
+ arguments[:at] = round_time_arguments(arguments[:at]) if arguments[:at]
634
+ arguments[:args] = round_time_arguments(arguments[:args]) if arguments[:args]
635
+ end
636
+ end
637
+
638
+ def round_time_arguments(argument)
639
+ case argument
640
+ when Time, ActiveSupport::TimeWithZone, DateTime
641
+ argument.change(usec: 0)
642
+ when Hash
643
+ argument.transform_values { |value| round_time_arguments(value) }
644
+ when Array
645
+ argument.map { |element| round_time_arguments(element) }
646
+ else
647
+ argument
648
+ end
649
+ end
650
+
651
+ def deserialize_args_for_assertion(job)
652
+ job.dup.tap do |new_job|
653
+ new_job[:at] = round_time_arguments(Time.at(new_job[:at])) if new_job[:at]
654
+ new_job[:args] = ActiveJob::Arguments.deserialize(new_job[:args]) if new_job[:args]
427
655
  end
428
656
  end
429
657
 
430
658
  def instantiate_job(payload)
431
- job = payload[:job].new(*payload[:args])
659
+ args = ActiveJob::Arguments.deserialize(payload[:args])
660
+ job = payload[:job].new(*args)
432
661
  job.scheduled_at = Time.at(payload[:at]) if payload.key?(:at)
433
662
  job.queue_name = payload[:queue]
434
663
  job
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveJob
4
+ module Timezones #:nodoc:
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ around_perform do |job, block|
9
+ Time.use_zone(job.timezone, &block)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -5,7 +5,7 @@ module ActiveJob
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  included do
8
- around_perform do |job, block, _|
8
+ around_perform do |job, block|
9
9
  I18n.with_locale(job.locale, &block)
10
10
  end
11
11
  end
data/lib/active_job.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # Copyright (c) 2014-2018 David Heinemeier Hansson
4
+ # Copyright (c) 2014-2019 David Heinemeier Hansson
5
5
  #
6
6
  # Permission is hereby granted, free of charge, to any person obtaining
7
7
  # a copy of this software and associated documentation files (the
@@ -33,6 +33,7 @@ module ActiveJob
33
33
 
34
34
  autoload :Base
35
35
  autoload :QueueAdapters
36
+ autoload :Serializers
36
37
  autoload :ConfiguredJob
37
38
  autoload :TestCase
38
39
  autoload :TestHelper