jerefrer-resque 1.1.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 (58) hide show
  1. data/.kick +26 -0
  2. data/HISTORY.md +10 -0
  3. data/LICENSE +20 -0
  4. data/README.markdown +703 -0
  5. data/Rakefile +61 -0
  6. data/bin/resque +69 -0
  7. data/bin/resque-web +47 -0
  8. data/config.ru +8 -0
  9. data/deps.rip +5 -0
  10. data/examples/async_helper.rb +31 -0
  11. data/examples/demo/README.markdown +71 -0
  12. data/examples/demo/Rakefile +3 -0
  13. data/examples/demo/app.rb +27 -0
  14. data/examples/demo/config.ru +19 -0
  15. data/examples/demo/job.rb +12 -0
  16. data/examples/god/resque.god +52 -0
  17. data/examples/god/stale.god +26 -0
  18. data/examples/instance.rb +11 -0
  19. data/examples/simple.rb +30 -0
  20. data/init.rb +1 -0
  21. data/lib/resque/errors.rb +7 -0
  22. data/lib/resque/failure/base.rb +58 -0
  23. data/lib/resque/failure/hoptoad.rb +88 -0
  24. data/lib/resque/failure/redis.rb +33 -0
  25. data/lib/resque/failure.rb +63 -0
  26. data/lib/resque/helpers.rb +57 -0
  27. data/lib/resque/job.rb +91 -0
  28. data/lib/resque/server/public/idle.png +0 -0
  29. data/lib/resque/server/public/jquery-1.3.2.min.js +19 -0
  30. data/lib/resque/server/public/jquery.relatize_date.js +95 -0
  31. data/lib/resque/server/public/poll.png +0 -0
  32. data/lib/resque/server/public/ranger.js +21 -0
  33. data/lib/resque/server/public/reset.css +48 -0
  34. data/lib/resque/server/public/style.css +75 -0
  35. data/lib/resque/server/public/working.png +0 -0
  36. data/lib/resque/server/views/error.erb +1 -0
  37. data/lib/resque/server/views/failed.erb +35 -0
  38. data/lib/resque/server/views/key.erb +17 -0
  39. data/lib/resque/server/views/layout.erb +41 -0
  40. data/lib/resque/server/views/next_more.erb +10 -0
  41. data/lib/resque/server/views/overview.erb +4 -0
  42. data/lib/resque/server/views/queues.erb +46 -0
  43. data/lib/resque/server/views/stats.erb +62 -0
  44. data/lib/resque/server/views/workers.erb +78 -0
  45. data/lib/resque/server/views/working.erb +67 -0
  46. data/lib/resque/server.rb +174 -0
  47. data/lib/resque/stat.rb +53 -0
  48. data/lib/resque/tasks.rb +24 -0
  49. data/lib/resque/version.rb +3 -0
  50. data/lib/resque/worker.rb +406 -0
  51. data/lib/resque.rb +184 -0
  52. data/tasks/redis.rake +125 -0
  53. data/tasks/resque.rake +2 -0
  54. data/test/redis-test.conf +132 -0
  55. data/test/resque_test.rb +160 -0
  56. data/test/test_helper.rb +90 -0
  57. data/test/worker_test.rb +220 -0
  58. metadata +121 -0
data/Rakefile ADDED
@@ -0,0 +1,61 @@
1
+ eval File.read('tasks/redis.rake')
2
+
3
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/lib'
4
+ require 'resque/tasks'
5
+
6
+ task :default => :test
7
+
8
+ desc "Run tests"
9
+ task :test do
10
+ # Don't use the rake/testtask because it loads a new
11
+ # Ruby interpreter - we want to run tests with the current
12
+ # `rake` so our library manager still works
13
+ Dir['test/*_test.rb'].each do |f|
14
+ require f
15
+ end
16
+ end
17
+
18
+ desc "Activate kicker - gem install kicker"
19
+ task :kick do
20
+ exec "kicker -e rake lib test"
21
+ end
22
+
23
+ task :install => [ 'redis:install', 'dtach:install' ]
24
+
25
+ desc "Build a gem"
26
+ task :gem => [ :test, :gemspec, :build ]
27
+
28
+ begin
29
+ require 'jeweler'
30
+ $LOAD_PATH.unshift 'lib'
31
+ require 'resque/version'
32
+
33
+ Jeweler::Tasks.new do |gemspec|
34
+ gemspec.name = "jerefrer-resque"
35
+ gemspec.summary = ""
36
+ gemspec.description = ""
37
+ gemspec.email = "frere.jeremy@gmail.com"
38
+ gemspec.homepage = "http://github.com/jerefrer/resque"
39
+ gemspec.authors = ["Chris Wanstrath", "Jérémy Frere"]
40
+ gemspec.version = Resque::Version
41
+ end
42
+ rescue LoadError
43
+ puts "Jeweler not available. Install it with: "
44
+ puts "gem install jeweler"
45
+ end
46
+
47
+ begin
48
+ require 'sdoc_helpers'
49
+ rescue LoadError
50
+ puts "sdoc support not enabled. Please gem install sdoc-helpers."
51
+ end
52
+
53
+ desc "Push a new version to Gemcutter"
54
+ task :publish => [ :test, :gemspec, :build ] do
55
+ system "git tag v#{Resque::Version}"
56
+ system "git push origin v#{Resque::Version}"
57
+ system "git push origin master"
58
+ system "gem push pkg/resque-#{Resque::Version}.gem"
59
+ system "git clean -fd"
60
+ exec "rake pages"
61
+ end
data/bin/resque ADDED
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
4
+ require 'resque'
5
+
6
+ def kill(worker)
7
+ abort "** resque kill WORKER_ID" if worker.nil?
8
+ pid = worker.split(':')[1].to_i
9
+
10
+ begin
11
+ Process.kill("KILL", pid)
12
+ puts "** killed #{worker}"
13
+ rescue Errno::ESRCH
14
+ puts "** worker #{worker} not running"
15
+ end
16
+
17
+ remove worker
18
+ end
19
+
20
+ def killall
21
+ abort "** resque killall " if Resque.workers.empty?
22
+ Resque.workers.each do |worker|
23
+ pid = worker.to_s.split(':')[1].to_i
24
+ Process.kill("KILL", pid)
25
+ puts "** killed #{worker}"
26
+ end
27
+ end
28
+
29
+ def remove(worker)
30
+ abort "** resque remove WORKER_ID" if worker.nil?
31
+
32
+ Resque.remove_worker(worker)
33
+ puts "** removed #{worker}"
34
+ end
35
+
36
+ def list
37
+ if Resque.workers.any?
38
+ Resque.workers.each do |worker|
39
+ puts "#{worker} (#{worker.state})"
40
+ end
41
+ else
42
+ puts "None"
43
+ end
44
+ end
45
+
46
+ if (i = ARGV.index('-r')) && ARGV[i+1]
47
+ Resque.redis = ARGV[i+1]
48
+ ARGV.delete_at(i)
49
+ ARGV.delete_at(i+1)
50
+ end
51
+
52
+ case ARGV[0]
53
+ when 'kill'
54
+ kill ARGV[1]
55
+ when 'killall'
56
+ killall
57
+ when 'remove'
58
+ remove ARGV[1]
59
+ when 'list'
60
+ list
61
+ else
62
+ puts "Usage: resque [-r redis_host:redis_port] COMMAND [option]"
63
+ puts
64
+ puts "Commands:"
65
+ puts " remove WORKER Removes a worker"
66
+ puts " kill WORKER Kills a worker"
67
+ puts " killall Kill all workers"
68
+ puts " list Lists known workers"
69
+ end
data/bin/resque-web ADDED
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ __DIR__ = File.expand_path(File.dirname(__FILE__))
4
+
5
+ if `which rackup`.empty?
6
+ abort "** Can't find `rackup` in PATH."
7
+ end
8
+
9
+ if ARGV.include?('-h')
10
+ puts <<-usage
11
+ Usage: resque-web [ruby options] [rack options] [resque config]
12
+
13
+ Starts a Resque front-end by way of `rackup`.
14
+
15
+ Ruby options:
16
+ -e, --eval LINE evaluate a LINE of code
17
+ -d, --debug set debugging flags (set $DEBUG to true)
18
+ -w, --warn turn warnings on for your script
19
+ -I, --include PATH specify $LOAD_PATH (may be used more than once)
20
+ -r, --require LIBRARY require the library, before executing your script
21
+ Rack options:
22
+ -s, --server SERVER serve using SERVER (webrick/mongrel)
23
+ -o, --host HOST listen on HOST (default: 0.0.0.0)
24
+ -p, --port PORT use PORT (default: 9292)
25
+ -E, --env ENVIRONMENT use ENVIRONMENT for defaults (default: development)
26
+ -D, --daemonize run daemonized in the background
27
+ -P, --pid FILE file to store PID (default: rack.pid)
28
+ Common options:
29
+ -h, --help Show this message
30
+ --version Show version
31
+ usage
32
+ else
33
+ if !ENV['CONFIG']&&ARGV[-1]&&ARGV[-1][0]!=?-&&(ARGV[-2]?ARGV[-2][0]!=?-:true)
34
+ if File.file?(file = File.expand_path(ARGV[-1]))
35
+ ARGV.delete_at(-1)
36
+ ENV['CONFIG'] = file
37
+ else
38
+ abort "** Can't find #{file}"
39
+ end
40
+ end
41
+
42
+ args = ARGV
43
+ ENV['RUBYLIB'] = ENV['RUBYLIB'].to_s + ':' + __DIR__ + '/../lib'
44
+ args.unshift '-e', 'require "resque";load ENV["CONFIG"] if ENV["CONFIG"]'
45
+ args.push File.expand_path(File.dirname(__FILE__) + "/../config.ru")
46
+ exec "rackup", *args
47
+ end
data/config.ru ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ require 'logger'
3
+
4
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/lib')
5
+ require 'resque/server'
6
+
7
+ use Rack::ShowExceptions
8
+ run Resque::Server.new
data/deps.rip ADDED
@@ -0,0 +1,5 @@
1
+ git://github.com/ezmobius/redis-rb.git eed200ad
2
+ git://github.com/brianmario/yajl-ruby.git 0.6.3
3
+ git://github.com/sinatra/sinatra.git 0.9.4
4
+ git://github.com/rack/rack.git 1.0
5
+ rake
@@ -0,0 +1,31 @@
1
+ # If you want to just call a method on an object in the background,
2
+ # we can easily add that functionality to Resque.
3
+ #
4
+ # This is similar to DelayedJob's `send_later`.
5
+ #
6
+ # Keep in mind that, unlike DelayedJob, only simple Ruby objects
7
+ # can be persisted.
8
+ #
9
+ # If it can be represented in JSON, it can be stored in a job.
10
+
11
+ # Here's our ActiveRecord class
12
+ class Repository < ActiveRecord::Base
13
+ # This will be called by a worker when a job needs to be processed
14
+ def self.perform(id, method, *args)
15
+ find(id).send(method, *args)
16
+ end
17
+
18
+ # We can pass this any Repository instance method that we want to
19
+ # run later.
20
+ def async(method, *args)
21
+ Resque.enqueue(Repository, id, method, *args)
22
+ end
23
+ end
24
+
25
+ # Now we can call any method and have it execute later:
26
+
27
+ @repo.async(:update_disk_usage)
28
+
29
+ # or
30
+
31
+ @repo.async(:update_network_source_id, 34)
@@ -0,0 +1,71 @@
1
+ Resque Demo
2
+ -----------
3
+
4
+ This is a dirt simple Resque setup for you to play with.
5
+
6
+
7
+ ### Starting the Demo App
8
+
9
+ Here's how to run the Sinatra app:
10
+
11
+ $ git clone git://github.com/defunkt/resque.git
12
+ $ cd resque/examples/demo
13
+ $ rackup config.ru
14
+ $ open http://localhost:9292/
15
+
16
+ Click 'Create New Job' a few times. You should see the number of
17
+ pending jobs rising.
18
+
19
+
20
+ ### Starting the Demo Worker
21
+
22
+ Now in another shell terminal start the worker:
23
+
24
+ $ cd resque/examples/demo
25
+ $ VERBOSE=true QUEUE=default rake resque:work
26
+
27
+ You should see the following output:
28
+
29
+ *** Starting worker hostname:90185:default
30
+ *** got: (Job{default} | Demo::Job | [{}])
31
+ Processed a job!
32
+ *** done: (Job{default} | Demo::Job | [{}])
33
+
34
+ You can also use `VVERBOSE` (very verbose) if you want to see more:
35
+
36
+ $ VERBOSE=true QUEUE=default rake resque:work
37
+ *** Starting worker hostname:90399:default
38
+ ** [05:55:09 2009-09-16] 90399: Registered signals
39
+ ** [05:55:09 2009-09-16] 90399: Checking default
40
+ ** [05:55:09 2009-09-16] 90399: Found job on default
41
+ ** [05:55:09 2009-09-16] 90399: got: (Job{default} | Demo::Job | [{}])
42
+ ** [05:55:09 2009-09-16] 90399: resque: Forked 90401 at 1253141709
43
+ ** [05:55:09 2009-09-16] 90401: resque: Processing default since 1253141709
44
+ Processed a job!
45
+ ** [05:55:10 2009-09-16] 90401: done: (Job{default} | Demo::Job | [{}])
46
+
47
+ Notice that our workers `require 'job'` in our `Rakefile`. This
48
+ ensures they have our app loaded and can access the job classes.
49
+
50
+
51
+ ### Starting the Resque frontend
52
+
53
+ Great, now let's check out the Resque frontend. Either click on 'View
54
+ Resque' in your web browser or run:
55
+
56
+ $ open http://localhost:9292/resque/
57
+
58
+ You should see the Resque web frontend. 404 page? Don't forget the
59
+ trailing slash!
60
+
61
+
62
+ ### config.ru
63
+
64
+ The `config.ru` shows you how to mount multiple Rack apps. Resque
65
+ should work fine on a subpath - feel free to load it up in your
66
+ Passenger app and protect it with some basic auth.
67
+
68
+
69
+ ### That's it!
70
+
71
+ Click around, add some more queues, add more jobs, do whatever, have fun.
@@ -0,0 +1,3 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../../lib'
2
+ require 'resque/tasks'
3
+ require 'job'
@@ -0,0 +1,27 @@
1
+ require 'sinatra/base'
2
+ require 'resque'
3
+ require 'job'
4
+
5
+ module Demo
6
+ class App < Sinatra::Base
7
+ get '/' do
8
+ info = Resque.info
9
+ out = "<html><head><title>Resque Demo</title></head><body>"
10
+ out << "<p>"
11
+ out << "There are #{info[:pending]} pending and "
12
+ out << "#{info[:processed]} processed jobs across #{info[:queues]} queues."
13
+ out << "</p>"
14
+ out << '<form method="POST">'
15
+ out << '<input type="submit" value="Create New Job"/>'
16
+ out << '&nbsp;&nbsp;<a href="/resque/">View Resque</a>'
17
+ out << '</form>'
18
+ out << "</body></html>"
19
+ out
20
+ end
21
+
22
+ post '/' do
23
+ Resque.enqueue(Job, params)
24
+ redirect "/"
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+ require 'logger'
3
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../../lib'
4
+ require 'app'
5
+ require 'resque/server'
6
+
7
+ use Rack::ShowExceptions
8
+
9
+ # Set the AUTH env variable to your basic auth password to protect Resque.
10
+ AUTH_PASSWORD = ENV['AUTH']
11
+ if AUTH_PASSWORD
12
+ Resque::Server.use Rack::Auth::Basic do |username, password|
13
+ password == AUTH_PASSWORD
14
+ end
15
+ end
16
+
17
+ run Rack::URLMap.new \
18
+ "/" => Demo::App.new,
19
+ "/resque" => Resque::Server.new
@@ -0,0 +1,12 @@
1
+ require 'resque'
2
+
3
+ module Demo
4
+ module Job
5
+ @queue = :default
6
+
7
+ def self.perform(params)
8
+ sleep 1
9
+ puts "Processed a job!"
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,52 @@
1
+ rails_env = ENV['RAILS_ENV'] || "production"
2
+ rails_root = ENV['RAILS_ROOT'] || "/data/github/current"
3
+ num_workers = rails_env == 'production' ? 5 : 2
4
+
5
+ num_workers.times do |num|
6
+ God.watch do |w|
7
+ w.name = "resque-#{num}"
8
+ w.group = 'resque'
9
+ w.interval = 30.seconds
10
+ w.start = "env QUEUE=critical,high,low /usr/bin/rake -f #{rails_root}/Rakefile #{rails_env} resque:work"
11
+
12
+ w.uid = 'git'
13
+ w.gid = 'git'
14
+
15
+ # retart if memory gets too high
16
+ w.transition(:up, :restart) do |on|
17
+ on.condition(:memory_usage) do |c|
18
+ c.above = 350.megabytes
19
+ c.times = 2
20
+ end
21
+ end
22
+
23
+ # determine the state on startup
24
+ w.transition(:init, { true => :up, false => :start }) do |on|
25
+ on.condition(:process_running) do |c|
26
+ c.running = true
27
+ end
28
+ end
29
+
30
+ # determine when process has finished starting
31
+ w.transition([:start, :restart], :up) do |on|
32
+ on.condition(:process_running) do |c|
33
+ c.running = true
34
+ c.interval = 5.seconds
35
+ end
36
+
37
+ # failsafe
38
+ on.condition(:tries) do |c|
39
+ c.times = 5
40
+ c.transition = :start
41
+ c.interval = 5.seconds
42
+ end
43
+ end
44
+
45
+ # start if process is not running
46
+ w.transition(:up, :start) do |on|
47
+ on.condition(:process_running) do |c|
48
+ c.running = false
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,26 @@
1
+ # This will ride alongside god and kill any rogue stale worker
2
+ # processes. Their sacrifice is for the greater good.
3
+
4
+ WORKER_TIMEOUT = 60 * 10 # 10 minutes
5
+
6
+ Thread.new do
7
+ loop do
8
+ begin
9
+ `ps -e -o pid,command | grep [r]esque`.split("\n").each do |line|
10
+ parts = line.split(' ')
11
+ next if parts[-2] != "at"
12
+ started = parts[-1].to_i
13
+ elapsed = Time.now - Time.at(started)
14
+
15
+ if elapsed >= WORKER_TIMEOUT
16
+ ::Process.kill('USR1', parts[0].to_i)
17
+ end
18
+ end
19
+ rescue
20
+ # don't die because of stupid exceptions
21
+ nil
22
+ end
23
+
24
+ sleep 30
25
+ end
26
+ end
@@ -0,0 +1,11 @@
1
+ # DelayedJob wants you to create instances. No problem.
2
+
3
+ class Archive < Struct.new(:repo_id, :branch)
4
+ def self.perform(*args)
5
+ new(*args).perform
6
+ end
7
+
8
+ def perform
9
+ # do work!
10
+ end
11
+ end
@@ -0,0 +1,30 @@
1
+ # This is a simple Resque job.
2
+ class Archive
3
+ @queue = :file_serve
4
+
5
+ def self.perform(repo_id, branch = 'master')
6
+ repo = Repository.find(repo_id)
7
+ repo.create_archive(branch)
8
+ end
9
+ end
10
+
11
+ # This is in our app code
12
+ class Repository < Model
13
+ # ... stuff ...
14
+
15
+ def async_create_archive(branch)
16
+ Resque.enqueue(Archive, self.id, branch)
17
+ end
18
+
19
+ # ... more stuff ...
20
+ end
21
+
22
+ # Calling this code:
23
+ repo = Repository.find(22)
24
+ repo.async_create_archive('homebrew')
25
+
26
+ # Will return immediately and create a Resque job which is later
27
+ # processed.
28
+
29
+ # Essentially, this code is run by the worker when processing:
30
+ Archive.perform(22, 'homebrew')
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'resque'
@@ -0,0 +1,7 @@
1
+ module Resque
2
+ # Raised whenever we need a queue but none is provided.
3
+ class NoQueueError < RuntimeError; end
4
+
5
+ # Raised when trying to create a job without a class
6
+ class NoClassError < RuntimeError; end
7
+ end
@@ -0,0 +1,58 @@
1
+ module Resque
2
+ module Failure
3
+ # All Failure classes are expected to subclass Base.
4
+ #
5
+ # When a job fails, a new instance of your Failure backend is created
6
+ # and #save is called.
7
+ class Base
8
+ # The exception object raised by the failed job
9
+ attr_accessor :exception
10
+
11
+ # The worker object who detected the failure
12
+ attr_accessor :worker
13
+
14
+ # The string name of the queue from which the failed job was pulled
15
+ attr_accessor :queue
16
+
17
+ # The payload object associated with the failed job
18
+ attr_accessor :payload
19
+
20
+ def initialize(exception, worker, queue, payload)
21
+ @exception = exception
22
+ @worker = worker
23
+ @queue = queue
24
+ @payload = payload
25
+ end
26
+
27
+ # When a job fails, a new instance of your Failure backend is created
28
+ # and #save is called.
29
+ #
30
+ # This is where you POST or PUT or whatever to your Failure service.
31
+ def save
32
+ end
33
+
34
+ # The number of failures.
35
+ def self.count
36
+ 0
37
+ end
38
+
39
+ # Returns a paginated array of failure objects.
40
+ def self.all(start = 0, count = 1)
41
+ []
42
+ end
43
+
44
+ # A URL where someone can go to view failures.
45
+ def self.url
46
+ end
47
+
48
+ # Clear all failure objects
49
+ def self.clear
50
+ end
51
+
52
+ # Logging!
53
+ def log(message)
54
+ @worker.log(message)
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,88 @@
1
+ require 'net/http'
2
+
3
+ module Resque
4
+ module Failure
5
+ # A Failure backend that sends exceptions raised by jobs to Hoptoad.
6
+ #
7
+ # To use it, put this code in an initializer, Rake task, or wherever:
8
+ #
9
+ # Resque::Failure::Hoptoad.configure do |config|
10
+ # config.api_key = 'blah'
11
+ # config.secure = true
12
+ # config.subdomain = 'your_hoptoad_subdomain'
13
+ # end
14
+ class Hoptoad < Base
15
+ class << self
16
+ attr_accessor :secure, :api_key, :subdomain
17
+ end
18
+
19
+ def self.url
20
+ "http://#{subdomain}.hoptoadapp.com/" if subdomain
21
+ end
22
+
23
+ def self.count
24
+ # We can't get the total # of errors from Hoptoad so we fake it
25
+ # by asking Resque how many errors it has seen.
26
+ Stat[:failed]
27
+ end
28
+
29
+ def self.configure
30
+ yield self
31
+ Resque::Failure.backend = self
32
+ end
33
+
34
+ def save
35
+ data = {
36
+ :api_key => api_key,
37
+ :error_class => exception.class.name,
38
+ :error_message => "#{exception.class.name}: #{exception.message}",
39
+ :backtrace => exception.backtrace,
40
+ :environment => {},
41
+ :session => {},
42
+ :request => {
43
+ :params => payload.merge(:worker => worker.to_s, :queue => queue.to_s)
44
+ }
45
+ }
46
+
47
+ send_to_hoptoad(:notice => data)
48
+ end
49
+
50
+ def send_to_hoptoad(data)
51
+ http = use_ssl? ? :https : :http
52
+ url = URI.parse("#{http}://hoptoadapp.com/notices/")
53
+
54
+ http = Net::HTTP.new(url.host, url.port)
55
+ headers = {
56
+ 'Content-type' => 'application/json',
57
+ 'Accept' => 'text/xml, application/xml'
58
+ }
59
+
60
+ http.read_timeout = 5 # seconds
61
+ http.open_timeout = 2 # seconds
62
+ http.use_ssl = use_ssl?
63
+
64
+ begin
65
+ response = http.post(url.path, Resque.encode(data), headers)
66
+ rescue TimeoutError => e
67
+ log "Timeout while contacting the Hoptoad server."
68
+ end
69
+
70
+ case response
71
+ when Net::HTTPSuccess then
72
+ log "Hoptoad Success: #{response.class}"
73
+ else
74
+ body = response.body if response.respond_to? :body
75
+ log "Hoptoad Failure: #{response.class}\n#{body}"
76
+ end
77
+ end
78
+
79
+ def use_ssl?
80
+ self.class.secure
81
+ end
82
+
83
+ def api_key
84
+ self.class.api_key
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,33 @@
1
+ module Resque
2
+ module Failure
3
+ # A Failure backend that stores exceptions in Redis. Very simple but
4
+ # works out of the box, along with support in the Resque web app.
5
+ class Redis < Base
6
+ def save
7
+ data = {
8
+ :failed_at => Time.now.to_s,
9
+ :payload => payload,
10
+ :error => exception.to_s,
11
+ :backtrace => exception.backtrace,
12
+ :worker => worker.to_s,
13
+ :queue => queue
14
+ }
15
+ data = Resque.encode(data)
16
+ Resque.redis.rpush(:failed, data)
17
+ end
18
+
19
+ def self.count
20
+ Resque.redis.llen(:failed).to_i
21
+ end
22
+
23
+ def self.all(start = 0, count = 1)
24
+ Resque.list_range(:failed, start, count)
25
+ end
26
+
27
+ def self.clear
28
+ Resque.redis.delete('resque:failed')
29
+ end
30
+
31
+ end
32
+ end
33
+ end