activejob 6.0.3.2 → 6.1.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.
@@ -11,6 +11,9 @@ module ActiveJob
11
11
  included do
12
12
  class_attribute :_queue_adapter_name, instance_accessor: false, instance_predicate: false
13
13
  class_attribute :_queue_adapter, instance_accessor: false, instance_predicate: false
14
+
15
+ delegate :queue_adapter, to: :class
16
+
14
17
  self.queue_adapter = :async
15
18
  end
16
19
 
@@ -72,7 +72,7 @@ module ActiveJob
72
72
  # Yes: Allows the priority to be set on the job object, at the queue level or
73
73
  # as default configuration option.
74
74
  #
75
- # No: Does not allow the priority of jobs to be configured.
75
+ # No: The adapter does not allow the priority of jobs to be configured.
76
76
  #
77
77
  # N/A: The adapter does not support queuing, and therefore sorting them.
78
78
  #
@@ -86,6 +86,8 @@ module ActiveJob
86
86
  #
87
87
  # Global: The adapter is configured that all jobs have a maximum run time.
88
88
  #
89
+ # No: The adapter does not allow the timeout of jobs to be configured.
90
+ #
89
91
  # N/A: This adapter does not run in a separate process, and therefore timeout
90
92
  # is unsupported.
91
93
  #
@@ -99,6 +101,8 @@ module ActiveJob
99
101
  #
100
102
  # Global: The adapter has a global number of retries.
101
103
  #
104
+ # No: The adapter does not allow the number of retries to be configured.
105
+ #
102
106
  # N/A: The adapter does not run in a separate process, and therefore doesn't
103
107
  # support retries.
104
108
  #
@@ -10,7 +10,7 @@ module ActiveJob
10
10
  # This reduces the cost of hosting on a service like Heroku along
11
11
  # with the memory footprint of having to maintain additional jobs if
12
12
  # hosting on a dedicated server. All queues can run within a
13
- # single application (eg. Rails, Sinatra, etc.) process.
13
+ # single application (e.g. Rails, Sinatra, etc.) process.
14
14
  #
15
15
  # Read more about Sucker Punch {here}[https://github.com/brandonhilkert/sucker_punch].
16
16
  #
@@ -12,7 +12,7 @@ module ActiveJob
12
12
  #
13
13
  # Rails.application.config.active_job.queue_adapter = :test
14
14
  class TestAdapter
15
- attr_accessor(:perform_enqueued_jobs, :perform_enqueued_at_jobs, :filter, :reject, :queue)
15
+ attr_accessor(:perform_enqueued_jobs, :perform_enqueued_at_jobs, :filter, :reject, :queue, :at)
16
16
  attr_writer(:enqueued_jobs, :performed_jobs)
17
17
 
18
18
  # Provides a store of all the enqueued jobs with the TestAdapter so you can check them.
@@ -54,7 +54,11 @@ module ActiveJob
54
54
  end
55
55
 
56
56
  def filtered?(job)
57
- filtered_queue?(job) || filtered_job_class?(job)
57
+ filtered_queue?(job) || filtered_job_class?(job) || filtered_time?(job)
58
+ end
59
+
60
+ def filtered_time?(job)
61
+ job.scheduled_at > at.to_f if at && job.scheduled_at
58
62
  end
59
63
 
60
64
  def filtered_queue?(job)
@@ -6,7 +6,6 @@ module ActiveJob
6
6
 
7
7
  # Includes the ability to override the default queue name and prefix.
8
8
  module ClassMethods
9
- mattr_accessor :queue_name_prefix
10
9
  mattr_accessor :default_queue_name, default: "default"
11
10
 
12
11
  # Specifies the name of the queue to process the job on.
@@ -49,13 +48,14 @@ module ActiveJob
49
48
  def queue_name_from_part(part_name) #:nodoc:
50
49
  queue_name = part_name || default_queue_name
51
50
  name_parts = [queue_name_prefix.presence, queue_name]
52
- name_parts.compact.join(queue_name_delimiter)
51
+ -name_parts.compact.join(queue_name_delimiter)
53
52
  end
54
53
  end
55
54
 
56
55
  included do
57
56
  class_attribute :queue_name, instance_accessor: false, default: -> { self.class.default_queue_name }
58
57
  class_attribute :queue_name_delimiter, instance_accessor: false, default: "_"
58
+ class_attribute :queue_name_prefix
59
59
  end
60
60
 
61
61
  # Returns the name of the queue the job will be run on.
@@ -34,6 +34,10 @@ module ActiveJob
34
34
  ActiveSupport.on_load(:action_dispatch_integration_test) do
35
35
  include ActiveJob::TestHelper
36
36
  end
37
+
38
+ ActiveSupport.on_load(:active_record) do
39
+ self.destroy_association_async_job = ActiveRecord::DestroyAssociationAsyncJob
40
+ end
37
41
  end
38
42
 
39
43
  initializer "active_job.set_reloader_hook" do |app|
@@ -9,12 +9,14 @@ module ActiveJob
9
9
  extend ActiveSupport::Autoload
10
10
 
11
11
  autoload :ObjectSerializer
12
+ autoload :TimeObjectSerializer
12
13
  autoload :SymbolSerializer
13
14
  autoload :DurationSerializer
14
15
  autoload :DateTimeSerializer
15
16
  autoload :DateSerializer
16
17
  autoload :TimeWithZoneSerializer
17
18
  autoload :TimeSerializer
19
+ autoload :ModuleSerializer
18
20
 
19
21
  mattr_accessor :_additional_serializers
20
22
  self._additional_serializers = Set.new
@@ -58,6 +60,7 @@ module ActiveJob
58
60
  DateTimeSerializer,
59
61
  DateSerializer,
60
62
  TimeWithZoneSerializer,
61
- TimeSerializer
63
+ TimeSerializer,
64
+ ModuleSerializer
62
65
  end
63
66
  end
@@ -2,11 +2,7 @@
2
2
 
3
3
  module ActiveJob
4
4
  module Serializers
5
- class DateTimeSerializer < ObjectSerializer # :nodoc:
6
- def serialize(time)
7
- super("value" => time.iso8601)
8
- end
9
-
5
+ class DateTimeSerializer < TimeObjectSerializer # :nodoc:
10
6
  def deserialize(hash)
11
7
  DateTime.iso8601(hash["value"])
12
8
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveJob
4
+ module Serializers
5
+ class ModuleSerializer < ObjectSerializer # :nodoc:
6
+ def serialize(constant)
7
+ super("value" => constant.name)
8
+ end
9
+
10
+ def deserialize(hash)
11
+ hash["value"].constantize
12
+ end
13
+
14
+ private
15
+ def klass
16
+ Module
17
+ end
18
+ end
19
+ end
20
+ end
@@ -39,7 +39,7 @@ module ActiveJob
39
39
  end
40
40
 
41
41
  # Deserializes an argument from a JSON primitive type.
42
- def deserialize(_argument)
42
+ def deserialize(json)
43
43
  raise NotImplementedError
44
44
  end
45
45
 
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveJob
4
+ module Serializers
5
+ class TimeObjectSerializer < ObjectSerializer # :nodoc:
6
+ NANO_PRECISION = 9
7
+
8
+ def serialize(time)
9
+ super("value" => time.iso8601(NANO_PRECISION))
10
+ end
11
+ end
12
+ end
13
+ end
@@ -2,11 +2,7 @@
2
2
 
3
3
  module ActiveJob
4
4
  module Serializers
5
- class TimeSerializer < ObjectSerializer # :nodoc:
6
- def serialize(time)
7
- super("value" => time.iso8601)
8
- end
9
-
5
+ class TimeSerializer < TimeObjectSerializer # :nodoc:
10
6
  def deserialize(hash)
11
7
  Time.iso8601(hash["value"])
12
8
  end
@@ -2,11 +2,7 @@
2
2
 
3
3
  module ActiveJob
4
4
  module Serializers
5
- class TimeWithZoneSerializer < ObjectSerializer # :nodoc:
6
- def serialize(time)
7
- super("value" => time.iso8601)
8
- end
9
-
5
+ class TimeWithZoneSerializer < TimeObjectSerializer # :nodoc:
10
6
  def deserialize(hash)
11
7
  Time.iso8601(hash["value"]).in_time_zone
12
8
  end
@@ -117,17 +117,17 @@ module ActiveJob
117
117
  # HelloJob.perform_later('elfassy')
118
118
  # end
119
119
  # end
120
- def assert_enqueued_jobs(number, only: nil, except: nil, queue: nil)
120
+ def assert_enqueued_jobs(number, only: nil, except: nil, queue: nil, &block)
121
121
  if block_given?
122
- original_count = enqueued_jobs_with(only: only, except: except, queue: queue)
122
+ original_jobs = enqueued_jobs_with(only: only, except: except, queue: queue)
123
123
 
124
- yield
124
+ assert_nothing_raised(&block)
125
125
 
126
- new_count = enqueued_jobs_with(only: only, except: except, queue: queue)
126
+ new_jobs = enqueued_jobs_with(only: only, except: except, queue: queue)
127
127
 
128
- actual_count = new_count - original_count
128
+ actual_count = (new_jobs - original_jobs).count
129
129
  else
130
- actual_count = enqueued_jobs_with(only: only, except: except, queue: queue)
130
+ actual_count = enqueued_jobs_with(only: only, except: except, queue: queue).count
131
131
  end
132
132
 
133
133
  assert_equal number, actual_count, "#{number} jobs expected, but #{actual_count} were enqueued"
@@ -279,7 +279,7 @@ module ActiveJob
279
279
 
280
280
  performed_jobs_size = new_count - original_count
281
281
  else
282
- performed_jobs_size = performed_jobs_with(only: only, except: except, queue: queue)
282
+ performed_jobs_size = performed_jobs_with(only: only, except: except, queue: queue).count
283
283
  end
284
284
 
285
285
  assert_equal number, performed_jobs_size, "#{number} jobs expected, but #{performed_jobs_size} were performed"
@@ -345,44 +345,40 @@ module ActiveJob
345
345
  #
346
346
  # def test_assert_enqueued_with
347
347
  # MyJob.perform_later(1,2,3)
348
- # assert_enqueued_with(job: MyJob, args: [1,2,3], queue: 'low')
348
+ # assert_enqueued_with(job: MyJob, args: [1,2,3])
349
349
  #
350
- # MyJob.set(wait_until: Date.tomorrow.noon).perform_later
351
- # assert_enqueued_with(job: MyJob, at: Date.tomorrow.noon)
350
+ # MyJob.set(wait_until: Date.tomorrow.noon, queue: "my_queue").perform_later
351
+ # assert_enqueued_with(at: Date.tomorrow.noon, queue: "my_queue")
352
352
  # end
353
353
  #
354
- # The +at+ and +args+ arguments also accept a proc.
354
+ # The given arguments may also be specified as matcher procs that return a
355
+ # boolean value indicating whether a job's attribute meets certain criteria.
355
356
  #
356
- # To the +at+ proc, it will get passed the actual job's at argument.
357
+ # For example, a proc can be used to match a range of times:
357
358
  #
358
359
  # def test_assert_enqueued_with
359
- # expected_time = ->(at) do
360
- # (Date.yesterday..Date.tomorrow).cover?(at)
361
- # end
360
+ # at_matcher = ->(job_at) { (Date.yesterday..Date.tomorrow).cover?(job_at) }
361
+ #
362
+ # MyJob.set(wait_until: Date.today.noon).perform_later
362
363
  #
363
- # MyJob.set(at: Date.today.noon).perform_later
364
- # assert_enqueued_with(job: MyJob, at: expected_time)
364
+ # assert_enqueued_with(job: MyJob, at: at_matcher)
365
365
  # end
366
366
  #
367
- # To the +args+ proc, it will get passed the actual job's arguments
368
- # Your proc needs to return a boolean value determining if
369
- # the job's arguments matches your expectation. This is useful to check only
370
- # for a subset of arguments.
367
+ # A proc can also be used to match a subset of a job's args:
371
368
  #
372
369
  # def test_assert_enqueued_with
373
- # expected_args = ->(job_args) do
374
- # assert job_args.first.key?(:foo)
375
- # end
370
+ # args_matcher = ->(job_args) { job_args[0].key?(:foo) }
376
371
  #
377
- # MyJob.perform_later(foo: 'bar', other_arg: 'No need to check in the test')
378
- # assert_enqueued_with(job: MyJob, args: expected_args, queue: 'low')
372
+ # MyJob.perform_later(foo: "bar", other_arg: "No need to check in the test")
373
+ #
374
+ # assert_enqueued_with(job: MyJob, args: args_matcher)
379
375
  # end
380
376
  #
381
377
  # If a block is passed, asserts that the block will cause the job to be
382
378
  # enqueued with the given arguments.
383
379
  #
384
380
  # def test_assert_enqueued_with
385
- # assert_enqueued_with(job: MyJob, args: [1,2,3], queue: 'low') do
381
+ # assert_enqueued_with(job: MyJob, args: [1,2,3]) do
386
382
  # MyJob.perform_later(1,2,3)
387
383
  # end
388
384
  #
@@ -390,22 +386,24 @@ module ActiveJob
390
386
  # MyJob.set(wait_until: Date.tomorrow.noon).perform_later
391
387
  # end
392
388
  # end
393
- def assert_enqueued_with(job: nil, args: nil, at: nil, queue: nil)
389
+ def assert_enqueued_with(job: nil, args: nil, at: nil, queue: nil, &block)
394
390
  expected = { job: job, args: args, at: at, queue: queue }.compact
395
391
  expected_args = prepare_args_for_assertion(expected)
392
+ potential_matches = []
396
393
 
397
394
  if block_given?
398
- original_enqueued_jobs_count = enqueued_jobs.count
395
+ original_enqueued_jobs = enqueued_jobs.dup
399
396
 
400
- yield
397
+ assert_nothing_raised(&block)
401
398
 
402
- jobs = enqueued_jobs.drop(original_enqueued_jobs_count)
399
+ jobs = enqueued_jobs - original_enqueued_jobs
403
400
  else
404
401
  jobs = enqueued_jobs
405
402
  end
406
403
 
407
404
  matching_job = jobs.find do |enqueued_job|
408
405
  deserialized_job = deserialize_args_for_assertion(enqueued_job)
406
+ potential_matches << deserialized_job
409
407
 
410
408
  expected_args.all? do |key, value|
411
409
  if value.respond_to?(:call)
@@ -416,7 +414,9 @@ module ActiveJob
416
414
  end
417
415
  end
418
416
 
419
- assert matching_job, "No enqueued job found with #{expected}"
417
+ message = +"No enqueued job found with #{expected}"
418
+ message << "\n\nPotential matches: #{potential_matches.join("\n")}" if potential_matches.present?
419
+ assert matching_job, message
420
420
  instantiate_job(matching_job)
421
421
  end
422
422
 
@@ -427,42 +427,40 @@ module ActiveJob
427
427
  #
428
428
  # perform_enqueued_jobs
429
429
  #
430
- # assert_performed_with(job: MyJob, args: [1,2,3], queue: 'high')
430
+ # assert_performed_with(job: MyJob, args: [1,2,3])
431
431
  #
432
- # MyJob.set(wait_until: Date.tomorrow.noon).perform_later
432
+ # MyJob.set(wait_until: Date.tomorrow.noon, queue: "my_queue").perform_later
433
433
  #
434
434
  # perform_enqueued_jobs
435
435
  #
436
- # assert_performed_with(job: MyJob, at: Date.tomorrow.noon)
436
+ # assert_performed_with(at: Date.tomorrow.noon, queue: "my_queue")
437
437
  # end
438
438
  #
439
- # The +at+ and +args+ arguments also accept a proc.
439
+ # The given arguments may also be specified as matcher procs that return a
440
+ # boolean value indicating whether a job's attribute meets certain criteria.
440
441
  #
441
- # To the +at+ proc, it will get passed the actual job's at argument.
442
+ # For example, a proc can be used to match a range of times:
442
443
  #
443
- # def test_assert_enqueued_with
444
- # expected_time = ->(at) do
445
- # (Date.yesterday..Date.tomorrow).cover?(at)
446
- # end
444
+ # def test_assert_performed_with
445
+ # at_matcher = ->(job_at) { (Date.yesterday..Date.tomorrow).cover?(job_at) }
446
+ #
447
+ # MyJob.set(wait_until: Date.today.noon).perform_later
447
448
  #
448
- # MyJob.set(at: Date.today.noon).perform_later
449
- # assert_enqueued_with(job: MyJob, at: expected_time)
449
+ # perform_enqueued_jobs
450
+ #
451
+ # assert_performed_with(job: MyJob, at: at_matcher)
450
452
  # end
451
453
  #
452
- # To the +args+ proc, it will get passed the actual job's arguments
453
- # Your proc needs to return a boolean value determining if
454
- # the job's arguments matches your expectation. This is useful to check only
455
- # for a subset of arguments.
454
+ # A proc can also be used to match a subset of a job's args:
456
455
  #
457
456
  # def test_assert_performed_with
458
- # expected_args = ->(job_args) do
459
- # assert job_args.first.key?(:foo)
460
- # end
461
- # MyJob.perform_later(foo: 'bar', other_arg: 'No need to check in the test')
457
+ # args_matcher = ->(job_args) { job_args[0].key?(:foo) }
458
+ #
459
+ # MyJob.perform_later(foo: "bar", other_arg: "No need to check in the test")
462
460
  #
463
461
  # perform_enqueued_jobs
464
462
  #
465
- # assert_performed_with(job: MyJob, args: expected_args, queue: 'high')
463
+ # assert_performed_with(job: MyJob, args: args_matcher)
466
464
  # end
467
465
  #
468
466
  # If a block is passed, that block performs all of the jobs that were
@@ -470,7 +468,7 @@ module ActiveJob
470
468
  # the job has been performed with the given arguments in the block.
471
469
  #
472
470
  # def test_assert_performed_with
473
- # assert_performed_with(job: MyJob, args: [1,2,3], queue: 'high') do
471
+ # assert_performed_with(job: MyJob, args: [1,2,3]) do
474
472
  # MyJob.perform_later(1,2,3)
475
473
  # end
476
474
  #
@@ -481,6 +479,7 @@ module ActiveJob
481
479
  def assert_performed_with(job: nil, args: nil, at: nil, queue: nil, &block)
482
480
  expected = { job: job, args: args, at: at, queue: queue }.compact
483
481
  expected_args = prepare_args_for_assertion(expected)
482
+ potential_matches = []
484
483
 
485
484
  if block_given?
486
485
  original_performed_jobs_count = performed_jobs.count
@@ -494,6 +493,7 @@ module ActiveJob
494
493
 
495
494
  matching_job = jobs.find do |enqueued_job|
496
495
  deserialized_job = deserialize_args_for_assertion(enqueued_job)
496
+ potential_matches << deserialized_job
497
497
 
498
498
  expected_args.all? do |key, value|
499
499
  if value.respond_to?(:call)
@@ -504,7 +504,10 @@ module ActiveJob
504
504
  end
505
505
  end
506
506
 
507
- assert matching_job, "No performed job found with #{expected}"
507
+ message = +"No performed job found with #{expected}"
508
+ message << "\n\nPotential matches: #{potential_matches.join("\n")}" if potential_matches.present?
509
+ assert matching_job, message
510
+
508
511
  instantiate_job(matching_job)
509
512
  end
510
513
 
@@ -563,8 +566,10 @@ module ActiveJob
563
566
  # assert_performed_jobs 1
564
567
  # end
565
568
  #
566
- def perform_enqueued_jobs(only: nil, except: nil, queue: nil)
567
- return flush_enqueued_jobs(only: only, except: except, queue: queue) unless block_given?
569
+ # If the +:at+ option is specified, then only run jobs enqueued to run
570
+ # immediately or before the given time
571
+ def perform_enqueued_jobs(only: nil, except: nil, queue: nil, at: nil, &block)
572
+ return flush_enqueued_jobs(only: only, except: except, queue: queue, at: at) unless block_given?
568
573
 
569
574
  validate_option(only: only, except: except)
570
575
 
@@ -573,6 +578,7 @@ module ActiveJob
573
578
  old_filter = queue_adapter.filter
574
579
  old_reject = queue_adapter.reject
575
580
  old_queue = queue_adapter.queue
581
+ old_at = queue_adapter.at
576
582
 
577
583
  begin
578
584
  queue_adapter.perform_enqueued_jobs = true
@@ -580,14 +586,16 @@ module ActiveJob
580
586
  queue_adapter.filter = only
581
587
  queue_adapter.reject = except
582
588
  queue_adapter.queue = queue
589
+ queue_adapter.at = at
583
590
 
584
- yield
591
+ assert_nothing_raised(&block)
585
592
  ensure
586
593
  queue_adapter.perform_enqueued_jobs = old_perform_enqueued_jobs
587
594
  queue_adapter.perform_enqueued_at_jobs = old_perform_enqueued_at_jobs
588
595
  queue_adapter.filter = old_filter
589
596
  queue_adapter.reject = old_reject
590
597
  queue_adapter.queue = old_queue
598
+ queue_adapter.at = old_at
591
599
  end
592
600
  end
593
601
 
@@ -609,10 +617,10 @@ module ActiveJob
609
617
  performed_jobs.clear
610
618
  end
611
619
 
612
- def jobs_with(jobs, only: nil, except: nil, queue: nil)
620
+ def jobs_with(jobs, only: nil, except: nil, queue: nil, at: nil)
613
621
  validate_option(only: only, except: except)
614
622
 
615
- jobs.count do |job|
623
+ jobs.dup.select do |job|
616
624
  job_class = job.fetch(:job)
617
625
 
618
626
  if only
@@ -625,6 +633,10 @@ module ActiveJob
625
633
  next false unless queue.to_s == job.fetch(:queue, job_class.queue_name)
626
634
  end
627
635
 
636
+ if at && job[:at]
637
+ next false if job[:at] > at.to_f
638
+ end
639
+
628
640
  yield job if block_given?
629
641
 
630
642
  true
@@ -637,41 +649,28 @@ module ActiveJob
637
649
  ->(job) { Array(filter).include?(job.fetch(:job)) }
638
650
  end
639
651
 
640
- def enqueued_jobs_with(only: nil, except: nil, queue: nil, &block)
641
- jobs_with(enqueued_jobs, only: only, except: except, queue: queue, &block)
652
+ def enqueued_jobs_with(only: nil, except: nil, queue: nil, at: nil, &block)
653
+ jobs_with(enqueued_jobs, only: only, except: except, queue: queue, at: at, &block)
642
654
  end
643
655
 
644
656
  def performed_jobs_with(only: nil, except: nil, queue: nil, &block)
645
657
  jobs_with(performed_jobs, only: only, except: except, queue: queue, &block)
646
658
  end
647
659
 
648
- def flush_enqueued_jobs(only: nil, except: nil, queue: nil)
649
- enqueued_jobs_with(only: only, except: except, queue: queue) do |payload|
650
- instantiate_job(payload).perform_now
660
+ def flush_enqueued_jobs(only: nil, except: nil, queue: nil, at: nil)
661
+ enqueued_jobs_with(only: only, except: except, queue: queue, at: at) do |payload|
662
+ queue_adapter.enqueued_jobs.delete(payload)
651
663
  queue_adapter.performed_jobs << payload
652
- end
664
+ instantiate_job(payload).perform_now
665
+ end.count
653
666
  end
654
667
 
655
668
  def prepare_args_for_assertion(args)
656
669
  args.dup.tap do |arguments|
657
- if arguments[:at] && !arguments[:at].respond_to?(:call)
670
+ if arguments[:at].acts_like?(:time)
658
671
  at_range = arguments[:at] - 1..arguments[:at] + 1
659
672
  arguments[:at] = ->(at) { at_range.cover?(at) }
660
673
  end
661
- arguments[:args] = round_time_arguments(arguments[:args]) if arguments[:args]
662
- end
663
- end
664
-
665
- def round_time_arguments(argument)
666
- case argument
667
- when Time, ActiveSupport::TimeWithZone, DateTime
668
- argument.change(usec: 0)
669
- when Hash
670
- argument.transform_values { |value| round_time_arguments(value) }
671
- when Array
672
- argument.map { |element| round_time_arguments(element) }
673
- else
674
- argument
675
674
  end
676
675
  end
677
676