Module: Interception

Defined in:
lib/interception.rb,
ext/interception.c

Overview

Provides global facility for monitoring exceptions raised in your application.

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.listenersObject

Returns the value of attribute listeners


8
9
10
# File 'lib/interception.rb', line 8

def listeners
  @listeners
end

.mutexObject

Returns the value of attribute mutex


8
9
10
# File 'lib/interception.rb', line 8

def mutex
  @mutex
end

.rescueingObject

Returns the value of attribute rescueing


8
9
10
# File 'lib/interception.rb', line 8

def rescueing
  @rescueing
end

Class Method Details

.listen(for_block = nil, &listen_block) {|exception, binding| ... } ⇒ Object

Listen for any exceptions raised.

The listener block that you pass in will be executed as though inside Kernel#raise, so your entire program is still actively running. If you have a gem like pry-stack_explorer you can access the stack frames that lead to the exception occurring.

NOTE: Be careful when writing a listener, if your listener raises an exception it will mask the original exception (though it will not recursively call your listener).

Examples:


# To report exceptions for the entire run of the program:
Interception.listen do |exception, binding|
  Emailer.spam!('[email protected]', exception, binding.eval('self.class.name'))
end

# To log exceptions for the duration of a given block.
def log_exceptions(&block)
  Interception.listen(block) do |exception, binding|
    puts "#{binding.eval("self.inspect")} raised #{exception.inspect}"
  end
end

# You can also turn listeners on and off manually

listener = Proc.new{ |exception, binding|
  binding.pry
}
Interception.listen(listener)
Async::Redis.get("foo") do
  Interception.unlisten(listener)
end

Parameters:

  • for_block (Proc) (defaults to: nil)

    (nil) If you pass for_block in, then you will only intercept exceptions raised while that block is running.

  • listen_block (Proc)

    The block to call when an exception occurs, takes two arguments, the exception and the binding

Yields:

  • (exception, binding)

Returns:

  • (Object)

    The return value of the for_block (if present)

Raises:

  • (ArgumentError)

See Also:


62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/interception.rb', line 62

def self.listen(for_block=nil, &listen_block)
  raise ArgumentError, "no block given" unless listen_block || for_block
  mutex.synchronize{
    start if listeners.empty?
    listeners << (listen_block || for_block)
  }

  if listen_block && for_block
    begin
      for_block.call
    ensure
      unlisten listen_block
    end
  else
    listen_block
  end
end

.rescue(exception, binding) ⇒ Object

Called by platform-specific implementations whenever an exception is raised.

The arguments will be forwarded on to all listeners added via listen that haven't been removed via unlisten.

For efficiency, this block will never be called unless there are active listeners.

Parameters:

  • exception (Exception)

    The exception that was raised

  • binding (Binding)

    The binding from which it was raised


101
102
103
104
105
106
107
108
109
# File 'lib/interception.rb', line 101

def self.rescue(exception, binding)
  return if rescueing
  self.rescueing = true
  listeners.each do |l|
    l.call(exception, binding)
  end
ensure
  self.rescueing = false
end

.startObject

Start sending events to rescue. Implemented per-platform

Raises:

  • (NotImplementedError)

113
# File 'lib/interception.rb', line 113

def self.start; raise NotImplementedError end

.stopObject

Stop sending events to rescue. Implemented per-platform

Raises:

  • (NotImplementedError)

117
# File 'lib/interception.rb', line 117

def self.stop; raise NotImplementedError end

.unlisten(listen_block) ⇒ Object

Disable a previously added listener

Parameters:

  • listen_block (Proc)

    The listen block you wish to remove.

See Also:


84
85
86
87
88
89
# File 'lib/interception.rb', line 84

def self.unlisten(listen_block)
  mutex.synchronize{
    listeners.delete listen_block
    stop if listeners.empty?
  }
end