sidekiq 5.2.8

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