mongo-resque 1.17.1

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 (46) hide show
  1. data/HISTORY.md +278 -0
  2. data/LICENSE +20 -0
  3. data/README.markdown +76 -0
  4. data/bin/resque +67 -0
  5. data/bin/resque-web +23 -0
  6. data/docs/HOOKS.md +132 -0
  7. data/docs/PLUGINS.md +93 -0
  8. data/lib/resque/errors.rb +13 -0
  9. data/lib/resque/failure/base.rb +64 -0
  10. data/lib/resque/failure/hoptoad.rb +133 -0
  11. data/lib/resque/failure/mongo.rb +45 -0
  12. data/lib/resque/failure/multiple.rb +54 -0
  13. data/lib/resque/failure/redis.rb +46 -0
  14. data/lib/resque/failure.rb +70 -0
  15. data/lib/resque/helpers.rb +75 -0
  16. data/lib/resque/job.rb +215 -0
  17. data/lib/resque/plugin.rb +51 -0
  18. data/lib/resque/server/public/favicon.ico +0 -0
  19. data/lib/resque/server/public/idle.png +0 -0
  20. data/lib/resque/server/public/jquery-1.3.2.min.js +19 -0
  21. data/lib/resque/server/public/jquery.relatize_date.js +95 -0
  22. data/lib/resque/server/public/poll.png +0 -0
  23. data/lib/resque/server/public/ranger.js +73 -0
  24. data/lib/resque/server/public/reset.css +48 -0
  25. data/lib/resque/server/public/style.css +92 -0
  26. data/lib/resque/server/public/working.png +0 -0
  27. data/lib/resque/server/test_helper.rb +19 -0
  28. data/lib/resque/server/views/error.erb +1 -0
  29. data/lib/resque/server/views/failed.erb +65 -0
  30. data/lib/resque/server/views/key_sets.erb +19 -0
  31. data/lib/resque/server/views/key_string.erb +11 -0
  32. data/lib/resque/server/views/layout.erb +42 -0
  33. data/lib/resque/server/views/next_more.erb +10 -0
  34. data/lib/resque/server/views/overview.erb +4 -0
  35. data/lib/resque/server/views/queues.erb +69 -0
  36. data/lib/resque/server/views/stats.erb +73 -0
  37. data/lib/resque/server/views/workers.erb +109 -0
  38. data/lib/resque/server/views/working.erb +72 -0
  39. data/lib/resque/server.rb +268 -0
  40. data/lib/resque/stat.rb +54 -0
  41. data/lib/resque/tasks.rb +42 -0
  42. data/lib/resque/version.rb +3 -0
  43. data/lib/resque/worker.rb +497 -0
  44. data/lib/resque.rb +417 -0
  45. data/lib/tasks/resque.rake +2 -0
  46. metadata +148 -0
@@ -0,0 +1,11 @@
1
+ <% if key = params[:key] %>
2
+ <h1>Key "<%= key %>" is a <%= resque.redis.type key %></h1>
3
+ <h2>size: <%= redis_get_size(key) %></h2>
4
+ <table>
5
+ <tr>
6
+ <td>
7
+ <%= redis_get_value_as_array(key) %>
8
+ </td>
9
+ </tr>
10
+ </table>
11
+ <% end %>
@@ -0,0 +1,42 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <title>Resque.</title>
6
+ <link href="<%=u 'reset.css' %>" media="screen" rel="stylesheet" type="text/css">
7
+ <link href="<%=u 'style.css' %>" media="screen" rel="stylesheet" type="text/css">
8
+ <script src="<%=u 'jquery-1.3.2.min.js' %>" type="text/javascript"></script>
9
+ <script src="<%=u 'jquery.relatize_date.js' %>" type="text/javascript"></script>
10
+ <script src="<%=u 'ranger.js' %>" type="text/javascript"></script>
11
+ </head>
12
+ <body>
13
+ <div class="header">
14
+ <ul class='nav'>
15
+ <% tabs.each do |tab_name| %>
16
+ <%= tab tab_name %>
17
+ <% end %>
18
+ </ul>
19
+ <abbr class="namespace" title="Mongo Connection">
20
+ <%= Resque.to_s %>
21
+ </abbr>
22
+ </div>
23
+
24
+ <% if @subtabs %>
25
+ <ul class='subnav'>
26
+ <% for subtab in @subtabs %>
27
+ <li <%= class_if_current "#{current_section}/#{subtab}" %>><a href="<%= current_section %>/<%= subtab %>"><span><%= subtab %></span></a></li>
28
+ <% end %>
29
+ </ul>
30
+ <% end %>
31
+
32
+ <div id="main">
33
+ <%= yield %>
34
+ </div>
35
+
36
+ <div id="footer">
37
+ <p>Powered by <a href="https://github.com/dbackeus/mongo-resque">Mongo-Resque</a> v<%=Resque::Version%></p>
38
+ <p>Connected to Mongo at <%= Resque.to_s %></p>
39
+ </div>
40
+
41
+ </body>
42
+ </html>
@@ -0,0 +1,10 @@
1
+ <%if start - 20 >= 0 || start + 20 <= size%>
2
+ <p class='pagination'>
3
+ <% if start - 20 >= 0 %>
4
+ <a href="<%= current_page %>?start=<%= start - 20 %>" class='less'>&laquo; less</a>
5
+ <% end %>
6
+ <% if start + 20 <= size %>
7
+ <a href="<%= current_page %>?start=<%= start + 20 %>" class='more'>more &raquo;</a>
8
+ <% end %>
9
+ </p>
10
+ <%end%>
@@ -0,0 +1,4 @@
1
+ <%= partial :queues %>
2
+ <hr />
3
+ <%= partial :working %>
4
+ <%= poll %>
@@ -0,0 +1,69 @@
1
+ <% @subtabs = resque.queues unless partial? || params[:id].nil? %>
2
+
3
+ <% if queue = params[:id] %>
4
+ <h1>Jobs on <span class='hl'><%= queue %></span></h1>
5
+ <form method="POST" action="<%=u "/queues/#{queue}/remove" %>" class='remove-queue'>
6
+ <input type='submit' name='' value='Remove Queue' onclick='return confirm("Are you absolutely sure? This cannot be undone.");' />
7
+ </form>
8
+ <% if !resque.queue_allows_delayed queue.to_sym %>
9
+ <p class='sub'>Showing <%= start = params[:start].to_i %> to <%= start + 20 %> of <b><%=size = resque.size(queue.to_sym)%></b> jobs</p><table class='jobs'>
10
+ <tr>
11
+ <th>Class</th>
12
+ <th>Args</th>
13
+ <th>Enqueued At</th>
14
+ </tr>
15
+ <% for job in (jobs = resque.peek(queue.to_sym, start, 20)) %>
16
+ <tr>
17
+ <td class='class'><%= job['class'] %></td>
18
+ <td class='args'><%=h job['args'].inspect %></td>
19
+ <td class='enqueuedat'><%=h enqueued_at(job['resque_enqueue_timestamp']) %></td>
20
+ </tr>
21
+ <% end %>
22
+ <% else %>
23
+ <p class='sub'>Showing <%= start = params[:start].to_i %> to <%= start + 20 %> of <b><%=size = resque.size(queue.to_sym)%></b> jobs</p><table class='delays'>
24
+ <tr>
25
+ <th>Class</th>
26
+ <th>Args</th>
27
+ <th>Enqueued At</th>
28
+ <th>Processes In</th>
29
+ </tr>
30
+ <% for job in (jobs = resque.peek(queue.to_sym, start, 20, :all_sorted)) %>
31
+ <tr>
32
+ <td class='class'><%= job['class'] %></td>
33
+ <td class='args'><%=h job['args'].inspect %></td>
34
+ <td class='enqueuedat'><%=h enqueued_at(job['resque_enqueue_timestamp']) %></td>
35
+ <td class='delay'><%=h processes_in(job['delay_until']) %></td>
36
+ </tr>
37
+ <% end %>
38
+ <% end %>
39
+ <% if jobs.empty? %>
40
+ <tr>
41
+ <td class='no-data' colspan='2'>There are no pending jobs in this queue</td>
42
+ </tr>
43
+ <% end %>
44
+ </table>
45
+ <%= partial :next_more, :start => start, :size => size %>
46
+ <% else %>
47
+
48
+ <h1 class='wi'>Queues</h1>
49
+ <p class='intro'>The list below contains all the registered queues with the number of jobs currently in the queue. Select a queue from above to view all jobs currently pending on the queue.</p>
50
+ <table class='queues'>
51
+ <tr>
52
+ <th>Name</th>
53
+ <th>Ready Jobs</th>
54
+ <th>Delayed Jobs</th>
55
+ </tr>
56
+ <% for queue in resque.queues.sort_by { |q| q.to_s } %>
57
+ <tr>
58
+ <td class='queue'><a class="queue" href="<%= u "queues/#{queue}" %>"><%= queue %></a></td>
59
+ <td class='size'><%= resque.ready_size queue.to_sym %></td>
60
+ <td class='size'><%= resque.delayed_size(queue.to_sym) if resque.queue_allows_delayed(queue.to_sym) %></td>
61
+ </tr>
62
+ <% end %>
63
+ <tr class="<%= Resque::Failure.count.zero? ? "failed" : "failure" %>">
64
+ <td class='queue failed'><a class="queue" href="<%= u :failed %>">failed</a></td>
65
+ <td class='size'><%= Resque::Failure.count %></td>
66
+ </tr>
67
+ </table>
68
+
69
+ <% end %>
@@ -0,0 +1,73 @@
1
+ <% @subtabs = %w( resque mongo keys ) %>
2
+
3
+ <% if params[:key] %>
4
+
5
+ <%= partial :key_sets %>
6
+
7
+ <% elsif params[:id] == "resque" %>
8
+
9
+ <h1><%= resque %></h1>
10
+ <table class='stats'>
11
+ <% for key, value in resque.info.to_a.sort_by { |i| i[0].to_s } %>
12
+ <tr>
13
+ <th>
14
+ <%= key %>
15
+ </th>
16
+ <td>
17
+ <%= value %>
18
+ </td>
19
+ </tr>
20
+ <% end %>
21
+ </table>
22
+
23
+ <% elsif params[:id] == 'mongo' %>
24
+
25
+ <h1><%= resque.to_s %></h1>
26
+ <table class='stats'>
27
+ <% resque.mongo_stats.find.to_a.each do |hash| %>
28
+ <tr>
29
+ <th>
30
+ <%= hash['stat'] %>
31
+ </th>
32
+ <td>
33
+ <%= hash['value'].inspect %>
34
+ </td>
35
+ </tr>
36
+ <% end %>
37
+ </table>
38
+
39
+ <h1>Mongo Database Health</h1>
40
+ <table class='stats'>
41
+ <% resque.mongo.stats.each_pair do |key, value| %>
42
+ <tr>
43
+ <th>
44
+ <%= key %>
45
+ </th>
46
+ <td>
47
+ <%= value %>
48
+ </td>
49
+ </tr>
50
+ <% end %>
51
+ </table>
52
+
53
+ <% elsif params[:id] == 'keys' %>
54
+
55
+ <h1>Collections in <%= resque.to_s %></h1>
56
+ <table class='stats'>
57
+ <tr>
58
+ <th>collection</th>
59
+ <th>size</th>
60
+ </tr>
61
+ <% for key in resque.keys.sort %>
62
+ <tr>
63
+ <th>
64
+ <a href="<%=u "/stats/keys/#{key}" %>"><%= key %></a>
65
+ </th>
66
+ <td><%= resque.mongo[key].count %></td>
67
+ </tr>
68
+ <% end %>
69
+ </table>
70
+
71
+ <% else %>
72
+
73
+ <% end %>
@@ -0,0 +1,109 @@
1
+ <% @subtabs = worker_hosts.keys.sort unless worker_hosts.size == 1 %>
2
+
3
+ <% if params[:id] && worker = Resque::Worker.find(params[:id]) %>
4
+
5
+ <h1>Worker <%= worker %></h1>
6
+ <table class='workers'>
7
+ <tr>
8
+ <th>&nbsp;</th>
9
+ <th>Host</th>
10
+ <th>Pid</th>
11
+ <th>Started</th>
12
+ <th>Queues</th>
13
+ <th>Processed</th>
14
+ <th>Failed</th>
15
+ <th>Processing</th>
16
+ </tr>
17
+ <tr>
18
+ <td class='icon'><img src="<%=u state = worker.state %>.png" alt="<%= state %>" title="<%= state %>"></td>
19
+
20
+ <% host, pid, queues = worker.to_s.split(':') %>
21
+ <td><%= host %></td>
22
+ <td><%= pid %></td>
23
+ <td><span class="time"><%= worker.started %></span></td>
24
+ <td class='queues'><%= queues.split(',').map { |q| '<a class="queue-tag" href="' + u("/queues/#{q}") + '">' + q + '</a>'}.join('') %></td>
25
+ <td><%= worker.processed %></td>
26
+ <td><%= worker.failed %></td>
27
+ <td class='process'>
28
+ <% data = worker.processing || {} %>
29
+ <% if data['queue'] %>
30
+ <code><%= data['payload']['class'] %></code>
31
+ <small><a class="queue time" href="<%=u "/working/#{worker}" %>"><%= data['run_at'] %></a></small>
32
+ <% else %>
33
+ <span class='waiting'>Waiting for a job...</span>
34
+ <% end %>
35
+ </td>
36
+ </tr>
37
+ </table>
38
+
39
+ <% elsif params[:id] && !worker_hosts.keys.include?(params[:id]) && params[:id] != 'all' %>
40
+
41
+ <h1>Worker doesn't exist</h1>
42
+
43
+ <% elsif worker_hosts.size == 1 || params[:id] %>
44
+
45
+ <% if worker_hosts.size == 1 || params[:id] == 'all' %>
46
+ <% workers = Resque.workers %>
47
+ <% else %>
48
+ <% workers = worker_hosts[params[:id]].map { |id| Resque::Worker.find(id) } %>
49
+ <% end %>
50
+
51
+ <h1 class='wi'><%= workers.size %> Workers</h1>
52
+ <p class='intro'>The workers listed below are all registered as active on your system.</p>
53
+ <table class='workers'>
54
+ <tr>
55
+ <th>&nbsp;</th>
56
+ <th>Where</th>
57
+ <th>Queues</th>
58
+ <th>Processing</th>
59
+ </tr>
60
+ <% for worker in (workers = workers.sort_by { |w| w.to_s }) %>
61
+ <tr class="<%=state = worker.state%>">
62
+ <td class='icon'><img src="<%=u state %>.png" alt="<%= state %>" title="<%= state %>"></td>
63
+
64
+ <% host, pid, queues = worker.to_s.split(':') %>
65
+ <td class='where'><a href="<%=u "workers/#{worker}"%>"><%= host %>:<%= pid %></a></td>
66
+ <td class='queues'><%= queues.split(',').map { |q| '<a class="queue-tag" href="' + u("/queues/#{q}") + '">' + q + '</a>'}.join('') %></td>
67
+
68
+ <td class='process'>
69
+ <% data = worker.processing || {} %>
70
+ <% if data['queue'] %>
71
+ <code><%= data['payload']['class'] %></code>
72
+ <small><a class="queue time" href="<%=u "/working/#{worker}" %>"><%= data['run_at'] %></a></small>
73
+ <% else %>
74
+ <span class='waiting'>Waiting for a job...</span>
75
+ <% end %>
76
+ </td>
77
+ </tr>
78
+ <% end %>
79
+ <% if workers.empty? %>
80
+ <tr>
81
+ <td colspan='4' class='no-data'>There are no registered workers</td>
82
+ </tr>
83
+ <% end %>
84
+ </table>
85
+ <%=poll%>
86
+
87
+ <% else %>
88
+ <% @subtabs = [] %>
89
+ <h1 class='wi'>Workers</h1>
90
+ <p class='intro'>The hostnames below all have registered workers. Select a hostname to view its workers, or "all" to see all workers.</p>
91
+ <table class='queues'>
92
+ <tr>
93
+ <th>Hostname</th>
94
+ <th>Workers</th>
95
+ </tr>
96
+ <% for hostname, workers in worker_hosts.sort_by { |h,w| h } %>
97
+ <tr>
98
+ <td class='queue'><a class="queue" href="<%= u "workers/#{hostname}" %>"><%= hostname %></a></td>
99
+ <td class='size'><%= workers.size %></td>
100
+ </tr>
101
+ <% end %>
102
+ <tr class="failed">
103
+ <td class='queue failed'><a class="queue" href="<%= u "workers/all" %>">all workers</a></td>
104
+ <td class='size'><%= Resque.workers.size %></td>
105
+ </tr>
106
+ </table>
107
+
108
+
109
+ <% end %>
@@ -0,0 +1,72 @@
1
+ <% if params[:id] && (worker = Resque::Worker.find(params[:id])) && worker.job %>
2
+ <h1><%= worker %>'s job</h1>
3
+
4
+ <table>
5
+ <tr>
6
+ <th>&nbsp;</th>
7
+ <th>Where</th>
8
+ <th>Queue</th>
9
+ <th>Started</th>
10
+ <th>Class</th>
11
+ <th>Args</th>
12
+ </tr>
13
+ <tr>
14
+ <td><img src="<%=u 'working.png' %>" alt="working" title="working"></td>
15
+ <% host, pid, _ = worker.to_s.split(':') %>
16
+ <td><a href="<%=u "/workers/#{worker}" %>"><%= host %>:<%= pid %></a></td>
17
+ <% data = worker.job %>
18
+ <% queue = data['queue'] %>
19
+ <td><a class="queue" href="<%=u "/queues/#{queue}" %>"><%= queue %></a></td>
20
+ <td><span class="time"><%= data['run_at'] %></span></td>
21
+ <td>
22
+ <code><%= data['payload']['class'] %></code>
23
+ </td>
24
+ <td><%=h data['payload']['args'].inspect %></td>
25
+ </tr>
26
+ </table>
27
+
28
+ <% else %>
29
+
30
+ <%
31
+ workers = resque.working
32
+ jobs = workers.collect {|w| w.job }
33
+ worker_jobs = workers.zip(jobs)
34
+ worker_jobs = worker_jobs.reject { |w, j| w.idle? }
35
+ %>
36
+
37
+ <h1 class='wi'><%= worker_jobs.size %> of <%= resque.workers.size %> Workers Working</h1>
38
+ <p class='intro'>The list below contains all workers which are currently running a job.</p>
39
+ <table class='workers'>
40
+ <tr>
41
+ <th>&nbsp;</th>
42
+ <th>Where</th>
43
+ <th>Queue</th>
44
+ <th>Processing</th>
45
+ </tr>
46
+ <% if worker_jobs.empty? %>
47
+ <tr>
48
+ <td colspan="4" class='no-data'>Nothing is happening right now...</td>
49
+ </tr>
50
+ <% end %>
51
+
52
+ <% worker_jobs.sort_by {|w, j| j['run_at'] ? j['run_at'] : '' }.each do |worker, job| %>
53
+ <tr>
54
+ <td class='icon'><img src="<%=u state = worker.state %>.png" alt="<%= state %>" title="<%= state %>"></td>
55
+ <% host, pid, queues = worker.to_s.split(':') %>
56
+ <td class='where'><a href="<%=u "/workers/#{worker}" %>"><%= host %>:<%= pid %></a></td>
57
+ <td class='queues queue'>
58
+ <a class="queue-tag" href="<%=u "/queues/#{job['queue']}" %>"><%= job['queue'] %></a>
59
+ </td>
60
+ <td class='process'>
61
+ <% if job['queue'] %>
62
+ <code><%= job['payload']['class'] %></code>
63
+ <small><a class="queue time" href="<%=u "/working/#{worker}" %>"><%= job['run_at'] %></a></small>
64
+ <% else %>
65
+ <span class='waiting'>Waiting for a job...</span>
66
+ <% end %>
67
+ </td>
68
+ </tr>
69
+ <% end %>
70
+ </table>
71
+
72
+ <% end %>
@@ -0,0 +1,268 @@
1
+ require 'sinatra/base'
2
+ require 'erb'
3
+ require 'resque'
4
+ require 'resque/version'
5
+ require 'time'
6
+
7
+ module Resque
8
+ class Server < Sinatra::Base
9
+ dir = File.dirname(File.expand_path(__FILE__))
10
+
11
+ set :views, "#{dir}/server/views"
12
+ set :public, "#{dir}/server/public"
13
+ set :static, true
14
+
15
+ helpers do
16
+ include Rack::Utils
17
+ alias_method :h, :escape_html
18
+
19
+ def current_section
20
+ url_path request.path_info.sub('/','').split('/')[0].downcase
21
+ end
22
+
23
+ def current_page
24
+ url_path request.path_info.sub('/','')
25
+ end
26
+
27
+ def url_path(*path_parts)
28
+ [ path_prefix, path_parts ].join("/").squeeze('/')
29
+ end
30
+ alias_method :u, :url_path
31
+
32
+ def path_prefix
33
+ request.env['SCRIPT_NAME']
34
+ end
35
+
36
+ def class_if_current(path = '')
37
+ 'class="current"' if current_page[0, path.size] == path
38
+ end
39
+
40
+ def tab(name)
41
+ dname = name.to_s.downcase
42
+ path = url_path(dname)
43
+ "<li #{class_if_current(path)}><a href='#{path}'>#{name}</a></li>"
44
+ end
45
+
46
+ def tabs
47
+ Resque::Server.tabs
48
+ end
49
+
50
+ def mongo_get_size(key)
51
+ Resque.mongo[key].count
52
+ end
53
+
54
+ def mongo_get_value_as_array(key, start=0)
55
+ Resque.mongo[key].find({ }, { :skip => start, :limit => 20}).to_a
56
+ end
57
+
58
+ def show_args(args)
59
+ Array(args).map { |a| a.inspect }.join("\n")
60
+ end
61
+
62
+ def worker_hosts
63
+ @worker_hosts ||= worker_hosts!
64
+ end
65
+
66
+ def worker_hosts!
67
+ hosts = Hash.new { [] }
68
+
69
+ Resque.workers.each do |worker|
70
+ host, _ = worker.to_s.split(':')
71
+ hosts[host] += [worker.to_s]
72
+ end
73
+
74
+ hosts
75
+ end
76
+
77
+ def partial?
78
+ @partial
79
+ end
80
+
81
+ def partial(template, local_vars = {})
82
+ @partial = true
83
+ erb(template.to_sym, {:layout => false}, local_vars)
84
+ ensure
85
+ @partial = false
86
+ end
87
+
88
+ def poll
89
+ if @polling
90
+ text = "Last Updated: #{Time.now.strftime("%H:%M:%S")}"
91
+ else
92
+ text = "<a href='#{u(request.path_info)}.poll' rel='poll'>Live Poll</a>"
93
+ end
94
+ "<p class='poll'>#{text}</p>"
95
+ end
96
+
97
+ end
98
+
99
+ def show(page, layout = true)
100
+ begin
101
+ erb page.to_sym, {:layout => layout}, :resque => Resque
102
+ rescue Errno::ECONNREFUSED
103
+ erb :error, {:layout => false}, :error => "Can't connect to Redis! (#{Resque.redis_id})"
104
+ end
105
+ end
106
+
107
+ def show_for_polling(page)
108
+ content_type "text/html"
109
+ @polling = true
110
+ show(page.to_sym, false).gsub(/\s{1,}/, ' ')
111
+ end
112
+
113
+ def processes_in(delay_until)
114
+ return 'Immediately' if delay_until.nil?
115
+ now = Time.now
116
+ time = distance_of_time_in_words(now, delay_until)
117
+ return "Immediately (#{time})" if now > delay_until
118
+ return time
119
+ end
120
+
121
+ def enqueued_at(resque_enqueue_timestamp)
122
+ return 'Unknown' if resque_enqueue_timestamp.nil?
123
+ now = Time.now
124
+ time = distance_of_time_in_words(now, resque_enqueue_timestamp)
125
+ return time
126
+ end
127
+
128
+ def distance_of_time_in_words(from_time, to_time = 0, include_seconds = true, options = {})
129
+ from_time = from_time.to_time if from_time.respond_to?(:to_time)
130
+ to_time = to_time.to_time if to_time.respond_to?(:to_time)
131
+ distance_in_minutes = (((to_time - from_time).abs)/60).round
132
+ distance_in_seconds = ((to_time - from_time).abs).round
133
+
134
+ ago = from_time > to_time ? ' ago' : ''
135
+
136
+
137
+ case distance_in_minutes
138
+ when 0..1
139
+ return distance_in_minutes == 0 ?
140
+ "less than 1 minute" + ago :
141
+ "#{distance_in_minutes} minutes" + ago unless include_seconds
142
+
143
+ case distance_in_seconds
144
+ when 0..4 then "less than 5 seconds" + ago
145
+ when 5..9 then "less than 10 seconds" + ago
146
+ when 10..19 then "less than 20 seconds" + ago
147
+ when 20..39 then "half a minute" + ago
148
+ when 40..59 then "less than 1 minute" + ago
149
+ else "1 minute" + ago
150
+ end
151
+
152
+ when 2..44 then "#{distance_in_minutes} minutes" + ago
153
+ when 45..89 then "about 1 hour" + ago
154
+ when 90..1439 then "about #{(distance_in_minutes.to_f / 60.0).round} hours" + ago
155
+ when 1440..2529 then "about 1 day" + ago
156
+ when 2530..43199 then "about #{(distance_in_minutes.to_f / 1440.0).round} days" + ago
157
+ when 43200..86399 then "about 1 month" + ago
158
+ when 86400..525599 then "about #{(distance_in_minutes.to_f / 43200.0).round} months" + ago
159
+ else
160
+ distance_in_years = distance_in_minutes / 525600
161
+ minute_offset_for_leap_year = (distance_in_years / 4) * 1440
162
+ remainder = ((distance_in_minutes - minute_offset_for_leap_year) % 525600)
163
+ if remainder < 131400
164
+ "about #{distance_in_years} years" + ago
165
+ elsif remainder < 394200
166
+ "over #{distance_in_years} years" + ago
167
+ else
168
+ "almost #{distance_in_years} years" + ago
169
+ end
170
+ end
171
+ end
172
+
173
+ # to make things easier on ourselves
174
+ get "/?" do
175
+ redirect url_path(:overview)
176
+ end
177
+
178
+ %w( overview workers ).each do |page|
179
+ get "/#{page}.poll" do
180
+ show_for_polling(page)
181
+ end
182
+
183
+ get "/#{page}/:id.poll" do
184
+ show_for_polling(page)
185
+ end
186
+ end
187
+
188
+ %w( overview queues working workers key ).each do |page|
189
+ get "/#{page}" do
190
+ show page
191
+ end
192
+
193
+ get "/#{page}/:id" do
194
+ show page
195
+ end
196
+ end
197
+
198
+ post "/queues/:id/remove" do
199
+ Resque.remove_queue(params[:id])
200
+ redirect u('queues')
201
+ end
202
+
203
+ get "/failed" do
204
+ if Resque::Failure.url
205
+ redirect Resque::Failure.url
206
+ else
207
+ show :failed
208
+ end
209
+ end
210
+
211
+ post "/failed/clear" do
212
+ Resque::Failure.clear
213
+ redirect u('failed')
214
+ end
215
+
216
+ get "/failed/requeue/:index" do
217
+ Resque::Failure.requeue(params[:index])
218
+ if request.xhr?
219
+ return Resque::Failure.all(params[:index])['retried_at']
220
+ else
221
+ redirect u('failed')
222
+ end
223
+ end
224
+
225
+ get "/failed/remove/:index" do
226
+ Resque::Failure.remove(params[:index])
227
+ redirect u('failed')
228
+ end
229
+
230
+ get "/stats" do
231
+ redirect url_path("/stats/resque")
232
+ end
233
+
234
+ get "/stats/:id" do
235
+ show :stats
236
+ end
237
+
238
+ get "/stats/keys/:key" do
239
+ show :stats
240
+ end
241
+
242
+ get "/stats.txt" do
243
+ info = Resque.info
244
+
245
+ stats = []
246
+ stats << "resque.pending=#{info[:pending]}"
247
+ stats << "resque.processed+=#{info[:processed]}"
248
+ stats << "resque.failed+=#{info[:failed]}"
249
+ stats << "resque.workers=#{info[:workers]}"
250
+ stats << "resque.working=#{info[:working]}"
251
+
252
+ Resque.queues.each do |queue|
253
+ stats << "queues.#{queue}=#{Resque.size(queue)}"
254
+ end
255
+
256
+ content_type 'text/plain'
257
+ stats.join "\n"
258
+ end
259
+
260
+ def resque
261
+ Resque
262
+ end
263
+
264
+ def self.tabs
265
+ @tabs ||= ["Overview", "Working", "Failed", "Queues", "Workers", "Stats"]
266
+ end
267
+ end
268
+ end