activejob 6.0.3.1 → 6.1.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
  #
@@ -12,7 +12,7 @@ module ActiveJob
12
12
  # Rails.application.config.active_job.queue_adapter = :inline
13
13
  class InlineAdapter
14
14
  def enqueue(job) #:nodoc:
15
- Base.execute(job.serialize)
15
+ Thread.new { Base.execute(job.serialize) }.join
16
16
  end
17
17
 
18
18
  def enqueue_at(*) #:nodoc:
@@ -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