sr-sidekiq 4.1.6

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.
Files changed (186) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/3.0-Upgrade.md +70 -0
  4. data/4.0-Upgrade.md +50 -0
  5. data/COMM-LICENSE (sidekiq) +95 -0
  6. data/Changes.md +1241 -0
  7. data/Ent-Changes.md +112 -0
  8. data/Gemfile +29 -0
  9. data/LICENSE (sidekiq) +9 -0
  10. data/LICENSE (sr-sidekiq) +5 -0
  11. data/Pro-2.0-Upgrade.md +138 -0
  12. data/Pro-3.0-Upgrade.md +44 -0
  13. data/Pro-Changes.md +539 -0
  14. data/README.md +8 -0
  15. data/Rakefile +9 -0
  16. data/bin/sidekiq +18 -0
  17. data/bin/sidekiqctl +99 -0
  18. data/bin/sidekiqload +167 -0
  19. data/code_of_conduct.md +50 -0
  20. data/lib/generators/sidekiq/templates/worker.rb.erb +9 -0
  21. data/lib/generators/sidekiq/templates/worker_spec.rb.erb +6 -0
  22. data/lib/generators/sidekiq/templates/worker_test.rb.erb +8 -0
  23. data/lib/generators/sidekiq/worker_generator.rb +49 -0
  24. data/lib/sidekiq.rb +237 -0
  25. data/lib/sidekiq/api.rb +844 -0
  26. data/lib/sidekiq/cli.rb +389 -0
  27. data/lib/sidekiq/client.rb +260 -0
  28. data/lib/sidekiq/core_ext.rb +106 -0
  29. data/lib/sidekiq/exception_handler.rb +31 -0
  30. data/lib/sidekiq/extensions/action_mailer.rb +57 -0
  31. data/lib/sidekiq/extensions/active_record.rb +40 -0
  32. data/lib/sidekiq/extensions/class_methods.rb +40 -0
  33. data/lib/sidekiq/extensions/generic_proxy.rb +25 -0
  34. data/lib/sidekiq/fetch.rb +81 -0
  35. data/lib/sidekiq/launcher.rb +160 -0
  36. data/lib/sidekiq/logging.rb +106 -0
  37. data/lib/sidekiq/manager.rb +137 -0
  38. data/lib/sidekiq/middleware/chain.rb +150 -0
  39. data/lib/sidekiq/middleware/i18n.rb +42 -0
  40. data/lib/sidekiq/middleware/server/active_record.rb +13 -0
  41. data/lib/sidekiq/middleware/server/logging.rb +40 -0
  42. data/lib/sidekiq/middleware/server/retry_jobs.rb +205 -0
  43. data/lib/sidekiq/paginator.rb +43 -0
  44. data/lib/sidekiq/processor.rb +186 -0
  45. data/lib/sidekiq/rails.rb +39 -0
  46. data/lib/sidekiq/redis_connection.rb +97 -0
  47. data/lib/sidekiq/scheduled.rb +146 -0
  48. data/lib/sidekiq/testing.rb +316 -0
  49. data/lib/sidekiq/testing/inline.rb +29 -0
  50. data/lib/sidekiq/util.rb +62 -0
  51. data/lib/sidekiq/version.rb +4 -0
  52. data/lib/sidekiq/web.rb +278 -0
  53. data/lib/sidekiq/web_helpers.rb +255 -0
  54. data/lib/sidekiq/worker.rb +121 -0
  55. data/sidekiq.gemspec +26 -0
  56. data/sr-sidekiq-4.1.3.gem +0 -0
  57. data/sr-sidekiq-4.1.4.gem +0 -0
  58. data/sr-sidekiq-4.1.5.gem +0 -0
  59. data/test/config.yml +9 -0
  60. data/test/env_based_config.yml +11 -0
  61. data/test/fake_env.rb +1 -0
  62. data/test/fixtures/en.yml +2 -0
  63. data/test/helper.rb +75 -0
  64. data/test/test_actors.rb +138 -0
  65. data/test/test_api.rb +528 -0
  66. data/test/test_cli.rb +406 -0
  67. data/test/test_client.rb +262 -0
  68. data/test/test_exception_handler.rb +56 -0
  69. data/test/test_extensions.rb +127 -0
  70. data/test/test_fetch.rb +50 -0
  71. data/test/test_launcher.rb +85 -0
  72. data/test/test_logging.rb +35 -0
  73. data/test/test_manager.rb +50 -0
  74. data/test/test_middleware.rb +158 -0
  75. data/test/test_processor.rb +201 -0
  76. data/test/test_rails.rb +22 -0
  77. data/test/test_redis_connection.rb +127 -0
  78. data/test/test_retry.rb +326 -0
  79. data/test/test_retry_exhausted.rb +149 -0
  80. data/test/test_scheduled.rb +115 -0
  81. data/test/test_scheduling.rb +50 -0
  82. data/test/test_sidekiq.rb +107 -0
  83. data/test/test_testing.rb +143 -0
  84. data/test/test_testing_fake.rb +357 -0
  85. data/test/test_testing_inline.rb +94 -0
  86. data/test/test_util.rb +13 -0
  87. data/test/test_web.rb +614 -0
  88. data/test/test_web_helpers.rb +54 -0
  89. data/web/assets/images/bootstrap/glyphicons-halflings-white.png +0 -0
  90. data/web/assets/images/bootstrap/glyphicons-halflings.png +0 -0
  91. data/web/assets/images/favicon.ico +0 -0
  92. data/web/assets/images/logo.png +0 -0
  93. data/web/assets/images/status-sd8051fd480.png +0 -0
  94. data/web/assets/images/status/active.png +0 -0
  95. data/web/assets/images/status/idle.png +0 -0
  96. data/web/assets/javascripts/application.js +88 -0
  97. data/web/assets/javascripts/dashboard.js +300 -0
  98. data/web/assets/javascripts/locales/README.md +27 -0
  99. data/web/assets/javascripts/locales/jquery.timeago.ar.js +96 -0
  100. data/web/assets/javascripts/locales/jquery.timeago.bg.js +18 -0
  101. data/web/assets/javascripts/locales/jquery.timeago.bs.js +49 -0
  102. data/web/assets/javascripts/locales/jquery.timeago.ca.js +18 -0
  103. data/web/assets/javascripts/locales/jquery.timeago.cs.js +18 -0
  104. data/web/assets/javascripts/locales/jquery.timeago.cy.js +20 -0
  105. data/web/assets/javascripts/locales/jquery.timeago.da.js +18 -0
  106. data/web/assets/javascripts/locales/jquery.timeago.de.js +18 -0
  107. data/web/assets/javascripts/locales/jquery.timeago.el.js +18 -0
  108. data/web/assets/javascripts/locales/jquery.timeago.en-short.js +20 -0
  109. data/web/assets/javascripts/locales/jquery.timeago.en.js +20 -0
  110. data/web/assets/javascripts/locales/jquery.timeago.es.js +18 -0
  111. data/web/assets/javascripts/locales/jquery.timeago.et.js +18 -0
  112. data/web/assets/javascripts/locales/jquery.timeago.fa.js +22 -0
  113. data/web/assets/javascripts/locales/jquery.timeago.fi.js +28 -0
  114. data/web/assets/javascripts/locales/jquery.timeago.fr-short.js +16 -0
  115. data/web/assets/javascripts/locales/jquery.timeago.fr.js +17 -0
  116. data/web/assets/javascripts/locales/jquery.timeago.he.js +18 -0
  117. data/web/assets/javascripts/locales/jquery.timeago.hr.js +49 -0
  118. data/web/assets/javascripts/locales/jquery.timeago.hu.js +18 -0
  119. data/web/assets/javascripts/locales/jquery.timeago.hy.js +18 -0
  120. data/web/assets/javascripts/locales/jquery.timeago.id.js +18 -0
  121. data/web/assets/javascripts/locales/jquery.timeago.it.js +16 -0
  122. data/web/assets/javascripts/locales/jquery.timeago.ja.js +19 -0
  123. data/web/assets/javascripts/locales/jquery.timeago.ko.js +17 -0
  124. data/web/assets/javascripts/locales/jquery.timeago.lt.js +20 -0
  125. data/web/assets/javascripts/locales/jquery.timeago.mk.js +20 -0
  126. data/web/assets/javascripts/locales/jquery.timeago.nl.js +20 -0
  127. data/web/assets/javascripts/locales/jquery.timeago.no.js +18 -0
  128. data/web/assets/javascripts/locales/jquery.timeago.pl.js +31 -0
  129. data/web/assets/javascripts/locales/jquery.timeago.pt-br.js +16 -0
  130. data/web/assets/javascripts/locales/jquery.timeago.pt.js +16 -0
  131. data/web/assets/javascripts/locales/jquery.timeago.ro.js +18 -0
  132. data/web/assets/javascripts/locales/jquery.timeago.rs.js +49 -0
  133. data/web/assets/javascripts/locales/jquery.timeago.ru.js +34 -0
  134. data/web/assets/javascripts/locales/jquery.timeago.sk.js +18 -0
  135. data/web/assets/javascripts/locales/jquery.timeago.sl.js +44 -0
  136. data/web/assets/javascripts/locales/jquery.timeago.sv.js +18 -0
  137. data/web/assets/javascripts/locales/jquery.timeago.th.js +20 -0
  138. data/web/assets/javascripts/locales/jquery.timeago.tr.js +16 -0
  139. data/web/assets/javascripts/locales/jquery.timeago.uk.js +34 -0
  140. data/web/assets/javascripts/locales/jquery.timeago.uz.js +19 -0
  141. data/web/assets/javascripts/locales/jquery.timeago.zh-cn.js +20 -0
  142. data/web/assets/javascripts/locales/jquery.timeago.zh-tw.js +20 -0
  143. data/web/assets/stylesheets/application.css +754 -0
  144. data/web/assets/stylesheets/bootstrap.css +9 -0
  145. data/web/locales/cs.yml +78 -0
  146. data/web/locales/da.yml +68 -0
  147. data/web/locales/de.yml +69 -0
  148. data/web/locales/el.yml +68 -0
  149. data/web/locales/en.yml +79 -0
  150. data/web/locales/es.yml +69 -0
  151. data/web/locales/fr.yml +78 -0
  152. data/web/locales/hi.yml +75 -0
  153. data/web/locales/it.yml +69 -0
  154. data/web/locales/ja.yml +78 -0
  155. data/web/locales/ko.yml +68 -0
  156. data/web/locales/nb.yml +77 -0
  157. data/web/locales/nl.yml +68 -0
  158. data/web/locales/pl.yml +59 -0
  159. data/web/locales/pt-br.yml +68 -0
  160. data/web/locales/pt.yml +67 -0
  161. data/web/locales/ru.yml +78 -0
  162. data/web/locales/sv.yml +68 -0
  163. data/web/locales/ta.yml +75 -0
  164. data/web/locales/uk.yml +76 -0
  165. data/web/locales/zh-cn.yml +68 -0
  166. data/web/locales/zh-tw.yml +68 -0
  167. data/web/views/_footer.erb +17 -0
  168. data/web/views/_job_info.erb +88 -0
  169. data/web/views/_nav.erb +66 -0
  170. data/web/views/_paging.erb +23 -0
  171. data/web/views/_poll_js.erb +5 -0
  172. data/web/views/_poll_link.erb +7 -0
  173. data/web/views/_status.erb +4 -0
  174. data/web/views/_summary.erb +40 -0
  175. data/web/views/busy.erb +94 -0
  176. data/web/views/dashboard.erb +75 -0
  177. data/web/views/dead.erb +34 -0
  178. data/web/views/layout.erb +32 -0
  179. data/web/views/morgue.erb +71 -0
  180. data/web/views/queue.erb +45 -0
  181. data/web/views/queues.erb +28 -0
  182. data/web/views/retries.erb +74 -0
  183. data/web/views/retry.erb +34 -0
  184. data/web/views/scheduled.erb +54 -0
  185. data/web/views/scheduled_job_info.erb +8 -0
  186. metadata +408 -0
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+ require 'sidekiq/testing'
3
+
4
+ ##
5
+ # The Sidekiq inline infrastructure overrides perform_async so that it
6
+ # actually calls perform instead. This allows workers to be run inline in a
7
+ # testing environment.
8
+ #
9
+ # This is similar to `Resque.inline = true` functionality.
10
+ #
11
+ # Example:
12
+ #
13
+ # require 'sidekiq/testing/inline'
14
+ #
15
+ # $external_variable = 0
16
+ #
17
+ # class ExternalWorker
18
+ # include Sidekiq::Worker
19
+ #
20
+ # def perform
21
+ # $external_variable = 1
22
+ # end
23
+ # end
24
+ #
25
+ # assert_equal 0, $external_variable
26
+ # ExternalWorker.perform_async
27
+ # assert_equal 1, $external_variable
28
+ #
29
+ Sidekiq::Testing.inline!
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+ require 'socket'
3
+ require 'securerandom'
4
+ require 'sidekiq/exception_handler'
5
+ require 'sidekiq/core_ext'
6
+
7
+ module Sidekiq
8
+ ##
9
+ # This module is part of Sidekiq core and not intended for extensions.
10
+ #
11
+ module Util
12
+ include ExceptionHandler
13
+
14
+ EXPIRY = 60 * 60 * 24
15
+
16
+ def watchdog(last_words)
17
+ yield
18
+ rescue Exception => ex
19
+ handle_exception(ex, { context: last_words })
20
+ raise ex
21
+ end
22
+
23
+ def safe_thread(name, &block)
24
+ Thread.new do
25
+ watchdog(name, &block)
26
+ end
27
+ end
28
+
29
+ def logger
30
+ Sidekiq.logger
31
+ end
32
+
33
+ def redis(&block)
34
+ Sidekiq.redis(&block)
35
+ end
36
+
37
+ def hostname
38
+ ENV['DYNO'] || Socket.gethostname
39
+ end
40
+
41
+ def process_nonce
42
+ @@process_nonce ||= SecureRandom.hex(6)
43
+ end
44
+
45
+ def identity
46
+ @@identity ||= "#{hostname}:#{$$}:#{process_nonce}"
47
+ end
48
+
49
+ def fire_event(event, reverse=false)
50
+ arr = Sidekiq.options[:lifecycle_events][event]
51
+ arr.reverse! if reverse
52
+ arr.each do |block|
53
+ begin
54
+ block.call
55
+ rescue => ex
56
+ handle_exception(ex, { event: event })
57
+ end
58
+ end
59
+ arr.clear
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ module Sidekiq
3
+ VERSION = "4.1.6"
4
+ end
@@ -0,0 +1,278 @@
1
+ # frozen_string_literal: true
2
+ require 'erb'
3
+ require 'yaml'
4
+ require 'sinatra/base'
5
+
6
+ require 'sidekiq'
7
+ require 'sidekiq/api'
8
+ require 'sidekiq/paginator'
9
+ require 'sidekiq/web_helpers'
10
+
11
+ module Sidekiq
12
+ class Web < Sinatra::Base
13
+ include Sidekiq::Paginator
14
+
15
+ enable :sessions
16
+ use ::Rack::Protection, :use => :authenticity_token unless ENV['RACK_ENV'] == 'test'
17
+
18
+ set :root, File.expand_path(File.dirname(__FILE__) + "/../../web")
19
+ set :public_folder, proc { "#{root}/assets" }
20
+ set :views, proc { "#{root}/views" }
21
+ set :locales, ["#{root}/locales"]
22
+
23
+ helpers WebHelpers
24
+
25
+ DEFAULT_TABS = {
26
+ "Dashboard" => '',
27
+ "Busy" => 'busy',
28
+ "Queues" => 'queues',
29
+ "Retries" => 'retries',
30
+ "Scheduled" => 'scheduled',
31
+ "Dead" => 'morgue',
32
+ }
33
+
34
+ class << self
35
+ def default_tabs
36
+ DEFAULT_TABS
37
+ end
38
+
39
+ def custom_tabs
40
+ @custom_tabs ||= {}
41
+ end
42
+ alias_method :tabs, :custom_tabs
43
+
44
+ attr_accessor :app_url
45
+ end
46
+
47
+ get "/busy" do
48
+ erb :busy
49
+ end
50
+
51
+ post "/busy" do
52
+ if params['identity']
53
+ p = Sidekiq::Process.new('identity' => params['identity'])
54
+ p.quiet! if params[:quiet]
55
+ p.stop! if params[:stop]
56
+ else
57
+ processes.each do |pro|
58
+ pro.quiet! if params[:quiet]
59
+ pro.stop! if params[:stop]
60
+ end
61
+ end
62
+ redirect "#{root_path}busy"
63
+ end
64
+
65
+ get "/queues" do
66
+ @queues = Sidekiq::Queue.all
67
+ erb :queues
68
+ end
69
+
70
+ get "/queues/:name" do
71
+ halt 404 unless params[:name]
72
+ @count = (params[:count] || 25).to_i
73
+ @name = params[:name]
74
+ @queue = Sidekiq::Queue.new(@name)
75
+ (@current_page, @total_size, @messages) = page("queue:#{@name}", params[:page], @count)
76
+ @messages = @messages.map { |msg| Sidekiq::Job.new(msg, @name) }
77
+ erb :queue
78
+ end
79
+
80
+ post "/queues/:name" do
81
+ Sidekiq::Queue.new(params[:name]).clear
82
+ redirect "#{root_path}queues"
83
+ end
84
+
85
+ post "/queues/:name/delete" do
86
+ Sidekiq::Job.new(params[:key_val], params[:name]).delete
87
+ redirect_with_query("#{root_path}queues/#{params[:name]}")
88
+ end
89
+
90
+ get '/morgue' do
91
+ @count = (params[:count] || 25).to_i
92
+ (@current_page, @total_size, @dead) = page("dead", params[:page], @count, reverse: true)
93
+ @dead = @dead.map { |msg, score| Sidekiq::SortedEntry.new(nil, score, msg) }
94
+ erb :morgue
95
+ end
96
+
97
+ get "/morgue/:key" do
98
+ halt 404 unless params['key']
99
+ @dead = Sidekiq::DeadSet.new.fetch(*parse_params(params['key'])).first
100
+ redirect "#{root_path}morgue" if @dead.nil?
101
+ erb :dead
102
+ end
103
+
104
+ post '/morgue' do
105
+ redirect request.path unless params['key']
106
+
107
+ params['key'].each do |key|
108
+ job = Sidekiq::DeadSet.new.fetch(*parse_params(key)).first
109
+ retry_or_delete_or_kill job, params if job
110
+ end
111
+ redirect_with_query("#{root_path}morgue")
112
+ end
113
+
114
+ post "/morgue/all/delete" do
115
+ Sidekiq::DeadSet.new.clear
116
+ redirect "#{root_path}morgue"
117
+ end
118
+
119
+ post "/morgue/all/retry" do
120
+ Sidekiq::DeadSet.new.retry_all
121
+ redirect "#{root_path}morgue"
122
+ end
123
+
124
+ post "/morgue/:key" do
125
+ halt 404 unless params['key']
126
+ job = Sidekiq::DeadSet.new.fetch(*parse_params(params['key'])).first
127
+ retry_or_delete_or_kill job, params if job
128
+ redirect_with_query("#{root_path}morgue")
129
+ end
130
+
131
+
132
+ get '/retries' do
133
+ @count = (params[:count] || 25).to_i
134
+ (@current_page, @total_size, @retries) = page("retry", params[:page], @count)
135
+ @retries = @retries.map { |msg, score| Sidekiq::SortedEntry.new(nil, score, msg) }
136
+ erb :retries
137
+ end
138
+
139
+ get "/retries/:key" do
140
+ @retry = Sidekiq::RetrySet.new.fetch(*parse_params(params['key'])).first
141
+ redirect "#{root_path}retries" if @retry.nil?
142
+ erb :retry
143
+ end
144
+
145
+ post '/retries' do
146
+ redirect request.path unless params['key']
147
+
148
+ params['key'].each do |key|
149
+ job = Sidekiq::RetrySet.new.fetch(*parse_params(key)).first
150
+ retry_or_delete_or_kill job, params if job
151
+ end
152
+ redirect_with_query("#{root_path}retries")
153
+ end
154
+
155
+ post "/retries/all/delete" do
156
+ Sidekiq::RetrySet.new.clear
157
+ redirect "#{root_path}retries"
158
+ end
159
+
160
+ post "/retries/all/retry" do
161
+ Sidekiq::RetrySet.new.retry_all
162
+ redirect "#{root_path}retries"
163
+ end
164
+
165
+ post "/retries/:key" do
166
+ job = Sidekiq::RetrySet.new.fetch(*parse_params(params['key'])).first
167
+ retry_or_delete_or_kill job, params if job
168
+ redirect_with_query("#{root_path}retries")
169
+ end
170
+
171
+ get '/scheduled' do
172
+ @count = (params[:count] || 25).to_i
173
+ (@current_page, @total_size, @scheduled) = page("schedule", params[:page], @count)
174
+ @scheduled = @scheduled.map { |msg, score| Sidekiq::SortedEntry.new(nil, score, msg) }
175
+ erb :scheduled
176
+ end
177
+
178
+ get "/scheduled/:key" do
179
+ @job = Sidekiq::ScheduledSet.new.fetch(*parse_params(params['key'])).first
180
+ redirect "#{root_path}scheduled" if @job.nil?
181
+ erb :scheduled_job_info
182
+ end
183
+
184
+ post '/scheduled' do
185
+ redirect request.path unless params['key']
186
+
187
+ params['key'].each do |key|
188
+ job = Sidekiq::ScheduledSet.new.fetch(*parse_params(key)).first
189
+ delete_or_add_queue job, params if job
190
+ end
191
+ redirect_with_query("#{root_path}scheduled")
192
+ end
193
+
194
+ post "/scheduled/:key" do
195
+ halt 404 unless params['key']
196
+ job = Sidekiq::ScheduledSet.new.fetch(*parse_params(params['key'])).first
197
+ delete_or_add_queue job, params if job
198
+ redirect_with_query("#{root_path}scheduled")
199
+ end
200
+
201
+ get '/' do
202
+ @redis_info = redis_info.select{ |k, v| REDIS_KEYS.include? k }
203
+ stats_history = Sidekiq::Stats::History.new((params[:days] || 30).to_i)
204
+ @processed_history = stats_history.processed
205
+ @failed_history = stats_history.failed
206
+ erb :dashboard
207
+ end
208
+
209
+ REDIS_KEYS = %w(redis_version uptime_in_days connected_clients used_memory_human used_memory_peak_human)
210
+
211
+ get '/dashboard/stats' do
212
+ redirect "#{root_path}stats"
213
+ end
214
+
215
+ get '/stats' do
216
+ sidekiq_stats = Sidekiq::Stats.new
217
+ redis_stats = redis_info.select { |k, v| REDIS_KEYS.include? k }
218
+
219
+ content_type :json
220
+ Sidekiq.dump_json(
221
+ sidekiq: {
222
+ processed: sidekiq_stats.processed,
223
+ failed: sidekiq_stats.failed,
224
+ busy: sidekiq_stats.workers_size,
225
+ processes: sidekiq_stats.processes_size,
226
+ enqueued: sidekiq_stats.enqueued,
227
+ scheduled: sidekiq_stats.scheduled_size,
228
+ retries: sidekiq_stats.retry_size,
229
+ dead: sidekiq_stats.dead_size,
230
+ default_latency: sidekiq_stats.default_queue_latency
231
+ },
232
+ redis: redis_stats
233
+ )
234
+ end
235
+
236
+ get '/stats/queues' do
237
+ queue_stats = Sidekiq::Stats::Queues.new
238
+
239
+ content_type :json
240
+ Sidekiq.dump_json(
241
+ queue_stats.lengths
242
+ )
243
+ end
244
+
245
+ private
246
+
247
+ def retry_or_delete_or_kill job, params
248
+ if params['retry']
249
+ job.retry
250
+ elsif params['delete']
251
+ job.delete
252
+ elsif params['kill']
253
+ job.kill
254
+ end
255
+ end
256
+
257
+ def delete_or_add_queue job, params
258
+ if params['delete']
259
+ job.delete
260
+ elsif params['add_to_queue']
261
+ job.add_to_queue
262
+ end
263
+ end
264
+ end
265
+ end
266
+
267
+ if defined?(::ActionDispatch::Request::Session) &&
268
+ !::ActionDispatch::Request::Session.respond_to?(:each)
269
+ # mperham/sidekiq#2460
270
+ # Rack apps can't reuse the Rails session store without
271
+ # this monkeypatch
272
+ class ActionDispatch::Request::Session
273
+ def each(&block)
274
+ hash = self.to_hash
275
+ hash.each(&block)
276
+ end
277
+ end
278
+ end
@@ -0,0 +1,255 @@
1
+ # frozen_string_literal: true
2
+ require 'uri'
3
+
4
+ module Sidekiq
5
+ # This is not a public API
6
+ module WebHelpers
7
+ def strings(lang)
8
+ @@strings ||= {}
9
+ @@strings[lang] ||= begin
10
+ # Allow sidekiq-web extensions to add locale paths
11
+ # so extensions can be localized
12
+ settings.locales.each_with_object({}) do |path, global|
13
+ find_locale_files(lang).each do |file|
14
+ strs = YAML.load(File.open(file))
15
+ global.deep_merge!(strs[lang])
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ def clear_caches
22
+ @@strings = nil
23
+ @@locale_files = nil
24
+ end
25
+
26
+ def locale_files
27
+ @@locale_files ||= settings.locales.flat_map do |path|
28
+ Dir["#{path}/*.yml"]
29
+ end
30
+ end
31
+
32
+ def find_locale_files(lang)
33
+ locale_files.select { |file| file =~ /\/#{lang}\.yml$/ }
34
+ end
35
+
36
+ # This is a hook for a Sidekiq Pro feature. Please don't touch.
37
+ def filtering(*)
38
+ end
39
+
40
+ # This view helper provide ability display you html code in
41
+ # to head of page. Example:
42
+ #
43
+ # <% add_to_head do %>
44
+ # <link rel="stylesheet" .../>
45
+ # <meta .../>
46
+ # <% end %>
47
+ #
48
+ def add_to_head(&block)
49
+ @head_html ||= []
50
+ @head_html << block if block_given?
51
+ end
52
+
53
+ def display_custom_head
54
+ return unless defined?(@head_html)
55
+ @head_html.map { |block| capture(&block) }.join
56
+ end
57
+
58
+ # Simple capture method for erb templates. The origin was
59
+ # capture method from sinatra-contrib library.
60
+ def capture(&block)
61
+ block.call
62
+ eval('', block.binding)
63
+ end
64
+
65
+ # Given a browser request Accept-Language header like
66
+ # "fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4,ru;q=0.2", this function
67
+ # will return "fr" since that's the first code with a matching
68
+ # locale in web/locales
69
+ def locale
70
+ @locale ||= begin
71
+ locale = 'en'.freeze
72
+ languages = request.env['HTTP_ACCEPT_LANGUAGE'.freeze] || 'en'.freeze
73
+ languages.downcase.split(','.freeze).each do |lang|
74
+ next if lang == '*'.freeze
75
+ lang = lang.split(';'.freeze)[0]
76
+ break locale = lang if find_locale_files(lang).any?
77
+ end
78
+ locale
79
+ end
80
+ end
81
+
82
+ def get_locale
83
+ strings(locale)
84
+ end
85
+
86
+ def t(msg, options={})
87
+ string = get_locale[msg] || msg
88
+ if options.empty?
89
+ string
90
+ else
91
+ string % options
92
+ end
93
+ end
94
+
95
+ def workers
96
+ @workers ||= Sidekiq::Workers.new
97
+ end
98
+
99
+ def processes
100
+ @processes ||= Sidekiq::ProcessSet.new
101
+ end
102
+
103
+ def stats
104
+ @stats ||= Sidekiq::Stats.new
105
+ end
106
+
107
+ def retries_with_score(score)
108
+ Sidekiq.redis do |conn|
109
+ conn.zrangebyscore('retry', score, score)
110
+ end.map { |msg| Sidekiq.load_json(msg) }
111
+ end
112
+
113
+ def location
114
+ Sidekiq.redis { |conn| conn.client.location }
115
+ end
116
+
117
+ def redis_connection
118
+ Sidekiq.redis { |conn| conn.client.id }
119
+ end
120
+
121
+ def namespace
122
+ @@ns ||= Sidekiq.redis { |conn| conn.respond_to?(:namespace) ? conn.namespace : nil }
123
+ end
124
+
125
+ def redis_info
126
+ Sidekiq.redis_info
127
+ end
128
+
129
+ def root_path
130
+ "#{env['SCRIPT_NAME']}/"
131
+ end
132
+
133
+ def current_path
134
+ @current_path ||= request.path_info.gsub(/^\//,'')
135
+ end
136
+
137
+ def current_status
138
+ workers.size == 0 ? 'idle' : 'active'
139
+ end
140
+
141
+ def relative_time(time)
142
+ %{<time datetime="#{time.getutc.iso8601}">#{time}</time>}
143
+ end
144
+
145
+ def job_params(job, score)
146
+ "#{score}-#{job['jid']}"
147
+ end
148
+
149
+ def parse_params(params)
150
+ score, jid = params.split("-")
151
+ [score.to_f, jid]
152
+ end
153
+
154
+ SAFE_QPARAMS = %w(page poll)
155
+
156
+ # Merge options with current params, filter safe params, and stringify to query string
157
+ def qparams(options)
158
+ options = options.stringify_keys
159
+ params.merge(options).map do |key, value|
160
+ SAFE_QPARAMS.include?(key) ? "#{key}=#{value}" : next
161
+ end.compact.join("&")
162
+ end
163
+
164
+ def truncate(text, truncate_after_chars = 2000)
165
+ truncate_after_chars && text.size > truncate_after_chars ? "#{text[0..truncate_after_chars]}..." : text
166
+ end
167
+
168
+ def display_args(args, truncate_after_chars = 2000)
169
+ args.map do |arg|
170
+ h(truncate(to_display(arg), truncate_after_chars))
171
+ end.join(", ")
172
+ end
173
+
174
+ def csrf_tag
175
+ "<input type='hidden' name='authenticity_token' value='#{session[:csrf]}'/>"
176
+ end
177
+
178
+ def to_display(arg)
179
+ begin
180
+ arg.inspect
181
+ rescue
182
+ begin
183
+ arg.to_s
184
+ rescue => ex
185
+ "Cannot display argument: [#{ex.class.name}] #{ex.message}"
186
+ end
187
+ end
188
+ end
189
+
190
+ RETRY_JOB_KEYS = Set.new(%w(
191
+ queue class args retry_count retried_at failed_at
192
+ jid error_message error_class backtrace
193
+ error_backtrace enqueued_at retry wrapped
194
+ created_at
195
+ ))
196
+
197
+ def retry_extra_items(retry_job)
198
+ @retry_extra_items ||= {}.tap do |extra|
199
+ retry_job.item.each do |key, value|
200
+ extra[key] = value unless RETRY_JOB_KEYS.include?(key)
201
+ end
202
+ end
203
+ end
204
+
205
+ def number_with_delimiter(number)
206
+ begin
207
+ Float(number)
208
+ rescue ArgumentError, TypeError
209
+ return number
210
+ end
211
+
212
+ options = {delimiter: ',', separator: '.'}
213
+ parts = number.to_s.to_str.split('.')
214
+ parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
215
+ parts.join(options[:separator])
216
+ end
217
+
218
+ def h(text)
219
+ ::Rack::Utils.escape_html(text)
220
+ rescue ArgumentError => e
221
+ raise unless e.message.eql?('invalid byte sequence in UTF-8')
222
+ text.encode!('UTF-16', 'UTF-8', invalid: :replace, replace: '').encode!('UTF-8', 'UTF-16')
223
+ retry
224
+ end
225
+
226
+ # Any paginated list that performs an action needs to redirect
227
+ # back to the proper page after performing that action.
228
+ def redirect_with_query(url)
229
+ r = request.referer
230
+ if r && r =~ /\?/
231
+ ref = URI(r)
232
+ redirect("#{url}?#{ref.query}")
233
+ else
234
+ redirect url
235
+ end
236
+ end
237
+
238
+ def environment_title_prefix
239
+ environment = Sidekiq.options[:environment] || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
240
+
241
+ "[#{environment.upcase}] " unless environment == "production"
242
+ end
243
+
244
+ def product_version
245
+ "Sidekiq v#{Sidekiq::VERSION}"
246
+ end
247
+
248
+ def redis_connection_and_namespace
249
+ @redis_connection_and_namespace ||= begin
250
+ namespace_suffix = namespace == nil ? '' : "##{namespace}"
251
+ "#{redis_connection}#{namespace_suffix}"
252
+ end
253
+ end
254
+ end
255
+ end