lifecycle.rb
2.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
module Delayed
class InvalidCallback < Exception; end
class Lifecycle
EVENTS = {
:enqueue => [:job],
:execute => [:worker],
:loop => [:worker],
:perform => [:worker, :job],
:error => [:worker, :job],
:failure => [:worker, :job],
:invoke_job => [:job]
}
def initialize
@callbacks = EVENTS.keys.inject({}) { |hash, e| hash[e] = Callback.new; hash }
end
def before(event, &block)
add(:before, event, &block)
end
def after(event, &block)
add(:after, event, &block)
end
def around(event, &block)
add(:around, event, &block)
end
def run_callbacks(event, *args, &block)
missing_callback(event) unless @callbacks.has_key?(event)
unless EVENTS[event].size == args.size
raise ArgumentError, "Callback #{event} expects #{EVENTS[event].size} parameter(s): #{EVENTS[event].join(', ')}"
end
@callbacks[event].execute(*args, &block)
end
private
def add(type, event, &block)
missing_callback(event) unless @callbacks.has_key?(event)
@callbacks[event].add(type, &block)
end
def missing_callback(event)
raise InvalidCallback, "Unknown callback event: #{event}"
end
end
class Callback
def initialize
@before = []
@after = []
# Identity proc. Avoids special cases when there is no existing around chain.
@around = lambda { |*args, &block| block.call(*args) }
end
def execute(*args, &block)
@before.each { |c| c.call(*args) }
result = @around.call(*args, &block)
@after.each { |c| c.call(*args) }
result
end
def add(type, &callback)
case type
when :before
@before << callback
when :after
@after << callback
when :around
chain = @around # use a local variable so that the current chain is closed over in the following lambda
@around = lambda { |*a, &block| chain.call(*a) { |*b| callback.call(*b, &block) } }
else
raise InvalidCallback, "Invalid callback type: #{type}"
end
end
end
end