Skip to content

Commit f793235

Browse files
committed
Merge pull request resque#680 from mrzor/hooks
Enable registering of multiple Resque hooks (2)
2 parents 4eabd83 + 486295b commit f793235

File tree

5 files changed

+171
-23
lines changed

5 files changed

+171
-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: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -85,41 +85,46 @@ 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).
@@ -403,5 +408,29 @@ def keys
403408
key.sub("#{redis.namespace}:", '')
404409
end
405410
end
411+
412+
private
413+
414+
# Register a new proc as a hook. If the block is nil this is the
415+
# equivalent of removing all hooks of the given name.
416+
#
417+
# `name` is the hook that the block should be registered with.
418+
def register_hook(name, block)
419+
return clear_hooks(name) if block.nil?
420+
421+
@hooks ||= {}
422+
@hooks[name] ||= []
423+
@hooks[name] << block
424+
end
425+
426+
# Clear all hooks given a hook name.
427+
def clear_hooks(name)
428+
@hooks && @hooks[name] = []
429+
end
430+
431+
# Retrieve all hooks of a given name.
432+
def hooks(name)
433+
(@hooks && @hooks[name]) || []
434+
end
406435
end
407436

lib/resque/worker.rb

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

406406
# Runs a named hook, passing along any arguments.
407407
def run_hook(name, *args)
408-
return unless hook = Resque.send(name)
409-
msg = "Running #{name} hook"
408+
return unless hooks = Resque.send(name)
409+
msg = "Running #{name} hooks"
410410
msg << " with #{args.inspect}" if args.any?
411411
log msg
412412

413-
args.any? ? hook.call(*args) : hook.call
413+
hooks.each do |hook|
414+
args.any? ? hook.call(*args) : hook.call
415+
end
414416
end
415417

416418
# 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

test/worker_test.rb

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,6 @@
55
Resque.redis = Resque.redis # reset state in Resque object
66
Resque.redis.flushall
77

8-
Resque.before_first_fork = nil
9-
Resque.before_fork = nil
10-
Resque.after_fork = nil
11-
128
@worker = Resque::Worker.new(:jobs)
139
Resque::Job.create(:jobs, SomeJob, 20, '/tmp')
1410
end

0 commit comments

Comments
 (0)