activejob 6.0.3.3 → 6.1.1

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
@@ -9,6 +9,8 @@ module ActiveJob
9
9
  :performed_jobs, :performed_jobs=,
10
10
  to: :queue_adapter
11
11
 
12
+ include ActiveSupport::Testing::Assertions
13
+
12
14
  module TestQueueAdapter
13
15
  extend ActiveSupport::Concern
14
16
 
@@ -117,17 +119,17 @@ module ActiveJob
117
119
  # HelloJob.perform_later('elfassy')
118
120
  # end
119
121
  # end
120
- def assert_enqueued_jobs(number, only: nil, except: nil, queue: nil)
122
+ def assert_enqueued_jobs(number, only: nil, except: nil, queue: nil, &block)
121
123
  if block_given?
122
- original_count = enqueued_jobs_with(only: only, except: except, queue: queue)
124
+ original_jobs = enqueued_jobs_with(only: only, except: except, queue: queue)
123
125
 
124
- yield
126
+ assert_nothing_raised(&block)
125
127
 
126
- new_count = enqueued_jobs_with(only: only, except: except, queue: queue)
128
+ new_jobs = enqueued_jobs_with(only: only, except: except, queue: queue)
127
129
 
128
- actual_count = new_count - original_count
130
+ actual_count = (new_jobs - original_jobs).count
129
131
  else
130
- actual_count = enqueued_jobs_with(only: only, except: except, queue: queue)
132
+ actual_count = enqueued_jobs_with(only: only, except: except, queue: queue).count
131
133
  end
132
134
 
133
135
  assert_equal number, actual_count, "#{number} jobs expected, but #{actual_count} were enqueued"
@@ -279,7 +281,7 @@ module ActiveJob
279
281
 
280
282
  performed_jobs_size = new_count - original_count
281
283
  else
282
- performed_jobs_size = performed_jobs_with(only: only, except: except, queue: queue)
284
+ performed_jobs_size = performed_jobs_with(only: only, except: except, queue: queue).count
283
285
  end
284
286
 
285
287
  assert_equal number, performed_jobs_size, "#{number} jobs expected, but #{performed_jobs_size} were performed"
@@ -345,44 +347,40 @@ module ActiveJob
345
347
  #
346
348
  # def test_assert_enqueued_with
347
349
  # MyJob.perform_later(1,2,3)
348
- # assert_enqueued_with(job: MyJob, args: [1,2,3], queue: 'low')
350
+ # assert_enqueued_with(job: MyJob, args: [1,2,3])
349
351
  #
350
- # MyJob.set(wait_until: Date.tomorrow.noon).perform_later
351
- # assert_enqueued_with(job: MyJob, at: Date.tomorrow.noon)
352
+ # MyJob.set(wait_until: Date.tomorrow.noon, queue: "my_queue").perform_later
353
+ # assert_enqueued_with(at: Date.tomorrow.noon, queue: "my_queue")
352
354
  # end
353
355
  #
354
- # The +at+ and +args+ arguments also accept a proc.
356
+ # The given arguments may also be specified as matcher procs that return a
357
+ # boolean value indicating whether a job's attribute meets certain criteria.
355
358
  #
356
- # To the +at+ proc, it will get passed the actual job's at argument.
359
+ # For example, a proc can be used to match a range of times:
357
360
  #
358
361
  # def test_assert_enqueued_with
359
- # expected_time = ->(at) do
360
- # (Date.yesterday..Date.tomorrow).cover?(at)
361
- # end
362
+ # at_matcher = ->(job_at) { (Date.yesterday..Date.tomorrow).cover?(job_at) }
363
+ #
364
+ # MyJob.set(wait_until: Date.today.noon).perform_later
362
365
  #
363
- # MyJob.set(at: Date.today.noon).perform_later
364
- # assert_enqueued_with(job: MyJob, at: expected_time)
366
+ # assert_enqueued_with(job: MyJob, at: at_matcher)
365
367
  # end
366
368
  #
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.
369
+ # A proc can also be used to match a subset of a job's args:
371
370
  #
372
371
  # def test_assert_enqueued_with
373
- # expected_args = ->(job_args) do
374
- # assert job_args.first.key?(:foo)
375
- # end
372
+ # args_matcher = ->(job_args) { job_args[0].key?(:foo) }
373
+ #
374
+ # MyJob.perform_later(foo: "bar", other_arg: "No need to check in the test")
376
375
  #
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')
376
+ # assert_enqueued_with(job: MyJob, args: args_matcher)
379
377
  # end
380
378
  #
381
379
  # If a block is passed, asserts that the block will cause the job to be
382
380
  # enqueued with the given arguments.
383
381
  #
384
382
  # def test_assert_enqueued_with
385
- # assert_enqueued_with(job: MyJob, args: [1,2,3], queue: 'low') do
383
+ # assert_enqueued_with(job: MyJob, args: [1,2,3]) do
386
384
  # MyJob.perform_later(1,2,3)
387
385
  # end
388
386
  #
@@ -390,22 +388,24 @@ module ActiveJob
390
388
  # MyJob.set(wait_until: Date.tomorrow.noon).perform_later
391
389
  # end
392
390
  # end
393
- def assert_enqueued_with(job: nil, args: nil, at: nil, queue: nil)
391
+ def assert_enqueued_with(job: nil, args: nil, at: nil, queue: nil, &block)
394
392
  expected = { job: job, args: args, at: at, queue: queue }.compact
395
393
  expected_args = prepare_args_for_assertion(expected)
394
+ potential_matches = []
396
395
 
397
396
  if block_given?
398
- original_enqueued_jobs_count = enqueued_jobs.count
397
+ original_enqueued_jobs = enqueued_jobs.dup
399
398
 
400
- yield
399
+ assert_nothing_raised(&block)
401
400
 
402
- jobs = enqueued_jobs.drop(original_enqueued_jobs_count)
401
+ jobs = enqueued_jobs - original_enqueued_jobs
403
402
  else
404
403
  jobs = enqueued_jobs
405
404
  end
406
405
 
407
406
  matching_job = jobs.find do |enqueued_job|
408
407
  deserialized_job = deserialize_args_for_assertion(enqueued_job)
408
+ potential_matches << deserialized_job
409
409
 
410
410
  expected_args.all? do |key, value|
411
411
  if value.respond_to?(:call)
@@ -416,7 +416,9 @@ module ActiveJob
416
416
  end
417
417
  end
418
418
 
419
- assert matching_job, "No enqueued job found with #{expected}"
419
+ message = +"No enqueued job found with #{expected}"
420
+ message << "\n\nPotential matches: #{potential_matches.join("\n")}" if potential_matches.present?
421
+ assert matching_job, message
420
422
  instantiate_job(matching_job)
421
423
  end
422
424
 
@@ -427,42 +429,40 @@ module ActiveJob
427
429
  #
428
430
  # perform_enqueued_jobs
429
431
  #
430
- # assert_performed_with(job: MyJob, args: [1,2,3], queue: 'high')
432
+ # assert_performed_with(job: MyJob, args: [1,2,3])
431
433
  #
432
- # MyJob.set(wait_until: Date.tomorrow.noon).perform_later
434
+ # MyJob.set(wait_until: Date.tomorrow.noon, queue: "my_queue").perform_later
433
435
  #
434
436
  # perform_enqueued_jobs
435
437
  #
436
- # assert_performed_with(job: MyJob, at: Date.tomorrow.noon)
438
+ # assert_performed_with(at: Date.tomorrow.noon, queue: "my_queue")
437
439
  # end
438
440
  #
439
- # The +at+ and +args+ arguments also accept a proc.
441
+ # The given arguments may also be specified as matcher procs that return a
442
+ # boolean value indicating whether a job's attribute meets certain criteria.
440
443
  #
441
- # To the +at+ proc, it will get passed the actual job's at argument.
444
+ # For example, a proc can be used to match a range of times:
442
445
  #
443
- # def test_assert_enqueued_with
444
- # expected_time = ->(at) do
445
- # (Date.yesterday..Date.tomorrow).cover?(at)
446
- # end
446
+ # def test_assert_performed_with
447
+ # at_matcher = ->(job_at) { (Date.yesterday..Date.tomorrow).cover?(job_at) }
448
+ #
449
+ # MyJob.set(wait_until: Date.today.noon).perform_later
450
+ #
451
+ # perform_enqueued_jobs
447
452
  #
448
- # MyJob.set(at: Date.today.noon).perform_later
449
- # assert_enqueued_with(job: MyJob, at: expected_time)
453
+ # assert_performed_with(job: MyJob, at: at_matcher)
450
454
  # end
451
455
  #
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.
456
+ # A proc can also be used to match a subset of a job's args:
456
457
  #
457
458
  # 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')
459
+ # args_matcher = ->(job_args) { job_args[0].key?(:foo) }
460
+ #
461
+ # MyJob.perform_later(foo: "bar", other_arg: "No need to check in the test")
462
462
  #
463
463
  # perform_enqueued_jobs
464
464
  #
465
- # assert_performed_with(job: MyJob, args: expected_args, queue: 'high')
465
+ # assert_performed_with(job: MyJob, args: args_matcher)
466
466
  # end
467
467
  #
468
468
  # If a block is passed, that block performs all of the jobs that were
@@ -470,7 +470,7 @@ module ActiveJob
470
470
  # the job has been performed with the given arguments in the block.
471
471
  #
472
472
  # def test_assert_performed_with
473
- # assert_performed_with(job: MyJob, args: [1,2,3], queue: 'high') do
473
+ # assert_performed_with(job: MyJob, args: [1,2,3]) do
474
474
  # MyJob.perform_later(1,2,3)
475
475
  # end
476
476
  #
@@ -481,6 +481,7 @@ module ActiveJob
481
481
  def assert_performed_with(job: nil, args: nil, at: nil, queue: nil, &block)
482
482
  expected = { job: job, args: args, at: at, queue: queue }.compact
483
483
  expected_args = prepare_args_for_assertion(expected)
484
+ potential_matches = []
484
485
 
485
486
  if block_given?
486
487
  original_performed_jobs_count = performed_jobs.count
@@ -494,6 +495,7 @@ module ActiveJob
494
495
 
495
496
  matching_job = jobs.find do |enqueued_job|
496
497
  deserialized_job = deserialize_args_for_assertion(enqueued_job)
498
+ potential_matches << deserialized_job
497
499
 
498
500
  expected_args.all? do |key, value|
499
501
  if value.respond_to?(:call)
@@ -504,7 +506,10 @@ module ActiveJob
504
506
  end
505
507
  end
506
508
 
507
- assert matching_job, "No performed job found with #{expected}"
509
+ message = +"No performed job found with #{expected}"
510
+ message << "\n\nPotential matches: #{potential_matches.join("\n")}" if potential_matches.present?
511
+ assert matching_job, message
512
+
508
513
  instantiate_job(matching_job)
509
514
  end
510
515
 
@@ -563,8 +568,10 @@ module ActiveJob
563
568
  # assert_performed_jobs 1
564
569
  # end
565
570
  #
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?
571
+ # If the +:at+ option is specified, then only run jobs enqueued to run
572
+ # immediately or before the given time
573
+ def perform_enqueued_jobs(only: nil, except: nil, queue: nil, at: nil, &block)
574
+ return flush_enqueued_jobs(only: only, except: except, queue: queue, at: at) unless block_given?
568
575
 
569
576
  validate_option(only: only, except: except)
570
577
 
@@ -573,6 +580,7 @@ module ActiveJob
573
580
  old_filter = queue_adapter.filter
574
581
  old_reject = queue_adapter.reject
575
582
  old_queue = queue_adapter.queue
583
+ old_at = queue_adapter.at
576
584
 
577
585
  begin
578
586
  queue_adapter.perform_enqueued_jobs = true
@@ -580,14 +588,16 @@ module ActiveJob
580
588
  queue_adapter.filter = only
581
589
  queue_adapter.reject = except
582
590
  queue_adapter.queue = queue
591
+ queue_adapter.at = at
583
592
 
584
- yield
593
+ assert_nothing_raised(&block)
585
594
  ensure
586
595
  queue_adapter.perform_enqueued_jobs = old_perform_enqueued_jobs
587
596
  queue_adapter.perform_enqueued_at_jobs = old_perform_enqueued_at_jobs
588
597
  queue_adapter.filter = old_filter
589
598
  queue_adapter.reject = old_reject
590
599
  queue_adapter.queue = old_queue
600
+ queue_adapter.at = old_at
591
601
  end
592
602
  end
593
603
 
@@ -609,10 +619,10 @@ module ActiveJob
609
619
  performed_jobs.clear
610
620
  end
611
621
 
612
- def jobs_with(jobs, only: nil, except: nil, queue: nil)
622
+ def jobs_with(jobs, only: nil, except: nil, queue: nil, at: nil)
613
623
  validate_option(only: only, except: except)
614
624
 
615
- jobs.count do |job|
625
+ jobs.dup.select do |job|
616
626
  job_class = job.fetch(:job)
617
627
 
618
628
  if only
@@ -625,6 +635,10 @@ module ActiveJob
625
635
  next false unless queue.to_s == job.fetch(:queue, job_class.queue_name)
626
636
  end
627
637
 
638
+ if at && job[:at]
639
+ next false if job[:at] > at.to_f
640
+ end
641
+
628
642
  yield job if block_given?
629
643
 
630
644
  true
@@ -637,41 +651,28 @@ module ActiveJob
637
651
  ->(job) { Array(filter).include?(job.fetch(:job)) }
638
652
  end
639
653
 
640
- def enqueued_jobs_with(only: nil, except: nil, queue: nil, &block)
641
- jobs_with(enqueued_jobs, only: only, except: except, queue: queue, &block)
654
+ def enqueued_jobs_with(only: nil, except: nil, queue: nil, at: nil, &block)
655
+ jobs_with(enqueued_jobs, only: only, except: except, queue: queue, at: at, &block)
642
656
  end
643
657
 
644
658
  def performed_jobs_with(only: nil, except: nil, queue: nil, &block)
645
659
  jobs_with(performed_jobs, only: only, except: except, queue: queue, &block)
646
660
  end
647
661
 
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
662
+ def flush_enqueued_jobs(only: nil, except: nil, queue: nil, at: nil)
663
+ enqueued_jobs_with(only: only, except: except, queue: queue, at: at) do |payload|
664
+ queue_adapter.enqueued_jobs.delete(payload)
651
665
  queue_adapter.performed_jobs << payload
652
- end
666
+ instantiate_job(payload).perform_now
667
+ end.count
653
668
  end
654
669
 
655
670
  def prepare_args_for_assertion(args)
656
671
  args.dup.tap do |arguments|
657
- if arguments[:at] && !arguments[:at].respond_to?(:call)
672
+ if arguments[:at].acts_like?(:time)
658
673
  at_range = arguments[:at] - 1..arguments[:at] + 1
659
674
  arguments[:at] = ->(at) { at_range.cover?(at) }
660
675
  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
676
  end
676
677
  end
677
678