scotttam-resque 0.0.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 (61) hide show
  1. data/.gitignore +3 -0
  2. data/.kick +26 -0
  3. data/CONTRIBUTORS +23 -0
  4. data/HISTORY.md +80 -0
  5. data/LICENSE +20 -0
  6. data/README.markdown +767 -0
  7. data/Rakefile +66 -0
  8. data/bin/resque +57 -0
  9. data/bin/resque-web +18 -0
  10. data/config.ru +14 -0
  11. data/deps.rip +6 -0
  12. data/examples/async_helper.rb +31 -0
  13. data/examples/demo/README.markdown +71 -0
  14. data/examples/demo/Rakefile +3 -0
  15. data/examples/demo/app.rb +38 -0
  16. data/examples/demo/config.ru +19 -0
  17. data/examples/demo/job.rb +22 -0
  18. data/examples/god/resque.god +53 -0
  19. data/examples/god/stale.god +26 -0
  20. data/examples/instance.rb +11 -0
  21. data/examples/simple.rb +30 -0
  22. data/init.rb +1 -0
  23. data/lib/resque/errors.rb +7 -0
  24. data/lib/resque/failure/base.rb +58 -0
  25. data/lib/resque/failure/hoptoad.rb +121 -0
  26. data/lib/resque/failure/multiple.rb +44 -0
  27. data/lib/resque/failure/redis.rb +33 -0
  28. data/lib/resque/failure.rb +63 -0
  29. data/lib/resque/helpers.rb +57 -0
  30. data/lib/resque/job.rb +146 -0
  31. data/lib/resque/server/public/idle.png +0 -0
  32. data/lib/resque/server/public/jquery-1.3.2.min.js +19 -0
  33. data/lib/resque/server/public/jquery.relatize_date.js +95 -0
  34. data/lib/resque/server/public/poll.png +0 -0
  35. data/lib/resque/server/public/ranger.js +24 -0
  36. data/lib/resque/server/public/reset.css +48 -0
  37. data/lib/resque/server/public/style.css +76 -0
  38. data/lib/resque/server/public/working.png +0 -0
  39. data/lib/resque/server/views/error.erb +1 -0
  40. data/lib/resque/server/views/failed.erb +35 -0
  41. data/lib/resque/server/views/key.erb +17 -0
  42. data/lib/resque/server/views/layout.erb +38 -0
  43. data/lib/resque/server/views/next_more.erb +10 -0
  44. data/lib/resque/server/views/overview.erb +4 -0
  45. data/lib/resque/server/views/queues.erb +46 -0
  46. data/lib/resque/server/views/stats.erb +62 -0
  47. data/lib/resque/server/views/workers.erb +78 -0
  48. data/lib/resque/server/views/working.erb +69 -0
  49. data/lib/resque/server.rb +187 -0
  50. data/lib/resque/stat.rb +53 -0
  51. data/lib/resque/tasks.rb +39 -0
  52. data/lib/resque/version.rb +3 -0
  53. data/lib/resque/worker.rb +453 -0
  54. data/lib/resque.rb +246 -0
  55. data/tasks/redis.rake +135 -0
  56. data/tasks/resque.rake +2 -0
  57. data/test/redis-test.conf +132 -0
  58. data/test/resque_test.rb +220 -0
  59. data/test/test_helper.rb +96 -0
  60. data/test/worker_test.rb +260 -0
  61. metadata +172 -0
data/Rakefile ADDED
@@ -0,0 +1,66 @@
1
+ load 'tasks/redis.rake'
2
+
3
+ $LOAD_PATH.unshift '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
+ require 'resque/version'
31
+
32
+ Jeweler::Tasks.new do |gemspec|
33
+ gemspec.name = "scotttam-resque"
34
+ gemspec.summary = %Q{scotttam-resque is an extension to the resque queue system that has pre-fork hooks and ability to turn off forking.}
35
+ gemspec.description = %Q{scotttam-resque is an extension to the resque queue system that has pre-fork hooks and ability to turn off forking. Add more description here.}
36
+ gemspec.email = "tamosunas@gmail.com"
37
+ gemspec.homepage = "http://github.com/scotttam/resque"
38
+ gemspec.authors = ["Chris Wanstrath, Scott Tamosunas"]
39
+ gemspec.version = Resque::Version
40
+
41
+ gemspec.add_dependency "redis"
42
+ gemspec.add_dependency "redis-namespace"
43
+ gemspec.add_dependency "vegas", ">=0.1.2"
44
+ gemspec.add_dependency "sinatra", ">=0.9.2"
45
+ gemspec.add_development_dependency "jeweler"
46
+ end
47
+ rescue LoadError
48
+ puts "Jeweler not available. Install it with: "
49
+ puts "gem install jeweler"
50
+ end
51
+
52
+ begin
53
+ require 'sdoc_helpers'
54
+ rescue LoadError
55
+ puts "sdoc support not enabled. Please gem install sdoc-helpers."
56
+ end
57
+
58
+ desc "Push a new version to Gemcutter"
59
+ task :publish => [ :test, :gemspec, :build ] do
60
+ system "git tag v#{Resque::Version}"
61
+ system "git push origin v#{Resque::Version}"
62
+ system "git push origin master"
63
+ system "gem push pkg/scotttam-resque-#{Resque::Version}.gem"
64
+ system "git clean -fd"
65
+ exec "rake pages"
66
+ end
data/bin/resque ADDED
@@ -0,0 +1,57 @@
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 remove(worker)
21
+ abort "** resque remove WORKER_ID" if worker.nil?
22
+
23
+ Resque.remove_worker(worker)
24
+ puts "** removed #{worker}"
25
+ end
26
+
27
+ def list
28
+ if Resque.workers.any?
29
+ Resque.workers.each do |worker|
30
+ puts "#{worker} (#{worker.state})"
31
+ end
32
+ else
33
+ puts "None"
34
+ end
35
+ end
36
+
37
+ if (i = ARGV.index('-r')) && ARGV[i+1]
38
+ Resque.redis = ARGV[i+1]
39
+ ARGV.delete_at(i)
40
+ ARGV.delete_at(i+1)
41
+ end
42
+
43
+ case ARGV[0]
44
+ when 'kill'
45
+ kill ARGV[1]
46
+ when 'remove'
47
+ remove ARGV[1]
48
+ when 'list'
49
+ list
50
+ else
51
+ puts "Usage: resque [-r redis_host:redis_port] COMMAND [option]"
52
+ puts
53
+ puts "Commands:"
54
+ puts " remove WORKER Removes a worker"
55
+ puts " kill WORKER Kills a worker"
56
+ puts " list Lists known workers"
57
+ end
data/bin/resque-web ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
4
+ begin
5
+ require 'vegas'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'vegas'
9
+ end
10
+ require 'resque/server'
11
+
12
+
13
+ Vegas::Runner.new(Resque::Server, 'resque-web', {
14
+ :before_run => lambda {|v|
15
+ path = (ENV['RESQUECONFIG'] || v.args.first)
16
+ load path.to_s.strip if path
17
+ }
18
+ })
data/config.ru ADDED
@@ -0,0 +1,14 @@
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
+ # Set the RESQUECONFIG env variable if you've a `resque.rb` or similar
8
+ # config file you want loaded on boot.
9
+ if ENV['RESQUECONFIG'] && ::File.exists?(::File.expand_path(ENV['RESQUECONFIG']))
10
+ load ::File.expand_path(ENV['RESQUECONFIG'])
11
+ end
12
+
13
+ use Rack::ShowExceptions
14
+ run Resque::Server.new
data/deps.rip ADDED
@@ -0,0 +1,6 @@
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
+ git://github.com/quirkey/vegas.git v0.1.2
6
+ 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,38 @@
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
+
19
+ out << "<form action='/failing' method='POST''>"
20
+ out << '<input type="submit" value="Create Failing New Job"/>'
21
+ out << '&nbsp;&nbsp;<a href="/resque/">View Resque</a>'
22
+ out << '</form>'
23
+
24
+ out << "</body></html>"
25
+ out
26
+ end
27
+
28
+ post '/' do
29
+ Resque.enqueue(Job, params)
30
+ redirect "/"
31
+ end
32
+
33
+ post '/failing' do
34
+ Resque.enqueue(FailingJob, params)
35
+ redirect "/"
36
+ end
37
+ end
38
+ 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,22 @@
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
+
13
+ module FailingJob
14
+ @queue = :failing
15
+
16
+ def self.perform(params)
17
+ sleep 1
18
+ raise 'not processable!'
19
+ puts "Processed a job!"
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,53 @@
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.env = {"QUEUE"=>"critical,high,low", "RAILS_ENV"=>rails_env}
11
+ w.start = "/usr/bin/rake -f #{rails_root}/Rakefile environment resque:work"
12
+
13
+ w.uid = 'git'
14
+ w.gid = 'git'
15
+
16
+ # retart if memory gets too high
17
+ w.transition(:up, :restart) do |on|
18
+ on.condition(:memory_usage) do |c|
19
+ c.above = 350.megabytes
20
+ c.times = 2
21
+ end
22
+ end
23
+
24
+ # determine the state on startup
25
+ w.transition(:init, { true => :up, false => :start }) do |on|
26
+ on.condition(:process_running) do |c|
27
+ c.running = true
28
+ end
29
+ end
30
+
31
+ # determine when process has finished starting
32
+ w.transition([:start, :restart], :up) do |on|
33
+ on.condition(:process_running) do |c|
34
+ c.running = true
35
+ c.interval = 5.seconds
36
+ end
37
+
38
+ # failsafe
39
+ on.condition(:tries) do |c|
40
+ c.times = 5
41
+ c.transition = :start
42
+ c.interval = 5.seconds
43
+ end
44
+ end
45
+
46
+ # start if process is not running
47
+ w.transition(:up, :start) do |on|
48
+ on.condition(:process_running) do |c|
49
+ c.running = false
50
+ end
51
+ end
52
+ end
53
+ 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,121 @@
1
+ require 'net/https'
2
+ require 'builder'
3
+ require 'uri'
4
+
5
+ module Resque
6
+ module Failure
7
+ # A Failure backend that sends exceptions raised by jobs to Hoptoad.
8
+ #
9
+ # To use it, put this code in an initializer, Rake task, or wherever:
10
+ #
11
+ # Resque::Failure::Hoptoad.configure do |config|
12
+ # config.api_key = 'blah'
13
+ # config.secure = true
14
+ #
15
+ # # optional proxy support
16
+ # config.proxy_host = 'x.y.z.t'
17
+ # config.proxy_port = 8080
18
+ # end
19
+ class Hoptoad < Base
20
+ # From the hoptoad plugin
21
+ INPUT_FORMAT = /^([^:]+):(\d+)(?::in `([^']+)')?$/
22
+
23
+ class << self
24
+ attr_accessor :secure, :api_key, :proxy_host, :proxy_port
25
+ end
26
+
27
+ def self.count
28
+ # We can't get the total # of errors from Hoptoad so we fake it
29
+ # by asking Resque how many errors it has seen.
30
+ Stat[:failed]
31
+ end
32
+
33
+ def self.configure
34
+ yield self
35
+ Resque::Failure.backend = self
36
+ end
37
+
38
+ def save
39
+ http = use_ssl? ? :https : :http
40
+ url = URI.parse("#{http}://hoptoadapp.com/notifier_api/v2/notices")
41
+
42
+ request = Net::HTTP::Proxy(self.class.proxy_host, self.class.proxy_port)
43
+ http = request.new(url.host, url.port)
44
+ headers = {
45
+ 'Content-type' => 'text/xml',
46
+ 'Accept' => 'text/xml, application/xml'
47
+ }
48
+
49
+ http.read_timeout = 5 # seconds
50
+ http.open_timeout = 2 # seconds
51
+
52
+ http.use_ssl = use_ssl?
53
+
54
+ begin
55
+ response = http.post(url.path, xml, headers)
56
+ rescue TimeoutError => e
57
+ log "Timeout while contacting the Hoptoad server."
58
+ end
59
+
60
+ case response
61
+ when Net::HTTPSuccess then
62
+ log "Hoptoad Success: #{response.class}"
63
+ else
64
+ body = response.body if response.respond_to? :body
65
+ log "Hoptoad Failure: #{response.class}\n#{body}"
66
+ end
67
+ end
68
+
69
+ def xml
70
+ x = Builder::XmlMarkup.new
71
+ x.instruct!
72
+ x.notice :version=>"2.0" do
73
+ x.tag! "api-key", api_key
74
+ x.notifier do
75
+ x.name "Resqueue"
76
+ x.version "0.1"
77
+ x.url "http://github.com/defunkt/resque"
78
+ end
79
+ x.error do
80
+ x.class exception.class.name
81
+ x.message "#{exception.class.name}: #{exception.message}"
82
+ x.backtrace do
83
+ fill_in_backtrace_lines(x)
84
+ end
85
+ end
86
+ x.request do
87
+ x.url queue.to_s
88
+ x.component worker.to_s
89
+ x.params do
90
+ x.var :key=>"payload_class" do
91
+ x.text! payload["class"].to_s
92
+ end
93
+ x.var :key=>"payload_args" do
94
+ x.text! payload["args"].to_s
95
+ end
96
+ end
97
+ end
98
+ x.tag!("server-environment") do
99
+ x.tag!("environment-name",RAILS_ENV)
100
+ end
101
+
102
+ end
103
+ end
104
+
105
+ def fill_in_backtrace_lines(x)
106
+ exception.backtrace.each do |unparsed_line|
107
+ _, file, number, method = unparsed_line.match(INPUT_FORMAT).to_a
108
+ x.line :file=>file,:number=>number
109
+ end
110
+ end
111
+
112
+ def use_ssl?
113
+ self.class.secure
114
+ end
115
+
116
+ def api_key
117
+ self.class.api_key
118
+ end
119
+ end
120
+ end
121
+ end