sidekiq 5.0.0 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sidekiq might be problematic. Click here for more details.

Files changed (79) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +61 -0
  3. data/.github/issue_template.md +3 -1
  4. data/.gitignore +1 -1
  5. data/.standard.yml +20 -0
  6. data/6.0-Upgrade.md +70 -0
  7. data/COMM-LICENSE +12 -10
  8. data/Changes.md +169 -1
  9. data/Ent-2.0-Upgrade.md +37 -0
  10. data/Ent-Changes.md +76 -0
  11. data/Gemfile +16 -21
  12. data/Gemfile.lock +196 -0
  13. data/LICENSE +1 -1
  14. data/Pro-4.0-Upgrade.md +35 -0
  15. data/Pro-5.0-Upgrade.md +25 -0
  16. data/Pro-Changes.md +137 -1
  17. data/README.md +18 -30
  18. data/Rakefile +6 -8
  19. data/bin/sidekiqload +28 -24
  20. data/bin/sidekiqmon +9 -0
  21. data/lib/generators/sidekiq/templates/worker_spec.rb.erb +1 -1
  22. data/lib/generators/sidekiq/templates/worker_test.rb.erb +1 -1
  23. data/lib/generators/sidekiq/worker_generator.rb +12 -14
  24. data/lib/sidekiq.rb +69 -49
  25. data/lib/sidekiq/api.rb +216 -160
  26. data/lib/sidekiq/cli.rb +174 -207
  27. data/lib/sidekiq/client.rb +55 -51
  28. data/lib/sidekiq/delay.rb +24 -4
  29. data/lib/sidekiq/exception_handler.rb +12 -16
  30. data/lib/sidekiq/extensions/action_mailer.rb +10 -20
  31. data/lib/sidekiq/extensions/active_record.rb +9 -7
  32. data/lib/sidekiq/extensions/class_methods.rb +9 -7
  33. data/lib/sidekiq/extensions/generic_proxy.rb +4 -4
  34. data/lib/sidekiq/fetch.rb +5 -6
  35. data/lib/sidekiq/job_logger.rb +42 -14
  36. data/lib/sidekiq/job_retry.rb +71 -57
  37. data/lib/sidekiq/launcher.rb +74 -60
  38. data/lib/sidekiq/logger.rb +69 -0
  39. data/lib/sidekiq/manager.rb +12 -15
  40. data/lib/sidekiq/middleware/chain.rb +3 -2
  41. data/lib/sidekiq/middleware/i18n.rb +5 -7
  42. data/lib/sidekiq/monitor.rb +148 -0
  43. data/lib/sidekiq/paginator.rb +11 -12
  44. data/lib/sidekiq/processor.rb +126 -82
  45. data/lib/sidekiq/rails.rb +24 -32
  46. data/lib/sidekiq/redis_connection.rb +46 -14
  47. data/lib/sidekiq/scheduled.rb +50 -25
  48. data/lib/sidekiq/testing.rb +35 -27
  49. data/lib/sidekiq/testing/inline.rb +2 -1
  50. data/lib/sidekiq/util.rb +20 -14
  51. data/lib/sidekiq/version.rb +2 -1
  52. data/lib/sidekiq/web.rb +45 -53
  53. data/lib/sidekiq/web/action.rb +14 -10
  54. data/lib/sidekiq/web/application.rb +83 -58
  55. data/lib/sidekiq/web/helpers.rb +105 -67
  56. data/lib/sidekiq/web/router.rb +18 -15
  57. data/lib/sidekiq/worker.rb +144 -41
  58. data/sidekiq.gemspec +16 -27
  59. data/web/assets/javascripts/application.js +0 -0
  60. data/web/assets/javascripts/dashboard.js +21 -23
  61. data/web/assets/stylesheets/application.css +35 -2
  62. data/web/assets/stylesheets/bootstrap.css +2 -2
  63. data/web/locales/ar.yml +1 -0
  64. data/web/locales/en.yml +2 -0
  65. data/web/locales/es.yml +4 -3
  66. data/web/locales/ja.yml +7 -4
  67. data/web/views/_footer.erb +4 -1
  68. data/web/views/_nav.erb +3 -17
  69. data/web/views/busy.erb +5 -1
  70. data/web/views/layout.erb +1 -1
  71. data/web/views/queue.erb +1 -0
  72. data/web/views/queues.erb +2 -0
  73. data/web/views/retries.erb +4 -0
  74. metadata +25 -171
  75. data/.travis.yml +0 -18
  76. data/bin/sidekiqctl +0 -99
  77. data/lib/sidekiq/core_ext.rb +0 -119
  78. data/lib/sidekiq/logging.rb +0 -106
  79. data/lib/sidekiq/middleware/server/active_record.rb +0 -22
@@ -1,35 +1,41 @@
1
1
  # frozen_string_literal: true
2
- require 'uri'
3
- require 'set'
4
- require 'yaml'
5
- require 'cgi'
2
+
3
+ require "uri"
4
+ require "set"
5
+ require "yaml"
6
+ require "cgi"
6
7
 
7
8
  module Sidekiq
8
9
  # This is not a public API
9
10
  module WebHelpers
10
11
  def strings(lang)
11
- @@strings ||= {}
12
- @@strings[lang] ||= begin
12
+ @strings ||= {}
13
+ @strings[lang] ||= begin
13
14
  # Allow sidekiq-web extensions to add locale paths
14
15
  # so extensions can be localized
15
16
  settings.locales.each_with_object({}) do |path, global|
16
17
  find_locale_files(lang).each do |file|
17
18
  strs = YAML.load(File.open(file))
18
- global.deep_merge!(strs[lang])
19
+ global.merge!(strs[lang])
19
20
  end
20
21
  end
21
22
  end
22
23
  end
23
24
 
24
25
  def clear_caches
25
- @@strings = nil
26
- @@locale_files = nil
26
+ @strings = nil
27
+ @locale_files = nil
28
+ @available_locales = nil
27
29
  end
28
30
 
29
31
  def locale_files
30
- @@locale_files ||= settings.locales.flat_map do |path|
32
+ @locale_files ||= settings.locales.flat_map { |path|
31
33
  Dir["#{path}/*.yml"]
32
- end
34
+ }
35
+ end
36
+
37
+ def available_locales
38
+ @available_locales ||= locale_files.map { |path| File.basename(path, ".yml") }.uniq
33
39
  end
34
40
 
35
41
  def find_locale_files(lang)
@@ -58,7 +64,7 @@ module Sidekiq
58
64
  end
59
65
 
60
66
  def poll_path
61
- if current_path != '' && params['poll']
67
+ if current_path != "" && params["poll"]
62
68
  root_path + current_path
63
69
  else
64
70
  ""
@@ -66,41 +72,57 @@ module Sidekiq
66
72
  end
67
73
 
68
74
  def text_direction
69
- get_locale['TextDirection'] || 'ltr'
75
+ get_locale["TextDirection"] || "ltr"
70
76
  end
71
77
 
72
78
  def rtl?
73
- text_direction == 'rtl'
79
+ text_direction == "rtl"
80
+ end
81
+
82
+ # See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
83
+ def user_preferred_languages
84
+ languages = env["HTTP_ACCEPT_LANGUAGE"]
85
+ languages.to_s.downcase.gsub(/\s+/, "").split(",").map { |language|
86
+ locale, quality = language.split(";q=", 2)
87
+ locale = nil if locale == "*" # Ignore wildcards
88
+ quality = quality ? quality.to_f : 1.0
89
+ [locale, quality]
90
+ }.sort { |(_, left), (_, right)|
91
+ right <=> left
92
+ }.map(&:first).compact
74
93
  end
75
94
 
76
- # Given a browser request Accept-Language header like
77
- # "fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4,ru;q=0.2", this function
78
- # will return "fr" since that's the first code with a matching
79
- # locale in web/locales
95
+ # Given an Accept-Language header like "fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4,ru;q=0.2"
96
+ # this method will try to best match the available locales to the user's preferred languages.
97
+ #
98
+ # Inspiration taken from https://github.com/iain/http_accept_language/blob/master/lib/http_accept_language/parser.rb
80
99
  def locale
81
100
  @locale ||= begin
82
- locale = 'en'.freeze
83
- languages = env['HTTP_ACCEPT_LANGUAGE'.freeze] || 'en'.freeze
84
- languages.downcase.split(','.freeze).each do |lang|
85
- next if lang == '*'.freeze
86
- lang = lang.split(';'.freeze)[0]
87
- break locale = lang if find_locale_files(lang).any?
88
- end
89
- locale
101
+ matched_locale = user_preferred_languages.map { |preferred|
102
+ preferred_language = preferred.split("-", 2).first
103
+
104
+ lang_group = available_locales.select { |available|
105
+ preferred_language == available.split("-", 2).first
106
+ }
107
+
108
+ lang_group.find { |lang| lang == preferred } || lang_group.min_by(&:length)
109
+ }.compact.first
110
+
111
+ matched_locale || "en"
90
112
  end
91
113
  end
92
114
 
93
115
  # mperham/sidekiq#3243
94
116
  def unfiltered?
95
- yield unless env['PATH_INFO'].start_with?("/filter/")
117
+ yield unless env["PATH_INFO"].start_with?("/filter/")
96
118
  end
97
119
 
98
120
  def get_locale
99
121
  strings(locale)
100
122
  end
101
123
 
102
- def t(msg, options={})
103
- string = get_locale[msg] || msg
124
+ def t(msg, options = {})
125
+ string = get_locale[msg] || strings("en")[msg] || msg
104
126
  if options.empty?
105
127
  string
106
128
  else
@@ -121,17 +143,20 @@ module Sidekiq
121
143
  end
122
144
 
123
145
  def retries_with_score(score)
124
- Sidekiq.redis do |conn|
125
- conn.zrangebyscore('retry', score, score)
126
- end.map { |msg| Sidekiq.load_json(msg) }
146
+ Sidekiq.redis { |conn|
147
+ conn.zrangebyscore("retry", score, score)
148
+ }.map { |msg| Sidekiq.load_json(msg) }
127
149
  end
128
150
 
129
151
  def redis_connection
130
- Sidekiq.redis { |conn| conn.client.id }
152
+ Sidekiq.redis do |conn|
153
+ c = conn.connection
154
+ "redis://#{c[:location]}/#{c[:db]}"
155
+ end
131
156
  end
132
157
 
133
158
  def namespace
134
- @@ns ||= Sidekiq.redis { |conn| conn.respond_to?(:namespace) ? conn.namespace : nil }
159
+ @ns ||= Sidekiq.redis { |conn| conn.respond_to?(:namespace) ? conn.namespace : nil }
135
160
  end
136
161
 
137
162
  def redis_info
@@ -139,39 +164,43 @@ module Sidekiq
139
164
  end
140
165
 
141
166
  def root_path
142
- "#{env['SCRIPT_NAME']}/"
167
+ "#{env["SCRIPT_NAME"]}/"
143
168
  end
144
169
 
145
170
  def current_path
146
- @current_path ||= request.path_info.gsub(/^\//,'')
171
+ @current_path ||= request.path_info.gsub(/^\//, "")
147
172
  end
148
173
 
149
174
  def current_status
150
- workers.size == 0 ? 'idle' : 'active'
175
+ workers.size == 0 ? "idle" : "active"
151
176
  end
152
177
 
153
178
  def relative_time(time)
154
179
  stamp = time.getutc.iso8601
155
- %{<time class="ltr" dir="ltr" title="#{stamp}" datetime="#{stamp}">#{time}</time>}
180
+ %(<time class="ltr" dir="ltr" title="#{stamp}" datetime="#{stamp}">#{time}</time>)
156
181
  end
157
182
 
158
183
  def job_params(job, score)
159
- "#{score}-#{job['jid']}"
184
+ "#{score}-#{job["jid"]}"
160
185
  end
161
186
 
162
187
  def parse_params(params)
163
- score, jid = params.split("-")
188
+ score, jid = params.split("-", 2)
164
189
  [score.to_f, jid]
165
190
  end
166
191
 
167
- SAFE_QPARAMS = %w(page poll)
192
+ SAFE_QPARAMS = %w[page poll]
168
193
 
169
194
  # Merge options with current params, filter safe params, and stringify to query string
170
195
  def qparams(options)
171
- options = options.stringify_keys
172
- params.merge(options).map do |key, value|
196
+ # stringify
197
+ options.keys.each do |key|
198
+ options[key.to_s] = options.delete(key)
199
+ end
200
+
201
+ params.merge(options).map { |key, value|
173
202
  SAFE_QPARAMS.include?(key) ? "#{key}=#{CGI.escape(value.to_s)}" : next
174
- end.compact.join("&")
203
+ }.compact.join("&")
175
204
  end
176
205
 
177
206
  def truncate(text, truncate_after_chars = 2000)
@@ -179,9 +208,16 @@ module Sidekiq
179
208
  end
180
209
 
181
210
  def display_args(args, truncate_after_chars = 2000)
182
- args.map do |arg|
183
- h(truncate(to_display(arg), truncate_after_chars))
184
- end.join(", ")
211
+ return "Invalid job payload, args is nil" if args.nil?
212
+ return "Invalid job payload, args must be an Array, not #{args.class.name}" unless args.is_a?(Array)
213
+
214
+ begin
215
+ args.map { |arg|
216
+ h(truncate(to_display(arg), truncate_after_chars))
217
+ }.join(", ")
218
+ rescue
219
+ "Illegal job arguments: #{h args.inspect}"
220
+ end
185
221
  end
186
222
 
187
223
  def csrf_tag
@@ -189,23 +225,21 @@ module Sidekiq
189
225
  end
190
226
 
191
227
  def to_display(arg)
228
+ arg.inspect
229
+ rescue
192
230
  begin
193
- arg.inspect
194
- rescue
195
- begin
196
- arg.to_s
197
- rescue => ex
198
- "Cannot display argument: [#{ex.class.name}] #{ex.message}"
199
- end
231
+ arg.to_s
232
+ rescue => ex
233
+ "Cannot display argument: [#{ex.class.name}] #{ex.message}"
200
234
  end
201
235
  end
202
236
 
203
- RETRY_JOB_KEYS = Set.new(%w(
237
+ RETRY_JOB_KEYS = Set.new(%w[
204
238
  queue class args retry_count retried_at failed_at
205
239
  jid error_message error_class backtrace
206
240
  error_backtrace enqueued_at retry wrapped
207
241
  created_at
208
- ))
242
+ ])
209
243
 
210
244
  def retry_extra_items(retry_job)
211
245
  @retry_extra_items ||= {}.tap do |extra|
@@ -222,8 +256,8 @@ module Sidekiq
222
256
  return number
223
257
  end
224
258
 
225
- options = {delimiter: ',', separator: '.'}
226
- parts = number.to_s.to_str.split('.')
259
+ options = {delimiter: ",", separator: "."}
260
+ parts = number.to_s.to_str.split(".")
227
261
  parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
228
262
  parts.join(options[:separator])
229
263
  end
@@ -231,8 +265,8 @@ module Sidekiq
231
265
  def h(text)
232
266
  ::Rack::Utils.escape_html(text)
233
267
  rescue ArgumentError => e
234
- raise unless e.message.eql?('invalid byte sequence in UTF-8')
235
- text.encode!('UTF-16', 'UTF-8', invalid: :replace, replace: '').encode!('UTF-8', 'UTF-16')
268
+ raise unless e.message.eql?("invalid byte sequence in UTF-8")
269
+ text.encode!("UTF-16", "UTF-8", invalid: :replace, replace: "").encode!("UTF-8", "UTF-16")
236
270
  retry
237
271
  end
238
272
 
@@ -249,7 +283,7 @@ module Sidekiq
249
283
  end
250
284
 
251
285
  def environment_title_prefix
252
- environment = Sidekiq.options[:environment] || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
286
+ environment = Sidekiq.options[:environment] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
253
287
 
254
288
  "[#{environment.upcase}] " unless environment == "production"
255
289
  end
@@ -258,27 +292,31 @@ module Sidekiq
258
292
  "Sidekiq v#{Sidekiq::VERSION}"
259
293
  end
260
294
 
295
+ def server_utc_time
296
+ Time.now.utc.strftime("%H:%M:%S UTC")
297
+ end
298
+
261
299
  def redis_connection_and_namespace
262
300
  @redis_connection_and_namespace ||= begin
263
- namespace_suffix = namespace == nil ? '' : "##{namespace}"
301
+ namespace_suffix = namespace.nil? ? "" : "##{namespace}"
264
302
  "#{redis_connection}#{namespace_suffix}"
265
303
  end
266
304
  end
267
305
 
268
306
  def retry_or_delete_or_kill(job, params)
269
- if params['retry']
307
+ if params["retry"]
270
308
  job.retry
271
- elsif params['delete']
309
+ elsif params["delete"]
272
310
  job.delete
273
- elsif params['kill']
311
+ elsif params["kill"]
274
312
  job.kill
275
313
  end
276
314
  end
277
315
 
278
316
  def delete_or_add_queue(job, params)
279
- if params['delete']
317
+ if params["delete"]
280
318
  job.delete
281
- elsif params['add_to_queue']
319
+ elsif params["add_to_queue"]
282
320
  job.add_to_queue
283
321
  end
284
322
  end
@@ -1,18 +1,19 @@
1
1
  # frozen_string_literal: true
2
- require 'rack'
2
+
3
+ require "rack"
3
4
 
4
5
  module Sidekiq
5
6
  module WebRouter
6
- GET = 'GET'.freeze
7
- DELETE = 'DELETE'.freeze
8
- POST = 'POST'.freeze
9
- PUT = 'PUT'.freeze
10
- PATCH = 'PATCH'.freeze
11
- HEAD = 'HEAD'.freeze
7
+ GET = "GET"
8
+ DELETE = "DELETE"
9
+ POST = "POST"
10
+ PUT = "PUT"
11
+ PATCH = "PATCH"
12
+ HEAD = "HEAD"
12
13
 
13
- ROUTE_PARAMS = 'rack.route_params'.freeze
14
- REQUEST_METHOD = 'REQUEST_METHOD'.freeze
15
- PATH_INFO = 'PATH_INFO'.freeze
14
+ ROUTE_PARAMS = "rack.route_params"
15
+ REQUEST_METHOD = "REQUEST_METHOD"
16
+ PATH_INFO = "PATH_INFO"
16
17
 
17
18
  def get(path, &block)
18
19
  route(GET, path, &block)
@@ -35,7 +36,7 @@ module Sidekiq
35
36
  end
36
37
 
37
38
  def route(method, path, &block)
38
- @routes ||= { GET => [], POST => [], PUT => [], PATCH => [], DELETE => [], HEAD => [] }
39
+ @routes ||= {GET => [], POST => [], PUT => [], PATCH => [], DELETE => [], HEAD => []}
39
40
 
40
41
  @routes[method] << WebRoute.new(method, path, block)
41
42
  @routes[HEAD] << WebRoute.new(method, path, block) if method == GET
@@ -50,7 +51,8 @@ module Sidekiq
50
51
  path_info = "/" if path_info == ""
51
52
 
52
53
  @routes[request_method].each do |route|
53
- if params = route.match(request_method, path_info)
54
+ params = route.match(request_method, path_info)
55
+ if params
54
56
  env[ROUTE_PARAMS] = params
55
57
 
56
58
  return WebAction.new(env, route.block)
@@ -64,7 +66,7 @@ module Sidekiq
64
66
  class WebRoute
65
67
  attr_accessor :request_method, :pattern, :block, :name
66
68
 
67
- NAMED_SEGMENTS_PATTERN = /\/([^\/]*):([^\.:$\/]+)/.freeze
69
+ NAMED_SEGMENTS_PATTERN = /\/([^\/]*):([^\.:$\/]+)/
68
70
 
69
71
  def initialize(request_method, pattern, block)
70
72
  @request_method = request_method
@@ -77,7 +79,7 @@ module Sidekiq
77
79
  end
78
80
 
79
81
  def compile
80
- if pattern.match(NAMED_SEGMENTS_PATTERN)
82
+ if pattern.match?(NAMED_SEGMENTS_PATTERN)
81
83
  p = pattern.gsub(NAMED_SEGMENTS_PATTERN, '/\1(?<\2>[^$/]+)')
82
84
 
83
85
  Regexp.new("\\A#{p}\\Z")
@@ -91,7 +93,8 @@ module Sidekiq
91
93
  when String
92
94
  {} if path == matcher
93
95
  else
94
- if path_match = path.match(matcher)
96
+ path_match = path.match(matcher)
97
+ if path_match
95
98
  Hash[path_match.names.map(&:to_sym).zip(path_match.captures)]
96
99
  end
97
100
  end
@@ -1,21 +1,19 @@
1
1
  # frozen_string_literal: true
2
- require 'sidekiq/client'
3
- require 'sidekiq/core_ext'
4
-
5
- module Sidekiq
6
2
 
3
+ require "sidekiq/client"
7
4
 
5
+ module Sidekiq
8
6
  ##
9
7
  # Include this module in your worker class and you can easily create
10
8
  # asynchronous jobs:
11
9
  #
12
- # class HardWorker
13
- # include Sidekiq::Worker
10
+ # class HardWorker
11
+ # include Sidekiq::Worker
14
12
  #
15
- # def perform(*args)
16
- # # do some work
13
+ # def perform(*args)
14
+ # # do some work
15
+ # end
17
16
  # end
18
- # end
19
17
  #
20
18
  # Then in your Rails app, you can do this:
21
19
  #
@@ -23,15 +21,124 @@ module Sidekiq
23
21
  #
24
22
  # Note that perform_async is a class method, perform is an instance method.
25
23
  module Worker
24
+ ##
25
+ # The Options module is extracted so we can include it in ActiveJob::Base
26
+ # and allow native AJs to configure Sidekiq features/internals.
27
+ module Options
28
+ def self.included(base)
29
+ base.extend(ClassMethods)
30
+ base.sidekiq_class_attribute :sidekiq_options_hash
31
+ base.sidekiq_class_attribute :sidekiq_retry_in_block
32
+ base.sidekiq_class_attribute :sidekiq_retries_exhausted_block
33
+ end
34
+
35
+ module ClassMethods
36
+ ACCESSOR_MUTEX = Mutex.new
37
+
38
+ ##
39
+ # Allows customization for this type of Worker.
40
+ # Legal options:
41
+ #
42
+ # queue - name of queue to use for this job type, default *default*
43
+ # retry - enable retries for this Worker in case of error during execution,
44
+ # *true* to use the default or *Integer* count
45
+ # backtrace - whether to save any error backtrace in the retry payload to display in web UI,
46
+ # can be true, false or an integer number of lines to save, default *false*
47
+ #
48
+ # In practice, any option is allowed. This is the main mechanism to configure the
49
+ # options for a specific job.
50
+ def sidekiq_options(opts = {})
51
+ opts = Hash[opts.map { |k, v| [k.to_s, v] }] # stringify
52
+ self.sidekiq_options_hash = get_sidekiq_options.merge(Hash[opts.map { |k, v| [k.to_s, v] }])
53
+ end
54
+
55
+ def sidekiq_retry_in(&block)
56
+ self.sidekiq_retry_in_block = block
57
+ end
58
+
59
+ def sidekiq_retries_exhausted(&block)
60
+ self.sidekiq_retries_exhausted_block = block
61
+ end
62
+
63
+ def get_sidekiq_options # :nodoc:
64
+ self.sidekiq_options_hash ||= Sidekiq.default_worker_options
65
+ end
66
+
67
+ def sidekiq_class_attribute(*attrs)
68
+ instance_reader = true
69
+ instance_writer = true
70
+
71
+ attrs.each do |name|
72
+ synchronized_getter = "__synchronized_#{name}"
73
+
74
+ singleton_class.instance_eval do
75
+ undef_method(name) if method_defined?(name) || private_method_defined?(name)
76
+ end
77
+
78
+ define_singleton_method(synchronized_getter) { nil }
79
+ singleton_class.class_eval do
80
+ private(synchronized_getter)
81
+ end
82
+
83
+ define_singleton_method(name) { ACCESSOR_MUTEX.synchronize { send synchronized_getter } }
84
+
85
+ ivar = "@#{name}"
86
+
87
+ singleton_class.instance_eval do
88
+ m = "#{name}="
89
+ undef_method(m) if method_defined?(m) || private_method_defined?(m)
90
+ end
91
+ define_singleton_method("#{name}=") do |val|
92
+ singleton_class.class_eval do
93
+ ACCESSOR_MUTEX.synchronize do
94
+ undef_method(synchronized_getter) if method_defined?(synchronized_getter) || private_method_defined?(synchronized_getter)
95
+ define_method(synchronized_getter) { val }
96
+ end
97
+ end
98
+
99
+ if singleton_class?
100
+ class_eval do
101
+ undef_method(name) if method_defined?(name) || private_method_defined?(name)
102
+ define_method(name) do
103
+ if instance_variable_defined? ivar
104
+ instance_variable_get ivar
105
+ else
106
+ singleton_class.send name
107
+ end
108
+ end
109
+ end
110
+ end
111
+ val
112
+ end
113
+
114
+ if instance_reader
115
+ undef_method(name) if method_defined?(name) || private_method_defined?(name)
116
+ define_method(name) do
117
+ if instance_variable_defined?(ivar)
118
+ instance_variable_get ivar
119
+ else
120
+ self.class.public_send name
121
+ end
122
+ end
123
+ end
124
+
125
+ if instance_writer
126
+ m = "#{name}="
127
+ undef_method(m) if method_defined?(m) || private_method_defined?(m)
128
+ attr_writer name
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
134
+
26
135
  attr_accessor :jid
27
136
 
28
137
  def self.included(base)
29
- raise ArgumentError, "You cannot include Sidekiq::Worker in an ActiveJob: #{base.name}" if base.ancestors.any? {|c| c.name == 'ActiveJob::Base' }
138
+ raise ArgumentError, "Sidekiq::Worker cannot be included in an ActiveJob: #{base.name}" if base.ancestors.any? { |c| c.name == "ActiveJob::Base" }
30
139
 
140
+ base.include(Options)
31
141
  base.extend(ClassMethods)
32
- base.class_attribute :sidekiq_options_hash
33
- base.class_attribute :sidekiq_retry_in_block
34
- base.class_attribute :sidekiq_retries_exhausted_block
35
142
  end
36
143
 
37
144
  def logger
@@ -43,12 +150,18 @@ module Sidekiq
43
150
  # SomeWorker.set(queue: 'foo').perform_async(....)
44
151
  #
45
152
  class Setter
46
- def initialize(opts)
153
+ def initialize(klass, opts)
154
+ @klass = klass
47
155
  @opts = opts
48
156
  end
49
157
 
158
+ def set(options)
159
+ @opts.merge!(options)
160
+ self
161
+ end
162
+
50
163
  def perform_async(*args)
51
- @opts['class'.freeze].client_push(@opts.merge!('args'.freeze => args))
164
+ @klass.client_push(@opts.merge("args" => args, "class" => @klass))
52
165
  end
53
166
 
54
167
  # +interval+ must be a timestamp, numeric or something that acts
@@ -58,16 +171,15 @@ module Sidekiq
58
171
  now = Time.now.to_f
59
172
  ts = (int < 1_000_000_000 ? now + int : int)
60
173
 
61
- @opts.merge! 'args'.freeze => args, 'at'.freeze => ts
174
+ payload = @opts.merge("class" => @klass, "args" => args, "at" => ts)
62
175
  # Optimization to enqueue something now that is scheduled to go out now or in the past
63
- @opts.delete('at'.freeze) if ts <= now
64
- @opts['class'.freeze].client_push(@opts)
176
+ payload.delete("at") if ts <= now
177
+ @klass.client_push(payload)
65
178
  end
66
179
  alias_method :perform_at, :perform_in
67
180
  end
68
181
 
69
182
  module ClassMethods
70
-
71
183
  def delay(*args)
72
184
  raise ArgumentError, "Do not call .delay on a Sidekiq::Worker class, call .perform_async"
73
185
  end
@@ -81,11 +193,11 @@ module Sidekiq
81
193
  end
82
194
 
83
195
  def set(options)
84
- Setter.new(options.merge!('class'.freeze => self))
196
+ Setter.new(self, options)
85
197
  end
86
198
 
87
199
  def perform_async(*args)
88
- client_push('class'.freeze => self, 'args'.freeze => args)
200
+ client_push("class" => self, "args" => args)
89
201
  end
90
202
 
91
203
  # +interval+ must be a timestamp, numeric or something that acts
@@ -95,10 +207,10 @@ module Sidekiq
95
207
  now = Time.now.to_f
96
208
  ts = (int < 1_000_000_000 ? now + int : int)
97
209
 
98
- item = { 'class'.freeze => self, 'args'.freeze => args, 'at'.freeze => ts }
210
+ item = {"class" => self, "args" => args, "at" => ts}
99
211
 
100
212
  # Optimization to enqueue something now that is scheduled to go out now or in the past
101
- item.delete('at'.freeze) if ts <= now
213
+ item.delete("at") if ts <= now
102
214
 
103
215
  client_push(item)
104
216
  end
@@ -117,28 +229,19 @@ module Sidekiq
117
229
  #
118
230
  # In practice, any option is allowed. This is the main mechanism to configure the
119
231
  # options for a specific job.
120
- def sidekiq_options(opts={})
121
- self.sidekiq_options_hash = get_sidekiq_options.merge(opts.stringify_keys)
122
- end
123
-
124
- def sidekiq_retry_in(&block)
125
- self.sidekiq_retry_in_block = block
126
- end
127
-
128
- def sidekiq_retries_exhausted(&block)
129
- self.sidekiq_retries_exhausted_block = block
130
- end
131
-
132
- def get_sidekiq_options # :nodoc:
133
- self.sidekiq_options_hash ||= Sidekiq.default_worker_options
232
+ def sidekiq_options(opts = {})
233
+ super
134
234
  end
135
235
 
136
236
  def client_push(item) # :nodoc:
137
- pool = Thread.current[:sidekiq_via_pool] || get_sidekiq_options['pool'.freeze] || Sidekiq.redis_pool
138
- hash = item.stringify_keys
139
- Sidekiq::Client.new(pool).push(hash)
140
- end
237
+ pool = Thread.current[:sidekiq_via_pool] || get_sidekiq_options["pool"] || Sidekiq.redis_pool
238
+ # stringify
239
+ item.keys.each do |key|
240
+ item[key.to_s] = item.delete(key)
241
+ end
141
242
 
243
+ Sidekiq::Client.new(pool).push(item)
244
+ end
142
245
  end
143
246
  end
144
247
  end