resque_unit 0.3.3 → 0.3.4
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.
- 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"
|