resque 0.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of resque might be problematic. Click here for more details.

Files changed (61) hide show
  1. data/.kick +26 -0
  2. data/HISTORY.md +3 -0
  3. data/LICENSE +20 -0
  4. data/README.markdown +638 -0
  5. data/Rakefile +61 -0
  6. data/TODO.md +60 -0
  7. data/bin/resque +57 -0
  8. data/bin/resque-web +47 -0
  9. data/config.ru +8 -0
  10. data/deps.rip +5 -0
  11. data/examples/async_helper.rb +31 -0
  12. data/examples/demo/README.markdown +71 -0
  13. data/examples/demo/Rakefile +3 -0
  14. data/examples/demo/app.rb +27 -0
  15. data/examples/demo/config.ru +19 -0
  16. data/examples/demo/job.rb +12 -0
  17. data/examples/existing_classes_as_jobs.rb +3 -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.rb +184 -0
  22. data/lib/resque/errors.rb +7 -0
  23. data/lib/resque/failure.rb +57 -0
  24. data/lib/resque/failure/base.rb +54 -0
  25. data/lib/resque/failure/hoptoad.rb +88 -0
  26. data/lib/resque/failure/redis.rb +28 -0
  27. data/lib/resque/helpers.rb +57 -0
  28. data/lib/resque/job.rb +91 -0
  29. data/lib/resque/server.rb +154 -0
  30. data/lib/resque/server/public/idle.png +0 -0
  31. data/lib/resque/server/public/jquery-1.3.2.min.js +19 -0
  32. data/lib/resque/server/public/jquery.relatize_date.js +95 -0
  33. data/lib/resque/server/public/nav-bg.png +0 -0
  34. data/lib/resque/server/public/ranger.js +7 -0
  35. data/lib/resque/server/public/reset.css +51 -0
  36. data/lib/resque/server/public/style.css +67 -0
  37. data/lib/resque/server/public/tab_b.gif +0 -0
  38. data/lib/resque/server/public/tab_r.gif +0 -0
  39. data/lib/resque/server/public/tabs.css +189 -0
  40. data/lib/resque/server/public/working.png +0 -0
  41. data/lib/resque/server/views/error.erb +1 -0
  42. data/lib/resque/server/views/failed.erb +29 -0
  43. data/lib/resque/server/views/key.erb +17 -0
  44. data/lib/resque/server/views/layout.erb +43 -0
  45. data/lib/resque/server/views/next_more.erb +12 -0
  46. data/lib/resque/server/views/overview.erb +2 -0
  47. data/lib/resque/server/views/queues.erb +40 -0
  48. data/lib/resque/server/views/stats.erb +62 -0
  49. data/lib/resque/server/views/workers.erb +72 -0
  50. data/lib/resque/server/views/working.erb +66 -0
  51. data/lib/resque/stat.rb +53 -0
  52. data/lib/resque/tasks.rb +24 -0
  53. data/lib/resque/version.rb +3 -0
  54. data/lib/resque/worker.rb +406 -0
  55. data/tasks/redis.rake +125 -0
  56. data/tasks/resque.rake +2 -0
  57. data/test/redis-test.conf +132 -0
  58. data/test/resque_test.rb +160 -0
  59. data/test/test_helper.rb +90 -0
  60. data/test/worker_test.rb +212 -0
  61. metadata +124 -0
@@ -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 = "resque"
35
+ gemspec.summary = ""
36
+ gemspec.description = ""
37
+ gemspec.email = "chris@ozmm.org"
38
+ gemspec.homepage = "http://github.com/defunkt/resque"
39
+ gemspec.authors = ["Chris Wanstrath"]
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/TODO.md ADDED
@@ -0,0 +1,60 @@
1
+ Little Stuff
2
+ -----------
3
+
4
+ [ ] Show stale workers in red with a warning icon in the Sinatra app
5
+
6
+
7
+ Big Stuff
8
+ ---------
9
+
10
+ ### async
11
+
12
+ I want the `async` helper to be first class. Something like this:
13
+
14
+ class Repository < ActiveRecord::Base
15
+ include Resque::AsyncHelper
16
+ end
17
+
18
+ This adds an `async` instance method and a `perform` class method.
19
+
20
+ The `async` method looks like this:
21
+
22
+ def async(method, *args)
23
+ Resque.enqueue(self.class, id, method, *args)
24
+ end
25
+
26
+ The `perform` method looks like this:
27
+
28
+ def self.perform(id, method, *args)
29
+ find(id).send(method, *args)
30
+ end
31
+
32
+ Of course, you can define your own `self.perform` and have it still
33
+ work beautifully. We might override ours in GitHub to look like this:
34
+
35
+ def self.perform(id, method, *args)
36
+ return unless repo = cached_by_id(id)
37
+ repo.send(method, *args)
38
+ end
39
+
40
+ Then: `@repo.async(:update_disk_usage)` issues a job equivalent to:
41
+
42
+ Resque.enqueue(Repository, 44, :update_disk_usage)
43
+
44
+ Booyah.
45
+
46
+
47
+ ### gem install
48
+
49
+ `gem install resque` should pull in yajl, redis, sinatra, rake, and rack
50
+
51
+ Do it like Unicorn does it.
52
+
53
+ ### Parent / Child => Master / Workers
54
+
55
+ On an ideal setup (REE + Linux) you'll have 2N Resque processes
56
+ running at any time: N parents and N children.
57
+
58
+ We can cut this number down to N+1 by moving from a parent / child
59
+ architecture to a master / workers architecture.
60
+
@@ -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
@@ -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
@@ -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
@@ -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'
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,3 @@
1
+ # Let's say this is a class in your app, already
2
+
3
+ # TODO: example
@@ -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')