Skip to content

Commit b4b6e80

Browse files
committed
run all hooks plugin style
1 parent 1e039e6 commit b4b6e80

File tree

2 files changed

+242
-12
lines changed

2 files changed

+242
-12
lines changed

lib/resque/job.rb

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ def perform
108108
job_args = args || []
109109
job_was_performed = false
110110

111+
# Plugins may come via modules extended which implement Resque::Plugin.
112+
# We also treat the payload_class itself like the last plugin.
111113
plugins = payload_class.instance_variable_get(:@plugins) || []
112114
plugins << payload_class
113115

@@ -121,30 +123,41 @@ def perform
121123
end
122124

123125
# Execute the job. Do it in an around_perform hook if available.
124-
if payload_class.respond_to?(:around_perform)
125-
payload_class.around_perform(*job_args) do
126-
payload_class.perform(*job_args)
127-
job_was_performed = true
128-
end
129-
else
126+
around_plugins = plugins.select { |p| p.respond_to?(:around_perform) }.reverse
127+
128+
if around_plugins.empty?
130129
payload_class.perform(*job_args)
131130
job_was_performed = true
131+
else
132+
# We want to nest all around_perform plugins, with the last one
133+
# finally calling perform
134+
stack = around_plugins.inject(nil) do |last_plugin, plugin|
135+
if last_plugin
136+
lambda do
137+
plugin.around_perform(*job_args) { last_plugin.call }
138+
end
139+
else
140+
lambda do
141+
plugin.around_perform(*job_args) do
142+
payload_class.perform(*job_args)
143+
job_was_performed = true
144+
end
145+
end
146+
end
147+
end
148+
stack.call
132149
end
133150

134151
# Execute after_perform hook
135-
if payload_class.respond_to?(:after_perform)
136-
payload_class.after_perform(*job_args)
137-
end
152+
plugins.each { |p| p.after_perform(*job_args) if p.respond_to?(:after_perform) }
138153

139154
# Return true if the job was performed
140155
return job_was_performed
141156

142157
# If an exception occurs during the job execution, look for an
143158
# on_failure hook then re-raise.
144159
rescue Object => e
145-
if payload_class.respond_to?(:on_failure)
146-
payload_class.on_failure(e, *job_args)
147-
end
160+
plugins.each { |p| p.on_failure(e, *job_args) if p.respond_to?(:on_failure) }
148161
raise
149162
end
150163
end

test/plugin_test.rb

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
require File.dirname(__FILE__) + '/test_helper'
2+
3+
context "Multiple plugins with multiple callbacks" do
4+
include PerformJob
5+
6+
module Plugin1
7+
extend Resque::Plugin
8+
def before_perform(history)
9+
history << :before_one
10+
end
11+
def after_perform(history)
12+
history << :after_one
13+
end
14+
end
15+
16+
module Plugin2
17+
extend Resque::Plugin
18+
def before_perform(history)
19+
history << :before_two
20+
end
21+
def after_perform(history)
22+
history << :after_two
23+
end
24+
end
25+
26+
class ManyBeforesJob
27+
extend Plugin1
28+
extend Plugin2
29+
def self.perform(history)
30+
history << :perform
31+
end
32+
end
33+
34+
test "all plugins are executed in order" do
35+
result = perform_job(ManyBeforesJob, history=[])
36+
assert_equal true, result, "perform returned true"
37+
assert_equal [:before_one, :before_two, :perform, :after_one, :after_two], history
38+
end
39+
end
40+
41+
context "Resque::Plugin before_perform" do
42+
include PerformJob
43+
44+
module BeforePerform
45+
extend Resque::Plugin
46+
def before_perform(history)
47+
history << :before_perform_plugin
48+
end
49+
end
50+
51+
class BeforePerformJob
52+
extend BeforePerform
53+
def self.perform(history)
54+
history << :perform
55+
end
56+
def self.before_perform(history)
57+
history << :before_perform
58+
end
59+
end
60+
61+
test "before_perform is executed in plugins first, then the job" do
62+
result = perform_job(BeforePerformJob, history=[])
63+
assert_equal true, result, "perform returned true"
64+
assert_equal [:before_perform_plugin, :before_perform, :perform], history
65+
end
66+
end
67+
68+
context "Resque::Plugin after_perform" do
69+
include PerformJob
70+
71+
module AfterPerform
72+
extend Resque::Plugin
73+
def after_perform(history)
74+
history << :after_perform_plugin
75+
end
76+
end
77+
78+
class AfterPerformJob
79+
extend AfterPerform
80+
def self.perform(history)
81+
history << :perform
82+
end
83+
def self.after_perform(history)
84+
history << :after_perform
85+
end
86+
end
87+
88+
test "after_perform is executed in plugins first, then the job" do
89+
result = perform_job(AfterPerformJob, history=[])
90+
assert_equal true, result, "perform returned true"
91+
assert_equal [:perform, :after_perform_plugin, :after_perform], history
92+
end
93+
end
94+
95+
context "Resque::Plugin around_perform" do
96+
include PerformJob
97+
98+
module AroundPerform
99+
extend Resque::Plugin
100+
def around_perform(history)
101+
history << :around_perform_plugin
102+
yield
103+
end
104+
end
105+
106+
class AroundPerformJustPerformsJob
107+
extend AroundPerform
108+
def self.perform(history)
109+
history << :perform
110+
end
111+
end
112+
113+
test "around_perform is executed in plugins first, then the job is executed" do
114+
result = perform_job(AroundPerformJustPerformsJob, history=[])
115+
assert_equal true, result, "perform returned true"
116+
assert_equal [:around_perform_plugin, :perform], history
117+
end
118+
119+
class AroundPerformJob
120+
extend AroundPerform
121+
def self.perform(history)
122+
history << :perform
123+
end
124+
def self.around_perform(history)
125+
history << :around_perform
126+
yield
127+
end
128+
end
129+
130+
test "around_perform is executed in plugins first, then the job" do
131+
result = perform_job(AroundPerformJob, history=[])
132+
assert_equal true, result, "perform returned true"
133+
assert_equal [:around_perform_plugin, :around_perform, :perform], history
134+
end
135+
136+
module AroundPerform2
137+
extend Resque::Plugin
138+
def around_perform(history)
139+
history << :around_perform_plugin2
140+
yield
141+
end
142+
end
143+
144+
class AroundPerformJob2
145+
extend AroundPerform
146+
extend AroundPerform2
147+
def self.perform(history)
148+
history << :perform
149+
end
150+
def self.around_perform(history)
151+
history << :around_perform
152+
yield
153+
end
154+
end
155+
156+
test "around_perform is executed in multiple plugins first, then the job" do
157+
result = perform_job(AroundPerformJob2, history=[])
158+
assert_equal true, result, "perform returned true"
159+
assert_equal [:around_perform_plugin, :around_perform_plugin2, :around_perform, :perform], history
160+
end
161+
162+
module AroundPerformDoesNotYield
163+
extend Resque::Plugin
164+
def around_perform(history)
165+
history << :around_perform_plugin_no_yield
166+
end
167+
end
168+
169+
class AroundPerformJob3
170+
extend AroundPerform
171+
extend AroundPerformDoesNotYield
172+
extend AroundPerform2
173+
def self.perform(history)
174+
history << :perform
175+
end
176+
def self.around_perform(history)
177+
history << :around_perform
178+
yield
179+
end
180+
end
181+
182+
test "around_perform is executed in multiple plugins but the job aborts if all plugins do not yield" do
183+
result = perform_job(AroundPerformJob3, history=[])
184+
assert_equal false, result, "perform returned false"
185+
assert_equal [:around_perform_plugin, :around_perform_plugin_no_yield], history
186+
end
187+
end
188+
189+
context "Resque::Plugin on_failure" do
190+
include PerformJob
191+
192+
module OnFailure
193+
extend Resque::Plugin
194+
def on_failure(exception, history)
195+
history << "#{exception.message} plugin"
196+
end
197+
end
198+
199+
class FailureJob
200+
extend OnFailure
201+
def self.perform(history)
202+
history << :perform
203+
raise StandardError, "oh no"
204+
end
205+
def self.on_failure(exception, history)
206+
history << exception.message
207+
end
208+
end
209+
210+
test "after_perform is executed in plugins first, then the job" do
211+
history = []
212+
assert_raises StandardError do
213+
perform_job(FailureJob, history)
214+
end
215+
assert_equal [:perform, "oh no plugin", "oh no"], history
216+
end
217+
end

0 commit comments

Comments
 (0)