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

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_restartsObject

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, message = "", &block)
  Cond.check_context(:handle)
  Cond.code_section_stack.last.handle(arg, message, &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, message = "", &block)
  Cond.check_context(:restart)
  Cond.code_section_stack.last.restart(arg, message, &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_handlersObject

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