cond 0.2.1 → 0.3.0

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.
Files changed (48) hide show
  1. data/CHANGES.rdoc +12 -0
  2. data/MANIFEST +39 -0
  3. data/{README → README.rdoc} +23 -28
  4. data/Rakefile +23 -180
  5. data/devel/jumpstart.rb +970 -0
  6. data/install.rb +2 -3
  7. data/lib/cond.rb +38 -453
  8. data/lib/cond/code_section.rb +51 -0
  9. data/lib/cond/cond.rb +159 -0
  10. data/lib/cond/defaults.rb +73 -0
  11. data/lib/cond/dsl.rb +2 -0
  12. data/lib/cond/dsl_definition.rb +74 -0
  13. data/lib/cond/error.rb +17 -0
  14. data/lib/cond/handler.rb +10 -0
  15. data/lib/cond/handling_section.rb +12 -0
  16. data/lib/cond/kernel_raise.rb +59 -0
  17. data/lib/cond/message_proc.rb +15 -0
  18. data/lib/cond/restart.rb +10 -0
  19. data/lib/cond/restartable_section.rb +12 -0
  20. data/lib/cond/symbol_generator.rb +41 -0
  21. data/lib/cond/thread_local.rb +71 -0
  22. data/lib/cond/wrapping.rb +45 -0
  23. data/readmes/restarts.rb +1 -2
  24. data/readmes/seibel_pcl.rb +1 -2
  25. data/{examples/bad_example.rb → spec/bad_spec.rb} +2 -2
  26. data/spec/basic_spec.rb +2 -2
  27. data/{examples/calc_example.rb → spec/calc_spec.rb} +2 -2
  28. data/spec/{common.rb → cond_spec_base.rb} +3 -20
  29. data/spec/error_spec.rb +2 -2
  30. data/spec/leave_again_spec.rb +10 -10
  31. data/spec/matching_spec.rb +1 -1
  32. data/spec/raise_spec.rb +1 -1
  33. data/spec/readme_spec.rb +10 -0
  34. data/spec/reraise_spec.rb +2 -2
  35. data/{examples/restarts_example.rb → spec/restarts_spec.rb} +7 -4
  36. data/{examples/seibel_example.rb → spec/seibel_spec.rb} +4 -6
  37. data/spec/symbols_spec.rb +2 -2
  38. data/spec/thread_local_spec.rb +8 -8
  39. data/spec/wrapping_spec.rb +2 -2
  40. metadata +110 -42
  41. data/cond.gemspec +0 -37
  42. data/examples/readme_example.rb +0 -27
  43. data/lib/cond/cond_private/defaults.rb +0 -78
  44. data/lib/cond/cond_private/symbol_generator.rb +0 -45
  45. data/lib/cond/cond_private/thread_local.rb +0 -77
  46. data/spec/specs_spec.rb +0 -16
  47. data/support/quix/ruby.rb +0 -51
  48. data/support/quix/simple_installer.rb +0 -88
data/install.rb CHANGED
@@ -1,3 +1,2 @@
1
- $LOAD_PATH.unshift "./support"
2
- require 'quix/simple_installer'
3
- Quix::SimpleInstaller.new.run
1
+ load './devel/jumpstart.rb'
2
+ Jumpstart::SimpleInstaller.new.run
data/lib/cond.rb CHANGED
@@ -1,456 +1,41 @@
1
-
2
- require 'cond/cond_private/thread_local'
3
- require 'cond/cond_private/symbol_generator'
4
- require 'cond/cond_private/defaults'
5
-
6
1
  #
7
- # Resolve errors without unwinding the stack.
2
+ # Copyright (c) 2008, 2009 James M. Lawrence. All rights reserved.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person
5
+ # obtaining a copy of this software and associated documentation files
6
+ # (the "Software"), to deal in the Software without restriction,
7
+ # including without limitation the rights to use, copy, modify, merge,
8
+ # publish, distribute, sublicense, and/or sell copies of the Software,
9
+ # and to permit persons to whom the Software is furnished to do so,
10
+ # subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19
+ # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20
+ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21
+ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ # SOFTWARE.
8
23
  #
9
- module Cond
10
- module CondPrivate
11
- class MessageProc < Proc
12
- def initialize(message = "", &block)
13
- @message = message
14
- end
15
-
16
- def message
17
- @message
18
- end
19
- end
20
- end
21
-
22
- #
23
- # A restart. Use of this class is optional: you could pass lambdas
24
- # to Cond.with_restarts, but you'll miss the description string
25
- # shown inside Cond.default_handler.
26
- #
27
- class Restart < CondPrivate::MessageProc
28
- end
29
-
30
- #
31
- # A handler. Use of this class is optional: you could pass lambdas
32
- # to Cond.with_handlers, but you'll miss the description string
33
- # shown by whichever tools might use it (currently none).
34
- #
35
- class Handler < CondPrivate::MessageProc
36
- end
37
-
38
- ######################################################################
39
- # errors
40
-
41
- #
42
- # Cond.invoke_restart was called with an unknown restart.
43
- #
44
- class NoRestartError < StandardError
45
- end
46
-
47
- #
48
- # `handle', `restart', `leave', or `again' called out of context.
49
- #
50
- class ContextError < StandardError
51
- end
52
-
53
- ######################################################################
54
- # singleton methods
55
-
56
- class << self
57
- #
58
- # Register a set of handlers. The given hash is merged with the
59
- # set of current handlers.
60
- #
61
- # When the block exits, the previous set of handlers (if any) are
62
- # restored.
63
- #
64
- def with_handlers(handlers)
65
- # note: leave unfactored due to notable yield vs &block performance
66
- handlers_stack.push(handlers_stack.last.merge(handlers))
67
- begin
68
- yield
69
- ensure
70
- handlers_stack.pop
71
- end
72
- end
73
-
74
- #
75
- # Register a set of restarts. The given hash is merged with the
76
- # set of current restarts.
77
- #
78
- # When the block exits, the previous set of restarts (if any) are
79
- # restored.
80
- #
81
- def with_restarts(restarts)
82
- # note: leave unfactored due to notable yield vs &block performance
83
- restarts_stack.push(restarts_stack.last.merge(restarts))
84
- begin
85
- yield
86
- ensure
87
- restarts_stack.pop
88
- end
89
- end
90
-
91
- #
92
- # A default handler is provided which runs a simple
93
- # choose-a-restart input loop when +raise+ is called.
94
- #
95
- def with_default_handlers
96
- # note: leave unfactored due to notable yield vs &block performance
97
- with_handlers(defaults.handlers) {
98
- yield
99
- }
100
- end
101
-
102
- #
103
- # The current set of restarts which have been registered.
104
- #
105
- def available_restarts
106
- restarts_stack.last
107
- end
108
-
109
- #
110
- # Find the closest-matching handler for the given Exception.
111
- #
112
- def find_handler(target) #:nodoc:
113
- find_handler_from(handlers_stack.last, target)
114
- end
115
-
116
- def find_handler_from(handlers, target) #:nodoc:
117
- handlers.fetch(target) {
118
- found = handlers.inject(Array.new) { |acc, (klass, func)|
119
- index = target.ancestors.index(klass)
120
- if index
121
- acc << [index, func]
122
- else
123
- acc
124
- end
125
- }.sort_by { |t| t.first }.first
126
- found and found[1]
127
- }
128
- end
129
-
130
- def run_code_section(klass, &block) #:nodoc:
131
- section = klass.new(&block)
132
- Cond.code_section_stack.push(section)
133
- begin
134
- section.instance_eval { run }
135
- ensure
136
- Cond.code_section_stack.pop
137
- end
138
- end
139
-
140
- def check_context(keyword) #:nodoc:
141
- section = Cond.code_section_stack.last
142
- case keyword
143
- when :restart
144
- unless section.is_a? CondPrivate::RestartableSection
145
- Cond.original_raise(
146
- ContextError,
147
- "`#{keyword}' called outside of `restartable' block"
148
- )
149
- end
150
- when :handle
151
- unless section.is_a? CondPrivate::HandlingSection
152
- Cond.original_raise(
153
- ContextError,
154
- "`#{keyword}' called outside of `handling' block"
155
- )
156
- end
157
- when :leave, :again
158
- unless section
159
- Cond.original_raise(
160
- ContextError,
161
- "`#{keyword}' called outside of `handling' or `restartable' block"
162
- )
163
- end
164
- end
165
- end
166
-
167
- ###############################################
168
- # wrapping
169
-
170
- #
171
- # Allow handlers to be called from C code by wrapping a method with
172
- # begin/rescue. Returns the aliased name of the original method.
173
- #
174
- # See the README.
175
- #
176
- # Example:
177
- #
178
- # Cond.wrap_instance_method(Fixnum, :/)
179
- #
180
- def wrap_instance_method(mod, method)
181
- original = "cond_original_#{mod.inspect}_#{method.inspect}"
182
- # TODO: jettison 1.8.6, remove eval and use |&block|
183
- # TODO: fix rcov bug -- does not see %{}
184
- mod.module_eval <<-eval_end
185
- alias_method :'#{original}', :'#{method}'
186
- def #{method}(*args, &block)
187
- begin
188
- send(:'#{original}', *args, &block)
189
- rescue Exception => e
190
- raise e
191
- end
192
- end
193
- eval_end
194
- original
195
- end
196
-
197
- #
198
- # Allow handlers to be called from C code by wrapping a method with
199
- # begin/rescue. Returns the aliased name of the original method.
200
- #
201
- # See the README.
202
- #
203
- # Example:
204
- #
205
- # Cond.wrap_singleton_method(IO, :read)
206
- #
207
- def wrap_singleton_method(mod, method)
208
- singleton_class = class << mod ; self ; end
209
- wrap_instance_method(singleton_class, method)
210
- end
211
-
212
- ###############################################
213
- # original raise
214
-
215
- define_method :original_raise, Kernel.instance_method(:raise)
216
-
217
- ######################################################################
218
- # data -- all data is per-thread and fetched from the singleton class
219
- #
220
- # Cond.defaults contains the default handlers. To replace it,
221
- # call
222
- #
223
- # Cond.defaults.clear(&block)
224
- #
225
- # where &block creates a new instance of your class which
226
- # implements the method 'handlers'.
227
- #
228
- # Note that &block should return a brand new instance. Otherwise
229
- # the returned object will be shared across threads.
230
- #
231
-
232
- stack_0 = lambda { Array.new }
233
- stack_1 = lambda { Array.new.push(Hash.new) }
234
- defaults = lambda { CondPrivate::Defaults.new }
235
- {
236
- :code_section_stack => stack_0,
237
- :exception_stack => stack_0,
238
- :handlers_stack => stack_1,
239
- :restarts_stack => stack_1,
240
- :defaults => defaults,
241
- }.each_pair { |name, create|
242
- include CondPrivate::ThreadLocal.reader_module(name) {
243
- create.call
244
- }
245
- }
246
-
247
- include CondPrivate::ThreadLocal.accessor_module(:reraise_count) { 0 }
248
- end
249
-
250
- ######################################################################
251
-
252
- module CondPrivate
253
- class CodeSection #:nodoc:
254
- include SymbolGenerator
255
-
256
- def initialize(with, &block)
257
- @with = with
258
- @block = block
259
- @again_args = []
260
- @leave, @again = gensym, gensym
261
- SymbolGenerator.track(self, [@leave, @again])
262
- end
263
-
264
- def again(*args)
265
- @again_args = (
266
- case args.size
267
- when 0
268
- []
269
- when 1
270
- args.first
271
- else
272
- args
273
- end
274
- )
275
- throw @again
276
- end
277
-
278
- def leave(*args)
279
- case args.size
280
- when 0
281
- throw @leave
282
- when 1
283
- throw @leave, args.first
284
- else
285
- throw @leave, args
286
- end
287
- end
288
-
289
- def run
290
- catch(@leave) {
291
- while true
292
- catch(@again) {
293
- Cond.send(@with, Hash.new) {
294
- throw @leave, @block.call(*@again_args)
295
- }
296
- }
297
- end
298
- }
299
- end
300
- end
301
-
302
- class RestartableSection < CodeSection #:nodoc:
303
- def initialize(&block)
304
- super(:with_restarts, &block)
305
- end
306
-
307
- def restart(sym, message, &block)
308
- Cond.restarts_stack.last[sym] = Restart.new(message, &block)
309
- end
310
- end
311
-
312
- class HandlingSection < CodeSection #:nodoc:
313
- def initialize(&block)
314
- super(:with_handlers, &block)
315
- end
316
-
317
- def handle(sym, message, &block)
318
- Cond.handlers_stack.last[sym] = Handler.new(message, &block)
319
- end
320
- end
321
- end
322
-
323
- ######################################################################
324
- # shiny exterior
325
-
326
- module_function
327
-
328
- #
329
- # Begin a handling block. Inside this block, a matching handler
330
- # gets called when +raise+ gets called.
331
- #
332
- def handling(&block)
333
- Cond.run_code_section(CondPrivate::HandlingSection, &block)
334
- end
335
-
336
- #
337
- # Begin a restartable block. A handler may transfer control to one
338
- # of the restarts in this block.
339
- #
340
- def restartable(&block)
341
- Cond.run_code_section(CondPrivate::RestartableSection, &block)
342
- end
343
-
344
- #
345
- # Define a handler.
346
- #
347
- # The exception instance is passed to the block.
348
- #
349
- def handle(arg, message = "", &block)
350
- Cond.check_context(:handle)
351
- Cond.code_section_stack.last.handle(arg, message, &block)
352
- end
353
-
354
- #
355
- # Define a restart.
356
- #
357
- # When a handler calls invoke_restart, it may pass additional
358
- # arguments which are in turn passed to &block.
359
- #
360
- def restart(arg, message = "", &block)
361
- Cond.check_context(:restart)
362
- Cond.code_section_stack.last.restart(arg, message, &block)
363
- end
364
-
365
- #
366
- # Leave the current handling or restartable block, optionally
367
- # providing a value for the block.
368
- #
369
- # The semantics are the same as 'return'. When given multiple
370
- # arguments, it returns an array. When given one argument, it
371
- # returns only that argument (not an array).
372
- #
373
- def leave(*args)
374
- Cond.check_context(:leave)
375
- Cond.code_section_stack.last.leave(*args)
376
- end
377
-
378
- #
379
- # Run the handling or restartable block again.
380
- #
381
- # Optionally pass arguments which are given to the block.
382
- #
383
- def again(*args)
384
- Cond.check_context(:again)
385
- Cond.code_section_stack.last.again(*args)
386
- end
387
-
388
- #
389
- # Call a restart from a handler; optionally pass it some arguments.
390
- #
391
- def invoke_restart(name, *args, &block)
392
- Cond.available_restarts.fetch(name) {
393
- raise NoRestartError,
394
- "Did not find `#{name.inspect}' in available restarts"
395
- }.call(*args, &block)
396
- end
397
- end
398
24
 
399
- module Kernel
400
- remove_method :raise
401
- def raise(*args)
402
- if Cond.handlers_stack.last.empty?
403
- # not using Cond
404
- Cond.original_raise(*args)
405
- else
406
- last_exception, current_handler = Cond.exception_stack.last
407
- exception = (
408
- if last_exception and args.empty?
409
- last_exception
410
- else
411
- begin
412
- Cond.original_raise(*args)
413
- rescue Exception => e
414
- e
415
- end
416
- end
417
- )
418
- if current_handler
419
- # inside a handler
420
- handler = loop {
421
- Cond.reraise_count += 1
422
- handlers = Cond.handlers_stack[-1 - Cond.reraise_count]
423
- if handlers.nil?
424
- break nil
425
- end
426
- found = Cond.find_handler_from(handlers, exception.class)
427
- if found and found != current_handler
428
- break found
429
- end
430
- }
431
- if handler
432
- handler.call(exception)
433
- else
434
- Cond.reraise_count = 0
435
- Cond.original_raise(exception)
436
- end
437
- else
438
- # not inside a handler
439
- Cond.reraise_count = 0
440
- handler = Cond.find_handler(exception.class)
441
- if handler
442
- Cond.exception_stack.push([exception, handler])
443
- begin
444
- handler.call(exception)
445
- ensure
446
- Cond.exception_stack.pop
447
- end
448
- else
449
- Cond.original_raise(exception)
450
- end
451
- end
452
- end
453
- end
454
- remove_method :fail
455
- alias_method :fail, :raise
456
- end
25
+ require 'thread'
26
+ require 'enumerator' if RUBY_VERSION <= "1.8.6"
27
+
28
+ require 'cond/symbol_generator'
29
+ require 'cond/thread_local'
30
+ require 'cond/defaults'
31
+ require 'cond/message_proc'
32
+ require 'cond/restart'
33
+ require 'cond/handler'
34
+ require 'cond/error'
35
+ require 'cond/code_section'
36
+ require 'cond/restartable_section'
37
+ require 'cond/handling_section'
38
+ require 'cond/dsl_definition'
39
+ require 'cond/wrapping'
40
+ require 'cond/cond'
41
+ require 'cond/kernel_raise'