sidekiq_cleaner 5.3.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) 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 +1536 -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 +55 -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/cleaner/assets/images/favicon.ico +0 -0
  25. data/cleaner/assets/images/logo.png +0 -0
  26. data/cleaner/assets/images/status.png +0 -0
  27. data/cleaner/assets/javascripts/application.js +172 -0
  28. data/cleaner/assets/javascripts/dashboard.js +315 -0
  29. data/cleaner/assets/stylesheets/application-rtl.css +246 -0
  30. data/cleaner/assets/stylesheets/application.css +1144 -0
  31. data/cleaner/assets/stylesheets/bootstrap-rtl.min.css +9 -0
  32. data/cleaner/assets/stylesheets/bootstrap.css +5 -0
  33. data/cleaner/locales/ar.yml +81 -0
  34. data/cleaner/locales/cs.yml +78 -0
  35. data/cleaner/locales/da.yml +68 -0
  36. data/cleaner/locales/de.yml +69 -0
  37. data/cleaner/locales/el.yml +68 -0
  38. data/cleaner/locales/en.yml +81 -0
  39. data/cleaner/locales/es.yml +70 -0
  40. data/cleaner/locales/fa.yml +80 -0
  41. data/cleaner/locales/fr.yml +78 -0
  42. data/cleaner/locales/he.yml +79 -0
  43. data/cleaner/locales/hi.yml +75 -0
  44. data/cleaner/locales/it.yml +69 -0
  45. data/cleaner/locales/ja.yml +80 -0
  46. data/cleaner/locales/ko.yml +68 -0
  47. data/cleaner/locales/nb.yml +77 -0
  48. data/cleaner/locales/nl.yml +68 -0
  49. data/cleaner/locales/pl.yml +59 -0
  50. data/cleaner/locales/pt-br.yml +68 -0
  51. data/cleaner/locales/pt.yml +67 -0
  52. data/cleaner/locales/ru.yml +78 -0
  53. data/cleaner/locales/sv.yml +68 -0
  54. data/cleaner/locales/ta.yml +75 -0
  55. data/cleaner/locales/uk.yml +76 -0
  56. data/cleaner/locales/ur.yml +80 -0
  57. data/cleaner/locales/zh-cn.yml +68 -0
  58. data/cleaner/locales/zh-tw.yml +68 -0
  59. data/cleaner/views/_footer.erb +20 -0
  60. data/cleaner/views/_job_info.erb +88 -0
  61. data/cleaner/views/_nav.erb +52 -0
  62. data/cleaner/views/_paging.erb +23 -0
  63. data/cleaner/views/_poll_link.erb +7 -0
  64. data/cleaner/views/_status.erb +4 -0
  65. data/cleaner/views/_summary.erb +40 -0
  66. data/cleaner/views/busy.erb +98 -0
  67. data/cleaner/views/dashboard.erb +75 -0
  68. data/cleaner/views/dead.erb +34 -0
  69. data/cleaner/views/errors.erb +84 -0
  70. data/cleaner/views/layout.erb +40 -0
  71. data/cleaner/views/morgue.erb +75 -0
  72. data/cleaner/views/queue.erb +46 -0
  73. data/cleaner/views/queues.erb +30 -0
  74. data/cleaner/views/retries.erb +80 -0
  75. data/cleaner/views/retry.erb +34 -0
  76. data/cleaner/views/scheduled.erb +54 -0
  77. data/cleaner/views/scheduled_job_info.erb +8 -0
  78. data/cleaner-stats.png +0 -0
  79. data/cleaner.png +0 -0
  80. data/code_of_conduct.md +50 -0
  81. data/lib/generators/sidekiq/templates/worker.rb.erb +9 -0
  82. data/lib/generators/sidekiq/templates/worker_spec.rb.erb +6 -0
  83. data/lib/generators/sidekiq/templates/worker_test.rb.erb +8 -0
  84. data/lib/generators/sidekiq/worker_generator.rb +49 -0
  85. data/lib/sidekiq/api.rb +940 -0
  86. data/lib/sidekiq/cleaner/action.rb +89 -0
  87. data/lib/sidekiq/cleaner/application.rb +385 -0
  88. data/lib/sidekiq/cleaner/helpers.rb +325 -0
  89. data/lib/sidekiq/cleaner/router.rb +100 -0
  90. data/lib/sidekiq/cleaner.rb +214 -0
  91. data/lib/sidekiq/cli.rb +445 -0
  92. data/lib/sidekiq/client.rb +243 -0
  93. data/lib/sidekiq/core_ext.rb +1 -0
  94. data/lib/sidekiq/ctl.rb +221 -0
  95. data/lib/sidekiq/delay.rb +42 -0
  96. data/lib/sidekiq/exception_handler.rb +29 -0
  97. data/lib/sidekiq/extensions/action_mailer.rb +57 -0
  98. data/lib/sidekiq/extensions/active_record.rb +40 -0
  99. data/lib/sidekiq/extensions/class_methods.rb +40 -0
  100. data/lib/sidekiq/extensions/generic_proxy.rb +31 -0
  101. data/lib/sidekiq/fetch.rb +81 -0
  102. data/lib/sidekiq/job_logger.rb +25 -0
  103. data/lib/sidekiq/job_retry.rb +262 -0
  104. data/lib/sidekiq/launcher.rb +173 -0
  105. data/lib/sidekiq/logging.rb +122 -0
  106. data/lib/sidekiq/manager.rb +137 -0
  107. data/lib/sidekiq/middleware/chain.rb +150 -0
  108. data/lib/sidekiq/middleware/i18n.rb +42 -0
  109. data/lib/sidekiq/middleware/server/active_record.rb +23 -0
  110. data/lib/sidekiq/paginator.rb +43 -0
  111. data/lib/sidekiq/processor.rb +279 -0
  112. data/lib/sidekiq/rails.rb +58 -0
  113. data/lib/sidekiq/redis_connection.rb +144 -0
  114. data/lib/sidekiq/scheduled.rb +174 -0
  115. data/lib/sidekiq/testing/inline.rb +29 -0
  116. data/lib/sidekiq/testing.rb +333 -0
  117. data/lib/sidekiq/util.rb +66 -0
  118. data/lib/sidekiq/version.rb +4 -0
  119. data/lib/sidekiq/worker.rb +220 -0
  120. data/lib/sidekiq.rb +237 -0
  121. data/sidekiq_cleaner.gemspec +21 -0
  122. metadata +235 -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 CleanerHelpers
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 CleanerRouter
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] << CleanerRoute.new(method, path, block)
41
+ @routes[HEAD] << CleanerRoute.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 CleanerAction.new(env, route.block)
57
+ end
58
+ end
59
+
60
+ nil
61
+ end
62
+ end
63
+
64
+ class CleanerRoute
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,214 @@
1
+ # frozen_string_literal: true
2
+ require 'erb'
3
+
4
+ require 'sidekiq'
5
+ require 'sidekiq/api'
6
+ require 'sidekiq/paginator'
7
+ require 'sidekiq/cleaner/helpers'
8
+
9
+ require 'sidekiq/cleaner/router'
10
+ require 'sidekiq/cleaner/action'
11
+ require 'sidekiq/cleaner/application'
12
+
13
+ require 'rack/protection'
14
+
15
+ require 'rack/builder'
16
+ require 'rack/file'
17
+ require 'rack/session/cookie'
18
+
19
+ module Sidekiq
20
+ class Cleaner
21
+ ROOT = File.expand_path("#{File.dirname(__FILE__)}/../../cleaner")
22
+ VIEWS = "#{ROOT}/views"
23
+ LOCALES = ["#{ROOT}/locales"]
24
+ LAYOUT = "#{VIEWS}/layout.erb"
25
+ ASSETS = "#{ROOT}/assets"
26
+
27
+ DEFAULT_TABS = {
28
+ "Dashboard" => '',
29
+ "Busy" => 'busy',
30
+ "Queues" => 'queues',
31
+ "Retries" => 'retries',
32
+ "Scheduled" => 'scheduled',
33
+ "Dead" => 'morgue',
34
+ "Cleaner" => 'errors'
35
+ }
36
+
37
+ class << self
38
+ def settings
39
+ self
40
+ end
41
+
42
+ def middlewares
43
+ @middlewares ||= []
44
+ end
45
+
46
+ def use(*middleware_args, &block)
47
+ middlewares << [middleware_args, block]
48
+ end
49
+
50
+ def default_tabs
51
+ DEFAULT_TABS
52
+ end
53
+
54
+ def custom_tabs
55
+ @custom_tabs ||= {}
56
+ end
57
+ alias_method :tabs, :custom_tabs
58
+
59
+ def locales
60
+ @locales ||= LOCALES
61
+ end
62
+
63
+ def views
64
+ @views ||= VIEWS
65
+ end
66
+
67
+ def enable(*opts)
68
+ opts.each {|key| set(key, true) }
69
+ end
70
+
71
+ def disable(*opts)
72
+ opts.each {|key| set(key, false) }
73
+ end
74
+
75
+ # Helper for the Sinatra syntax: Sidekiq::Cleaner.set(:session_secret, Rails.application.secrets...)
76
+ def set(attribute, value)
77
+ send(:"#{attribute}=", value)
78
+ end
79
+
80
+ attr_accessor :app_url, :session_secret, :redis_pool, :sessions
81
+ attr_writer :locales, :views
82
+ end
83
+
84
+ def self.inherited(child)
85
+ child.app_url = self.app_url
86
+ child.session_secret = self.session_secret
87
+ child.redis_pool = self.redis_pool
88
+ child.sessions = self.sessions
89
+ end
90
+
91
+ def settings
92
+ self.class.settings
93
+ end
94
+
95
+ def use(*middleware_args, &block)
96
+ middlewares << [middleware_args, block]
97
+ end
98
+
99
+ def middlewares
100
+ @middlewares ||= Cleaner.middlewares.dup
101
+ end
102
+
103
+ def call(env)
104
+ app.call(env)
105
+ end
106
+
107
+ def self.call(env)
108
+ @app ||= new
109
+ @app.call(env)
110
+ end
111
+
112
+ def app
113
+ @app ||= build
114
+ end
115
+
116
+ def enable(*opts)
117
+ opts.each {|key| set(key, true) }
118
+ end
119
+
120
+ def disable(*opts)
121
+ opts.each {|key| set(key, false) }
122
+ end
123
+
124
+ def set(attribute, value)
125
+ send(:"#{attribute}=", value)
126
+ end
127
+
128
+ # Default values
129
+ set :sessions, true
130
+
131
+ attr_writer :sessions
132
+
133
+ def sessions
134
+ unless instance_variable_defined?("@sessions")
135
+ @sessions = self.class.sessions
136
+ @sessions = @sessions.to_hash.dup if @sessions.respond_to?(:to_hash)
137
+ end
138
+
139
+ @sessions
140
+ end
141
+
142
+ def self.register(extension)
143
+ extension.registered(CleanerApplication)
144
+ end
145
+
146
+ private
147
+
148
+ def using?(middleware)
149
+ middlewares.any? do |(m,_)|
150
+ m.kind_of?(Array) && (m[0] == middleware || m[0].kind_of?(middleware))
151
+ end
152
+ end
153
+
154
+ def build_sessions
155
+ middlewares = self.middlewares
156
+
157
+ unless using?(::Rack::Protection) || ENV['RACK_ENV'] == 'test'
158
+ middlewares.unshift [[::Rack::Protection, { use: :authenticity_token }], nil]
159
+ end
160
+
161
+ s = sessions
162
+ return unless s
163
+
164
+ unless using? ::Rack::Session::Cookie
165
+ unless secret = Cleaner.session_secret
166
+ require 'securerandom'
167
+ secret = SecureRandom.hex(64)
168
+ end
169
+
170
+ options = { secret: secret }
171
+ options = options.merge(s.to_hash) if s.respond_to? :to_hash
172
+
173
+ middlewares.unshift [[::Rack::Session::Cookie, options], nil]
174
+ end
175
+ end
176
+
177
+ def build
178
+ build_sessions
179
+
180
+ middlewares = self.middlewares
181
+ klass = self.class
182
+
183
+ ::Rack::Builder.new do
184
+ %w(stylesheets javascripts images).each do |asset_dir|
185
+ map "/#{asset_dir}" do
186
+ run ::Rack::File.new("#{ASSETS}/#{asset_dir}", { 'Cache-Control' => 'public, max-age=86400' })
187
+ end
188
+ end
189
+
190
+ middlewares.each {|middleware, block| use(*middleware, &block) }
191
+
192
+ run CleanerApplication.new(klass)
193
+ end
194
+ end
195
+ end
196
+
197
+ Sidekiq::CleanerApplication.helpers CleanerHelpers
198
+ Sidekiq::CleanerApplication.helpers Sidekiq::Paginator
199
+
200
+ Sidekiq::CleanerAction.class_eval "def _render\n#{ERB.new(File.read(Cleaner::LAYOUT)).src}\nend"
201
+ end
202
+
203
+ if defined?(::ActionDispatch::Request::Session) &&
204
+ !::ActionDispatch::Request::Session.method_defined?(:each)
205
+ # mperham/sidekiq#2460
206
+ # Rack apps can't reuse the Rails session store without
207
+ # this monkeypatch, fixed in Rails 5.
208
+ class ActionDispatch::Request::Session
209
+ def each(&block)
210
+ hash = self.to_hash
211
+ hash.each(&block)
212
+ end
213
+ end
214
+ end