puppet 0.9.2 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puppet might be problematic. Click here for more details.

Files changed (213) hide show
  1. data/CHANGELOG +58 -0
  2. data/README +21 -18
  3. data/Rakefile +176 -36
  4. data/bin/puppet +34 -48
  5. data/bin/puppetca +41 -28
  6. data/bin/puppetd +87 -65
  7. data/bin/puppetdoc +99 -23
  8. data/bin/puppetmasterd +72 -91
  9. data/conf/redhat/client.init +80 -0
  10. data/conf/redhat/client.sysconfig +11 -0
  11. data/conf/redhat/fileserver.conf +12 -0
  12. data/conf/redhat/puppet.spec +130 -0
  13. data/conf/redhat/server.init +89 -0
  14. data/conf/redhat/server.sysconfig +9 -0
  15. data/examples/code/allatonce +2 -2
  16. data/examples/code/assignments +1 -1
  17. data/examples/code/classing +2 -2
  18. data/examples/code/components +2 -2
  19. data/examples/code/file.bl +5 -5
  20. data/examples/code/filedefaults +2 -2
  21. data/examples/code/fileparsing +1 -1
  22. data/examples/code/filerecursion +1 -1
  23. data/examples/code/functions +1 -1
  24. data/examples/code/groups +1 -1
  25. data/examples/code/importing +1 -1
  26. data/examples/code/nodes +1 -1
  27. data/examples/code/one +1 -1
  28. data/examples/code/relationships +2 -2
  29. data/examples/code/simpletests +5 -5
  30. data/examples/code/snippets/argumentdefaults +2 -2
  31. data/examples/code/snippets/casestatement +16 -8
  32. data/examples/code/snippets/classheirarchy.pp +4 -4
  33. data/examples/code/snippets/classincludes.pp +4 -4
  34. data/examples/code/snippets/classpathtest +2 -2
  35. data/examples/code/snippets/componentmetaparams.pp +11 -0
  36. data/examples/code/snippets/dirchmod +5 -5
  37. data/examples/code/snippets/emptyclass.pp +9 -0
  38. data/examples/code/snippets/failmissingexecpath.pp +1 -1
  39. data/examples/code/snippets/falsevalues.pp +1 -1
  40. data/examples/code/snippets/filecreate +5 -5
  41. data/examples/code/snippets/implicititeration +5 -5
  42. data/examples/code/snippets/multipleinstances +4 -4
  43. data/examples/code/snippets/namevartest +3 -3
  44. data/examples/code/snippets/scopetest +1 -1
  45. data/examples/code/snippets/selectorvalues.pp +3 -3
  46. data/examples/code/snippets/simpledefaults +2 -2
  47. data/examples/code/snippets/simpleselector +5 -5
  48. data/examples/code/snippets/singleary.pp +19 -0
  49. data/examples/root/etc/init.d/sleeper +3 -2
  50. data/ext/emacs/puppet-mode-init.el +6 -0
  51. data/ext/emacs/puppet-mode.el +189 -0
  52. data/ext/ldap/puppet.schema +17 -0
  53. data/ext/{module:puppet → module_puppet} +30 -31
  54. data/ext/vim/filetype.vim +9 -0
  55. data/ext/vim/puppet.vim +87 -0
  56. data/install.rb +63 -30
  57. data/lib/puppet.rb +216 -122
  58. data/lib/puppet/client.rb +51 -416
  59. data/lib/puppet/client/ca.rb +17 -0
  60. data/lib/puppet/client/dipper.rb +78 -0
  61. data/lib/puppet/client/file.rb +20 -0
  62. data/lib/puppet/client/log.rb +17 -0
  63. data/lib/puppet/client/master.rb +246 -0
  64. data/lib/puppet/client/proxy.rb +27 -0
  65. data/lib/puppet/client/status.rb +7 -0
  66. data/lib/puppet/config.rb +563 -13
  67. data/lib/puppet/daemon.rb +50 -22
  68. data/lib/puppet/element.rb +4 -4
  69. data/lib/puppet/event-loop.rb +1 -0
  70. data/lib/puppet/event-loop/better-definers.rb +367 -0
  71. data/lib/puppet/event-loop/event-loop.rb +355 -0
  72. data/lib/puppet/event-loop/signal-system.rb +220 -0
  73. data/lib/puppet/event.rb +9 -11
  74. data/lib/puppet/filetype.rb +195 -0
  75. data/lib/puppet/log.rb +35 -12
  76. data/lib/puppet/metric.rb +2 -2
  77. data/lib/puppet/networkclient.rb +145 -0
  78. data/lib/puppet/parameter.rb +335 -0
  79. data/lib/puppet/parser/ast.rb +42 -1453
  80. data/lib/puppet/parser/ast/astarray.rb +88 -0
  81. data/lib/puppet/parser/ast/branch.rb +47 -0
  82. data/lib/puppet/parser/ast/caseopt.rb +66 -0
  83. data/lib/puppet/parser/ast/casestatement.rb +78 -0
  84. data/lib/puppet/parser/ast/classdef.rb +78 -0
  85. data/lib/puppet/parser/ast/compdef.rb +111 -0
  86. data/lib/puppet/parser/ast/component.rb +105 -0
  87. data/lib/puppet/parser/ast/hostclass.rb +82 -0
  88. data/lib/puppet/parser/ast/leaf.rb +86 -0
  89. data/lib/puppet/parser/ast/node.rb +103 -0
  90. data/lib/puppet/parser/ast/nodedef.rb +68 -0
  91. data/lib/puppet/parser/ast/objectdef.rb +336 -0
  92. data/lib/puppet/parser/ast/objectparam.rb +30 -0
  93. data/lib/puppet/parser/ast/objectref.rb +76 -0
  94. data/lib/puppet/parser/ast/selector.rb +60 -0
  95. data/lib/puppet/parser/ast/typedefaults.rb +45 -0
  96. data/lib/puppet/parser/ast/vardef.rb +44 -0
  97. data/lib/puppet/parser/interpreter.rb +31 -14
  98. data/lib/puppet/parser/lexer.rb +2 -4
  99. data/lib/puppet/parser/parser.rb +332 -242
  100. data/lib/puppet/parser/scope.rb +55 -38
  101. data/lib/puppet/server.rb +43 -44
  102. data/lib/puppet/server/authstore.rb +3 -6
  103. data/lib/puppet/server/ca.rb +5 -2
  104. data/lib/puppet/server/filebucket.rb +2 -4
  105. data/lib/puppet/server/fileserver.rb +28 -12
  106. data/lib/puppet/server/logger.rb +15 -4
  107. data/lib/puppet/server/master.rb +62 -7
  108. data/lib/puppet/sslcertificates.rb +41 -607
  109. data/lib/puppet/sslcertificates/ca.rb +291 -0
  110. data/lib/puppet/sslcertificates/certificate.rb +283 -0
  111. data/lib/puppet/statechange.rb +6 -1
  112. data/lib/puppet/storage.rb +67 -56
  113. data/lib/puppet/transaction.rb +25 -9
  114. data/lib/puppet/transportable.rb +102 -22
  115. data/lib/puppet/type.rb +1096 -315
  116. data/lib/puppet/type/component.rb +30 -21
  117. data/lib/puppet/type/cron.rb +409 -448
  118. data/lib/puppet/type/exec.rb +234 -174
  119. data/lib/puppet/type/group.rb +65 -82
  120. data/lib/puppet/type/nameservice.rb +247 -3
  121. data/lib/puppet/type/nameservice/netinfo.rb +29 -40
  122. data/lib/puppet/type/nameservice/objectadd.rb +52 -66
  123. data/lib/puppet/type/nameservice/posix.rb +6 -194
  124. data/lib/puppet/type/package.rb +447 -295
  125. data/lib/puppet/type/package/apt.rb +51 -50
  126. data/lib/puppet/type/package/bsd.rb +82 -0
  127. data/lib/puppet/type/package/dpkg.rb +85 -88
  128. data/lib/puppet/type/package/rpm.rb +67 -63
  129. data/lib/puppet/type/package/sun.rb +119 -98
  130. data/lib/puppet/type/package/yum.rb +41 -37
  131. data/lib/puppet/type/parsedtype.rb +295 -0
  132. data/lib/puppet/type/parsedtype/host.rb +143 -0
  133. data/lib/puppet/type/parsedtype/port.rb +232 -0
  134. data/lib/puppet/type/parsedtype/sshkey.rb +129 -0
  135. data/lib/puppet/type/pfile.rb +484 -460
  136. data/lib/puppet/type/pfile/checksum.rb +237 -181
  137. data/lib/puppet/type/pfile/content.rb +67 -0
  138. data/lib/puppet/type/pfile/ensure.rb +212 -0
  139. data/lib/puppet/type/pfile/group.rb +106 -105
  140. data/lib/puppet/type/pfile/mode.rb +98 -101
  141. data/lib/puppet/type/pfile/source.rb +228 -209
  142. data/lib/puppet/type/pfile/type.rb +18 -21
  143. data/lib/puppet/type/pfile/uid.rb +127 -130
  144. data/lib/puppet/type/pfilebucket.rb +68 -63
  145. data/lib/puppet/type/schedule.rb +341 -0
  146. data/lib/puppet/type/service.rb +351 -255
  147. data/lib/puppet/type/service/base.rb +9 -14
  148. data/lib/puppet/type/service/debian.rb +32 -38
  149. data/lib/puppet/type/service/init.rb +130 -130
  150. data/lib/puppet/type/service/smf.rb +48 -20
  151. data/lib/puppet/type/state.rb +229 -16
  152. data/lib/puppet/type/symlink.rb +51 -63
  153. data/lib/puppet/type/tidy.rb +105 -102
  154. data/lib/puppet/type/user.rb +118 -180
  155. data/lib/puppet/util.rb +100 -6
  156. data/test/certmgr/certmgr.rb +0 -1
  157. data/test/client/client.rb +4 -4
  158. data/test/executables/puppetbin.rb +7 -14
  159. data/test/executables/puppetca.rb +18 -24
  160. data/test/executables/puppetd.rb +7 -16
  161. data/test/executables/puppetmasterd.rb +7 -9
  162. data/test/executables/puppetmodule.rb +11 -16
  163. data/test/language/ast.rb +11 -7
  164. data/test/language/interpreter.rb +1 -1
  165. data/test/language/scope.rb +2 -0
  166. data/test/language/snippets.rb +30 -5
  167. data/test/language/transportable.rb +77 -0
  168. data/test/other/config.rb +316 -0
  169. data/test/other/events.rb +22 -21
  170. data/test/other/log.rb +14 -14
  171. data/test/other/metrics.rb +4 -8
  172. data/test/other/overrides.rb +5 -5
  173. data/test/other/relationships.rb +4 -2
  174. data/test/other/storage.rb +64 -3
  175. data/test/other/transactions.rb +20 -20
  176. data/test/parser/parser.rb +7 -4
  177. data/test/puppet/conffiles.rb +12 -12
  178. data/test/puppet/defaults.rb +13 -11
  179. data/test/puppet/utiltest.rb +14 -11
  180. data/test/puppettest.rb +156 -48
  181. data/test/server/bucket.rb +2 -2
  182. data/test/server/fileserver.rb +6 -6
  183. data/test/server/logger.rb +19 -11
  184. data/test/server/master.rb +33 -4
  185. data/test/server/server.rb +2 -7
  186. data/test/types/basic.rb +5 -7
  187. data/test/types/component.rb +22 -18
  188. data/test/types/cron.rb +111 -44
  189. data/test/types/exec.rb +116 -59
  190. data/test/types/file.rb +262 -137
  191. data/test/types/filebucket.rb +13 -15
  192. data/test/types/fileignoresource.rb +12 -16
  193. data/test/types/filesources.rb +73 -48
  194. data/test/types/filetype.rb +13 -15
  195. data/test/types/group.rb +15 -13
  196. data/test/types/host.rb +146 -0
  197. data/test/types/package.rb +74 -63
  198. data/test/types/port.rb +139 -0
  199. data/test/types/query.rb +8 -8
  200. data/test/types/schedule.rb +335 -0
  201. data/test/types/service.rb +137 -21
  202. data/test/types/sshkey.rb +140 -0
  203. data/test/types/symlink.rb +3 -5
  204. data/test/types/tidy.rb +5 -14
  205. data/test/types/type.rb +67 -11
  206. data/test/types/user.rb +25 -23
  207. metadata +186 -122
  208. data/lib/puppet/type/pfile/create.rb +0 -108
  209. data/lib/puppet/type/pprocess.rb +0 -97
  210. data/lib/puppet/type/typegen.rb +0 -149
  211. data/lib/puppet/type/typegen/filerecord.rb +0 -243
  212. data/lib/puppet/type/typegen/filetype.rb +0 -316
  213. data/test/other/state.rb +0 -106
@@ -0,0 +1,355 @@
1
+ ## event-loop.rb --- high-level IO multiplexer
2
+ # Copyright (C) 2005 Daniel Brockman
3
+
4
+ # This program is free software; you can redistribute it
5
+ # and/or modify it under the terms of the GNU General Public
6
+ # License as published by the Free Software Foundation;
7
+ # either version 2 of the License, or (at your option) any
8
+ # later version.
9
+
10
+ # This file is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty
12
+ # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
+ # See the GNU General Public License for more details.
14
+
15
+ # You should have received a copy of the GNU General Public
16
+ # License along with this program; if not, write to the Free
17
+ # Software Foundation, 51 Franklin Street, Fifth Floor,
18
+ # Boston, MA 02110-1301, USA.
19
+
20
+ require "puppet/event-loop/better-definers"
21
+ require "puppet/event-loop/signal-system"
22
+
23
+ require "fcntl"
24
+
25
+ class EventLoop
26
+ include SignalEmitter
27
+
28
+ IO_STATES = [:readable, :writable, :exceptional]
29
+
30
+ class << self
31
+ def default ; @default ||= new end
32
+ def default= x ; @default = x end
33
+
34
+ def current
35
+ Thread.current["event-loop::current"] || default end
36
+ def current= x
37
+ Thread.current["event-loop::current"] = x end
38
+
39
+ def with_current (new)
40
+ if current == new
41
+ yield
42
+ else
43
+ begin
44
+ old = self.current
45
+ self.current = new
46
+ yield
47
+ ensure
48
+ self.current = old
49
+ end
50
+ end
51
+ end
52
+
53
+ def method_missing (name, *args, &block)
54
+ if current.respond_to? name
55
+ current.__send__(name, *args, &block)
56
+ else
57
+ super
58
+ end
59
+ end
60
+ end
61
+
62
+ define_signals :before_sleep, :after_sleep
63
+
64
+ def initialize
65
+ @running = false
66
+ @awake = false
67
+ @wakeup_time = nil
68
+ @timers = []
69
+
70
+ @io_arrays = [[], [], []]
71
+ @ios = Hash.new do |h, k| raise ArgumentError,
72
+ "invalid IO event: #{k}", caller(2) end
73
+ IO_STATES.each_with_index { |x, i| @ios[x] = @io_arrays[i] }
74
+
75
+ @notify_src, @notify_snk = IO.pipe
76
+
77
+ @notify_src.will_block = false
78
+ @notify_snk.will_block = false
79
+
80
+ # Each time a byte is sent through the notification pipe
81
+ # we need to read it, or IO.select will keep returning.
82
+ monitor_io(@notify_src, :readable)
83
+ @notify_src.extend(Watchable)
84
+ @notify_src.on_readable do
85
+ begin
86
+ @notify_src.sysread(256)
87
+ rescue Errno::EAGAIN
88
+ # The pipe wasn't readable after all.
89
+ end
90
+ end
91
+ end
92
+
93
+ define_opposite_accessors \
94
+ :stopped? => :running?,
95
+ :sleeping? => :awake?
96
+
97
+ def run
98
+ if block_given?
99
+ thread = Thread.new { run }
100
+ yield ; quit ; thread.join
101
+ else
102
+ running!
103
+ iterate while running?
104
+ end
105
+ ensure
106
+ quit
107
+ end
108
+
109
+ def iterate (user_timeout=nil)
110
+ t1, t2 = user_timeout, max_timeout
111
+ timeout = t1 && t2 ? [t1, t2].min : t1 || t2
112
+ select(timeout).zip(IO_STATES) do |ios, state|
113
+ ios.each { |x| x.signal(state) } if ios
114
+ end
115
+ end
116
+
117
+ private
118
+
119
+ def select (timeout)
120
+ @wakeup_time = timeout ? Time.now + timeout : nil
121
+ # puts "waiting: #{timeout} seconds"
122
+ signal :before_sleep ; sleeping!
123
+ IO.select(*@io_arrays + [timeout]) || []
124
+ ensure
125
+ awake! ; signal :after_sleep
126
+ @timers.each { |x| x.sound_alarm if x.ready? }
127
+ end
128
+
129
+ public
130
+
131
+ def quit ; stopped! ; wake_up ; self end
132
+
133
+ def monitoring_io? (io, event)
134
+ @ios[event].include? io end
135
+ def monitoring_timer? (timer)
136
+ @timers.include? timer end
137
+
138
+ def monitor_io (io, *events)
139
+ for event in events do
140
+ unless monitoring_io?(io, event)
141
+ @ios[event] << io ; wake_up
142
+ end
143
+ end
144
+ end
145
+
146
+ def monitor_timer (timer)
147
+ unless monitoring_timer? timer
148
+ @timers << timer
149
+ end
150
+ end
151
+
152
+ def check_timer (timer)
153
+ wake_up if timer.end_time < @wakeup_time
154
+ end
155
+
156
+ def ignore_io (io, *events)
157
+ events = IO_STATES if events.empty?
158
+ for event in events do
159
+ wake_up if @ios[event].delete(io)
160
+ end
161
+ end
162
+
163
+ def ignore_timer (timer)
164
+ # Don't need to wake up for this.
165
+ @timers.delete(timer)
166
+ end
167
+
168
+ def max_timeout
169
+ return nil if @timers.empty?
170
+ [@timers.collect { |x| x.time_left }.min, 0].max
171
+ end
172
+
173
+ def wake_up
174
+ @notify_snk.write('.') if sleeping?
175
+ end
176
+ end
177
+
178
+ class Symbol
179
+ def io_state?
180
+ EventLoop::IO_STATES.include? self
181
+ end
182
+ end
183
+
184
+ module EventLoop::Watchable
185
+ include SignalEmitter
186
+
187
+ define_signals :readable, :writable, :exceptional
188
+
189
+ def monitor_events (*events)
190
+ EventLoop.monitor_io(self, *events) end
191
+ def ignore_events (*events)
192
+ EventLoop.ignore_io(self, *events) end
193
+
194
+ define_soft_aliases \
195
+ :monitor_event => :monitor_events,
196
+ :ignore_event => :ignore_events
197
+
198
+ def close ; super
199
+ ignore_events end
200
+ def close_read ; super
201
+ ignore_event :readable end
202
+ def close_write ; super
203
+ ignore_event :writable end
204
+
205
+ module Automatic
206
+ include EventLoop::Watchable
207
+
208
+ def add_signal_handler (name, &handler) super
209
+ monitor_event(name) if name.io_state?
210
+ end
211
+
212
+ def remove_signal_handler (name, handler) super
213
+ if @signal_handlers[name].empty?
214
+ ignore_event(name) if name.io_state?
215
+ end
216
+ end
217
+ end
218
+ end
219
+
220
+ class IO
221
+ def on_readable &block
222
+ extend EventLoop::Watchable::Automatic
223
+ on_readable(&block)
224
+ end
225
+
226
+ def on_writable &block
227
+ extend EventLoop::Watchable::Automatic
228
+ on_writable(&block)
229
+ end
230
+
231
+ def on_exceptional &block
232
+ extend EventLoop::Watchable::Automatic
233
+ on_exceptional(&block)
234
+ end
235
+
236
+ def will_block?
237
+ require "fcntl"
238
+ fcntl(Fcntl::F_GETFL, 0) & Fcntl::O_NONBLOCK == 0
239
+ end
240
+
241
+ def will_block= (wants_blocking)
242
+ require "fcntl"
243
+ flags = fcntl(Fcntl::F_GETFL, 0)
244
+ if wants_blocking
245
+ flags &= ~Fcntl::O_NONBLOCK
246
+ else
247
+ flags |= Fcntl::O_NONBLOCK
248
+ end
249
+ fcntl(Fcntl::F_SETFL, flags)
250
+ end
251
+ end
252
+
253
+ class EventLoop::Timer
254
+ include SignalEmitter
255
+
256
+ DEFAULT_INTERVAL = 0.0
257
+ DEFAULT_TOLERANCE = 0.001
258
+
259
+ def initialize (options={}, &handler)
260
+ @running = false
261
+ @start_time = nil
262
+
263
+ if options.kind_of? Numeric
264
+ options = { :interval => options }
265
+ end
266
+
267
+ if options[:interval]
268
+ @interval = options[:interval].to_f
269
+ else
270
+ @interval = DEFAULT_INTERVAL
271
+ end
272
+
273
+ if options[:tolerance]
274
+ @tolerance = options[:tolerance].to_f
275
+ elsif DEFAULT_TOLERANCE < @interval
276
+ @tolerance = DEFAULT_TOLERANCE
277
+ else
278
+ @tolerance = 0.0
279
+ end
280
+
281
+ @event_loop = options[:event_loop] || EventLoop.current
282
+
283
+ if block_given?
284
+ add_signal_handler(:alarm, &handler)
285
+ start unless options[:start?] == false
286
+ else
287
+ start if options[:start?]
288
+ end
289
+ end
290
+
291
+ define_readers :interval, :tolerance
292
+ define_signal :alarm
293
+
294
+ def stopped? ; @start_time == nil end
295
+ def running? ; @start_time != nil end
296
+
297
+ def interval= (new_interval)
298
+ old_interval = @interval
299
+ @interval = new_interval
300
+ if new_interval < old_interval
301
+ @event_loop.check_timer(self)
302
+ end
303
+ end
304
+
305
+ def end_time
306
+ @start_time + @interval end
307
+ def time_left
308
+ end_time - Time.now end
309
+ def ready?
310
+ time_left <= @tolerance end
311
+
312
+ def restart
313
+ @start_time = Time.now
314
+ end
315
+
316
+ def sound_alarm
317
+ signal :alarm
318
+ restart if running?
319
+ end
320
+
321
+ def start
322
+ @start_time = Time.now
323
+ @event_loop.monitor_timer(self)
324
+ end
325
+
326
+ def stop
327
+ @start_time = nil
328
+ @event_loop.ignore_timer(self)
329
+ end
330
+ end
331
+
332
+ if __FILE__ == $0
333
+ require "test/unit"
334
+
335
+ class TimerTest < Test::Unit::TestCase
336
+ def setup
337
+ @timer = EventLoop::Timer.new(:interval => 0.001)
338
+ end
339
+
340
+ def test_timer
341
+ @timer.on_alarm do
342
+ puts "[#{@timer.time_left} seconds left after alarm]"
343
+ EventLoop.quit
344
+ end
345
+ 8.times do
346
+ t0 = Time.now
347
+ @timer.start ; EventLoop.run
348
+ t1 = Time.now
349
+ assert(t1 - t0 > @timer.interval - @timer.tolerance)
350
+ end
351
+ end
352
+ end
353
+ end
354
+
355
+ ## event-loop.rb ends here.
@@ -0,0 +1,220 @@
1
+ ## signal-system.rb --- simple intra-process signal system
2
+ # Copyright (C) 2005 Daniel Brockman
3
+
4
+ # This program is free software; you can redistribute it
5
+ # and/or modify it under the terms of the GNU General Public
6
+ # License as published by the Free Software Foundation;
7
+ # either version 2 of the License, or (at your option) any
8
+ # later version.
9
+
10
+ # This file is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty
12
+ # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
+ # See the GNU General Public License for more details.
14
+
15
+ # You should have received a copy of the GNU General Public
16
+ # License along with this program; if not, write to the Free
17
+ # Software Foundation, 51 Franklin Street, Fifth Floor,
18
+ # Boston, MA 02110-1301, USA.
19
+
20
+ require "puppet/event-loop/better-definers"
21
+
22
+ module SignalEmitterModule
23
+ def self.extended (object)
24
+ if object.kind_of? Module and not object < SignalEmitter
25
+ if object.respond_to? :fcall
26
+ # This is the way to call private methods
27
+ # in Ruby 1.9 as of November 16.
28
+ object.fcall :include, SignalEmitter
29
+ else
30
+ object.__send__ :include, SignalEmitter
31
+ end
32
+ end
33
+ end
34
+
35
+ def define_signal (name, slot=:before, &body)
36
+ # Can't use `define_method' and take a block pre-1.9.
37
+ class_eval %{ def on_#{name} &block
38
+ add_signal_handler(:#{name}, &block) end }
39
+ define_signal_handler(name, :before, &lambda {|*a|})
40
+ define_signal_handler(name, :after, &lambda {|*a|})
41
+ define_signal_handler(name, slot, &body) if block_given?
42
+ end
43
+
44
+ def define_signals (*names, &body)
45
+ names.each { |x| define_signal(x, &body) }
46
+ end
47
+
48
+ def define_signal_handler (name, slot=:before, &body)
49
+ case slot
50
+ when :before
51
+ define_protected_method "handle_#{name}", &body
52
+ when :after
53
+ define_protected_method "after_handle_#{name}", &body
54
+ else
55
+ raise ArgumentError, "invalid slot `#{slot.inspect}'; " +
56
+ "should be `:before' or `:after'", caller(1)
57
+ end
58
+ end
59
+ end
60
+
61
+ # This is an old name for the same thing.
62
+ SignalEmitterClass = SignalEmitterModule
63
+
64
+ module SignalEmitter
65
+ def self.included (includer)
66
+ if not includer.kind_of? SignalEmitterClass
67
+ includer.extend SignalEmitterClass
68
+ end
69
+ end
70
+
71
+ def __maybe_initialize_signal_emitter
72
+ @signal_handlers ||= Hash.new { |h, k| h[k] = Array.new }
73
+ @allow_dynamic_signals ||= false
74
+ end
75
+
76
+ define_accessors :allow_dynamic_signals?
77
+
78
+ def add_signal_handler (name, &handler)
79
+ __maybe_initialize_signal_emitter
80
+ @signal_handlers[name] << handler
81
+ return handler
82
+ end
83
+
84
+ define_soft_aliases [:on, :on_signal] => :add_signal_handler
85
+
86
+ def remove_signal_handler (name, handler)
87
+ __maybe_initialize_signal_emitter
88
+ @signal_handlers[name].delete(handler)
89
+ end
90
+
91
+ def __signal__ (name, *args, &block)
92
+ __maybe_initialize_signal_emitter
93
+ respond_to? "on_#{name}" or allow_dynamic_signals? or
94
+ fail "undefined signal `#{name}' for #{self}:#{self.class}"
95
+ __send__("handle_#{name}", *args, &block) if
96
+ respond_to? "handle_#{name}"
97
+ @signal_handlers[name].each { |x| x.call(*args, &block) }
98
+ __send__("after_handle_#{name}", *args, &block) if
99
+ respond_to? "after_handle_#{name}"
100
+ end
101
+
102
+ define_soft_alias :signal => :__signal__
103
+ end
104
+
105
+ # This module is indended to be a convenience mixin to be used by
106
+ # classes whose objects need to observe foreign signals. That is,
107
+ # if you want to observe some signals coming from an object, *you*
108
+ # should mix in this module.
109
+ #
110
+ # You cannot use this module at two different places of the same
111
+ # inheritance chain to observe signals coming from the same object.
112
+ #
113
+ # XXX: This has not seen much use, and I'd like to provide a
114
+ # better solution for the problem in the future.
115
+ module SignalObserver
116
+ def __maybe_initialize_signal_observer
117
+ @observed_signals ||= Hash.new do |signals, object|
118
+ signals[object] = Hash.new do |handlers, name|
119
+ handlers[name] = Array.new
120
+ end
121
+ end
122
+ end
123
+
124
+ def observe_signal (subject, name, &handler)
125
+ __maybe_initialize_signal_observer
126
+ @observed_signals[subject][name] << handler
127
+ subject.add_signal_handler(name, &handler)
128
+ end
129
+
130
+ def map_signals (source, pairs={})
131
+ pairs.each do |src_name, dst_name|
132
+ observe_signal(source, src_name) do |*args|
133
+ __signal__(dst_name, *args)
134
+ end
135
+ end
136
+ end
137
+
138
+ def absorb_signals (subject, *names)
139
+ names.each do |name|
140
+ observe_signal(subject, name) do |*args|
141
+ __signal__(name, *args)
142
+ end
143
+ end
144
+ end
145
+
146
+ define_soft_aliases \
147
+ :map_signal => :map_signals,
148
+ :absorb_signal => :absorb_signals
149
+
150
+ def ignore_signal (subject, name)
151
+ __maybe_initialize_signal_observer
152
+ __ignore_signal_1(subject, name)
153
+ @observed_signals.delete(subject) if
154
+ @observed_signals[subject].empty?
155
+ end
156
+
157
+ def ignore_signals (subject, *names)
158
+ __maybe_initialize_signal_observer
159
+ names = @observed_signals[subject] if names.empty?
160
+ names.each { |x| __ignore_signal_1(subject, x) }
161
+ end
162
+
163
+ private
164
+
165
+ def __ignore_signal_1(subject, name)
166
+ @observed_signals[subject][name].each do |handler|
167
+ subject.remove_signal_handler(name, handler) end
168
+ @observed_signals[subject].delete(name)
169
+ end
170
+ end
171
+
172
+ if __FILE__ == $0
173
+ require "test/unit"
174
+ class SignalEmitterTest < Test::Unit::TestCase
175
+ class X
176
+ include SignalEmitter
177
+ define_signal :foo
178
+ end
179
+
180
+ def setup
181
+ @x = X.new
182
+ end
183
+
184
+ def test_on_signal
185
+ moomin = 0
186
+ @x.on_signal(:foo) { moomin = 1 }
187
+ @x.signal :foo
188
+ assert moomin == 1
189
+ end
190
+
191
+ def test_on_foo
192
+ moomin = 0
193
+ @x.on_foo { moomin = 1 }
194
+ @x.signal :foo
195
+ assert moomin == 1
196
+ end
197
+
198
+ def test_multiple_on_signal
199
+ moomin = 0
200
+ @x.on_signal(:foo) { moomin += 1 }
201
+ @x.on_signal(:foo) { moomin += 2 }
202
+ @x.on_signal(:foo) { moomin += 4 }
203
+ @x.on_signal(:foo) { moomin += 8 }
204
+ @x.signal :foo
205
+ assert moomin == 15
206
+ end
207
+
208
+ def test_multiple_on_foo
209
+ moomin = 0
210
+ @x.on_foo { moomin += 1 }
211
+ @x.on_foo { moomin += 2 }
212
+ @x.on_foo { moomin += 4 }
213
+ @x.on_foo { moomin += 8 }
214
+ @x.signal :foo
215
+ assert moomin == 15
216
+ end
217
+ end
218
+ end
219
+
220
+ ## application-signals.rb ends here.