Abstracting exception handling

Hello,

I am interested in abstracting exception handling so it can be included
to any script from some external file. First, I want to setup signal
handling, and last I want to deal with exceptions. Now, in this little
test of mine something is bad, since the exception is not caught. I
suspect something is going out of scope and lost?

Martin

#!/usr/bin/env ruby

def do_at_begin
  exit_status = "OK"

  # Install signal handlers
  %w( INT TERM QUIT ).each do |signal|
    Signal.trap(signal) do
      exit_status = signal
      exit
    end
  end

  exit_status
end

def do_at_exit(exit_status="OK-default")
  begin
  rescue Exception => exception
    $stderr.puts "Exception caught!"
    $stderr.puts exit_status
    $stderr.puts exception.backtrace
  ensure
    puts "DEBUG EXIT STATUS: #{exit_status}"
  end
end

exit_status = do_at_begin

raise

END {
  do_at_exit(exit_status)
}

···

--
Posted via http://www.ruby-forum.com/.

I suspect something is going out of scope and lost?

Nope. The exception is raised, nothing catches it, *then* your END block
is executed. You can only catch exceptions which are raised after the
'begin' of the exception block.

Depending on what you're trying to do, the at_exit function might help
you. Otherwise, you need to restructure your code like this:

begin
  run_my_program
rescue Exception
  puts "I got #{$!}"
end

···

--
Posted via http://www.ruby-forum.com/\.

I was hoping for a setup like this in two files:

my_script.rb:

include "my_class"

<do_stuff> or raise

__END__

my_class.rb:

def do_at_begin
  set trap for signals.
  status
end

def do_at_exit
  begin
  rescue Exception
  ensure
    p status
  end
end

status = do_at_begin

END {
  do_at_exit(status)
}

__END__

I want to keep the my_script.rb file minimal and hence move the error
handling to the included file. I have no idea if this can be done?

Martin

···

--
Posted via http://www.ruby-forum.com/.

What kind of stuff are you doing in my_script.rb? Defining a class? A
method? Several methods? Just evaluating code? You might be able to use hook
methods to wrap new code that gets defined, but you're being pretty vague
right now.

···

On Thu, Aug 5, 2010 at 9:51 AM, Martin Hansen <mail@maasha.dk> wrote:

I was hoping for a setup like this in two files:

my_script.rb:

include "my_class"

<do_stuff> or raise

__END__

my_class.rb:

def do_at_begin
set trap for signals.
  status
end

def do_at_exit
  begin
rescue Exception
ensure
   p status
end
end

status = do_at_begin

END {
do_at_exit(status)
}

__END__

I want to keep the my_script.rb file minimal and hence move the error
handling to the included file. I have no idea if this can be done?

Martin
--
Posted via http://www.ruby-forum.com/\.

Andrew Wagner wrote:

What kind of stuff are you doing in my_script.rb? Defining a class? A
method? Several methods? Just evaluating code?

<do_stuff> would be something simple like parsing a tabular file and
raising an error if the format is wrong.

Cheers,

Martin

···

--
Posted via http://www.ruby-forum.com/\.

So if the do_stuff is in a format like this:

def do_something
.
.
.
end

do_something

Then when you could do is have a module which overrides Ruby's method that
gets called when you define a method. You can alias the method being created
to something else, then define a new method which calls that one, but wraps
it in whatever error-handling code you want.

···

On Thu, Aug 5, 2010 at 10:55 AM, Martin Hansen <mail@maasha.dk> wrote:

Andrew Wagner wrote:
> What kind of stuff are you doing in my_script.rb? Defining a class? A
> method? Several methods? Just evaluating code?

<do_stuff> would be something simple like parsing a tabular file and
raising an error if the format is wrong.

Cheers,

Martin
--
Posted via http://www.ruby-forum.com/\.

Andrew Wagner wrote:

Then when you could do is have a module which overrides Ruby's method
that
gets called when you define a method. You can alias the method being
created
to something else, then define a new method which calls that one, but
wraps
it in whatever error-handling code you want.

Sorry, I have no idea how to do that - it sounds terribly complicated.
Is there no simple solution? Could you perhaps give a small example?

Martin

···

--
Posted via http://www.ruby-forum.com/\.

Ok, so here's something fairly simple to try. There are better ways to do
this, but this should work. In your error-handling file, put this:

module ErrorHandling
  def self.included(cls)
    def cls.method_added(method)
      return if @_adding_a_method
      @_adding_a_method = true

      original_method = "original #{method}"
      alias_method original_method, method

      define_method(method) do |*args|
        begin
          puts "gonna try something dangerous"
          send original_method, *args
        rescue Exception
        ensure
          puts "My last will and testament..."
        end
      end

      @_adding_a_method = false
    end
  end
end

The idea is, when you include this module, then it watches for any new
methods you define. When you define one, it immediately hides it away, and
creates a new method, which calls the original one, but inside a
begin/rescue/ensure block. The @_adding_a_method flag is just to keep from
infinite recursion when you create the new method (because it will also
trigger method_added.

Now in your main class, do something like this:

include ErrorHandling

def do_something
  puts "do something dangerous"
end

do_something

···

On Thu, Aug 5, 2010 at 1:37 PM, Martin Hansen <mail@maasha.dk> wrote:

Andrew Wagner wrote:

> Then when you could do is have a module which overrides Ruby's method
> that
> gets called when you define a method. You can alias the method being
> created
> to something else, then define a new method which calls that one, but
> wraps
> it in whatever error-handling code you want.

Sorry, I have no idea how to do that - it sounds terribly complicated.
Is there no simple solution? Could you perhaps give a small example?

Martin
--
Posted via http://www.ruby-forum.com/\.

For a somewhat more robust ErrorHandling module, which requires 1.9, try the
code below. I should also give credit to the ruby metaprogramming
screencast<http://pragprog.com/screencasts/v-dtrubyom/the-ruby-object-model-and-metaprogramming&gt;
for
inspiration for this code. It's practically ripped straight out of there,
except theirs does tracing, as opposed to just error-handling.

module ErrorHandling
  def self.included(klass)
    klass.instance_methods(false).each do |existing_method|
      wrap(klass, existing_method)
    end

    def klass.method_added(method)
      unless @watch_calls_internal
        @watch_calls_internal = true
        ErrorHandling.wrap(self, method)
        @watch_calls_internal = false
      end
    end
  end

  def self.wrap(klass, method)
    klass.class_eval do
      name = method.to_s
      original_method = instance_method(name)

      define_method(name) do |*args, &block|
        begin
          puts "attempting..."
          result = original_method.bind(self).call(*args, &block)
        rescue Exception
        ensure
          puts "last will..."
        end
        result
      end
    end
  end
end

···

On Thu, Aug 5, 2010 at 5:13 PM, Andrew Wagner <wagner.andrew@gmail.com>wrote:

Ok, so here's something fairly simple to try. There are better ways to do
this, but this should work. In your error-handling file, put this:

module ErrorHandling
def self.included(cls)
   def cls.method_added(method)
     return if @_adding_a_method
     @_adding_a_method = true

     original_method = "original #{method}"
     alias_method original_method, method

     define_method(method) do |*args|
       begin
         puts "gonna try something dangerous"
         send original_method, *args
       rescue Exception
       ensure
         puts "My last will and testament..."
       end
     end

     @_adding_a_method = false
   end
end
end

The idea is, when you include this module, then it watches for any new
methods you define. When you define one, it immediately hides it away, and
creates a new method, which calls the original one, but inside a
begin/rescue/ensure block. The @_adding_a_method flag is just to keep from
infinite recursion when you create the new method (because it will also
trigger method_added.

Now in your main class, do something like this:

include ErrorHandling

def do_something
puts "do something dangerous"
end

do_something

On Thu, Aug 5, 2010 at 1:37 PM, Martin Hansen <mail@maasha.dk> wrote:

> Andrew Wagner wrote:
>
> > Then when you could do is have a module which overrides Ruby's method
> > that
> > gets called when you define a method. You can alias the method being
> > created
> > to something else, then define a new method which calls that one, but
> > wraps
> > it in whatever error-handling code you want.
>
> Sorry, I have no idea how to do that - it sounds terribly complicated.
> Is there no simple solution? Could you perhaps give a small example?
>
>
> Martin
> --
> Posted via http://www.ruby-forum.com/\.
>
>

I have been looking at your suggestions and done a few tests. However, I
wonder if there is not a more simple way - and especially since these
suggestions depends on methods being defined in order to invoke error
handling. Here is a real life example of the type of minimalistic
scripts I want to write:

#!/usr/bin/env ruby

require 'biopieces'

casts = []
casts <<
{:long=>'compress',:short=>'Z',:type=>'flag',:mandatory=>false,:default=>nil,:allowed=>nil,:disallowed=>nil}

bp = Biopieces.new

options = bp.parse(ARGV,casts)

bp.mktmpdir # create an optional temporary directory that is auto
deleted.

bp.each_record do |record|
  # do something to each record (possibly according to the options).
e.g.:
  record["SEQ"].upcase! if record.has_key? "SEQ"
  bp.puts record
  raise # Debug test of invokation of error handling
end

Now, I would like error handling to be invoked, if possible from
biopieces.rb, in the case some exception is raised (either by raise or
ctrl-c, etc), but I am not defining any methods and thus the suggested
meta-programming approaches seem to fall short.

Cheers,

Martin

···

--
Posted via http://www.ruby-forum.com/.

Martin Hansen wrote:

Now, I would like error handling to be invoked, if possible from
biopieces.rb, in the case some exception is raised (either by raise or
ctrl-c, etc)

What do you want the exception handler to do? Will the program still
terminate afterwards?

If so, why not just add an at_exit handler?

--- prog1.rb ---
at_exit { puts "Last exception was #{$!.inspect}" if $! }
raise "Boom"

--- prog2.rb ---
at_exit { puts "Last exception was #{$!.inspect}" if $! }
puts "Nothing happened"

--- prog3.rb ---
at_exit { puts "Last exception was #{$!.inspect}" if $! }
begin
  raise "Caught it"
rescue RuntimeError
end
puts "Nothing happened"

--- prog4.rb ---
at_exit { puts "Last exception was #{$!.inspect}" if $! }
begin
  raise "Caught it"
rescue RuntimeError
end
sleep 10
puts "Nothing happened"

Note that both prog2 and prog3 terminate successfully and don't report
any exception. prog4 reports Interrupt if you terminate with ^C.

Your at_exit code can of course sit in a separate source file that you
require.

···

--
Posted via http://www.ruby-forum.com/\.

What do you want the exception handler to do? Will the program still
terminate afterwards?

I want to write the exception type to a log file and terminate the
script. $! does not tell the difference between interrupt, terminate,
and quit (perhaps that could be fixed? - just a thought).

If so, why not just add an at_exit handler?

I have been messing around with this for a while, but I have failed to
get it right - and now I am utterly confused.

If I have test.rb:

#!/usr/bin/env ruby

require 'my_aux_class'

Greet.new

raise "raise from main"

And my_aux_class.rb:

class Greet
  def initialize
    puts "Hello World"
  end
end

at_exit {
  puts "Last exception was #{$!.inspect}" if $!
}

begin
  exit_status = "OK"

  %w( INT TERM QUIT ).each do |signal|
    Signal.trap(signal) do
      exit_status = signal
      exit
    end
  end

  raise "raise from aux"
rescue Exception => exception
  puts "rescuing #{exception}"
  puts exception.backtrace
ensure
  puts "exit status: #{exit_status}"
end

Running this I get the following output:

rescuing raise from aux
/Users/maasha/my_aux_class.rb:21:in `<top (required)>'
./test.rb:3:in `require'
./test.rb:3:in `<main>'
exit status: OK
Hello World
Last exception was #<RuntimeError: raise from main>
./test.rb:7:in `<main>': raise from main (RuntimeError)

I actually wanted the raise from main rescued, but the
begin/rescue/ensure is misplaced/misused :o(

Martin

···

--
Posted via http://www.ruby-forum.com/\.

Martin Hansen wrote:

$! does not tell the difference between interrupt, terminate,
and quit (perhaps that could be fixed? - just a thought).

It does for me.

$ cat ert.rb
at_exit { puts "Last exception was #{$!.inspect}" if $!; exit! }
sleep 100
puts "Nothing happened"

### Ctrl-C:
$ ruby ert.rb
^CLast exception was Interrupt
ert.rb:2:in `sleep': Interrupt
  from ert.rb:2

### kill -TERM (from another window):
$ ruby ert.rb
Last exception was #<SignalException: SIGTERM>
Terminated

### kill -QUIT:
Last exception was #<SignalException: SIGQUIT>
Quit

This is with ruby 1.8.7 (2010-01-10 patchlevel 249) [x86_64-linux] under
Ubuntu Lucid amd64.

So I don't understand why you're trying to trap all of these signals,
when they all cause the program to terminate, and they can all be
distinguished in $!. But perhaps your platform behaves differently?

Final note: inside your at_exit handler, you can use 'exit!' to prevent
any further processing, i.e. the default output of the backtrace or
signal.

···

--
Posted via http://www.ruby-forum.com/\.

at_exit { puts "Last exception was #{$!.inspect}" if $!; exit! }

Actually, the sample output I showed was without the 'exit!'

···

--
Posted via http://www.ruby-forum.com/\.

This works great! I was sure I had tested this, but I had screwed up the
test somehow - perhaps trap changed something. Anyways - this will work!

Thanks a zillion for the patient assistance!

Martin

···

--
Posted via http://www.ruby-forum.com/.