inst-jobs 0.15.22 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/db/migrate/20101216224513_create_delayed_jobs.rb +2 -0
  3. data/db/migrate/20110208031356_add_delayed_jobs_tag.rb +2 -0
  4. data/db/migrate/20110426161613_add_delayed_jobs_max_attempts.rb +2 -0
  5. data/db/migrate/20110516225834_add_delayed_jobs_strand.rb +2 -0
  6. data/db/migrate/20110531144916_cleanup_delayed_jobs_indexes.rb +2 -0
  7. data/db/migrate/20110610213249_optimize_delayed_jobs.rb +2 -0
  8. data/db/migrate/20110831210257_add_delayed_jobs_next_in_strand.rb +2 -0
  9. data/db/migrate/20120510004759_delayed_jobs_delete_trigger_lock_for_update.rb +2 -0
  10. data/db/migrate/20120531150712_drop_psql_jobs_pop_fn.rb +2 -0
  11. data/db/migrate/20120607164022_delayed_jobs_use_advisory_locks.rb +2 -0
  12. data/db/migrate/20120607181141_index_jobs_on_locked_by.rb +2 -0
  13. data/db/migrate/20120608191051_add_jobs_run_at_index.rb +2 -0
  14. data/db/migrate/20120927184213_change_delayed_jobs_handler_to_text.rb +2 -0
  15. data/db/migrate/20140505215131_add_failed_jobs_original_job_id.rb +2 -0
  16. data/db/migrate/20140505215510_copy_failed_jobs_original_id.rb +2 -0
  17. data/db/migrate/20140505223637_drop_failed_jobs_original_id.rb +2 -0
  18. data/db/migrate/20140512213941_add_source_to_jobs.rb +2 -0
  19. data/db/migrate/20150807133223_add_max_concurrent_to_jobs.rb +2 -0
  20. data/db/migrate/20151123210429_add_expires_at_to_jobs.rb +2 -0
  21. data/db/migrate/20151210162949_improve_max_concurrent.rb +2 -0
  22. data/db/migrate/20161206323555_add_back_default_string_limits_jobs.rb +2 -0
  23. data/db/migrate/20181217155351_speed_up_max_concurrent_triggers.rb +2 -0
  24. data/db/migrate/20190726154743_make_critical_columns_not_null.rb +2 -0
  25. data/db/migrate/20200330230722_add_id_to_get_delayed_jobs_index.rb +2 -0
  26. data/db/migrate/20200824222232_speed_up_max_concurrent_delete_trigger.rb +97 -0
  27. data/db/migrate/20200825011002_add_strand_order_override.rb +128 -0
  28. data/lib/delayed/backend/active_record.rb +9 -5
  29. data/lib/delayed/backend/base.rb +34 -20
  30. data/lib/delayed/backend/redis/functions.rb +2 -0
  31. data/lib/delayed/backend/redis/job.rb +2 -0
  32. data/lib/delayed/batch.rb +5 -3
  33. data/lib/delayed/cli.rb +2 -0
  34. data/lib/delayed/core_ext/kernel.rb +9 -0
  35. data/lib/delayed/daemon.rb +2 -0
  36. data/lib/delayed/engine.rb +2 -0
  37. data/lib/delayed/job_tracking.rb +2 -0
  38. data/lib/delayed/lifecycle.rb +2 -0
  39. data/lib/delayed/log_tailer.rb +2 -0
  40. data/lib/delayed/logging.rb +2 -0
  41. data/lib/delayed/message_sending.rb +90 -106
  42. data/lib/delayed/performable_method.rb +34 -6
  43. data/lib/delayed/periodic.rb +6 -4
  44. data/lib/delayed/plugin.rb +2 -0
  45. data/lib/delayed/pool.rb +2 -0
  46. data/lib/delayed/server.rb +2 -0
  47. data/lib/delayed/server/helpers.rb +2 -0
  48. data/lib/delayed/settings.rb +2 -0
  49. data/lib/delayed/testing.rb +2 -0
  50. data/lib/delayed/version.rb +3 -1
  51. data/lib/delayed/work_queue/in_process.rb +2 -0
  52. data/lib/delayed/work_queue/parent_process.rb +2 -0
  53. data/lib/delayed/work_queue/parent_process/client.rb +2 -0
  54. data/lib/delayed/work_queue/parent_process/server.rb +2 -0
  55. data/lib/delayed/worker.rb +2 -0
  56. data/lib/delayed/worker/consul_health_check.rb +3 -1
  57. data/lib/delayed/worker/health_check.rb +2 -0
  58. data/lib/delayed/worker/null_health_check.rb +2 -0
  59. data/lib/delayed/worker/process_helper.rb +2 -0
  60. data/lib/delayed/yaml_extensions.rb +2 -0
  61. data/lib/delayed_job.rb +4 -0
  62. data/lib/inst-jobs.rb +2 -0
  63. data/spec/active_record_job_spec.rb +4 -6
  64. data/spec/delayed/cli_spec.rb +2 -0
  65. data/spec/delayed/daemon_spec.rb +2 -0
  66. data/spec/delayed/message_sending_spec.rb +101 -0
  67. data/spec/delayed/server_spec.rb +2 -4
  68. data/spec/delayed/settings_spec.rb +2 -0
  69. data/spec/delayed/work_queue/in_process_spec.rb +2 -4
  70. data/spec/delayed/work_queue/parent_process/client_spec.rb +2 -4
  71. data/spec/delayed/work_queue/parent_process/server_spec.rb +2 -1
  72. data/spec/delayed/work_queue/parent_process_spec.rb +2 -1
  73. data/spec/delayed/worker/consul_health_check_spec.rb +3 -1
  74. data/spec/delayed/worker/health_check_spec.rb +2 -0
  75. data/spec/delayed/worker_spec.rb +2 -0
  76. data/spec/gemfiles/42.gemfile.lock +192 -0
  77. data/spec/gemfiles/50.gemfile.lock +197 -0
  78. data/spec/gemfiles/51.gemfile.lock +198 -0
  79. data/spec/gemfiles/52.gemfile.lock +206 -0
  80. data/spec/gemfiles/60.gemfile.lock +224 -0
  81. data/spec/migrate/20140924140513_add_story_table.rb +2 -0
  82. data/spec/redis_job_spec.rb +10 -12
  83. data/spec/sample_jobs.rb +2 -0
  84. data/spec/shared/delayed_batch.rb +17 -15
  85. data/spec/shared/delayed_method.rb +49 -204
  86. data/spec/shared/performable_method.rb +11 -9
  87. data/spec/shared/shared_backend.rb +27 -25
  88. data/spec/shared/testing.rb +7 -5
  89. data/spec/shared/worker.rb +15 -13
  90. data/spec/shared_jobs_specs.rb +2 -0
  91. data/spec/spec_helper.rb +12 -1
  92. metadata +36 -7
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Delayed
2
4
  module Backend
3
5
  class DeserializationError < StandardError
@@ -28,19 +30,31 @@ module Delayed
28
30
  # The first argument should be an object that respond_to?(:perform)
29
31
  # The rest should be named arguments, these keys are expected:
30
32
  # :priority, :run_at, :queue, :strand, :singleton
31
- # Example: Delayed::Job.enqueue(object, :priority => 0, :run_at => time, :queue => queue)
32
- def enqueue(*args)
33
- object = args.shift
33
+ # Example: Delayed::Job.enqueue(object, priority: 0, run_at: time, queue: queue)
34
+ def enqueue(object,
35
+ priority: default_priority,
36
+ run_at: nil,
37
+ expires_at: nil,
38
+ queue: Delayed::Settings.queue,
39
+ strand: nil,
40
+ singleton: nil,
41
+ n_strand: nil,
42
+ max_attempts: Delayed::Settings.max_attempts,
43
+ **kwargs)
44
+
34
45
  unless object.respond_to?(:perform)
35
46
  raise ArgumentError, 'Cannot enqueue items which do not respond to perform'
36
47
  end
37
48
 
38
- options = Settings.default_job_options.merge(args.first || {})
39
- options[:priority] ||= self.default_priority
40
- options[:payload_object] = object
41
- options[:queue] = Delayed::Settings.queue unless options.key?(:queue)
42
- options[:max_attempts] ||= Delayed::Settings.max_attempts
43
- options[:source] = Marginalia::Comment.construct_comment if defined?(Marginalia) && Marginalia::Comment.components
49
+ kwargs = Settings.default_job_options.merge(kwargs)
50
+ kwargs[:payload_object] = object
51
+ kwargs[:priority] = priority
52
+ kwargs[:run_at] = run_at if run_at
53
+ kwargs[:strand] = strand
54
+ kwargs[:max_attempts] = max_attempts
55
+ kwargs[:source] = Marginalia::Comment.construct_comment if defined?(Marginalia) && Marginalia::Comment.components
56
+ kwargs[:expires_at] = expires_at
57
+ kwargs[:queue] = queue
44
58
 
45
59
  # If two parameters are given to n_strand, the first param is used
46
60
  # as the strand name for looking up the Setting, while the second
@@ -49,8 +63,8 @@ module Delayed
49
63
  # For instance, you can pass ["my_job_type", # root_account.global_id]
50
64
  # to get a set of n strands per root account, and you can apply the
51
65
  # same default to all.
52
- if options[:n_strand]
53
- strand_name, ext = options.delete(:n_strand)
66
+ if n_strand
67
+ strand_name, ext = n_strand
54
68
 
55
69
  if ext
56
70
  full_strand_name = "#{strand_name}/#{ext}"
@@ -62,18 +76,18 @@ module Delayed
62
76
  num_strands ||= Delayed::Settings.num_strands.call(strand_name)
63
77
  num_strands = num_strands ? num_strands.to_i : 1
64
78
 
65
- options.merge!(n_strand_options(full_strand_name, num_strands))
79
+ kwargs.merge!(n_strand_options(full_strand_name, num_strands))
66
80
  end
67
81
 
68
- if options[:singleton]
69
- options[:strand] = options.delete :singleton
70
- job = self.create_singleton(options)
71
- elsif batches && options.slice(:strand, :run_at).empty?
72
- batch_enqueue_args = options.slice(*self.batch_enqueue_args)
73
- batches[batch_enqueue_args] << options
82
+ if singleton
83
+ kwargs[:strand] = singleton
84
+ job = self.create_singleton(**kwargs)
85
+ elsif batches && strand.nil? && run_at.nil?
86
+ batch_enqueue_args = kwargs.slice(*self.batch_enqueue_args)
87
+ batches[batch_enqueue_args] << kwargs
74
88
  return true
75
89
  else
76
- job = self.create(options)
90
+ job = self.create(**kwargs)
77
91
  end
78
92
 
79
93
  JobTracking.job_created(job)
@@ -87,7 +101,7 @@ module Delayed
87
101
  def n_strand_options(strand_name, num_strands)
88
102
  strand_num = num_strands > 1 ? rand(num_strands) + 1 : 1
89
103
  strand_name += ":#{strand_num}" if strand_num > 1
90
- {:strand => strand_name}
104
+ { strand: strand_name }
91
105
  end
92
106
 
93
107
  def in_delayed_job?
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'redis/scripting'
2
4
 
3
5
  # This module handles loading the Lua functions into Redis and running them
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This can't currently be made compatible with redis cluster, because the Lua functions
2
4
  # access keys that aren't in their keys argument list (since they pop jobs off
3
5
  # a queue and then update the job with that id).
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Delayed
2
4
  module Batch
3
5
  class PerformableBatch < Struct.new(:mode, :items)
@@ -18,7 +20,7 @@ module Delayed
18
20
  end
19
21
 
20
22
  def jobs
21
- items.map { |opts| Delayed::Job.new(opts) }
23
+ items.map { |opts| Delayed::Job.new(**opts) }
22
24
  end
23
25
  end
24
26
 
@@ -45,9 +47,9 @@ module Delayed
45
47
  elsif batch.size == 1
46
48
  args = batch.first.merge(batch_args)
47
49
  payload_object = args.delete(:payload_object)
48
- Delayed::Job.enqueue(payload_object, args)
50
+ Delayed::Job.enqueue(payload_object, **args)
49
51
  else
50
- Delayed::Job.enqueue(Delayed::Batch::PerformableBatch.new(mode, batch), enqueue_args.merge(batch_args))
52
+ Delayed::Job.enqueue(Delayed::Batch::PerformableBatch.new(mode, batch), **enqueue_args.merge(batch_args))
51
53
  end
52
54
  end
53
55
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'optparse'
2
4
 
3
5
  module Delayed
@@ -0,0 +1,9 @@
1
+ module Kernel
2
+ def sender(i = 0)
3
+ frame_self = nil
4
+ # 3. one for the block, one for this method, one for the method calling this
5
+ # method, and _then_ we get to the self for who sent the message we want
6
+ RubyVM::DebugInspector.open { |dc| frame_self = dc.frame_self(3 + i) }
7
+ frame_self
8
+ end
9
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'fileutils'
2
4
 
3
5
  module Delayed
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Delayed
2
4
  class Engine < ::Rails::Engine
3
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Delayed
2
4
  # Used when a block of code wants to track what jobs are created,
3
5
  # for instance in tests.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Delayed
2
4
  class InvalidCallback < Exception; end
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Delayed
2
4
  class LogTailer
3
5
  def run
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'date'
2
4
 
3
5
  module Delayed
@@ -1,109 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ if ::Rails.env.test? || ::Rails.env.development?
4
+ require 'debug_inspector'
5
+ end
6
+
1
7
  module Delayed
2
8
  module MessageSending
3
- def send_later(method, *args)
4
- send_later_enqueue_args(method, {}, *args)
5
- end
9
+ class DelayProxy < BasicObject
10
+ def initialize(object, synchronous: false, sender: nil, **enqueue_args)
11
+ @object = object
12
+ @enqueue_args = enqueue_args
13
+ @synchronous = synchronous
14
+ @sender = sender
15
+ end
6
16
 
7
- def send_later_enqueue_args(method, enqueue_args = {}, *args)
8
- enqueue_args = enqueue_args.dup
9
- # support procs/methods as enqueue arguments
10
- enqueue_args.each do |k,v|
11
- if v.respond_to?(:call)
12
- enqueue_args[k] = v.call(self)
17
+ def method_missing(method, *args, **kwargs)
18
+ # method doesn't exist? must be method_missing; assume private access
19
+ @sender = nil if !@sender.nil? &&
20
+ !@object.methods.include?(method) &&
21
+ !@object.protected_methods.include?(method) &&
22
+ !@object.private_methods.include?(method)
23
+
24
+ sender_is_object = @sender == @object
25
+ sender_is_class = @sender.is_a?(@object.class)
26
+
27
+ # even if the call is async, if the call is _going_ to generate an error, we make it synchronous
28
+ # so that the error is generated immediately, instead of waiting for it to fail in a job,
29
+ # which might go unnoticed
30
+ if !@sender.nil? && !@synchronous
31
+ @synchronous = true if !sender_is_object && @object.private_methods.include?(method)
32
+ @synchronous = true if !sender_is_class && @object.protected_methods.include?(method)
13
33
  end
14
- end
15
34
 
16
- no_delay = enqueue_args.delete(:no_delay)
17
- on_failure = enqueue_args.delete(:on_failure)
18
- on_permanent_failure = enqueue_args.delete(:on_permanent_failure)
19
- if !no_delay
20
- # delay queuing up the job in another database until the results of the current
21
- # transaction are visible
22
- connection = self.class.connection if self.class.respond_to?(:connection)
23
- connection ||= self.connection if respond_to?(:connection)
24
- connection ||= ActiveRecord::Base.connection
25
-
26
- if (Delayed::Job != Delayed::Backend::ActiveRecord::Job || connection != Delayed::Job.connection)
27
- connection.after_transaction_commit do
28
- Delayed::Job.enqueue(Delayed::PerformableMethod.new(self, method.to_sym, args,
29
- on_failure, on_permanent_failure), enqueue_args)
35
+ if @synchronous
36
+ if @sender.nil? || sender_is_object || sender_is_class && @object.protected_methods.include?(method)
37
+ if kwargs.empty?
38
+ return @object.send(method, *args)
39
+ else
40
+ return @object.send(method, *args, **kwargs)
41
+ end
42
+ end
43
+
44
+ if kwargs.empty?
45
+ return @object.public_send(method, *args)
46
+ else
47
+ return @object.public_send(method, *args, **kwargs)
30
48
  end
31
- return nil
32
49
  end
33
- end
34
-
35
- result = Delayed::Job.enqueue(Delayed::PerformableMethod.new(self, method.to_sym, args,
36
- on_failure, on_permanent_failure), enqueue_args)
37
- result = nil unless no_delay
38
- result
39
- end
40
-
41
- def send_later_with_queue(method, queue, *args)
42
- send_later_enqueue_args(method, { :queue => queue }, *args)
43
- end
44
50
 
45
- def send_at(time, method, *args)
46
- send_later_enqueue_args(method,
47
- { :run_at => time }, *args)
48
- end
49
-
50
- def send_at_with_queue(time, method, queue, *args)
51
- send_later_enqueue_args(method,
52
- { :run_at => time, :queue => queue },
53
- *args)
54
- end
51
+ ignore_transaction = @enqueue_args.delete(:ignore_transaction)
52
+ on_failure = @enqueue_args.delete(:on_failure)
53
+ on_permanent_failure = @enqueue_args.delete(:on_permanent_failure)
54
+ if !ignore_transaction
55
+ # delay queuing up the job in another database until the results of the current
56
+ # transaction are visible
57
+ connection = @object.class.connection if @object.class.respond_to?(:connection)
58
+ connection ||= @object.connection if @object.respond_to?(:connection)
59
+ connection ||= ::ActiveRecord::Base.connection
60
+
61
+ if (::Delayed::Job != ::Delayed::Backend::ActiveRecord::Job || connection != ::Delayed::Job.connection)
62
+ connection.after_transaction_commit do
63
+ ::Delayed::Job.enqueue(::Delayed::PerformableMethod.new(@object, method,
64
+ args: args, kwargs: kwargs,
65
+ on_failure: on_failure,
66
+ on_permanent_failure: on_permanent_failure,
67
+ sender: @sender),
68
+ **@enqueue_args)
69
+ end
70
+ return nil
71
+ end
72
+ end
55
73
 
56
- def send_later_unless_in_job(method, *args)
57
- if Delayed::Job.in_delayed_job?
58
- send(method, *args)
59
- else
60
- send_later(method, *args)
74
+ result = ::Delayed::Job.enqueue(::Delayed::PerformableMethod.new(@object, method,
75
+ args: args,
76
+ kwargs: kwargs,
77
+ on_failure: on_failure,
78
+ on_permanent_failure: on_permanent_failure,
79
+ sender: @sender),
80
+ **@enqueue_args)
81
+ result = nil unless ignore_transaction
82
+ result
61
83
  end
62
- nil # can't rely on the type of return value, so return nothing
63
84
  end
64
85
 
65
- def send_later_if_production(*args)
66
- if Rails.env.production?
67
- send_later(*args)
68
- else
69
- send(*args)
86
+ def delay(sender: nil, **enqueue_args)
87
+ # support procs/methods as enqueue arguments
88
+ enqueue_args.each do |k,v|
89
+ if v.respond_to?(:call)
90
+ enqueue_args[k] = v.call(self)
91
+ end
70
92
  end
71
- end
72
93
 
73
- def send_later_if_production_enqueue_args(method, enqueue_args, *args)
74
- if Rails.env.production?
75
- send_later_enqueue_args(method, enqueue_args, *args)
76
- else
77
- send(method, *args)
78
- end
79
- end
94
+ sender ||= __calculate_sender_for_delay
80
95
 
81
- def send_now_or_later(_when, *args)
82
- if _when == :now
83
- send(*args)
84
- else
85
- send_later(*args)
86
- end
96
+ DelayProxy.new(self, sender: sender, **enqueue_args)
87
97
  end
88
98
 
89
- def send_now_or_later_if_production(_when, *args)
90
- if _when == :now
91
- send(*args)
92
- else
93
- send_later_if_production(*args)
94
- end
99
+ def __calculate_sender_for_delay
100
+ # enforce public send in dev and test, but not prod (since it uses
101
+ # debug APIs, it's expensive)
102
+ return sender(1) if ::Rails.env.test? || ::Rails.env.development?
95
103
  end
96
104
 
97
105
  module ClassMethods
98
106
  KWARG_ARG_TYPES = %i{key keyreq keyrest}.freeze
99
107
  private_constant :KWARG_ARG_TYPES
100
108
 
101
- def add_send_later_methods(method_name, enqueue_args={}, default_async=false)
109
+ def handle_asynchronously(method_name, **enqueue_args)
102
110
  aliased_method, punctuation = method_name.to_s.sub(/([?!=])$/, ''), $1
103
111
 
104
- # we still need this for backwards compatibility
105
- without_method = "#{aliased_method}_without_send_later#{punctuation}"
106
-
107
112
  if public_method_defined?(method_name)
108
113
  visibility = :public
109
114
  elsif private_method_defined?(method_name)
@@ -114,49 +119,28 @@ module Delayed
114
119
 
115
120
  if has_kwargs? method_name
116
121
  generated_delayed_methods.class_eval do
117
- define_method without_method do |*args, **kwargs|
118
- send(method_name, *args, synchronous: true, **kwargs)
119
- end
120
-
121
- define_method(method_name, -> (*args, synchronous: !default_async, **kwargs) do
122
+ define_method(method_name, -> (*args, synchronous: false, **kwargs) do
122
123
  if synchronous
123
124
  super(*args, **kwargs)
124
125
  else
125
- send_later_enqueue_args(method_name, enqueue_args, *args, synchronous: true, **kwargs)
126
+ delay(sender: __calculate_sender_for_delay, **enqueue_args).method_missing(method_name, *args, synchronous: true, **kwargs)
126
127
  end
127
128
  end)
128
129
  end
129
130
  else
130
131
  generated_delayed_methods.class_eval do
131
- define_method without_method do |*args|
132
- send(method_name, *args, synchronous: true)
133
- end
134
-
135
- define_method(method_name, -> (*args, synchronous: !default_async) do
132
+ define_method(method_name, -> (*args, synchronous: false) do
136
133
  if synchronous
137
134
  super(*args)
138
135
  else
139
- send_later_enqueue_args(method_name, enqueue_args, *args, synchronous: true)
136
+ delay(**enqueue_args).method_missing(method_name, *args, synchronous: true)
140
137
  end
141
138
  end)
142
139
  end
143
140
  end
144
- generated_delayed_methods.send(visibility, without_method)
145
141
  generated_delayed_methods.send(visibility, method_name)
146
142
  end
147
143
 
148
- def handle_asynchronously(method, enqueue_args={})
149
- add_send_later_methods(method, enqueue_args, true)
150
- end
151
-
152
- def handle_asynchronously_with_queue(method, queue)
153
- add_send_later_methods(method, {:queue => queue}, true)
154
- end
155
-
156
- def handle_asynchronously_if_production(method, enqueue_args={})
157
- add_send_later_methods(method, enqueue_args, Rails.env.production?)
158
- end
159
-
160
144
  private
161
145
 
162
146
  def generated_delayed_methods
@@ -1,13 +1,23 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Delayed
2
- class PerformableMethod < Struct.new(:object, :method, :args, :fail_cb, :permanent_fail_cb)
3
- def initialize(object, method, args = [], fail_cb = nil, permanent_fail_cb = nil)
4
+ class PerformableMethod < Struct.new(:object, :method, :args, :kwargs, :fail_cb, :permanent_fail_cb, :sender)
5
+ def initialize(object, method, args: [], kwargs: {}, on_failure: nil, on_permanent_failure: nil, sender: nil)
4
6
  raise NoMethodError, "undefined method `#{method}' for #{object.inspect}" unless object.respond_to?(method, true)
5
7
 
6
8
  self.object = object
7
9
  self.args = args
10
+ self.kwargs = kwargs
8
11
  self.method = method.to_sym
9
- self.fail_cb = fail_cb
10
- self.permanent_fail_cb = permanent_fail_cb
12
+ self.fail_cb = on_failure
13
+ self.permanent_fail_cb = on_permanent_failure
14
+ self.sender = sender
15
+ begin
16
+ YAML.load(YAML.dump(sender))
17
+ rescue
18
+ # if for some reason you can't dump the sender, just drop it
19
+ self.sender = nil
20
+ end
11
21
  end
12
22
 
13
23
  def display_name
@@ -20,7 +30,24 @@ module Delayed
20
30
  alias_method :tag, :display_name
21
31
 
22
32
  def perform
23
- object.send(method, *args)
33
+ kwargs = self.kwargs || {}
34
+
35
+ sender_is_object = sender == object
36
+ sender_is_class = sender.is_a?(object.class)
37
+
38
+ if sender.nil? || sender_is_object || sender_is_class && object.protected_methods.include?(method)
39
+ if kwargs.empty?
40
+ object.send(method, *args)
41
+ else
42
+ object.send(method, *args, **kwargs)
43
+ end
44
+ else
45
+ if kwargs.empty?
46
+ object.public_send(method, *args)
47
+ else
48
+ object.public_send(method, *args, **kwargs)
49
+ end
50
+ end
24
51
  end
25
52
 
26
53
  def on_failure(error)
@@ -46,7 +73,8 @@ module Delayed
46
73
 
47
74
  def full_name
48
75
  obj_name = object.is_a?(ActiveRecord::Base) ? "#{object.class}.find(#{object.id}).#{method}" : display_name
49
- "#{obj_name}(#{args.map { |a| deep_de_ar_ize(a) }.join(', ')})"
76
+ kwargs_str = kwargs.map { |(k, v)| ", #{k}: #{deep_de_ar_ize(v)}"}.join("")
77
+ "#{obj_name}(#{args.map { |a| deep_de_ar_ize(a) }.join(', ')}#{kwargs_str})"
50
78
  end
51
79
  end
52
80
  end