resque_sqs 1.25.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +7 -0
  2. data/HISTORY.md +467 -0
  3. data/LICENSE +20 -0
  4. data/README.markdown +866 -0
  5. data/Rakefile +70 -0
  6. data/bin/resque-sqs +81 -0
  7. data/bin/resque-sqs-web +27 -0
  8. data/lib/resque_sqs/errors.rb +13 -0
  9. data/lib/resque_sqs/failure/airbrake.rb +33 -0
  10. data/lib/resque_sqs/failure/base.rb +73 -0
  11. data/lib/resque_sqs/failure/multiple.rb +59 -0
  12. data/lib/resque_sqs/failure/redis.rb +108 -0
  13. data/lib/resque_sqs/failure/redis_multi_queue.rb +89 -0
  14. data/lib/resque_sqs/failure.rb +113 -0
  15. data/lib/resque_sqs/helpers.rb +107 -0
  16. data/lib/resque_sqs/job.rb +346 -0
  17. data/lib/resque_sqs/log_formatters/quiet_formatter.rb +7 -0
  18. data/lib/resque_sqs/log_formatters/verbose_formatter.rb +7 -0
  19. data/lib/resque_sqs/log_formatters/very_verbose_formatter.rb +8 -0
  20. data/lib/resque_sqs/logging.rb +18 -0
  21. data/lib/resque_sqs/plugin.rb +66 -0
  22. data/lib/resque_sqs/server/helpers.rb +52 -0
  23. data/lib/resque_sqs/server/public/favicon.ico +0 -0
  24. data/lib/resque_sqs/server/public/idle.png +0 -0
  25. data/lib/resque_sqs/server/public/jquery-1.3.2.min.js +19 -0
  26. data/lib/resque_sqs/server/public/jquery.relatize_date.js +95 -0
  27. data/lib/resque_sqs/server/public/poll.png +0 -0
  28. data/lib/resque_sqs/server/public/ranger.js +78 -0
  29. data/lib/resque_sqs/server/public/reset.css +44 -0
  30. data/lib/resque_sqs/server/public/style.css +91 -0
  31. data/lib/resque_sqs/server/public/working.png +0 -0
  32. data/lib/resque_sqs/server/test_helper.rb +19 -0
  33. data/lib/resque_sqs/server/views/error.erb +1 -0
  34. data/lib/resque_sqs/server/views/failed.erb +29 -0
  35. data/lib/resque_sqs/server/views/failed_job.erb +50 -0
  36. data/lib/resque_sqs/server/views/failed_queues_overview.erb +24 -0
  37. data/lib/resque_sqs/server/views/key_sets.erb +19 -0
  38. data/lib/resque_sqs/server/views/key_string.erb +11 -0
  39. data/lib/resque_sqs/server/views/layout.erb +44 -0
  40. data/lib/resque_sqs/server/views/next_more.erb +22 -0
  41. data/lib/resque_sqs/server/views/overview.erb +4 -0
  42. data/lib/resque_sqs/server/views/queues.erb +58 -0
  43. data/lib/resque_sqs/server/views/stats.erb +62 -0
  44. data/lib/resque_sqs/server/views/workers.erb +109 -0
  45. data/lib/resque_sqs/server/views/working.erb +72 -0
  46. data/lib/resque_sqs/server.rb +271 -0
  47. data/lib/resque_sqs/stat.rb +57 -0
  48. data/lib/resque_sqs/tasks.rb +83 -0
  49. data/lib/resque_sqs/vendor/utf8_util/utf8_util_18.rb +91 -0
  50. data/lib/resque_sqs/vendor/utf8_util/utf8_util_19.rb +5 -0
  51. data/lib/resque_sqs/vendor/utf8_util.rb +20 -0
  52. data/lib/resque_sqs/version.rb +3 -0
  53. data/lib/resque_sqs/worker.rb +779 -0
  54. data/lib/resque_sqs.rb +479 -0
  55. data/lib/tasks/redis_sqs.rake +161 -0
  56. data/lib/tasks/resque_sqs.rake +2 -0
  57. data/test/airbrake_test.rb +27 -0
  58. data/test/failure_base_test.rb +15 -0
  59. data/test/job_hooks_test.rb +465 -0
  60. data/test/job_plugins_test.rb +230 -0
  61. data/test/logging_test.rb +24 -0
  62. data/test/plugin_test.rb +116 -0
  63. data/test/redis-test-cluster.conf +115 -0
  64. data/test/redis-test.conf +115 -0
  65. data/test/resque-web_test.rb +59 -0
  66. data/test/resque_failure_redis_test.rb +19 -0
  67. data/test/resque_hook_test.rb +165 -0
  68. data/test/resque_test.rb +278 -0
  69. data/test/stdout +42 -0
  70. data/test/test_helper.rb +228 -0
  71. data/test/worker_test.rb +1080 -0
  72. metadata +202 -0
@@ -0,0 +1,107 @@
1
+ require 'multi_json'
2
+
3
+ # OkJson won't work because it doesn't serialize symbols
4
+ # in the same way yajl and json do.
5
+ if MultiJson.respond_to?(:adapter)
6
+ raise "Please install the yajl-ruby or json gem" if MultiJson.adapter.to_s == 'MultiJson::Adapters::OkJson'
7
+ elsif MultiJson.respond_to?(:engine)
8
+ raise "Please install the yajl-ruby or json gem" if MultiJson.engine.to_s == 'MultiJson::Engines::OkJson'
9
+ end
10
+
11
+ module ResqueSqs
12
+ # Methods used by various classes in ResqueSqs.
13
+ module Helpers
14
+ def self.extended(parent_class)
15
+ warn("ResqueSqs::Helpers will be gone with no replacement in Resque 2.0.0.")
16
+ end
17
+
18
+ def self.included(parent_class)
19
+ warn("ResqueSqs::Helpers will be gone with no replacement in Resque 2.0.0.")
20
+ end
21
+
22
+ class DecodeException < StandardError; end
23
+
24
+ # Direct access to the Redis instance.
25
+ def redis
26
+ # No infinite recursions, please.
27
+ # Some external libraries depend on ResqueSqs::Helpers being mixed into
28
+ # Resque, but this method causes recursions. If we have a super method,
29
+ # assume it is canonical. (see #1150)
30
+ return super if defined?(super)
31
+
32
+ ResqueSqs.redis
33
+ end
34
+
35
+ # Given a Ruby object, returns a string suitable for storage in a
36
+ # queue.
37
+ def encode(object)
38
+ if MultiJson.respond_to?(:dump) && MultiJson.respond_to?(:load)
39
+ MultiJson.dump object
40
+ else
41
+ MultiJson.encode object
42
+ end
43
+ end
44
+
45
+ # Given a string, returns a Ruby object.
46
+ def decode(object)
47
+ return unless object
48
+
49
+ begin
50
+ if MultiJson.respond_to?(:dump) && MultiJson.respond_to?(:load)
51
+ MultiJson.load object
52
+ else
53
+ MultiJson.decode object
54
+ end
55
+ rescue ::MultiJson::DecodeError => e
56
+ raise DecodeException, e.message, e.backtrace
57
+ end
58
+ end
59
+
60
+ # Given a word with dashes, returns a camel cased version of it.
61
+ #
62
+ # classify('job-name') # => 'JobName'
63
+ def classify(dashed_word)
64
+ dashed_word.split('-').each { |part| part[0] = part[0].chr.upcase }.join
65
+ end
66
+
67
+ # Tries to find a constant with the name specified in the argument string:
68
+ #
69
+ # constantize("Module") # => Module
70
+ # constantize("Test::Unit") # => Test::Unit
71
+ #
72
+ # The name is assumed to be the one of a top-level constant, no matter
73
+ # whether it starts with "::" or not. No lexical context is taken into
74
+ # account:
75
+ #
76
+ # C = 'outside'
77
+ # module M
78
+ # C = 'inside'
79
+ # C # => 'inside'
80
+ # constantize("C") # => 'outside', same as ::C
81
+ # end
82
+ #
83
+ # NameError is raised when the constant is unknown.
84
+ def constantize(camel_cased_word)
85
+ camel_cased_word = camel_cased_word.to_s
86
+
87
+ if camel_cased_word.include?('-')
88
+ camel_cased_word = classify(camel_cased_word)
89
+ end
90
+
91
+ names = camel_cased_word.split('::')
92
+ names.shift if names.empty? || names.first.empty?
93
+
94
+ constant = Object
95
+ names.each do |name|
96
+ args = Module.method(:const_get).arity != 1 ? [false] : []
97
+
98
+ if constant.const_defined?(name, *args)
99
+ constant = constant.const_get(name)
100
+ else
101
+ constant = constant.const_missing(name)
102
+ end
103
+ end
104
+ constant
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,346 @@
1
+ module ResqueSqs
2
+ # A ResqueSqs::Job represents a unit of work. Each job lives on a
3
+ # single queue and has an associated payload object. The payload
4
+ # is a hash with two attributes: `class` and `args`. The `class` is
5
+ # the name of the Ruby class which should be used to run the
6
+ # job. The `args` are an array of arguments which should be passed
7
+ # to the Ruby class's `perform` class-level method.
8
+ #
9
+ # You can manually run a job using this code:
10
+ #
11
+ # job = ResqueSqs::Job.reserve(:high)
12
+ # klass = ResqueSqs::Job.constantize(job.payload['class'])
13
+ # klass.perform(*job.payload['args'])
14
+ class Job
15
+ def redis
16
+ ResqueSqs.redis
17
+ end
18
+
19
+ def self.redis
20
+ ResqueSqs.redis
21
+ end
22
+
23
+ # Given a Ruby object, returns a string suitable for storage in a
24
+ # queue.
25
+ def encode(object)
26
+ if MultiJson.respond_to?(:dump) && MultiJson.respond_to?(:load)
27
+ MultiJson.dump object
28
+ else
29
+ MultiJson.encode object
30
+ end
31
+ end
32
+
33
+ # Given a string, returns a Ruby object.
34
+ def decode(object)
35
+ return unless object
36
+
37
+ begin
38
+ if MultiJson.respond_to?(:dump) && MultiJson.respond_to?(:load)
39
+ MultiJson.load object
40
+ else
41
+ MultiJson.decode object
42
+ end
43
+ rescue ::MultiJson::DecodeError => e
44
+ raise DecodeException, e.message, e.backtrace
45
+ end
46
+ end
47
+
48
+ # Given a Ruby object, returns a string suitable for storage in a
49
+ # queue.
50
+ def self.encode(object)
51
+ if MultiJson.respond_to?(:dump) && MultiJson.respond_to?(:load)
52
+ MultiJson.dump object
53
+ else
54
+ MultiJson.encode object
55
+ end
56
+ end
57
+
58
+ # Given a string, returns a Ruby object.
59
+ def self.decode(object)
60
+ return unless object
61
+
62
+ begin
63
+ if MultiJson.respond_to?(:dump) && MultiJson.respond_to?(:load)
64
+ MultiJson.load object
65
+ else
66
+ MultiJson.decode object
67
+ end
68
+ rescue ::MultiJson::DecodeError => e
69
+ raise DecodeException, e.message, e.backtrace
70
+ end
71
+ end
72
+
73
+ # Given a word with dashes, returns a camel cased version of it.
74
+ #
75
+ # classify('job-name') # => 'JobName'
76
+ def classify(dashed_word)
77
+ dashed_word.split('-').each { |part| part[0] = part[0].chr.upcase }.join
78
+ end
79
+
80
+ # Tries to find a constant with the name specified in the argument string:
81
+ #
82
+ # constantize("Module") # => Module
83
+ # constantize("Test::Unit") # => Test::Unit
84
+ #
85
+ # The name is assumed to be the one of a top-level constant, no matter
86
+ # whether it starts with "::" or not. No lexical context is taken into
87
+ # account:
88
+ #
89
+ # C = 'outside'
90
+ # module M
91
+ # C = 'inside'
92
+ # C # => 'inside'
93
+ # constantize("C") # => 'outside', same as ::C
94
+ # end
95
+ #
96
+ # NameError is raised when the constant is unknown.
97
+ def constantize(camel_cased_word)
98
+ camel_cased_word = camel_cased_word.to_s
99
+
100
+ if camel_cased_word.include?('-')
101
+ camel_cased_word = classify(camel_cased_word)
102
+ end
103
+
104
+ names = camel_cased_word.split('::')
105
+ names.shift if names.empty? || names.first.empty?
106
+
107
+ constant = Object
108
+ names.each do |name|
109
+ args = Module.method(:const_get).arity != 1 ? [false] : []
110
+
111
+ if constant.const_defined?(name, *args)
112
+ constant = constant.const_get(name)
113
+ else
114
+ constant = constant.const_missing(name)
115
+ end
116
+ end
117
+ constant
118
+ end
119
+
120
+ # Raise ResqueSqs::Job::DontPerform from a before_perform hook to
121
+ # abort the job.
122
+ DontPerform = Class.new(StandardError)
123
+
124
+ # The worker object which is currently processing this job.
125
+ attr_accessor :worker
126
+
127
+ # The name of the queue from which this job was pulled (or is to be
128
+ # placed)
129
+ attr_reader :queue
130
+
131
+ # This job's associated payload object.
132
+ attr_reader :payload
133
+
134
+ def initialize(queue, payload)
135
+ @queue = queue
136
+ @payload = payload
137
+ @failure_hooks_ran = false
138
+ end
139
+
140
+ # Creates a job by placing it on a queue. Expects a string queue
141
+ # name, a string class name, and an optional array of arguments to
142
+ # pass to the class' `perform` method.
143
+ #
144
+ # Raises an exception if no queue or class is given.
145
+ def self.create(queue, klass, *args)
146
+ ResqueSqs.validate(klass, queue)
147
+
148
+ if ResqueSqs.inline?
149
+ # Instantiating a ResqueSqs::Job and calling perform on it so callbacks run
150
+ # decode(encode(args)) to ensure that args are normalized in the same manner as a non-inline job
151
+ new(:inline, {'class' => klass, 'args' => decode(encode(args))}).perform
152
+ else
153
+ ResqueSqs.push(queue, :class => klass.to_s, :args => args)
154
+ end
155
+ end
156
+
157
+ # Removes a job from a queue. Expects a string queue name, a
158
+ # string class name, and, optionally, args.
159
+ #
160
+ # Returns the number of jobs destroyed.
161
+ #
162
+ # If no args are provided, it will remove all jobs of the class
163
+ # provided.
164
+ #
165
+ # That is, for these two jobs:
166
+ #
167
+ # { 'class' => 'UpdateGraph', 'args' => ['defunkt'] }
168
+ # { 'class' => 'UpdateGraph', 'args' => ['mojombo'] }
169
+ #
170
+ # The following call will remove both:
171
+ #
172
+ # ResqueSqs::Job.destroy(queue, 'UpdateGraph')
173
+ #
174
+ # Whereas specifying args will only remove the 2nd job:
175
+ #
176
+ # ResqueSqs::Job.destroy(queue, 'UpdateGraph', 'mojombo')
177
+ #
178
+ # This method can be potentially very slow and memory intensive,
179
+ # depending on the size of your queue, as it loads all jobs into
180
+ # a Ruby array before processing.
181
+ def self.destroy(queue, klass, *args)
182
+ klass = klass.to_s
183
+ queue = "queue:#{queue}"
184
+ destroyed = 0
185
+
186
+ if args.empty?
187
+ redis.lrange(queue, 0, -1).each do |string|
188
+ if decode(string)['class'] == klass
189
+ destroyed += redis.lrem(queue, 0, string).to_i
190
+ end
191
+ end
192
+ else
193
+ destroyed += redis.lrem(queue, 0, encode(:class => klass, :args => args))
194
+ end
195
+
196
+ destroyed
197
+ end
198
+
199
+ # Given a string queue name, returns an instance of ResqueSqs::Job
200
+ # if any jobs are available. If not, returns nil.
201
+ def self.reserve(queue)
202
+ return unless payload = ResqueSqs.pop(queue)
203
+ new(queue, payload)
204
+ end
205
+
206
+ # Attempts to perform the work represented by this job instance.
207
+ # Calls #perform on the class given in the payload with the
208
+ # arguments given in the payload.
209
+ def perform
210
+ job = payload_class
211
+ job_args = args || []
212
+ job_was_performed = false
213
+
214
+ begin
215
+ # Execute before_perform hook. Abort the job gracefully if
216
+ # ResqueSqs::DontPerform is raised.
217
+ begin
218
+ before_hooks.each do |hook|
219
+ job.send(hook, *job_args)
220
+ end
221
+ rescue DontPerform
222
+ return false
223
+ end
224
+
225
+ # Execute the job. Do it in an around_perform hook if available.
226
+ if around_hooks.empty?
227
+ job.perform(*job_args)
228
+ job_was_performed = true
229
+ else
230
+ # We want to nest all around_perform plugins, with the last one
231
+ # finally calling perform
232
+ stack = around_hooks.reverse.inject(nil) do |last_hook, hook|
233
+ if last_hook
234
+ lambda do
235
+ job.send(hook, *job_args) { last_hook.call }
236
+ end
237
+ else
238
+ lambda do
239
+ job.send(hook, *job_args) do
240
+ result = job.perform(*job_args)
241
+ job_was_performed = true
242
+ result
243
+ end
244
+ end
245
+ end
246
+ end
247
+ stack.call
248
+ end
249
+
250
+ # Execute after_perform hook
251
+ after_hooks.each do |hook|
252
+ job.send(hook, *job_args)
253
+ end
254
+
255
+ # Return true if the job was performed
256
+ return job_was_performed
257
+
258
+ # If an exception occurs during the job execution, look for an
259
+ # on_failure hook then re-raise.
260
+ rescue Object => e
261
+ run_failure_hooks(e)
262
+ raise e
263
+ end
264
+ end
265
+
266
+ # Returns the actual class constant represented in this job's payload.
267
+ def payload_class
268
+ @payload_class ||= constantize(@payload['class'])
269
+ end
270
+
271
+ # Returns the payload class as a string without raising NameError
272
+ def payload_class_name
273
+ payload_class.to_s
274
+ rescue NameError
275
+ 'No Name'
276
+ end
277
+
278
+ def has_payload_class?
279
+ payload_class != Object
280
+ rescue NameError
281
+ false
282
+ end
283
+
284
+ # Returns an array of args represented in this job's payload.
285
+ def args
286
+ @payload['args']
287
+ end
288
+
289
+ # Given an exception object, hands off the needed parameters to
290
+ # the Failure module.
291
+ def fail(exception)
292
+ run_failure_hooks(exception)
293
+ Failure.create \
294
+ :payload => payload,
295
+ :exception => exception,
296
+ :worker => worker,
297
+ :queue => queue
298
+ end
299
+
300
+ # Creates an identical job, essentially placing this job back on
301
+ # the queue.
302
+ def recreate
303
+ self.class.create(queue, payload_class, *args)
304
+ end
305
+
306
+ # String representation
307
+ def inspect
308
+ obj = @payload
309
+ "(Job{%s} | %s | %s)" % [ @queue, obj['class'], obj['args'].inspect ]
310
+ end
311
+
312
+ # Equality
313
+ def ==(other)
314
+ queue == other.queue &&
315
+ payload_class == other.payload_class &&
316
+ args == other.args
317
+ end
318
+
319
+ def before_hooks
320
+ @before_hooks ||= Plugin.before_hooks(payload_class)
321
+ end
322
+
323
+ def around_hooks
324
+ @around_hooks ||= Plugin.around_hooks(payload_class)
325
+ end
326
+
327
+ def after_hooks
328
+ @after_hooks ||= Plugin.after_hooks(payload_class)
329
+ end
330
+
331
+ def failure_hooks
332
+ @failure_hooks ||= Plugin.failure_hooks(payload_class)
333
+ end
334
+
335
+ def run_failure_hooks(exception)
336
+ begin
337
+ job_args = args || []
338
+ if has_payload_class?
339
+ failure_hooks.each { |hook| payload_class.send(hook, exception, *job_args) } unless @failure_hooks_ran
340
+ end
341
+ ensure
342
+ @failure_hooks_ran = true
343
+ end
344
+ end
345
+ end
346
+ end
@@ -0,0 +1,7 @@
1
+ module ResqueSqs
2
+ class QuietFormatter
3
+ def call(serverity, datetime, progname, msg)
4
+ ""
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module ResqueSqs
2
+ class VerboseFormatter
3
+ def call(serverity, datetime, progname, msg)
4
+ "*** #{msg}\n"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ module ResqueSqs
2
+ class VeryVerboseFormatter
3
+ def call(serverity, datetime, progname, msg)
4
+ time = Time.now.strftime('%H:%M:%S %Y-%m-%d')
5
+ "** [#{time}] #$$: #{msg}\n"
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,18 @@
1
+ module ResqueSqs
2
+ # Include this module in classes you wish to have logging facilities
3
+ module Logging
4
+ module_function
5
+
6
+ # Thunk to the logger's own log method (if configured)
7
+ def self.log(severity, message)
8
+ ResqueSqs.logger.__send__(severity, message) if ResqueSqs.logger
9
+ end
10
+
11
+ # Log level aliases
12
+ def debug(message); Logging.log :debug, message; end
13
+ def info(message); Logging.log :info, message; end
14
+ def warn(message); Logging.log :warn, message; end
15
+ def error(message); Logging.log :error, message; end
16
+ def fatal(message); Logging.log :fatal, message; end
17
+ end
18
+ end
@@ -0,0 +1,66 @@
1
+ module ResqueSqs
2
+ module Plugin
3
+ extend self
4
+
5
+ LintError = Class.new(RuntimeError)
6
+
7
+ # Ensure that your plugin conforms to good hook naming conventions.
8
+ #
9
+ # ResqueSqs::Plugin.lint(MyResquePlugin)
10
+ def lint(plugin)
11
+ hooks = before_hooks(plugin) + around_hooks(plugin) + after_hooks(plugin)
12
+
13
+ hooks.each do |hook|
14
+ if hook =~ /perform$/
15
+ raise LintError, "#{plugin}.#{hook} is not namespaced"
16
+ end
17
+ end
18
+
19
+ failure_hooks(plugin).each do |hook|
20
+ if hook =~ /failure$/
21
+ raise LintError, "#{plugin}.#{hook} is not namespaced"
22
+ end
23
+ end
24
+ end
25
+
26
+ # Given an object, returns a list `before_perform` hook names.
27
+ def before_hooks(job)
28
+ job.methods.grep(/^before_perform/).sort
29
+ end
30
+
31
+ # Given an object, returns a list `around_perform` hook names.
32
+ def around_hooks(job)
33
+ job.methods.grep(/^around_perform/).sort
34
+ end
35
+
36
+ # Given an object, returns a list `after_perform` hook names.
37
+ def after_hooks(job)
38
+ job.methods.grep(/^after_perform/).sort
39
+ end
40
+
41
+ # Given an object, returns a list `on_failure` hook names.
42
+ def failure_hooks(job)
43
+ job.methods.grep(/^on_failure/).sort
44
+ end
45
+
46
+ # Given an object, returns a list `after_enqueue` hook names.
47
+ def after_enqueue_hooks(job)
48
+ job.methods.grep(/^after_enqueue/).sort
49
+ end
50
+
51
+ # Given an object, returns a list `before_enqueue` hook names.
52
+ def before_enqueue_hooks(job)
53
+ job.methods.grep(/^before_enqueue/).sort
54
+ end
55
+
56
+ # Given an object, returns a list `after_dequeue` hook names.
57
+ def after_dequeue_hooks(job)
58
+ job.methods.grep(/^after_dequeue/).sort
59
+ end
60
+
61
+ # Given an object, returns a list `before_dequeue` hook names.
62
+ def before_dequeue_hooks(job)
63
+ job.methods.grep(/^before_dequeue/).sort
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,52 @@
1
+ ResqueSqs::Server.helpers do
2
+ ####################
3
+ #failed.erb helpers#
4
+ ####################
5
+
6
+ def failed_date_format
7
+ "%Y/%m/%d %T %z"
8
+ end
9
+
10
+ def failed_multiple_queues?
11
+ return @multiple_failed_queues if defined?(@multiple_failed_queues)
12
+ @multiple_failed_queues = ResqueSqs::Failure.queues.size > 1
13
+ end
14
+
15
+ def failed_size
16
+ @failed_size ||= ResqueSqs::Failure.count(params[:queue], params[:class])
17
+ end
18
+
19
+ def failed_per_page
20
+ @failed_per_page = if params[:class]
21
+ failed_size
22
+ else
23
+ 20
24
+ end
25
+ end
26
+
27
+ def failed_start_at
28
+ params[:start].to_i
29
+ end
30
+
31
+ def failed_end_at
32
+ if failed_start_at + failed_per_page > failed_size
33
+ failed_size
34
+ else
35
+ failed_start_at + failed_per_page
36
+ end
37
+ end
38
+
39
+ def failed_order
40
+ params[:order] || 'desc'
41
+ end
42
+
43
+ def failed_class_counts(queue = params[:queue])
44
+ classes = Hash.new(0)
45
+ ResqueSqs::Failure.each(0, ResqueSqs::Failure.count(queue), queue) do |_, item|
46
+ class_name = item['payload']['class'] if item['payload']
47
+ class_name ||= "nil"
48
+ classes[class_name] += 1
49
+ end
50
+ classes
51
+ end
52
+ end