ace-eye 0.6.1
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.
- checksums.yaml +7 -0
- data/.gitignore +38 -0
- data/.rspec +2 -0
- data/.travis.yml +6 -0
- data/CHANGES.md +77 -0
- data/Gemfile +6 -0
- data/LICENSE +22 -0
- data/README.md +212 -0
- data/Rakefile +35 -0
- data/bin/eye +5 -0
- data/bin/loader_eye +72 -0
- data/bin/runner +16 -0
- data/examples/dependency.eye +17 -0
- data/examples/notify.eye +19 -0
- data/examples/plugin/README.md +15 -0
- data/examples/plugin/main.eye +15 -0
- data/examples/plugin/plugin.rb +63 -0
- data/examples/process_thin.rb +29 -0
- data/examples/processes/em.rb +57 -0
- data/examples/processes/forking.rb +20 -0
- data/examples/processes/sample.rb +144 -0
- data/examples/processes/thin.ru +12 -0
- data/examples/puma.eye +29 -0
- data/examples/rbenv.eye +11 -0
- data/examples/sidekiq.eye +23 -0
- data/examples/test.eye +87 -0
- data/examples/thin-farm.eye +30 -0
- data/examples/unicorn.eye +39 -0
- data/eye.gemspec +40 -0
- data/lib/eye.rb +28 -0
- data/lib/eye/application.rb +73 -0
- data/lib/eye/checker.rb +258 -0
- data/lib/eye/checker/children_count.rb +44 -0
- data/lib/eye/checker/children_memory.rb +12 -0
- data/lib/eye/checker/cpu.rb +17 -0
- data/lib/eye/checker/cputime.rb +13 -0
- data/lib/eye/checker/file_ctime.rb +24 -0
- data/lib/eye/checker/file_size.rb +34 -0
- data/lib/eye/checker/file_touched.rb +15 -0
- data/lib/eye/checker/http.rb +96 -0
- data/lib/eye/checker/memory.rb +17 -0
- data/lib/eye/checker/nop.rb +6 -0
- data/lib/eye/checker/runtime.rb +18 -0
- data/lib/eye/checker/socket.rb +159 -0
- data/lib/eye/child_process.rb +101 -0
- data/lib/eye/cli.rb +185 -0
- data/lib/eye/cli/commands.rb +78 -0
- data/lib/eye/cli/render.rb +130 -0
- data/lib/eye/cli/server.rb +93 -0
- data/lib/eye/client.rb +32 -0
- data/lib/eye/config.rb +91 -0
- data/lib/eye/control.rb +2 -0
- data/lib/eye/controller.rb +54 -0
- data/lib/eye/controller/commands.rb +88 -0
- data/lib/eye/controller/helpers.rb +101 -0
- data/lib/eye/controller/load.rb +224 -0
- data/lib/eye/controller/options.rb +18 -0
- data/lib/eye/controller/send_command.rb +177 -0
- data/lib/eye/controller/status.rb +72 -0
- data/lib/eye/dsl.rb +53 -0
- data/lib/eye/dsl/application_opts.rb +39 -0
- data/lib/eye/dsl/chain.rb +12 -0
- data/lib/eye/dsl/child_process_opts.rb +13 -0
- data/lib/eye/dsl/config_opts.rb +55 -0
- data/lib/eye/dsl/group_opts.rb +32 -0
- data/lib/eye/dsl/helpers.rb +20 -0
- data/lib/eye/dsl/main.rb +51 -0
- data/lib/eye/dsl/opts.rb +151 -0
- data/lib/eye/dsl/process_opts.rb +36 -0
- data/lib/eye/dsl/pure_opts.rb +121 -0
- data/lib/eye/dsl/validation.rb +88 -0
- data/lib/eye/group.rb +140 -0
- data/lib/eye/group/chain.rb +81 -0
- data/lib/eye/loader.rb +10 -0
- data/lib/eye/local.rb +100 -0
- data/lib/eye/logger.rb +104 -0
- data/lib/eye/notify.rb +118 -0
- data/lib/eye/notify/jabber.rb +30 -0
- data/lib/eye/notify/mail.rb +48 -0
- data/lib/eye/process.rb +85 -0
- data/lib/eye/process/children.rb +60 -0
- data/lib/eye/process/commands.rb +280 -0
- data/lib/eye/process/config.rb +81 -0
- data/lib/eye/process/controller.rb +73 -0
- data/lib/eye/process/data.rb +78 -0
- data/lib/eye/process/monitor.rb +108 -0
- data/lib/eye/process/notify.rb +32 -0
- data/lib/eye/process/scheduler.rb +82 -0
- data/lib/eye/process/states.rb +86 -0
- data/lib/eye/process/states_history.rb +66 -0
- data/lib/eye/process/system.rb +97 -0
- data/lib/eye/process/trigger.rb +34 -0
- data/lib/eye/process/validate.rb +33 -0
- data/lib/eye/process/watchers.rb +66 -0
- data/lib/eye/reason.rb +20 -0
- data/lib/eye/server.rb +60 -0
- data/lib/eye/sigar.rb +5 -0
- data/lib/eye/system.rb +139 -0
- data/lib/eye/system_resources.rb +99 -0
- data/lib/eye/trigger.rb +136 -0
- data/lib/eye/trigger/check_dependency.rb +30 -0
- data/lib/eye/trigger/flapping.rb +41 -0
- data/lib/eye/trigger/stop_children.rb +17 -0
- data/lib/eye/trigger/transition.rb +15 -0
- data/lib/eye/trigger/wait_dependency.rb +49 -0
- data/lib/eye/utils.rb +45 -0
- data/lib/eye/utils/alive_array.rb +57 -0
- data/lib/eye/utils/celluloid_chain.rb +71 -0
- data/lib/eye/utils/celluloid_klass.rb +5 -0
- data/lib/eye/utils/leak_19.rb +10 -0
- data/lib/eye/utils/mini_active_support.rb +111 -0
- data/lib/eye/utils/pmap.rb +7 -0
- data/lib/eye/utils/tail.rb +20 -0
- metadata +398 -0
data/lib/eye/trigger.rb
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
class Eye::Trigger
|
2
|
+
include Eye::Dsl::Validation
|
3
|
+
|
4
|
+
autoload :Flapping, 'eye/trigger/flapping'
|
5
|
+
autoload :Transition, 'eye/trigger/transition'
|
6
|
+
autoload :StopChildren, 'eye/trigger/stop_children'
|
7
|
+
autoload :WaitDependency, 'eye/trigger/wait_dependency'
|
8
|
+
autoload :CheckDependency, 'eye/trigger/check_dependency'
|
9
|
+
|
10
|
+
TYPES = {:flapping => 'Flapping', :transition => 'Transition', :stop_children => 'StopChildren',
|
11
|
+
:wait_dependency => 'WaitDependency', :check_dependency => 'CheckDependency'
|
12
|
+
}
|
13
|
+
|
14
|
+
attr_reader :message, :options, :process
|
15
|
+
|
16
|
+
def self.name_and_class(type)
|
17
|
+
type = type.to_sym
|
18
|
+
return {:name => type, :type => type} if TYPES[type]
|
19
|
+
|
20
|
+
if type =~ /\A(.*?)_?[0-9]+\z/
|
21
|
+
ctype = $1.to_sym
|
22
|
+
return {:name => type, :type => ctype} if TYPES[ctype]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.get_class(type)
|
27
|
+
klass = eval("Eye::Trigger::#{TYPES[type]}") rescue nil
|
28
|
+
raise "unknown trigger #{type}" unless klass
|
29
|
+
if deps = klass.requires
|
30
|
+
Array(deps).each { |d| require d }
|
31
|
+
end
|
32
|
+
klass
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.create(process, options = {})
|
36
|
+
get_class(options[:type]).new(process, options)
|
37
|
+
|
38
|
+
rescue Exception, Timeout::Error => ex
|
39
|
+
log_ex(ex)
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.validate!(options = {})
|
44
|
+
get_class(options[:type]).validate(options)
|
45
|
+
end
|
46
|
+
|
47
|
+
def initialize(process, options = {})
|
48
|
+
@options = options
|
49
|
+
@process = process
|
50
|
+
@full_name = @process.full_name if @process
|
51
|
+
|
52
|
+
debug "add #{options}"
|
53
|
+
end
|
54
|
+
|
55
|
+
def inspect
|
56
|
+
"<#{self.class} @process='#{@full_name}' @options=#{@options}>"
|
57
|
+
end
|
58
|
+
|
59
|
+
def logger_tag
|
60
|
+
@process.logger.prefix
|
61
|
+
end
|
62
|
+
|
63
|
+
def logger_sub_tag
|
64
|
+
"trigger(#{@options[:type]})"
|
65
|
+
end
|
66
|
+
|
67
|
+
def notify(transition, reason)
|
68
|
+
debug "check (:#{transition.event}) :#{transition.from} => :#{transition.to}"
|
69
|
+
@reason = reason
|
70
|
+
@transition = transition
|
71
|
+
|
72
|
+
check(transition) if filter_transition(transition)
|
73
|
+
|
74
|
+
rescue Exception, Timeout::Error => ex
|
75
|
+
if ex.class == Eye::Process::StateError
|
76
|
+
raise ex
|
77
|
+
else
|
78
|
+
log_ex(ex)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
param :to, [Symbol, Array]
|
83
|
+
param :from, [Symbol, Array]
|
84
|
+
param :event, [Symbol, Array]
|
85
|
+
|
86
|
+
def filter_transition(trans)
|
87
|
+
return true unless to || from || event
|
88
|
+
|
89
|
+
compare_state(trans.to_name, to) &&
|
90
|
+
compare_state(trans.from_name, from) &&
|
91
|
+
compare_state(trans.event, event)
|
92
|
+
end
|
93
|
+
|
94
|
+
def check(transition)
|
95
|
+
raise NotImplementedError
|
96
|
+
end
|
97
|
+
|
98
|
+
def run_in_process_context(p)
|
99
|
+
process.instance_exec(&p) if process.alive?
|
100
|
+
end
|
101
|
+
|
102
|
+
def defer(&block)
|
103
|
+
Celluloid::Future.new(&block).value
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.register(base)
|
107
|
+
name = base.to_s.gsub('Eye::Trigger::', '')
|
108
|
+
type = name.underscore.to_sym
|
109
|
+
Eye::Trigger::TYPES[type] = name
|
110
|
+
Eye::Trigger.const_set(name, base)
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.requires
|
114
|
+
end
|
115
|
+
|
116
|
+
class Custom < Eye::Trigger
|
117
|
+
def self.inherited(base)
|
118
|
+
super
|
119
|
+
register(base)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
def compare_state(state_name, condition)
|
126
|
+
case condition
|
127
|
+
when Symbol
|
128
|
+
state_name == condition
|
129
|
+
when Array
|
130
|
+
condition.include?(state_name)
|
131
|
+
else
|
132
|
+
true
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class Eye::Trigger::CheckDependency < Eye::Trigger
|
2
|
+
param :names, [Array], true, 5
|
3
|
+
|
4
|
+
def check(transition)
|
5
|
+
check_dependency(transition.to_name) if transition.from_name == :up
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def check_dependency(to)
|
11
|
+
processes = names.map do |name|
|
12
|
+
Eye::Control.find_nearest_process(name, process.group_name_pure, process.app_name)
|
13
|
+
end.compact
|
14
|
+
return if processes.empty?
|
15
|
+
processes = Eye::Utils::AliveArray.new(processes)
|
16
|
+
|
17
|
+
act = case to
|
18
|
+
when :down, :restarting; :restart
|
19
|
+
when :stopping; :stop
|
20
|
+
when :unmonitored; :unmonitor
|
21
|
+
end
|
22
|
+
|
23
|
+
if act
|
24
|
+
processes.each do |p|
|
25
|
+
p.schedule act, Eye::Reason.new(:"#{act} dependecies")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class Eye::Trigger::Flapping < Eye::Trigger
|
2
|
+
|
3
|
+
# trigger :flapping, :times => 10, :within => 1.minute,
|
4
|
+
# :retry_in => 10.minutes, :retry_times => 15
|
5
|
+
|
6
|
+
param :times, [Fixnum], true, 5
|
7
|
+
param :within, [Float, Fixnum], true
|
8
|
+
param :retry_in, [Float, Fixnum]
|
9
|
+
param :retry_times, [Fixnum]
|
10
|
+
|
11
|
+
def check(transition)
|
12
|
+
on_flapping if transition.event == :crashed && !good?
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def good?
|
18
|
+
states = process.states_history.states_for_period( within, @last_at )
|
19
|
+
down_count = states.count{|st| st == :down }
|
20
|
+
|
21
|
+
if down_count >= times
|
22
|
+
@last_at = process.states_history.last_state_changed_at
|
23
|
+
false
|
24
|
+
else
|
25
|
+
true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def on_flapping
|
30
|
+
debug 'flapping recognized!!!'
|
31
|
+
|
32
|
+
process.notify :error, 'flapping!'
|
33
|
+
process.schedule :unmonitor, Eye::Reason.new(:flapping)
|
34
|
+
|
35
|
+
return unless retry_in
|
36
|
+
return if retry_times && process.flapping_times >= retry_times
|
37
|
+
|
38
|
+
process.schedule_in(retry_in.to_f, :retry_start_after_flapping)
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Eye::Trigger::StopChildren < Eye::Trigger
|
2
|
+
|
3
|
+
# Kill process children when parent process crashed, or stopped:
|
4
|
+
#
|
5
|
+
# trigger :stop_children
|
6
|
+
|
7
|
+
param :timeout, [Fixnum, Float], nil, 60
|
8
|
+
|
9
|
+
# default on stopped, crashed
|
10
|
+
param_default :event, [:stopped, :crashed]
|
11
|
+
|
12
|
+
def check(trans)
|
13
|
+
debug 'stopping children'
|
14
|
+
process.children.pmap { |pid, c| c.stop }
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Eye::Trigger::Transition < Eye::Trigger
|
2
|
+
|
3
|
+
# trigger :transition, :to => :up, :from => :starting, :do => ->{ ... }
|
4
|
+
|
5
|
+
param :do, [Proc, Symbol]
|
6
|
+
|
7
|
+
def check(trans)
|
8
|
+
act = @options[:do]
|
9
|
+
if act
|
10
|
+
instance_exec(&@options[:do]) if act.is_a?(Proc)
|
11
|
+
send(act, process) if act.is_a?(Symbol)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
class Eye::Trigger::WaitDependency < Eye::Trigger
|
2
|
+
param :names, [Array], true
|
3
|
+
param :wait_timeout, [Numeric], nil, 15.seconds
|
4
|
+
param :retry_after, [Numeric], nil, 1.minute
|
5
|
+
param :should_start, [TrueClass, FalseClass]
|
6
|
+
|
7
|
+
def check(transition)
|
8
|
+
wait_dependency if transition.to_name == :starting
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def wait_dependency
|
14
|
+
processes = names.map do |name|
|
15
|
+
Eye::Control.find_nearest_process(name, process.group_name_pure, process.app_name)
|
16
|
+
end.compact
|
17
|
+
return if processes.empty?
|
18
|
+
processes = Eye::Utils::AliveArray.new(processes)
|
19
|
+
|
20
|
+
processes.each do |p|
|
21
|
+
if p.state_name != :up && (should_start == nil || should_start)
|
22
|
+
p.schedule :start, Eye::Reason.new(:start_dependency)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
res = true
|
27
|
+
|
28
|
+
processes.pmap do |p|
|
29
|
+
name = p.name
|
30
|
+
|
31
|
+
res &= process.wait_for_condition(wait_timeout, 0.5) do
|
32
|
+
info "wait for #{name} until it :up"
|
33
|
+
p.state_name == :up
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
unless res
|
38
|
+
warn "not waited for #{names} to be up"
|
39
|
+
process.switch :unmonitoring
|
40
|
+
|
41
|
+
if retry_after
|
42
|
+
process.schedule_in retry_after, :start, Eye::Reason.new(:wait_dependency)
|
43
|
+
end
|
44
|
+
|
45
|
+
raise Eye::Process::StateError.new('stop transition because dependency is not up')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
data/lib/eye/utils.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module Eye::Utils
|
4
|
+
autoload :Tail, 'eye/utils/tail'
|
5
|
+
autoload :AliveArray, 'eye/utils/alive_array'
|
6
|
+
autoload :CelluloidChain, 'eye/utils/celluloid_chain'
|
7
|
+
|
8
|
+
def self.deep_clone(value)
|
9
|
+
case
|
10
|
+
when value.is_a?(Array) then value.map{|v| deep_clone(v) }
|
11
|
+
when value.is_a?(Hash) then value.inject({}){|r, (k, v)| r[ deep_clone(k) ] = deep_clone(v); r }
|
12
|
+
else value
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# deep merging b into a (a deeply changed)
|
17
|
+
def self.deep_merge!(a, b, allowed_keys = nil)
|
18
|
+
b.each do |k, v|
|
19
|
+
next if allowed_keys && !allowed_keys.include?(k)
|
20
|
+
if a[k].is_a?(Hash) && v.is_a?(Hash)
|
21
|
+
deep_merge!(a[k], v)
|
22
|
+
else
|
23
|
+
a[k] = v
|
24
|
+
end
|
25
|
+
end
|
26
|
+
a
|
27
|
+
end
|
28
|
+
|
29
|
+
D1 = '%H:%M'
|
30
|
+
D2 = '%b%d'
|
31
|
+
|
32
|
+
def self.human_time(unix_time)
|
33
|
+
time = Time.at(unix_time.to_i)
|
34
|
+
d1 = time.to_date
|
35
|
+
d2 = Time.now.to_date
|
36
|
+
time.strftime (d1 == d2) ? D1 : D2
|
37
|
+
end
|
38
|
+
|
39
|
+
DF = '%d %b %H:%M'
|
40
|
+
|
41
|
+
def self.human_time2(unix_time)
|
42
|
+
Time.at(unix_time.to_i).strftime(DF)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
class Eye::Utils::AliveArray
|
2
|
+
extend Forwardable
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
def_delegators :@arr, :[], :<<, :clear, :delete, :size, :empty?, :push,
|
6
|
+
:flatten, :present?, :uniq!, :select!
|
7
|
+
|
8
|
+
def initialize(arr = [])
|
9
|
+
@arr = arr
|
10
|
+
end
|
11
|
+
|
12
|
+
def each(&block)
|
13
|
+
@arr.each{|elem| elem && elem.alive? && block[elem] }
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_a
|
17
|
+
map{|x| x }
|
18
|
+
end
|
19
|
+
|
20
|
+
def full_size
|
21
|
+
@arr.size
|
22
|
+
end
|
23
|
+
|
24
|
+
def pure
|
25
|
+
@arr
|
26
|
+
end
|
27
|
+
|
28
|
+
def sort_by(&block)
|
29
|
+
self.class.new super
|
30
|
+
end
|
31
|
+
|
32
|
+
def sort(&block)
|
33
|
+
self.class.new super
|
34
|
+
end
|
35
|
+
|
36
|
+
def +(other)
|
37
|
+
if other.is_a?(Eye::Utils::AliveArray)
|
38
|
+
@arr += other.pure
|
39
|
+
elsif other.is_a?(Array)
|
40
|
+
@arr += other
|
41
|
+
else
|
42
|
+
raise "Unexpected + #{other}"
|
43
|
+
end
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
def ==(other)
|
48
|
+
if other.is_a?(Eye::Utils::AliveArray)
|
49
|
+
@arr == other.pure
|
50
|
+
elsif other.is_a?(Array)
|
51
|
+
@arr == other
|
52
|
+
else
|
53
|
+
raise "Unexpected == #{other}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'celluloid'
|
2
|
+
|
3
|
+
class Eye::Utils::CelluloidChain
|
4
|
+
include Celluloid
|
5
|
+
|
6
|
+
def initialize(target)
|
7
|
+
@target = target
|
8
|
+
@calls = []
|
9
|
+
@running = false
|
10
|
+
@target_class = @target.class
|
11
|
+
end
|
12
|
+
|
13
|
+
def add(method_name, *args, &block)
|
14
|
+
@calls << {:method_name => method_name, :args => args, :block => block}
|
15
|
+
ensure_process
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_wo_dups(method_name, *args, &block)
|
19
|
+
h = {:method_name => method_name, :args => args, :block => block}
|
20
|
+
if @calls[-1] != h
|
21
|
+
@calls << h
|
22
|
+
ensure_process
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def add_wo_dups_current(method_name, *args, &block)
|
27
|
+
h = {:method_name => method_name, :args => args, :block => block}
|
28
|
+
if !@calls.include?(h) && @call != h
|
29
|
+
@calls << h
|
30
|
+
ensure_process
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def list
|
35
|
+
@calls
|
36
|
+
end
|
37
|
+
|
38
|
+
def names_list
|
39
|
+
list.map{|el| el[:method_name].to_sym }
|
40
|
+
end
|
41
|
+
|
42
|
+
def clear
|
43
|
+
@calls = []
|
44
|
+
end
|
45
|
+
|
46
|
+
alias :clear_pending_list :clear
|
47
|
+
|
48
|
+
# need, because of https://github.com/celluloid/celluloid/issues/22
|
49
|
+
def inspect
|
50
|
+
"Celluloid::Chain(#{@target_class}: #{@calls.size})"
|
51
|
+
end
|
52
|
+
|
53
|
+
attr_reader :running
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def ensure_process
|
58
|
+
unless @running
|
59
|
+
@running = true
|
60
|
+
async.process
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def process
|
65
|
+
while @call = @calls.shift
|
66
|
+
@running = true
|
67
|
+
@target.send(@call[:method_name], *@call[:args], &@call[:block]) if @target.alive?
|
68
|
+
end
|
69
|
+
@running = false
|
70
|
+
end
|
71
|
+
end
|