sidekiq_cleaner 5.3.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 (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