opengotham_resque 1.8.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. data/.gitignore +2 -0
  2. data/.kick +26 -0
  3. data/HISTORY.md +142 -0
  4. data/LICENSE +20 -0
  5. data/README.markdown +794 -0
  6. data/Rakefile +112 -0
  7. data/bin/resque +57 -0
  8. data/bin/resque-web +23 -0
  9. data/config.ru +14 -0
  10. data/deps.rip +7 -0
  11. data/docs/HOOKS.md +121 -0
  12. data/docs/PLUGINS.md +93 -0
  13. data/examples/async_helper.rb +31 -0
  14. data/examples/demo/README.markdown +71 -0
  15. data/examples/demo/Rakefile +8 -0
  16. data/examples/demo/app.rb +38 -0
  17. data/examples/demo/config.ru +19 -0
  18. data/examples/demo/job.rb +22 -0
  19. data/examples/god/resque.god +53 -0
  20. data/examples/god/stale.god +26 -0
  21. data/examples/instance.rb +11 -0
  22. data/examples/monit/resque.monit +6 -0
  23. data/examples/simple.rb +30 -0
  24. data/init.rb +1 -0
  25. data/lib/resque.rb +287 -0
  26. data/lib/resque/errors.rb +10 -0
  27. data/lib/resque/failure.rb +66 -0
  28. data/lib/resque/failure/base.rb +61 -0
  29. data/lib/resque/failure/hoptoad.rb +132 -0
  30. data/lib/resque/failure/multiple.rb +48 -0
  31. data/lib/resque/failure/redis.rb +40 -0
  32. data/lib/resque/helpers.rb +63 -0
  33. data/lib/resque/job.rb +207 -0
  34. data/lib/resque/plugin.rb +46 -0
  35. data/lib/resque/server.rb +201 -0
  36. data/lib/resque/server/public/idle.png +0 -0
  37. data/lib/resque/server/public/jquery-1.3.2.min.js +19 -0
  38. data/lib/resque/server/public/jquery.relatize_date.js +95 -0
  39. data/lib/resque/server/public/poll.png +0 -0
  40. data/lib/resque/server/public/ranger.js +67 -0
  41. data/lib/resque/server/public/reset.css +48 -0
  42. data/lib/resque/server/public/style.css +81 -0
  43. data/lib/resque/server/public/working.png +0 -0
  44. data/lib/resque/server/test_helper.rb +19 -0
  45. data/lib/resque/server/views/error.erb +1 -0
  46. data/lib/resque/server/views/failed.erb +53 -0
  47. data/lib/resque/server/views/key_sets.erb +20 -0
  48. data/lib/resque/server/views/key_string.erb +11 -0
  49. data/lib/resque/server/views/layout.erb +44 -0
  50. data/lib/resque/server/views/next_more.erb +10 -0
  51. data/lib/resque/server/views/overview.erb +4 -0
  52. data/lib/resque/server/views/queues.erb +49 -0
  53. data/lib/resque/server/views/stats.erb +62 -0
  54. data/lib/resque/server/views/workers.erb +78 -0
  55. data/lib/resque/server/views/working.erb +69 -0
  56. data/lib/resque/stat.rb +53 -0
  57. data/lib/resque/tasks.rb +39 -0
  58. data/lib/resque/version.rb +3 -0
  59. data/lib/resque/worker.rb +478 -0
  60. data/tasks/redis.rake +159 -0
  61. data/tasks/resque.rake +2 -0
  62. data/test/job_hooks_test.rb +302 -0
  63. data/test/job_plugins_test.rb +209 -0
  64. data/test/plugin_test.rb +116 -0
  65. data/test/redis-test.conf +132 -0
  66. data/test/resque-web_test.rb +54 -0
  67. data/test/resque_test.rb +225 -0
  68. data/test/test_helper.rb +111 -0
  69. data/test/worker_test.rb +302 -0
  70. metadata +199 -0
@@ -0,0 +1,112 @@
1
+ #
2
+ # Setup
3
+ #
4
+
5
+ load 'tasks/redis.rake'
6
+ require 'rake/testtask'
7
+
8
+ $LOAD_PATH.unshift 'lib'
9
+ require 'resque/tasks'
10
+
11
+ def command?(command)
12
+ system("type #{command} > /dev/null")
13
+ end
14
+
15
+ begin
16
+ require 'jeweler'
17
+ Jeweler::Tasks.new do |gemspec|
18
+ gemspec.name = "opengotham_resque"
19
+ gemspec.summary = "resque is a Redis-backed queueing system."
20
+ gemspec.email = "mjording@opengotham.com"
21
+ gemspec.homepage = "http://github.com/opengotham/resque"
22
+ gemspec.authors = ["Matthew Jording"]
23
+ gemspec.version = '1.8.2'
24
+ gemspec.add_dependency 'redis', ">= 2.0.0.rc2"
25
+ gemspec.add_dependency "opengotham_redis-namespace", ">= 0.4.4"
26
+ gemspec.add_dependency "vegas", ">= 0.1.2"
27
+ gemspec.add_dependency "sinatra", ">= 0.9.2"
28
+ gemspec.description = <<description
29
+ Resque is a Redis-backed Ruby library for creating background jobs,
30
+ placing those jobs on multiple queues, and processing them later.
31
+
32
+ Background jobs can be any Ruby class or module that responds to
33
+ perform. Your existing classes can easily be converted to background
34
+ jobs or you can create new classes specifically to do work. Or, you
35
+ can do both.
36
+
37
+ Resque is heavily inspired by DelayedJob (which rocks) and is
38
+ comprised of three parts:
39
+
40
+ * A Ruby library for creating, querying, and processing jobs
41
+ * A Rake task for starting a worker which processes jobs
42
+ * A Sinatra app for monitoring queues, jobs, and workers.
43
+ description
44
+ end
45
+ Jeweler::GemcutterTasks.new
46
+ rescue LoadError
47
+ warn "Jeweler not available. Install it with:"
48
+ warn "gem install jeweler"
49
+ end
50
+ #
51
+ # Tests
52
+ #
53
+
54
+ task :default => :test
55
+
56
+ desc "Run the test suite"
57
+ task :test do
58
+ rg = command?(:rg)
59
+ Dir['test/**/*_test.rb'].each do |f|
60
+ rg ? sh("rg #{f}") : ruby(f)
61
+ end
62
+ end
63
+
64
+ if command? :kicker
65
+ desc "Launch Kicker (like autotest)"
66
+ task :kicker do
67
+ puts "Kicking... (ctrl+c to cancel)"
68
+ exec "kicker -e rake test lib examples"
69
+ end
70
+ end
71
+
72
+
73
+ #
74
+ # Gem
75
+ #
76
+
77
+ task :install => [ 'redis:install', 'dtach:install' ]
78
+
79
+ # begin
80
+ # require 'mg'
81
+ # MG.new("resque.gemspec")
82
+ # rescue LoadError
83
+ # warn "mg not available."
84
+ # warn "Install it with: gem i mg"
85
+ # end
86
+
87
+
88
+ #
89
+ # Documentation
90
+ #
91
+ #
92
+ # begin
93
+ # require 'sdoc_helpers'
94
+ # rescue LoadError
95
+ # puts "sdoc support not enabled. Please gem install sdoc-helpers."
96
+ # end
97
+
98
+
99
+ #
100
+ # Publishing
101
+ #
102
+ #
103
+ # desc "Push a new version to Gemcutter"
104
+ # task :publish => "gem:publish" do
105
+ # require 'resque/version'
106
+ #
107
+ # sh "git tag v#{Resque::Version}"
108
+ # sh "git push origin v#{Resque::Version}"
109
+ # sh "git push origin master"
110
+ # sh "git clean -fd"
111
+ # exec "rake pages"
112
+ # end
@@ -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,23 @@
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
+ }) do |runner, opts, app|
19
+ opts.on('-N NAMESPACE', "--namespace NAMESPACE", "set the Redis namespace") {|namespace|
20
+ runner.logger.info "Using Redis namespace '#{namespace}'"
21
+ Resque.redis.namespace = namespace
22
+ }
23
+ end
@@ -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
@@ -0,0 +1,7 @@
1
+ git://github.com/ezmobius/redis-rb.git 2.0.0.rc2
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
+ git://github.com/brynary/rack-test.git v0.5.3
7
+ rake
@@ -0,0 +1,121 @@
1
+ Resque Hooks
2
+ ============
3
+
4
+ You can customize Resque or write plugins using its hook API. In many
5
+ cases you can use a hook rather than mess with Resque's internals.
6
+
7
+ For a list of available plugins see
8
+ <http://wiki.github.com/defunkt/resque/plugins>.
9
+
10
+
11
+ Worker Hooks
12
+ ------------
13
+
14
+ If you wish to have a Proc called before the worker forks for the
15
+ first time, you can add it in the initializer like so:
16
+
17
+ Resque.before_first_fork do
18
+ puts "Call me once before the worker forks the first time"
19
+ end
20
+
21
+ You can also run a hook before _every_ fork:
22
+
23
+ Resque.before_fork do |job|
24
+ puts "Call me before the worker forks"
25
+ end
26
+
27
+ The `before_fork` hook will be run in the **parent** process. So, be
28
+ careful - any changes you make will be permanent for the lifespan of
29
+ the worker.
30
+
31
+ And after forking:
32
+
33
+ Resque.after_fork do |job|
34
+ puts "Call me after the worker forks"
35
+ end
36
+
37
+ The `after_fork` hook will be run in the child process and is passed
38
+ the current job. Any changes you make, therefor, will only live as
39
+ long as the job currently being processes.
40
+
41
+ All worker hooks can also be set using a setter, e.g.
42
+
43
+ Resque.after_fork = proc { puts "called" }
44
+
45
+
46
+ Job Hooks
47
+ ---------
48
+
49
+ Plugins can utilize job hooks to provide additional behavior. A job
50
+ hook is a method name in the following format:
51
+
52
+ HOOKNAME_IDENTIFIER
53
+
54
+ For example, a `before_perform` hook which adds locking may be defined
55
+ like this:
56
+
57
+ def before_perform_with_lock(*args)
58
+ set_lock!
59
+ end
60
+
61
+ Once this hook is made available to your job (either by way of
62
+ inheritence or `extend`), it will be run before the job's `perform`
63
+ method is called. Hooks of each type are executed in alphabetical order,
64
+ so `before_perform_a` will always be executed before `before_perform_b`.
65
+ An unnamed hook (`before_perform`) will be executed first.
66
+
67
+ The available hooks are:
68
+
69
+ * `before_perform`: Called with the job args before perform. If it raises
70
+ `Resque::Job::DontPerform`, the job is aborted. If other exceptions
71
+ are raised, they will be propagated up the the `Resque::Failure`
72
+ backend.
73
+
74
+ * `after_perform`: Called with the job args after it performs. Uncaught
75
+ exceptions will propagate up to the `Resque::Failure` backend.
76
+
77
+ * `around_perform`: Called with the job args. It is expected to yield in order
78
+ to perform the job (but is not required to do so). It may handle exceptions
79
+ thrown by `perform`, but any that are not caught will propagate up to the
80
+ `Resque::Failure` backend.
81
+
82
+ * `on_failure`: Called with the exception and job args if any exception occurs
83
+ while performing the job (or hooks).
84
+
85
+ Hooks are easily implemented with superclasses or modules. A superclass could
86
+ look something like this.
87
+
88
+ class LoggedJob
89
+ def self.before_perform_log_job(*args)
90
+ Logger.info "About to perform #{self} with #{args.inspect}"
91
+ end
92
+ end
93
+
94
+ class MyJob < LoggedJob
95
+ def self.perform(*args)
96
+ ...
97
+ end
98
+ end
99
+
100
+ Modules are even better because jobs can use many of them.
101
+
102
+ module LoggedJob
103
+ def before_perform_log_job(*args)
104
+ Logger.info "About to perform #{self} with #{args.inspect}"
105
+ end
106
+ end
107
+
108
+ module RetriedJob
109
+ def on_failure_retry(e, *args)
110
+ Logger.info "Performing #{self} caused an exception (#{e}). Retrying..."
111
+ Resque.enqueue self, *args
112
+ end
113
+ end
114
+
115
+ class MyJob
116
+ extend LoggedJob
117
+ extend RetriedJob
118
+ def self.perform(*args)
119
+ ...
120
+ end
121
+ end
@@ -0,0 +1,93 @@
1
+ Resque Plugins
2
+ ==============
3
+
4
+ Resque encourages plugin development. For a list of available plugins,
5
+ please see <http://wiki.github.com/defunkt/resque/plugins>.
6
+
7
+ The `docs/HOOKS.md` file included with Resque documents the available
8
+ hooks you can use to add or change Resque functionality. This document
9
+ describes best practice for plugins themselves.
10
+
11
+
12
+ Version
13
+ -------
14
+
15
+ Plugins should declare the major.minor version of Resque they are
16
+ known to work with explicitly in their README.
17
+
18
+ For example, if your plugin depends on features in Resque 2.1, please
19
+ list "Depends on Resque 2.1" very prominently near the beginning of
20
+ your README.
21
+
22
+ Because Resque uses [Semantic Versioning][sv], you can safely make the
23
+ following assumptions:
24
+
25
+ * Your plugin will work with 2.2, 2.3, etc - no methods will be
26
+ removed or changed, only added.
27
+ * Your plugin might not work with 3.0+, as APIs may change or be
28
+ removed.
29
+
30
+
31
+ Namespace
32
+ ---------
33
+
34
+ All plugins should live under the `Resque::Plugins` module to avoid
35
+ clashing with first class Resque constants or other Ruby libraries.
36
+
37
+ Good:
38
+
39
+ * Resque::Plugins::Lock
40
+ * Resque::Plugins::FastRetry
41
+
42
+ Bad:
43
+
44
+ * Resque::Lock
45
+ * ResqueQueue
46
+
47
+
48
+ Gem Name
49
+ --------
50
+
51
+ Gem names should be in the format of `resque-FEATURE`, where `FEATURE`
52
+ succinctly describes the feature your plugin adds to Resque.
53
+
54
+ Good:
55
+
56
+ * resque-status
57
+ * resque-scheduler
58
+
59
+ Bad:
60
+
61
+ * multi-queue
62
+ * defunkt-resque-lock
63
+
64
+
65
+ Hooks
66
+ -----
67
+
68
+ Job hook names should be namespaced to work properly.
69
+
70
+ Good:
71
+
72
+ * before_perform_lock
73
+ * around_perform_check_status
74
+
75
+ Bad:
76
+
77
+ * before_perform
78
+ * on_failure
79
+
80
+
81
+ Lint
82
+ ----
83
+
84
+ Plugins should test compliance to this document using the
85
+ `Resque::Plugin.lint` method.
86
+
87
+ For example:
88
+
89
+ assert_nothing_raised do
90
+ Resque::Plugin.lint(Resque::Plugins::Lock)
91
+ end
92
+
93
+ [sv]: http://semver.org/
@@ -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)