resque_unit 0.3.3 → 0.3.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +38 -1
- data/lib/resque_unit.rb +1 -0
- data/lib/resque_unit/plugin.rb +65 -0
- data/lib/resque_unit/resque.rb +85 -3
- data/lib/resque_unit/scheduler.rb +1 -1
- data/test/resque_unit_test.rb +48 -0
- data/test/sample_jobs.rb +56 -0
- metadata +9 -6
data/README.md
CHANGED
@@ -27,7 +27,7 @@ example, if you have code that queues a resque job:
|
|
27
27
|
@queue = :low
|
28
28
|
|
29
29
|
def self.perform(x)
|
30
|
-
|
30
|
+
# do stuff
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
@@ -65,11 +65,48 @@ You can also access the queues directly:
|
|
65
65
|
assert_equal 1, Resque.queue(:low).length
|
66
66
|
end
|
67
67
|
|
68
|
+
Finally, you can enable hooks:
|
69
|
+
|
70
|
+
Resque.enable_hooks!
|
71
|
+
|
72
|
+
class MyJobWithHooks
|
73
|
+
@queue = :hooked
|
74
|
+
|
75
|
+
def self.perform(x)
|
76
|
+
# do stuff
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.after_enqueue_mark(*args)
|
80
|
+
# called when the job is enqueued
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.before_perform_mark(*args)
|
84
|
+
# called just before the +perform+ method
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.after_perform_mark(*args)
|
88
|
+
# called just after the +perform+ method
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.failure_perform_mark(*args)
|
92
|
+
# called if the +perform+ method raised
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
def queue_job
|
98
|
+
Resque.enqueue(MyJobWithHooks, 1)
|
99
|
+
end
|
100
|
+
|
68
101
|
Caveats
|
69
102
|
=======
|
70
103
|
|
71
104
|
* You should make sure that you call `Resque.reset!` in your test's
|
72
105
|
setup method to clear all of the test queues.
|
106
|
+
* Hooks support is optional. Just because you probably don't want to call
|
107
|
+
them during unit tests if they play with a DB. Call `Resque.enable_hooks!`
|
108
|
+
in your tests's setup method to enable hooks. To disable hooks, call
|
109
|
+
`Resque.disable_hooks!`.
|
73
110
|
|
74
111
|
Resque-Scheduler Support
|
75
112
|
========================
|
data/lib/resque_unit.rb
CHANGED
@@ -0,0 +1,65 @@
|
|
1
|
+
# A copy of the original Resque:Plugin class from the "resque" gem
|
2
|
+
# No need to redefine this class if the resque gem was already loaded
|
3
|
+
unless defined?(Resque::Plugin)
|
4
|
+
|
5
|
+
module Resque
|
6
|
+
module Plugin
|
7
|
+
extend self
|
8
|
+
|
9
|
+
LintError = Class.new(RuntimeError)
|
10
|
+
|
11
|
+
# Ensure that your plugin conforms to good hook naming conventions.
|
12
|
+
#
|
13
|
+
# Resque::Plugin.lint(MyResquePlugin)
|
14
|
+
def lint(plugin)
|
15
|
+
hooks = before_hooks(plugin) + around_hooks(plugin) + after_hooks(plugin)
|
16
|
+
|
17
|
+
hooks.each do |hook|
|
18
|
+
if hook =~ /perform$/
|
19
|
+
raise LintError, "#{plugin}.#{hook} is not namespaced"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
failure_hooks(plugin).each do |hook|
|
24
|
+
if hook =~ /failure$/
|
25
|
+
raise LintError, "#{plugin}.#{hook} is not namespaced"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Given an object, returns a list `before_perform` hook names.
|
31
|
+
def before_hooks(job)
|
32
|
+
job.methods.grep(/^before_perform/).sort
|
33
|
+
end
|
34
|
+
|
35
|
+
# Given an object, returns a list `around_perform` hook names.
|
36
|
+
def around_hooks(job)
|
37
|
+
job.methods.grep(/^around_perform/).sort
|
38
|
+
end
|
39
|
+
|
40
|
+
# Given an object, returns a list `after_perform` hook names.
|
41
|
+
def after_hooks(job)
|
42
|
+
job.methods.grep(/^after_perform/).sort
|
43
|
+
end
|
44
|
+
|
45
|
+
# Given an object, returns a list `on_failure` hook names.
|
46
|
+
def failure_hooks(job)
|
47
|
+
job.methods.grep(/^on_failure/).sort
|
48
|
+
end
|
49
|
+
|
50
|
+
# Given an object, returns a list `after_enqueue` hook names.
|
51
|
+
def after_enqueue_hooks(job)
|
52
|
+
job.methods.grep(/^after_enqueue/).sort
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
unless defined?(Resque::Job::DontPerform)
|
60
|
+
module Resque
|
61
|
+
class Job
|
62
|
+
DontPerform = Class.new(StandardError)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/lib/resque_unit/resque.rb
CHANGED
@@ -31,6 +31,15 @@ module Resque
|
|
31
31
|
queues[queue_name]
|
32
32
|
end
|
33
33
|
|
34
|
+
# Yes, all Resque hooks!
|
35
|
+
def enable_hooks!
|
36
|
+
@hooks_enabled = true
|
37
|
+
end
|
38
|
+
|
39
|
+
def disable_hooks!
|
40
|
+
@hooks_enabled = nil
|
41
|
+
end
|
42
|
+
|
34
43
|
# Executes all jobs in all queues in an undefined order.
|
35
44
|
def run!
|
36
45
|
old_queue = @queue.dup
|
@@ -38,7 +47,7 @@ module Resque
|
|
38
47
|
|
39
48
|
old_queue.each do |k, v|
|
40
49
|
while job = v.shift
|
41
|
-
job
|
50
|
+
@hooks_enabled ? perform_with_hooks(job) : perform_without_hooks(job)
|
42
51
|
end
|
43
52
|
end
|
44
53
|
end
|
@@ -49,7 +58,7 @@ module Resque
|
|
49
58
|
self.reset!(queue_name)
|
50
59
|
|
51
60
|
while job = jobs.shift
|
52
|
-
job
|
61
|
+
@hooks_enabled ? perform_with_hooks(job) : perform_without_hooks(job)
|
53
62
|
end
|
54
63
|
end
|
55
64
|
|
@@ -74,7 +83,7 @@ module Resque
|
|
74
83
|
queue_name = queue_for(klass)
|
75
84
|
# Behaves like Resque, raise if no queue was specifed
|
76
85
|
raise NoQueueError.new("Jobs must be placed onto a queue.") unless queue_name
|
77
|
-
|
86
|
+
enqueue_unit(queue_name, {:klass => klass, :args => normalized_args(args) })
|
78
87
|
end
|
79
88
|
|
80
89
|
def normalized_args(args)
|
@@ -93,4 +102,77 @@ module Resque
|
|
93
102
|
end
|
94
103
|
end
|
95
104
|
|
105
|
+
def enqueue_unit(queue_name, hash)
|
106
|
+
queue(queue_name) << hash
|
107
|
+
if @hooks_enabled
|
108
|
+
Plugin.after_enqueue_hooks(hash[:klass]).each do |hook|
|
109
|
+
hash[:klass].send(hook, *hash[:args])
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Call perform on the job class
|
115
|
+
def perform_without_hooks(job)
|
116
|
+
job[:klass].perform(*job[:args])
|
117
|
+
end
|
118
|
+
|
119
|
+
# Call perform on the job class, and adds support for Resque hooks.
|
120
|
+
def perform_with_hooks(job)
|
121
|
+
before_hooks = Resque::Plugin.before_hooks(job[:klass])
|
122
|
+
around_hooks = Resque::Plugin.around_hooks(job[:klass])
|
123
|
+
after_hooks = Resque::Plugin.after_hooks(job[:klass])
|
124
|
+
failure_hooks = Resque::Plugin.failure_hooks(job[:klass])
|
125
|
+
|
126
|
+
begin
|
127
|
+
# Execute before_perform hook. Abort the job gracefully if
|
128
|
+
# Resque::DontPerform is raised.
|
129
|
+
begin
|
130
|
+
before_hooks.each do |hook|
|
131
|
+
job[:klass].send(hook, *job[:args])
|
132
|
+
end
|
133
|
+
rescue Resque::Job::DontPerform
|
134
|
+
return false
|
135
|
+
end
|
136
|
+
|
137
|
+
# Execute the job. Do it in an around_perform hook if available.
|
138
|
+
if around_hooks.empty?
|
139
|
+
perform_without_hooks(job)
|
140
|
+
job_was_performed = true
|
141
|
+
else
|
142
|
+
# We want to nest all around_perform plugins, with the last one
|
143
|
+
# finally calling perform
|
144
|
+
stack = around_hooks.reverse.inject(nil) do |last_hook, hook|
|
145
|
+
if last_hook
|
146
|
+
lambda do
|
147
|
+
job[:klass].send(hook, *job[:args]) { last_hook.call }
|
148
|
+
end
|
149
|
+
else
|
150
|
+
lambda do
|
151
|
+
job[:klass].send(hook, *job[:args]) do
|
152
|
+
result = perform_without_hooks(job)
|
153
|
+
job_was_performed = true
|
154
|
+
result
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
stack.call
|
160
|
+
end
|
161
|
+
|
162
|
+
# Execute after_perform hook
|
163
|
+
after_hooks.each do |hook|
|
164
|
+
job[:klass].send(hook, *job[:args])
|
165
|
+
end
|
166
|
+
|
167
|
+
# Return true if the job was performed
|
168
|
+
return job_was_performed
|
169
|
+
|
170
|
+
# If an exception occurs during the job execution, look for an
|
171
|
+
# on_failure hook then re-raise.
|
172
|
+
rescue => e
|
173
|
+
failure_hooks.each { |hook| job[:klass].send(hook, e, *job[:args]) }
|
174
|
+
raise e
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
96
178
|
end
|
@@ -19,7 +19,7 @@ module ResqueUnit
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def enqueue_with_timestamp(timestamp, klass, *args)
|
22
|
-
|
22
|
+
enqueue_unit(queue_for(klass), {:klass => klass, :args => decode(encode(args)), :timestamp => timestamp})
|
23
23
|
end
|
24
24
|
|
25
25
|
end
|
data/test/resque_unit_test.rb
CHANGED
@@ -57,6 +57,54 @@ class ResqueUnitTest < Test::Unit::TestCase
|
|
57
57
|
# assert number of jobs?
|
58
58
|
end
|
59
59
|
|
60
|
+
context "A task that schedules a resque job with hooks" do
|
61
|
+
setup do
|
62
|
+
Resque.enable_hooks!
|
63
|
+
JobWithHooks.clear_markers
|
64
|
+
Resque.enqueue(JobWithHooks)
|
65
|
+
end
|
66
|
+
|
67
|
+
teardown do
|
68
|
+
Resque.disable_hooks!
|
69
|
+
end
|
70
|
+
|
71
|
+
should "have run the after_enqueue hook" do
|
72
|
+
assert_queued(JobWithHooks)
|
73
|
+
assert(JobWithHooks.markers[:after_enqueue], 'no after_queue marker set')
|
74
|
+
end
|
75
|
+
|
76
|
+
should "run the before and after hooks during a run" do
|
77
|
+
Resque.run!
|
78
|
+
assert(JobWithHooks.markers[:before], 'no before marker set')
|
79
|
+
assert(JobWithHooks.markers[:around], 'no around marker set')
|
80
|
+
assert(JobWithHooks.markers[:after], 'no after marker set')
|
81
|
+
assert(!JobWithHooks.markers[:failed], 'failed marker set, and it should not')
|
82
|
+
end
|
83
|
+
|
84
|
+
should "run the before and failed hooks during a run" do
|
85
|
+
JobWithHooks.make_it_fail do
|
86
|
+
assert_raise(RuntimeError) do
|
87
|
+
Resque.run!
|
88
|
+
assert(JobWithHooks.markers[:before], 'no before marker set')
|
89
|
+
assert(JobWithHooks.markers[:around], 'no around marker set')
|
90
|
+
assert(!JobWithHooks.markers[:after], 'after marker set, and it should not')
|
91
|
+
assert(JobWithHooks.markers[:failed], 'no failed marker set')
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
should "not call perform if the around hook raised Resque::Job::DontPerform" do
|
97
|
+
JobWithHooks.make_it_dont_perform do
|
98
|
+
Resque.run!
|
99
|
+
assert(JobWithHooks.markers[:before], 'no before marker set')
|
100
|
+
assert(JobWithHooks.markers[:around], 'no around marker set')
|
101
|
+
assert(!JobWithHooks.markers[:after], 'after marker set, and it should not')
|
102
|
+
assert(!JobWithHooks.markers[:failed], 'failed marker set, and it should not')
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
60
108
|
context "Block assertions" do
|
61
109
|
should "pass the assert_queued(job) assertion when queued in block" do
|
62
110
|
assert_queues(HighPriorityJob) do
|
data/test/sample_jobs.rb
CHANGED
@@ -47,4 +47,60 @@ class JobThatDoesNotSpecifyAQueue
|
|
47
47
|
|
48
48
|
def self.perform
|
49
49
|
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class JobWithHooks
|
53
|
+
@queue = :with_hooks
|
54
|
+
@markers = {}
|
55
|
+
|
56
|
+
def self.perform
|
57
|
+
raise 'FAIL!' if @will_fail
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.markers
|
61
|
+
@markers
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.clear_markers
|
65
|
+
@markers = {}
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.after_enqueue_mark(*args)
|
69
|
+
markers[:after_enqueue] = true
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.before_perform_mark(*args)
|
73
|
+
markers[:before] = true
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.around_perform_mark(*args)
|
77
|
+
markers[:around] = true
|
78
|
+
if @dont_perform
|
79
|
+
raise Resque::Job::DontPerform
|
80
|
+
else
|
81
|
+
yield
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.after_perform_mark(*args)
|
86
|
+
markers[:after] = true
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.failure_perform_mark(*args)
|
90
|
+
markers[:failure] = true
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.make_it_fail(&block)
|
94
|
+
@will_fail = true
|
95
|
+
yield
|
96
|
+
ensure
|
97
|
+
@will_fail = false
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.make_it_dont_perform(&block)
|
101
|
+
@dont_perform = true
|
102
|
+
ensure
|
103
|
+
@dont_perform = false
|
104
|
+
end
|
105
|
+
|
50
106
|
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 3
|
8
|
-
-
|
9
|
-
version: 0.3.
|
8
|
+
- 4
|
9
|
+
version: 0.3.4
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Justin Weiss
|
@@ -14,12 +14,11 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date:
|
17
|
+
date: 2011-02-14 00:00:00 -08:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: json
|
22
|
-
prerelease: false
|
23
22
|
requirement: &id001 !ruby/object:Gem::Requirement
|
24
23
|
none: false
|
25
24
|
requirements:
|
@@ -31,10 +30,10 @@ dependencies:
|
|
31
30
|
- 6
|
32
31
|
version: 1.4.6
|
33
32
|
type: :runtime
|
33
|
+
prerelease: false
|
34
34
|
version_requirements: *id001
|
35
35
|
- !ruby/object:Gem::Dependency
|
36
36
|
name: bundler
|
37
|
-
prerelease: false
|
38
37
|
requirement: &id002 !ruby/object:Gem::Requirement
|
39
38
|
none: false
|
40
39
|
requirements:
|
@@ -44,10 +43,10 @@ dependencies:
|
|
44
43
|
- 0
|
45
44
|
version: "0"
|
46
45
|
type: :development
|
46
|
+
prerelease: false
|
47
47
|
version_requirements: *id002
|
48
48
|
- !ruby/object:Gem::Dependency
|
49
49
|
name: shoulda
|
50
|
-
prerelease: false
|
51
50
|
requirement: &id003 !ruby/object:Gem::Requirement
|
52
51
|
none: false
|
53
52
|
requirements:
|
@@ -57,6 +56,7 @@ dependencies:
|
|
57
56
|
- 0
|
58
57
|
version: "0"
|
59
58
|
type: :development
|
59
|
+
prerelease: false
|
60
60
|
version_requirements: *id003
|
61
61
|
description:
|
62
62
|
email: justin@uberweiss.org
|
@@ -70,6 +70,7 @@ files:
|
|
70
70
|
- lib/resque_unit/assertions.rb
|
71
71
|
- lib/resque_unit/errors.rb
|
72
72
|
- lib/resque_unit/helpers.rb
|
73
|
+
- lib/resque_unit/plugin.rb
|
73
74
|
- lib/resque_unit/resque.rb
|
74
75
|
- lib/resque_unit/scheduler.rb
|
75
76
|
- lib/resque_unit/scheduler_assertions.rb
|
@@ -94,6 +95,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
94
95
|
requirements:
|
95
96
|
- - ">="
|
96
97
|
- !ruby/object:Gem::Version
|
98
|
+
hash: 1471830878985269609
|
97
99
|
segments:
|
98
100
|
- 0
|
99
101
|
version: "0"
|
@@ -102,6 +104,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
102
104
|
requirements:
|
103
105
|
- - ">="
|
104
106
|
- !ruby/object:Gem::Version
|
107
|
+
hash: 1471830878985269609
|
105
108
|
segments:
|
106
109
|
- 0
|
107
110
|
version: "0"
|