resque_manager 3.3.0

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 (120) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README.markdown +417 -0
  4. data/Rakefile +41 -0
  5. data/app/assets/images/resque_manager/idle.png +0 -0
  6. data/app/assets/images/resque_manager/poll.png +0 -0
  7. data/app/assets/images/resque_manager/working.png +0 -0
  8. data/app/assets/javascripts/resque_manager/application.js +15 -0
  9. data/app/assets/javascripts/resque_manager/jquery-1.3.2.min.js +19 -0
  10. data/app/assets/javascripts/resque_manager/jquery.relatize_date.js +95 -0
  11. data/app/assets/javascripts/resque_manager/ranger.js +24 -0
  12. data/app/assets/stylesheets/resque_manager/application.css +14 -0
  13. data/app/assets/stylesheets/resque_manager/resque/resque.css +93 -0
  14. data/app/assets/stylesheets/resque_manager/resque/resque_reset.css +48 -0
  15. data/app/assets/stylesheets/resque_manager/resque_cleaner/cleaner.css +62 -0
  16. data/app/controllers/resque_manager/resque_controller.rb +313 -0
  17. data/app/helpers/resque_manager/application_helper.rb +4 -0
  18. data/app/helpers/resque_manager/resque_helper.rb +142 -0
  19. data/app/models/resque_manager/paginate.rb +54 -0
  20. data/app/views/layouts/resque_manager/application.html.erb +37 -0
  21. data/app/views/resque_manager/resque/_key.html.erb +17 -0
  22. data/app/views/resque_manager/resque/_limiter.html.erb +12 -0
  23. data/app/views/resque_manager/resque/_next_more.html.erb +10 -0
  24. data/app/views/resque_manager/resque/_paginate.html.erb +53 -0
  25. data/app/views/resque_manager/resque/_queues.html.erb +59 -0
  26. data/app/views/resque_manager/resque/_status_styles.erb +98 -0
  27. data/app/views/resque_manager/resque/_workers.html.erb +138 -0
  28. data/app/views/resque_manager/resque/_working.html.erb +69 -0
  29. data/app/views/resque_manager/resque/cleaner.html.erb +41 -0
  30. data/app/views/resque_manager/resque/cleaner_exec.html.erb +6 -0
  31. data/app/views/resque_manager/resque/cleaner_list.html.erb +172 -0
  32. data/app/views/resque_manager/resque/delayed.html.erb +35 -0
  33. data/app/views/resque_manager/resque/delayed_timestamp.html.erb +26 -0
  34. data/app/views/resque_manager/resque/error.erb +1 -0
  35. data/app/views/resque_manager/resque/overview.html.erb +4 -0
  36. data/app/views/resque_manager/resque/schedule.html.erb +96 -0
  37. data/app/views/resque_manager/resque/stats.html.erb +62 -0
  38. data/app/views/resque_manager/resque/status.html.erb +57 -0
  39. data/app/views/resque_manager/resque/statuses.html.erb +72 -0
  40. data/app/views/resque_manager/resque/workers.html.erb +1 -0
  41. data/config/routes.rb +38 -0
  42. data/config/sample_redis.yml +43 -0
  43. data/config/sample_resque_manager.yml +23 -0
  44. data/lib/resque_manager/engine.rb +9 -0
  45. data/lib/resque_manager/overrides/resque/failure/redis.rb +11 -0
  46. data/lib/resque_manager/overrides/resque/job.rb +69 -0
  47. data/lib/resque_manager/overrides/resque/resque.rb +8 -0
  48. data/lib/resque_manager/overrides/resque/worker.rb +291 -0
  49. data/lib/resque_manager/overrides/resque_scheduler/resque_scheduler.rb +58 -0
  50. data/lib/resque_manager/overrides/resque_status/chained_status.rb +46 -0
  51. data/lib/resque_manager/overrides/resque_status/hash.rb +12 -0
  52. data/lib/resque_manager/overrides/resque_status/status.rb +161 -0
  53. data/lib/resque_manager/recipes.rb +185 -0
  54. data/lib/resque_manager/version.rb +3 -0
  55. data/lib/resque_manager.rb +47 -0
  56. data/lib/tasks/failure.rake +8 -0
  57. data/lib/tasks/scheduler.rake +11 -0
  58. data/lib/tasks/worker.rake +129 -0
  59. data/test/dummy/README.rdoc +261 -0
  60. data/test/dummy/Rakefile +7 -0
  61. data/test/dummy/app/assets/javascripts/application.js +15 -0
  62. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  63. data/test/dummy/app/controllers/application_controller.rb +3 -0
  64. data/test/dummy/app/helpers/application_helper.rb +2 -0
  65. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  66. data/test/dummy/config/application.rb +65 -0
  67. data/test/dummy/config/boot.rb +10 -0
  68. data/test/dummy/config/environment.rb +5 -0
  69. data/test/dummy/config/environments/development.rb +37 -0
  70. data/test/dummy/config/environments/production.rb +67 -0
  71. data/test/dummy/config/environments/test.rb +37 -0
  72. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  73. data/test/dummy/config/initializers/inflections.rb +15 -0
  74. data/test/dummy/config/initializers/mime_types.rb +5 -0
  75. data/test/dummy/config/initializers/secret_token.rb +7 -0
  76. data/test/dummy/config/initializers/session_store.rb +8 -0
  77. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  78. data/test/dummy/config/locales/en.yml +5 -0
  79. data/test/dummy/config/routes.rb +4 -0
  80. data/test/dummy/config.ru +4 -0
  81. data/test/dummy/log/development.log +5045 -0
  82. data/test/dummy/public/404.html +26 -0
  83. data/test/dummy/public/422.html +26 -0
  84. data/test/dummy/public/500.html +25 -0
  85. data/test/dummy/public/favicon.ico +0 -0
  86. data/test/dummy/script/rails +6 -0
  87. data/test/dummy/tmp/cache/assets/C2A/A10/sprockets%2Fb2e622954654f415590723e9b882063e +0 -0
  88. data/test/dummy/tmp/cache/assets/C60/1D0/sprockets%2F8ed12e4193473760f95b973567a8c206 +0 -0
  89. data/test/dummy/tmp/cache/assets/CA1/970/sprockets%2Fc387148880e015d1eab0dc838b326022 +0 -0
  90. data/test/dummy/tmp/cache/assets/CAE/930/sprockets%2Fe227278d3c65d8aa1159da720263f771 +0 -0
  91. data/test/dummy/tmp/cache/assets/CD8/370/sprockets%2F357970feca3ac29060c1e3861e2c0953 +0 -0
  92. data/test/dummy/tmp/cache/assets/CDC/E30/sprockets%2Fe1207380d69eeee3284e02636c26f24a +0 -0
  93. data/test/dummy/tmp/cache/assets/CF1/720/sprockets%2Fd91a5918f5aa43a43c8135a67c78e989 +0 -0
  94. data/test/dummy/tmp/cache/assets/D0E/820/sprockets%2F00c6cc9dc46bf64347b3775d7d15541b +0 -0
  95. data/test/dummy/tmp/cache/assets/D16/180/sprockets%2F73d6fa09352cb76ac81e1683e832b93f +0 -0
  96. data/test/dummy/tmp/cache/assets/D27/170/sprockets%2Fec164819553e2e5b28f1efc9bd970978 +0 -0
  97. data/test/dummy/tmp/cache/assets/D2B/DA0/sprockets%2F989465d3ea8575dd0b54981a9e8add38 +0 -0
  98. data/test/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
  99. data/test/dummy/tmp/cache/assets/D37/1F0/sprockets%2F97119b908ebed2633edfd00ac90d9011 +0 -0
  100. data/test/dummy/tmp/cache/assets/D38/FB0/sprockets%2F74e5ba1cca7a1470d53c54fb60368b78 +0 -0
  101. data/test/dummy/tmp/cache/assets/D42/4E0/sprockets%2F0fa6e3c14356aa527d68a8d56fa37f28 +0 -0
  102. data/test/dummy/tmp/cache/assets/D43/C20/sprockets%2F1efd074fd1074b3dc88145b480ff961f +0 -0
  103. data/test/dummy/tmp/cache/assets/D46/CD0/sprockets%2F67f1ef70e7ede542318b8d55e25b16c3 +0 -0
  104. data/test/dummy/tmp/cache/assets/D4E/1B0/sprockets%2Ff7cbd26ba1d28d48de824f0e94586655 +0 -0
  105. data/test/dummy/tmp/cache/assets/D5A/EA0/sprockets%2Fd771ace226fc8215a3572e0aa35bb0d6 +0 -0
  106. data/test/dummy/tmp/cache/assets/D68/080/sprockets%2Fa26f2ae225aa4b87c462d540c7cf43f9 +0 -0
  107. data/test/dummy/tmp/cache/assets/D9A/B20/sprockets%2F0eddc19d46318e2e286cc171ae4cc73e +0 -0
  108. data/test/dummy/tmp/cache/assets/DA4/900/sprockets%2F515bf984438c6ec4b8a515fcc13baf8e +0 -0
  109. data/test/dummy/tmp/cache/assets/DBD/070/sprockets%2F60ffef45ddefd5c7746d17977fff0717 +0 -0
  110. data/test/dummy/tmp/cache/assets/DD7/AC0/sprockets%2Fc7c983c5c607dbfdb726eecc36146ca9 +0 -0
  111. data/test/dummy/tmp/cache/assets/DDC/400/sprockets%2Fcffd775d018f68ce5dba1ee0d951a994 +0 -0
  112. data/test/dummy/tmp/cache/assets/DF5/480/sprockets%2Fea4f3c726fc1046cad1ad243faf84e7d +0 -0
  113. data/test/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
  114. data/test/dummy/tmp/cache/assets/E2B/7A0/sprockets%2Fd44ef07be0aa6d5b5dea4d37d7f72b4f +0 -0
  115. data/test/functional/resque_manager/resque_controller_test.rb +9 -0
  116. data/test/integration/navigation_test.rb +10 -0
  117. data/test/resque_manager_test.rb +7 -0
  118. data/test/test_helper.rb +15 -0
  119. data/test/unit/helpers/resque_manager/resque_helper_test.rb +6 -0
  120. metadata +307 -0
@@ -0,0 +1,313 @@
1
+ require 'resque'
2
+ require 'resque/version'
3
+ require 'digest/sha1'
4
+
5
+
6
+ class ResqueManager::ResqueController < ApplicationController
7
+ unloadable(self) #needed to prevent errors with authenticated system in dev env.
8
+
9
+ layout 'resque_manager/application'
10
+
11
+ before_filter :check_connection
12
+
13
+ before_filter :get_cleaner, :only => [:cleaner, :cleaner_exec, :cleaner_list, :cleaner_stale, :cleaner_dump]
14
+
15
+ def working
16
+ render('_working')
17
+ end
18
+
19
+ def queues
20
+ render('_queues', :locals => {:partial => nil})
21
+ end
22
+
23
+ def poll
24
+ @polling = true
25
+ render(:text => (render_to_string(:action => "#{params[:page]}", :formats => [:html], :layout => false, :resque => Resque)).gsub(/\s{1,}/, ' '))
26
+ end
27
+
28
+ def status_poll
29
+ @polling = true
30
+
31
+ @start = params[:start].to_i
32
+ @end = @start + (params[:per_page] || 20)
33
+ @statuses = Resque::Plugins::Status::Hash.statuses(@start, @end)
34
+ @size = Resque::Plugins::Status::Hash.status_ids.size
35
+
36
+ render(:text => (render_to_string(:action => 'statuses', :formats => [:html], :layout => false)))
37
+ end
38
+
39
+ def remove_job
40
+ # We can only dequeue a job when that job is in the same application as the UI.
41
+ # Otherwise we get an error when we try to constantize a class that does not exist
42
+ # in the application the UI is in.
43
+ if ResqueManager.applications.blank?
44
+ Resque.dequeue(params['class'].constantize, *Resque.decode(params['args']))
45
+ redirect_to request.referrer
46
+ end
47
+ end
48
+
49
+ def stop_worker
50
+ worker = find_worker(params[:worker])
51
+ worker.quit if worker
52
+ redirect_to(:action => "workers")
53
+ end
54
+
55
+ def pause_worker
56
+ worker = find_worker(params[:worker])
57
+ worker.pause if worker
58
+ redirect_to(:action => "workers")
59
+ end
60
+
61
+ def continue_worker
62
+ worker = find_worker(params[:worker])
63
+ worker.continue if worker
64
+ redirect_to(:action => "workers")
65
+ end
66
+
67
+ def restart_worker
68
+ worker = find_worker(params[:worker])
69
+ worker.restart if worker
70
+ redirect_to(:action => "workers")
71
+ end
72
+
73
+ def start_worker
74
+ Resque::Worker.start(params)
75
+ redirect_to(:action => "workers")
76
+ end
77
+
78
+ def stats
79
+ unless params[:id]
80
+ redirect_to(:action => 'stats', :id => 'resque')
81
+ end
82
+
83
+ if params[:id] == 'txt'
84
+ info = Resque.info
85
+
86
+ stats = []
87
+ stats << "resque.pending=#{info[:pending]}"
88
+ stats << "resque.processed+=#{info[:processed]}"
89
+ stats << "resque.failed+=#{info[:failed]}"
90
+ stats << "resque.workers=#{info[:workers]}"
91
+ stats << "resque.working=#{info[:working]}"
92
+
93
+ Resque.queues.each do |queue|
94
+ stats << "queues.#{queue}=#{Resque.size(queue)}"
95
+ end
96
+
97
+ render(:text => stats.join("</br>").html_safe)
98
+ end
99
+ end
100
+
101
+ # resque-scheduler actions
102
+
103
+ def schedule
104
+ @farm_status = ResqueScheduler.farm_status
105
+ end
106
+
107
+ def schedule_requeue
108
+ config = Resque.schedule[params['job_name']]
109
+ Resque::Scheduler.enqueue_from_config(config)
110
+ redirect_to(:action => 'overview')
111
+ end
112
+
113
+ def add_scheduled_job
114
+ errors = []
115
+ if Resque.schedule.keys.include?(params[:name])
116
+ errors << 'Name already exists'
117
+ end
118
+ if params[:ip].blank?
119
+ errors << 'You must enter an ip address for the server you want this job to run on.'
120
+ end
121
+ if params[:cron].blank?
122
+ errors << 'You must enter the cron schedule.'
123
+ end
124
+ if errors.blank?
125
+ config = {params['name'] => {'class' => params['class'],
126
+ 'ip' => params['ip'],
127
+ 'cron' => params['cron'],
128
+ 'args' => Resque.decode(params['args'].blank? ? nil : params['args']),
129
+ 'description' => params['description']}
130
+ }
131
+ Resque.redis.rpush(:scheduled, Resque.encode(config))
132
+ ResqueScheduler.restart('ip')
133
+ else
134
+ flash[:error] = errors.join('<br>')
135
+ end
136
+ redirect_to(:action => 'schedule')
137
+ end
138
+
139
+ def remove_from_schedule
140
+ Resque.list_range(:scheduled, 0, -0).each do |s|
141
+
142
+ if s[params['job_name']]
143
+ Resque.redis.lrem(:scheduled, 0, s.to_json)
144
+ # Restart the scheduler on the server that has changed it's schedule
145
+ ResqueScheduler.restart(params['ip'])
146
+ end
147
+ end
148
+ redirect_to(:action => 'schedule')
149
+ end
150
+
151
+ def start_scheduler
152
+ ResqueScheduler.start(params[:ip])
153
+ redirect_to(:action => 'schedule')
154
+ end
155
+
156
+ def stop_scheduler
157
+ ResqueScheduler.quit(params[:ip])
158
+ redirect_to(:action => 'schedule')
159
+ end
160
+
161
+ # resque-status actions
162
+
163
+ def statuses
164
+ @start = params[:start].to_i
165
+ @end = @start + (params[:per_page] || 20)
166
+ @statuses = Resque::Plugins::Status::Hash.statuses(@start, @end)
167
+ @size = Resque::Plugins::Status::Hash.status_ids.size
168
+ if params[:format] == 'js'
169
+ render :text => @statuses.to_json
170
+ end
171
+ end
172
+
173
+ def clear_statuses
174
+ Resque::Plugins::Status::Hash.clear
175
+ redirect_to(:action => 'statuses')
176
+ end
177
+
178
+ def status
179
+ @status = Resque::Plugins::Status::Hash.get(params[:id])
180
+ if params[:format] == 'js'
181
+ render :text => @status.to_json
182
+ end
183
+ end
184
+
185
+ def kill
186
+ Resque::Plugins::Status::Hash.kill(params[:id])
187
+ s = Resque::Plugins::Status::Hash.get(params[:id])
188
+ s.status = 'killed'
189
+ Resque::Plugins::Status::Hash.set(params[:id], s)
190
+ redirect_to(:action => 'statuses')
191
+ end
192
+
193
+ def cleaner
194
+ load_cleaner_filter
195
+
196
+ @jobs = @cleaner.select
197
+ @stats, @total = {}, {"total" => 0, "1h" => 0, "3h" => 0, "1d" => 0, "3d" => 0, "7d" => 0}
198
+ @jobs.each do |job|
199
+ klass = job["payload"]["class"]
200
+ failed_at = Time.parse job["failed_at"]
201
+
202
+ @stats[klass] ||= {"total" => 0, "1h" => 0, "3h" => 0, "1d" => 0, "3d" => 0, "7d" => 0}
203
+ items = [@stats[klass], @total]
204
+
205
+ items.each { |a| a["total"] += 1 }
206
+ items.each { |a| a["1h"] += 1 } if failed_at >= hours_ago(1)
207
+ items.each { |a| a["3h"] += 1 } if failed_at >= hours_ago(3)
208
+ items.each { |a| a["1d"] += 1 } if failed_at >= hours_ago(24)
209
+ items.each { |a| a["3d"] += 1 } if failed_at >= hours_ago(24*3)
210
+ items.each { |a| a["7d"] += 1 } if failed_at >= hours_ago(24*7)
211
+ end
212
+ end
213
+
214
+ def cleaner_list
215
+ load_cleaner_filter
216
+
217
+ block = filter_block
218
+
219
+ @failed = @cleaner.select(&block).reverse
220
+
221
+ url = "cleaner_list?c=#{@klass}&ex=#{@exception}&f=#{@from}&t=#{@to}"
222
+ @dump_url = "cleaner_dump?c=#{@klass}&ex=#{@exception}&f=#{@from}&t=#{@to}"
223
+ @paginate = ResqueManager::Paginate.new(@failed, url, params[:p].to_i)
224
+
225
+ @klasses = @cleaner.stats_by_class.keys
226
+ @exceptions = @cleaner.stats_by_exception.keys
227
+ @count = @cleaner.select(&block).size
228
+ end
229
+
230
+ def cleaner_exec
231
+ load_cleaner_filter
232
+
233
+ if params[:select_all_pages]!="1"
234
+ @sha1 = {}
235
+ params[:sha1].split(",").each { |s| @sha1[s] = true }
236
+ end
237
+
238
+ block = filter_block
239
+
240
+ @count =
241
+ case params[:form_action]
242
+ when "clear" then
243
+ @cleaner.clear(&block)
244
+ when "retry_and_clear" then
245
+ @cleaner.requeue(true, &block)
246
+ when "retry" then
247
+ @cleaner.requeue(false, {}, &block)
248
+ end
249
+
250
+ @link_url = "cleaner_list?c=#{@klass}&ex=#{@exception}&f=#{@from}&t=#{@to}"
251
+ end
252
+
253
+ def cleaner_dump
254
+ load_cleaner_filter
255
+
256
+ block = filter_block
257
+ failures = @cleaner.select(&block)
258
+ # pretty generate throws an error with the json gem on jruby
259
+ output = JSON.pretty_generate(failures) rescue failures.to_json
260
+ render :json => output
261
+ end
262
+
263
+ def cleaner_stale
264
+ @cleaner.clear_stale
265
+ redirect_to :action => "cleaner"
266
+ end
267
+
268
+
269
+ private
270
+
271
+ def check_connection
272
+ Resque.keys
273
+ rescue Errno::ECONNREFUSED
274
+ render(:template => 'resque/error', :layout => false, :locals => {:error => "Can't connect to Redis! (#{Resque.redis.server})"})
275
+ false
276
+ end
277
+
278
+ def find_worker(worker)
279
+ first_part, *rest = worker.split(':')
280
+ first_part.gsub!(/_/, '.')
281
+ Resque::Worker.find("#{first_part}:#{rest.join(':')}")
282
+ end
283
+
284
+ # resque-cleaner methods
285
+
286
+ def get_cleaner
287
+ @cleaner ||= Resque::Plugins::ResqueCleaner.new
288
+ @cleaner.print_message = false
289
+ @cleaner
290
+ end
291
+
292
+ def load_cleaner_filter
293
+ @from = params[:f]=="" ? nil : params[:f]
294
+ @to = params[:t]=="" ? nil : params[:t]
295
+ @klass = params[:c]=="" ? nil : params[:c]
296
+ @exception = params[:ex]=="" ? nil : params[:ex]
297
+ end
298
+
299
+ def filter_block
300
+ block = lambda { |j|
301
+ (!@from || j.after?(hours_ago(@from))) &&
302
+ (!@to || j.before?(hours_ago(@to))) &&
303
+ (!@klass || j.klass?(@klass)) &&
304
+ (!@exception || j.exception?(@exception)) &&
305
+ (!@sha1 || @sha1[Digest::SHA1.hexdigest(j.to_json)])
306
+ }
307
+ end
308
+
309
+ def hours_ago(h)
310
+ Time.now - h.to_i*60*60
311
+ end
312
+
313
+ end
@@ -0,0 +1,4 @@
1
+ module ResqueManager
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,142 @@
1
+ module ResqueManager
2
+ module ResqueHelper
3
+
4
+ def flash_helper
5
+ [:notice, :warning, :message, :error].collect do |key|
6
+ content_tag(:div, flash[key], :class => "flash #{key}") unless flash[key].blank?
7
+ end.join
8
+ end
9
+
10
+ def format_time(t)
11
+ t.strftime("%Y/%m/%d %H:%M:%S %Z")
12
+ end
13
+
14
+ def current_section
15
+ (request.path_info.sub('/', '').split('/').first || 'overview').downcase
16
+ end
17
+
18
+ def current_page
19
+ url request.path_info.sub('/', '').downcase
20
+ end
21
+
22
+ def url(*path_parts)
23
+ [path_prefix, path_parts].join("/").squeeze('/')
24
+ end
25
+
26
+ alias_method :u, :url
27
+
28
+ def path_prefix
29
+ request.env['SCRIPT_NAME']
30
+ end
31
+
32
+ def class_if_current(page = '')
33
+ 'class="current"' if current_page.include? page.to_s
34
+ end
35
+
36
+ def tab(name)
37
+ dname = "#{name.to_s.downcase}"
38
+ "<li #{class_if_current(dname)}>#{link_to(name, url(dname))}</li>".html_safe
39
+ end
40
+
41
+ def find_worker(worker)
42
+ first_part, *rest = worker.split(':')
43
+ first_part.gsub!(/_/, '.')
44
+ Resque::Worker.find("#{first_part}:#{rest.join(':')}")
45
+ end
46
+
47
+ def redis_get_size(key)
48
+ case Resque.redis.type(key)
49
+ when 'none'
50
+ []
51
+ when 'list'
52
+ Resque.redis.llen(key)
53
+ when 'set'
54
+ Resque.redis.scard(key)
55
+ when 'string'
56
+ Resque.redis.get(key).length
57
+ end
58
+ end
59
+
60
+ def redis_get_value_as_array(key)
61
+ case Resque.redis.type(key)
62
+ when 'none'
63
+ []
64
+ when 'list'
65
+ Resque.list_range(key, 0, 20)
66
+ when 'set'
67
+ Resque.redis.smembers(key)
68
+ when 'string'
69
+ [Resque.redis.get(key)]
70
+ end
71
+ end
72
+
73
+ def show_args(args)
74
+ Array(args).map { |a| a.inspect }.join("\n")
75
+ end
76
+
77
+ def partial?
78
+ @partial
79
+ end
80
+
81
+ def poll
82
+ if @polling
83
+ text = "Last Updated: #{Time.now.strftime("%H:%M:%S")}"
84
+ else
85
+ text = link_to('Live Poll', {:action => 'poll', :page => current_section}, :rel => 'poll')
86
+ end
87
+ "<p class='poll'>#{text}</p>".html_safe
88
+ end
89
+
90
+ def status_poll(start)
91
+ if @polling
92
+ text = "Last Updated: #{Time.now.strftime("%H:%M:%S")}"
93
+ else
94
+ text = link_to('Live Poll', {:action => 'status_poll', :start => start}, :rel => 'poll')
95
+ end
96
+ "<p class='poll'>#{text}</p>".html_safe
97
+ end
98
+
99
+ def resque
100
+ Resque
101
+ end
102
+
103
+ # resque-cleaner helpers
104
+
105
+ def time_filter(id, name, value)
106
+ html = "<select id=\"#{id}\" name=\"#{name}\">"
107
+ html += "<option value=\"\">-</option>"
108
+ [1, 3, 6, 12, 24].each do |h|
109
+ selected = h.to_s == value ? 'selected="selected"' : ''
110
+ html += "<option #{selected} value=\"#{h}\">#{h} #{h==1 ? "hour" : "hours"} ago</option>"
111
+ end
112
+ [3, 7, 14, 28].each do |d|
113
+ selected = (d*24).to_s == value ? 'selected="selected"' : ''
114
+ html += "<option #{selected} value=\"#{d*24}\">#{d} days ago</option>"
115
+ end
116
+ html += "</select>"
117
+ html.html_safe
118
+ end
119
+
120
+ def class_filter(id, name, klasses, value)
121
+ html = "<select id=\"#{id}\" name=\"#{name}\">"
122
+ html += "<option value=\"\">-</option>"
123
+ klasses.each do |k|
124
+ selected = k == value ? 'selected="selected"' : ''
125
+ html += "<option #{selected} value=\"#{k}\">#{k}</option>"
126
+ end
127
+ html += "</select>"
128
+ html.html_safe
129
+ end
130
+
131
+ def exception_filter(id, name, exceptions, value)
132
+ html = "<select id=\"#{id}\" name=\"#{name}\">"
133
+ html += "<option value=\"\">-</option>"
134
+ exceptions.each do |ex|
135
+ selected = ex == value ? 'selected="selected"' : ''
136
+ html += "<option #{selected} value=\"#{ex}\">#{ex}</option>"
137
+ end
138
+ html += "</select>"
139
+ html.html_safe
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,54 @@
1
+ module ResqueManager
2
+ # Paginate class used by resque-cleaner
3
+
4
+ #Pagination helper for list page.
5
+ class Paginate
6
+ attr_accessor :page_size, :page, :jobs, :url
7
+
8
+ def initialize(jobs, url, page=1, page_size=20)
9
+ @jobs = jobs
10
+ @url = url
11
+ @page = (!page || page < 1) ? 1 : page
12
+ @page_size = 20
13
+ end
14
+
15
+ def first_index
16
+ @page_size * (@page-1)
17
+ end
18
+
19
+ def last_index
20
+ last = first_index + @page_size - 1
21
+ last > @jobs.size-1 ? @jobs.size-1 : last
22
+ end
23
+
24
+ def paginated_jobs
25
+ @jobs[first_index, @page_size]
26
+ end
27
+
28
+ def first_page?
29
+ @page <= 1
30
+ end
31
+
32
+ def last_page?
33
+ @page >= max_page
34
+ end
35
+
36
+ def page_url(page)
37
+ u = @url
38
+ u += @url.include?("?") ? "&" : "?"
39
+ if page.is_a?(Symbol)
40
+ page = @page - 1 if page==:prev
41
+ page = @page + 1 if page==:next
42
+ end
43
+ u += "p=#{page}"
44
+ end
45
+
46
+ def total_size
47
+ @jobs.size
48
+ end
49
+
50
+ def max_page
51
+ ((total_size-1) / @page_size) + 1
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,37 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>ResqueManager</title>
5
+ <%= stylesheet_link_tag "resque_manager/application", :media => "all" %>
6
+ <%= javascript_include_tag "resque_manager/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+ <div class="header">
11
+ <ul class='nav'>
12
+ <li><%=link_to("Home",url_for('/'))%></li>
13
+ <% Resque::Server.tabs.each do |name| -%>
14
+ <%= tab name -%>
15
+ <%end-%>
16
+ </ul>
17
+ </div>
18
+
19
+ <% if @subtabs %>
20
+ <ul class='subnav'>
21
+ <% for subtab in @subtabs %>
22
+ <li <%= class_if_current "#{current_section}/#{subtab}" %>><%= link_to(subtab, :action => current_section, :id => subtab) %> </li>
23
+ <% end %>
24
+ </ul>
25
+ <% end %>
26
+
27
+ <div id="main">
28
+ <%= yield %>
29
+ </div>
30
+
31
+ <div id="footer">
32
+ <p>Powered by <a href="http://github.com/defunkt/resque">Resque</a> v<%=Resque::Version%></p>
33
+ <p>Connected to Redis namespace <%= Resque.redis.namespace %> on <%=Resque.redis_id%></p>
34
+ </div>
35
+
36
+ </body>
37
+ </html>
@@ -0,0 +1,17 @@
1
+ <% if key = params[:key] %>
2
+
3
+ <h1>Key "<%= key %>" is a <%= resque.redis.type key %></h1>
4
+ <h2>size: <%= redis_get_size(key) %></h2>
5
+ <table>
6
+ <% for row in redis_get_value_as_array(key) %>
7
+ <tr>
8
+ <td>
9
+ <%= row %>
10
+ </td>
11
+ </tr>
12
+ <% end %>
13
+ </table>
14
+
15
+ <% else %>
16
+
17
+ <% end %>
@@ -0,0 +1,12 @@
1
+ <div class="warning">
2
+ <p>
3
+ There are more than <%= @cleaner.limiter.maximum%> jobs. ResqueCleaner handles only recent <%= @cleaner.limiter.maximum %> jobs. See the Limiter section on <a href="https://github.com/ono/resque-cleaner" target="_blank">README</a> for more detail.
4
+ </p>
5
+ <p>
6
+ <form method="post" action="cleaner_stale">
7
+ <input type="hidden" name="action" value="clear_stale" />
8
+ <input type="submit" onclick="return confirm('Are you sure?')" value="Clear all jobs older than <%= @cleaner.limiter.count %>th job."/>
9
+ (<%= @cleaner.failure.count - @cleaner.limiter.maximum %> jobs will be cleared)
10
+ </form>
11
+ </p>
12
+ </div>
@@ -0,0 +1,10 @@
1
+ <%if start - 20 >= 0 || start + 20 <= size%>
2
+ <p class='pagination'>
3
+ <% if start - 20 >= 0 %>
4
+ <%= link_to('« less',:start => start - 20, :class => 'less') %>
5
+ <% end %>
6
+ <% if start + 20 < size %>
7
+ <%= link_to('more »',:start => start + 20, :class => 'more') %>
8
+ <% end %>
9
+ </p>
10
+ <%end%>
@@ -0,0 +1,53 @@
1
+ <div class="list_info clearfix">
2
+
3
+ <ul class="pagination">
4
+ <li class="summary">
5
+ <%= @paginate.first_index + 1%> - <%= @paginate.last_index + 1%> / <%= @paginate.total_size%>
6
+ </li>
7
+ <% if @paginate.max_page>1 %>
8
+ <% if @paginate.first_page? %>
9
+ <li class="prev off">&lt;&lt;newer</li>
10
+ <% else %>
11
+ <li class="prev"><a href="<%= @paginate.page_url(:prev)%>">&lt;&lt;newer</a></li>
12
+ <% end %>
13
+
14
+ <% 1.upto(6<@paginate.max_page ? 6 : @paginate.max_page) do |pg| %>
15
+ <% if @paginate.page==pg %>
16
+ <li class="current"><%=pg%></li>
17
+ <% else %>
18
+ <li><a href="<%= @paginate.page_url(pg)%>"><%=pg%></a></li>
19
+ <% end %>
20
+ <% end %>
21
+
22
+ <% if @paginate.max_page > 6 %>
23
+ <% if @paginate.max_page!=@paginate.page %>
24
+ <% if @paginate.page>7 %>
25
+ <li class="off">...</li>
26
+ <% end %>
27
+ <% if @paginate.page>6 %>
28
+ <li class="current"><%=@paginate.page%></li>
29
+ <% end %>
30
+ <% end %>
31
+ <% if @paginate.max_page>7 && @paginate.page+1!=@paginate.max_page %>
32
+ <li class="off">...</li>
33
+ <% end %>
34
+
35
+ <% if @paginate.max_page==@paginate.page %>
36
+ <li class="current"><%=@paginate.max_page%></li>
37
+ <% else %>
38
+ <li><a href="<%= @paginate.page_url(@paginate.max_page)%>"><%=@paginate.max_page%></a></li>
39
+ <% end %>
40
+ <% end %>
41
+
42
+ <% if @paginate.last_page? %>
43
+ <li class="next off">older&gt;&gt;</li>
44
+ <% else %>
45
+ <li class="next"><a href="<%= @paginate.page_url(:next)%>">older&gt;&gt;</a></li>
46
+ <% end %>
47
+ <% end %>
48
+
49
+ </ul>
50
+ <div class="dump">
51
+ <a href="<%= @dump_url%>" target="_blank">Dump</a>
52
+ </div>
53
+ </div>