documentation updates
[god.git] / lib / god / watch.rb
blobaf8f38b47bc550eed517062b107460531ebf8722
1 module God
2   
3   class Watch < Base
4     # config
5     attr_accessor :name, :start, :stop, :restart, :interval, :grace
6     
7     # api
8     attr_accessor :behaviors, :conditions
9     
10     # internal
11     attr_accessor :mutex
12     
13     # 
14     def initialize(meddle)
15       @meddle = meddle
16       
17       # no grace period by default
18       self.grace = 0
19       
20       # keep track of which action each condition belongs to
21       @action = nil
22       
23       self.behaviors = []
24       
25       # the list of conditions for each action
26       self.conditions = {:start => [],
27                          :restart => []}
28                          
29       # mutex
30       self.mutex = Mutex.new
31     end
32     
33     def behavior(kind)
34       # create the behavior
35       begin
36         b = Behavior.generate(kind)
37       rescue NoSuchBehaviorError => e
38         abort e.message
39       end
40       
41       # send to block so config can set attributes
42       yield(b) if block_given?
43       
44       # abort if the Behavior is invalid, the Behavior will have printed
45       # out its own error messages by now
46       abort unless b.valid?
47       
48       self.behaviors << b
49     end
50     
51     def start_if
52       @action = :start
53       yield(self)
54       @action = nil
55     end
56     
57     def restart_if
58       @action = :restart
59       yield(self)
60       @action = nil
61     end
62     
63     # Instantiate a Condition of type +kind+ and pass it into the optional
64     # block. Attributes of the condition must be set in the config file
65     def condition(kind)
66       # must be in a _if block
67       unless @action
68         abort "Watch#condition can only be called from inside a start_if block"
69       end
70       
71       # create the condition
72       begin
73         c = Condition.generate(kind)
74       rescue NoSuchConditionError => e
75         abort e.message
76       end
77       
78       # send to block so config can set attributes
79       yield(c) if block_given?
80       
81       # call prepare on the condition
82       c.prepare
83       
84       # abort if the Condition is invalid, the Condition will have printed
85       # out its own error messages by now
86       unless c.valid?
87         abort
88       end
89       
90       # inherit interval from meddle if no poll condition specific interval was set
91       if c.kind_of?(PollCondition) && !c.interval
92         if self.interval
93           c.interval = self.interval
94         else
95           abort "No interval set for Condition '#{c.class.name}' in Watch '#{self.name}', and no default Watch interval from which to inherit"
96         end
97       end
98       
99       self.conditions[@action] << c
100     end
101         
102     # Schedule all poll conditions and register all condition events
103     def monitor
104       [:start, :restart].each do |cmd|
105         self.conditions[cmd].each do |c|
106           @meddle.timer.register(self, c, cmd) if c.kind_of?(PollCondition)
107         end
108       end
109     end
110     
111     def action(a, c)
112       case a
113       when :start
114         puts self.start
115         call_action(c, :start, self.start)
116         sleep(self.grace)
117       when :restart
118         if self.restart
119           puts self.restart
120           call_action(c, :restart, self.restart)
121         else
122           action(:stop, c)
123           action(:start, c)
124         end
125         sleep(self.grace)
126       when :stop
127         puts self.stop
128         call_action(c, :stop, self.stop)
129         sleep(self.grace)
130       end      
131     end
132     
133     def call_action(condition, action, command)
134       # before
135       (self.behaviors + [condition]).each { |b| b.send("before_#{action}") }
136       
137       # action
138       if command.kind_of?(String)
139         # string command
140         system(command)
141       else
142         # lambda command
143         command.call
144       end
145       
146       # after
147       (self.behaviors + [condition]).each { |b| b.send("after_#{action}") }
148     end
149   end
150