Module: Cond
- Defined in:
- lib/cond.rb,
lib/cond/cond_private/defaults.rb,
lib/cond/cond_private/thread_local.rb,
lib/cond/cond_private/symbol_generator.rb
Overview
Resolve errors without unwinding the stack.
Defined Under Namespace
Modules: CondPrivate Classes: ContextError, Handler, NoRestartError, Restart
Class Method Summary collapse
-
.again(*args) ⇒ Object
Run the handling or restartable block again.
-
.available_restarts ⇒ Object
The current set of restarts which have been registered.
-
.check_context(keyword) ⇒ Object
:nodoc:.
-
.find_handler(target) ⇒ Object
Find the closest-matching handler for the given Exception.
-
.find_handler_from(handlers, target) ⇒ Object
:nodoc:.
-
.handle(arg, message = "", &block) ⇒ Object
Define a handler.
-
.handling(&block) ⇒ Object
Begin a handling block.
-
.invoke_restart(name, *args, &block) ⇒ Object
Call a restart from a handler; optionally pass it some arguments.
-
.leave(*args) ⇒ Object
Leave the current handling or restartable block, optionally providing a value for the block.
-
.restart(arg, message = "", &block) ⇒ Object
Define a restart.
-
.restartable(&block) ⇒ Object
Begin a restartable block.
-
.run_code_section(klass, &block) ⇒ Object
:nodoc:.
-
.with_default_handlers ⇒ Object
A default handler is provided which runs a simple choose-a-restart input loop when
raiseis called. -
.with_handlers(handlers) ⇒ Object
Register a set of handlers.
-
.with_restarts(restarts) ⇒ Object
Register a set of restarts.
-
.wrap_instance_method(mod, method) ⇒ Object
Allow handlers to be called from C code by wrapping a method with begin/rescue.
-
.wrap_singleton_method(mod, method) ⇒ Object
Allow handlers to be called from C code by wrapping a method with begin/rescue.
Class Method Details
.again(*args) ⇒ Object
Run the handling or restartable block again.
Optionally pass arguments which are given to the block.
383 384 385 386 |
# File 'lib/cond.rb', line 383 def again(*args) Cond.check_context(:again) Cond.code_section_stack.last.again(*args) end |
.available_restarts ⇒ Object
The current set of restarts which have been registered.
105 106 107 |
# File 'lib/cond.rb', line 105 def available_restarts restarts_stack.last end |
.check_context(keyword) ⇒ Object
:nodoc:
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/cond.rb', line 140 def check_context(keyword) #:nodoc: section = Cond.code_section_stack.last case keyword when :restart unless section.is_a? CondPrivate::RestartableSection Cond.original_raise( ContextError, "`#{keyword}' called outside of `restartable' block" ) end when :handle unless section.is_a? CondPrivate::HandlingSection Cond.original_raise( ContextError, "`#{keyword}' called outside of `handling' block" ) end when :leave, :again unless section Cond.original_raise( ContextError, "`#{keyword}' called outside of `handling' or `restartable' block" ) end end end |
.find_handler(target) ⇒ Object
Find the closest-matching handler for the given Exception.
112 113 114 |
# File 'lib/cond.rb', line 112 def find_handler(target) #:nodoc: find_handler_from(handlers_stack.last, target) end |
.find_handler_from(handlers, target) ⇒ Object
:nodoc:
116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/cond.rb', line 116 def find_handler_from(handlers, target) #:nodoc: handlers.fetch(target) { found = handlers.inject(Array.new) { |acc, (klass, func)| index = target.ancestors.index(klass) if index acc << [index, func] else acc end }.sort_by { |t| t.first }.first found and found[1] } end |
.handle(arg, message = "", &block) ⇒ Object
Define a handler.
The exception instance is passed to the block.
349 350 351 352 |
# File 'lib/cond.rb', line 349 def handle(arg, = "", &block) Cond.check_context(:handle) Cond.code_section_stack.last.handle(arg, , &block) end |
.handling(&block) ⇒ Object
Begin a handling block. Inside this block, a matching handler gets called when raise gets called.
332 333 334 |
# File 'lib/cond.rb', line 332 def handling(&block) Cond.run_code_section(CondPrivate::HandlingSection, &block) end |
.invoke_restart(name, *args, &block) ⇒ Object
Call a restart from a handler; optionally pass it some arguments.
391 392 393 394 395 396 |
# File 'lib/cond.rb', line 391 def invoke_restart(name, *args, &block) Cond.available_restarts.fetch(name) { raise NoRestartError, "Did not find `#{name.inspect}' in available restarts" }.call(*args, &block) end |
.leave(*args) ⇒ Object
Leave the current handling or restartable block, optionally providing a value for the block.
The semantics are the same as ‘return’. When given multiple arguments, it returns an array. When given one argument, it returns only that argument (not an array).
373 374 375 376 |
# File 'lib/cond.rb', line 373 def leave(*args) Cond.check_context(:leave) Cond.code_section_stack.last.leave(*args) end |
.restart(arg, message = "", &block) ⇒ Object
Define a restart.
When a handler calls invoke_restart, it may pass additional arguments which are in turn passed to &block.
360 361 362 363 |
# File 'lib/cond.rb', line 360 def restart(arg, = "", &block) Cond.check_context(:restart) Cond.code_section_stack.last.restart(arg, , &block) end |
.restartable(&block) ⇒ Object
Begin a restartable block. A handler may transfer control to one of the restarts in this block.
340 341 342 |
# File 'lib/cond.rb', line 340 def restartable(&block) Cond.run_code_section(CondPrivate::RestartableSection, &block) end |
.run_code_section(klass, &block) ⇒ Object
:nodoc:
130 131 132 133 134 135 136 137 138 |
# File 'lib/cond.rb', line 130 def run_code_section(klass, &block) #:nodoc: section = klass.new(&block) Cond.code_section_stack.push(section) begin section.instance_eval { run } ensure Cond.code_section_stack.pop end end |
.with_default_handlers ⇒ Object
A default handler is provided which runs a simple choose-a-restart input loop when raise is called.
95 96 97 98 99 100 |
# File 'lib/cond.rb', line 95 def with_default_handlers # note: leave unfactored due to notable yield vs &block performance with_handlers(defaults.handlers) { yield } end |
.with_handlers(handlers) ⇒ Object
Register a set of handlers. The given hash is merged with the set of current handlers.
When the block exits, the previous set of handlers (if any) are restored.
64 65 66 67 68 69 70 71 72 |
# File 'lib/cond.rb', line 64 def with_handlers(handlers) # note: leave unfactored due to notable yield vs &block performance handlers_stack.push(handlers_stack.last.merge(handlers)) begin yield ensure handlers_stack.pop end end |
.with_restarts(restarts) ⇒ Object
Register a set of restarts. The given hash is merged with the set of current restarts.
When the block exits, the previous set of restarts (if any) are restored.
81 82 83 84 85 86 87 88 89 |
# File 'lib/cond.rb', line 81 def with_restarts(restarts) # note: leave unfactored due to notable yield vs &block performance restarts_stack.push(restarts_stack.last.merge(restarts)) begin yield ensure restarts_stack.pop end end |
.wrap_instance_method(mod, method) ⇒ Object
Allow handlers to be called from C code by wrapping a method with begin/rescue. Returns the aliased name of the original method.
See the README.
Example:
Cond.wrap_instance_method(Fixnum, :/)
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
# File 'lib/cond.rb', line 180 def wrap_instance_method(mod, method) original = "cond_original_#{mod.inspect}_#{method.inspect}" # TODO: jettison 1.8.6, remove eval and use |&block| # TODO: fix rcov bug -- does not see %{} mod.module_eval " alias_method :'\#{original}', :'\#{method}'\n def \#{method}(*args, &block)\n begin\n send(:'\#{original}', *args, &block)\n rescue Exception => e\n raise e\n end\n end\n eval_end\n original\nend\n" |
.wrap_singleton_method(mod, method) ⇒ Object
Allow handlers to be called from C code by wrapping a method with begin/rescue. Returns the aliased name of the original method.
See the README.
Example:
Cond.wrap_singleton_method(IO, :read)
207 208 209 210 |
# File 'lib/cond.rb', line 207 def wrap_singleton_method(mod, method) singleton_class = class << mod ; self ; end wrap_instance_method(singleton_class, method) end |