Skip to content

Commit 842e6e6

Browse files
committed
Merge pull request resque#692 from panthomakos/updated-hooks
Enable registering of multiple Resque hooks.
2 parents 2c874b3 + 2427c97 commit 842e6e6

File tree

4 files changed

+173
-23
lines changed

4 files changed

+173
-23
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
Gemfile.lock
22
doc/
33
test/dump.rdb
4+
test/dump-cluster.rdb

lib/resque.rb

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -85,47 +85,51 @@ def redis_id
8585
# changes you make will be permanent for the lifespan of the
8686
# worker.
8787
#
88-
# Call with a block to set the hook.
89-
# Call with no arguments to return the hook.
88+
# Call with a block to register a hook.
89+
# Call with no arguments to return all registered hooks.
9090
def before_first_fork(&block)
91-
block ? (@before_first_fork = block) : @before_first_fork
91+
block ? register_hook(:before_first_fork, block) : hooks(:before_first_fork)
9292
end
9393

94-
# Set a proc that will be called in the parent process before the
95-
# worker forks for the first time.
96-
attr_writer :before_first_fork
94+
# Register a before_first_fork proc.
95+
def before_first_fork=(block)
96+
register_hook(:before_first_fork, block)
97+
end
9798

9899
# The `before_fork` hook will be run in the **parent** process
99100
# before every job, so be careful- any changes you make will be
100101
# permanent for the lifespan of the worker.
101102
#
102-
# Call with a block to set the hook.
103-
# Call with no arguments to return the hook.
103+
# Call with a block to register a hook.
104+
# Call with no arguments to return all registered hooks.
104105
def before_fork(&block)
105-
block ? (@before_fork = block) : @before_fork
106+
block ? register_hook(:before_fork, block) : hooks(:before_fork)
106107
end
107108

108-
# Set the before_fork proc.
109-
attr_writer :before_fork
109+
# Register a before_fork proc.
110+
def before_fork=(block)
111+
register_hook(:before_fork, block)
112+
end
110113

111114
# The `after_fork` hook will be run in the child process and is passed
112115
# the current job. Any changes you make, therefore, will only live as
113116
# long as the job currently being processed.
114117
#
115-
# Call with a block to set the hook.
116-
# Call with no arguments to return the hook.
118+
# Call with a block to register a hook.
119+
# Call with no arguments to return all registered hooks.
117120
def after_fork(&block)
118-
block ? (@after_fork = block) : @after_fork
121+
block ? register_hook(:after_fork, block) : hooks(:after_fork)
119122
end
120123

121-
# Set the after_fork proc.
122-
attr_writer :after_fork
124+
# Register an after_fork proc.
125+
def after_fork=(block)
126+
register_hook(:after_fork, block)
127+
end
123128

124129
# The `before_pause` hook will be run in the parent process before the
125130
# worker has paused processing (via #pause_processing or SIGUSR2).
126131
def before_pause(&block)
127-
@before_pause = block if block_given?
128-
@before_pause
132+
block ? register_hook(:before_pause, block) : hooks(:before_pause)
129133
end
130134

131135
# Set the after_pause proc.
@@ -134,8 +138,7 @@ def before_pause(&block)
134138
# The `after_pause` hook will be run in the parent process after the
135139
# worker has paused (via SIGCONT).
136140
def after_pause(&block)
137-
@after_pause = block if block_given?
138-
@after_pause
141+
block ? register_hook(:after_pause, block) : hooks(:after_pause)
139142
end
140143

141144
# Set the after_continue proc.
@@ -403,5 +406,29 @@ def keys
403406
key.sub("#{redis.namespace}:", '')
404407
end
405408
end
409+
410+
private
411+
412+
# Register a new proc as a hook. If the block is nil this is the
413+
# equivalent of removing all hooks of the given name.
414+
#
415+
# `name` is the hook that the block should be registered with.
416+
def register_hook(name, block)
417+
return clear_hooks(name) if block.nil?
418+
419+
@hooks ||= {}
420+
@hooks[name] ||= []
421+
@hooks[name] << block
422+
end
423+
424+
# Clear all hooks given a hook name.
425+
def clear_hooks(name)
426+
@hooks && @hooks[name] = []
427+
end
428+
429+
# Retrieve all hooks of a given name.
430+
def hooks(name)
431+
(@hooks && @hooks[name]) || []
432+
end
406433
end
407434

lib/resque/worker.rb

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -425,12 +425,14 @@ def register_worker
425425

426426
# Runs a named hook, passing along any arguments.
427427
def run_hook(name, *args)
428-
return unless hook = Resque.send(name)
429-
msg = "Running #{name} hook"
428+
return unless hooks = Resque.send(name)
429+
msg = "Running #{name} hooks"
430430
msg << " with #{args.inspect}" if args.any?
431431
log msg
432432

433-
args.any? ? hook.call(*args) : hook.call
433+
hooks.each do |hook|
434+
args.any? ? hook.call(*args) : hook.call
435+
end
434436
end
435437

436438
# Unregisters ourself as a worker. Useful when shutting down.

test/resque_hook_test.rb

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
require 'test_helper'
2+
3+
describe "Resque Hooks" do
4+
before do
5+
Resque.redis.flushall
6+
7+
Resque.before_first_fork = nil
8+
Resque.before_fork = nil
9+
Resque.after_fork = nil
10+
11+
@worker = Resque::Worker.new(:jobs)
12+
13+
$called = false
14+
15+
class CallNotifyJob
16+
def self.perform
17+
$called = true
18+
end
19+
end
20+
end
21+
22+
it 'retrieving hooks if none have been set' do
23+
assert_equal [], Resque.before_first_fork
24+
assert_equal [], Resque.before_fork
25+
assert_equal [], Resque.after_fork
26+
end
27+
28+
it 'it calls before_first_fork once' do
29+
counter = 0
30+
31+
Resque.before_first_fork { counter += 1 }
32+
2.times { Resque::Job.create(:jobs, CallNotifyJob) }
33+
34+
assert_equal(0, counter)
35+
@worker.work(0)
36+
assert_equal(1, counter)
37+
end
38+
39+
it 'it calls before_fork before each job' do
40+
counter = 0
41+
42+
Resque.before_fork { counter += 1 }
43+
2.times { Resque::Job.create(:jobs, CallNotifyJob) }
44+
45+
assert_equal(0, counter)
46+
@worker.work(0)
47+
assert_equal(2, counter)
48+
end
49+
50+
it 'it calls after_fork after each job' do
51+
counter = 0
52+
53+
Resque.after_fork { counter += 1 }
54+
2.times { Resque::Job.create(:jobs, CallNotifyJob) }
55+
56+
assert_equal(0, counter)
57+
@worker.work(0)
58+
assert_equal(2, counter)
59+
end
60+
61+
it 'it calls before_first_fork before forking' do
62+
Resque.before_first_fork { assert(!$called) }
63+
64+
Resque::Job.create(:jobs, CallNotifyJob)
65+
@worker.work(0)
66+
end
67+
68+
it 'it calls before_fork before forking' do
69+
Resque.before_fork { assert(!$called) }
70+
71+
Resque::Job.create(:jobs, CallNotifyJob)
72+
@worker.work(0)
73+
end
74+
75+
it 'it calls after_fork after forking' do
76+
Resque.after_fork { assert($called) }
77+
78+
Resque::Job.create(:jobs, CallNotifyJob)
79+
@worker.work(0)
80+
end
81+
82+
it 'it registeres multiple before_first_forks' do
83+
first = false
84+
second = false
85+
86+
Resque.before_first_fork { first = true }
87+
Resque.before_first_fork { second = true }
88+
Resque::Job.create(:jobs, CallNotifyJob)
89+
90+
assert(!first && !second)
91+
@worker.work(0)
92+
assert(first && second)
93+
end
94+
95+
it 'it registers multiple before_forks' do
96+
first = false
97+
second = false
98+
99+
Resque.before_fork { first = true }
100+
Resque.before_fork { second = true }
101+
Resque::Job.create(:jobs, CallNotifyJob)
102+
103+
assert(!first && !second)
104+
@worker.work(0)
105+
assert(first && second)
106+
end
107+
108+
it 'it registers multiple after_forks' do
109+
first = false
110+
second = false
111+
112+
Resque.after_fork { first = true }
113+
Resque.after_fork { second = true }
114+
Resque::Job.create(:jobs, CallNotifyJob)
115+
116+
assert(!first && !second)
117+
@worker.work(0)
118+
assert(first && second)
119+
end
120+
end

0 commit comments

Comments
 (0)