Nov 28, 2011

Anatomy of Ruby Exception

Today I was trying to find how to properly override Ruby standard Exception. To my surprise, I was not able to find any tutorial or even paragraph about it. No recommendations, no design patterns. Everything was the same:

class MyError < StandardError; end

As a Java guy, I tend to encapsulate lots of things into Exceptions. It's not a bad thing, actually it is a good thing. So I asked myself - what should I override, how, and why?

Let's skip the general Ruby recommendation to override StandardError instead Exception. Different class, the very same interface. To my surprise again, both Exception and StandardError classes are defined in the Ruby C code.

So I have rewritten the Ruby Exception into this Ruby code. Please note I made my life a little easier and some of the methods do not return the exact output as Ruby does (e.g. inspect), but we get very good overview with it. Please note this is Ruby 1.8, there is one small change in 1.9 in regard to method to_str, but it makes no difference for us.

class Exception
  def initialize(msg)
    @mesg = msg
    @bt = nil
  end

  def to_s
    return self.class.name if @mesg.nil?
    @mesg
  end

  alias :message :to_s
  alias :to_str :to_s

  def set_backtrace(bt)
    @bt = bt
  end

  def backtrace
    @bt
  end

  def inspect
    "\#<#{self.class.name}>: #{to_s}"
  end

  def exception(msg)
    return self if msg == message
    self.class.new(msg)
  end
end

I think the code says it all. Now we can see what we actually need to redefine. Now, let's do a small example.

class MyError < StandardError
  attr_accessor :info

  def initialize(msg, my_additional_info = "")
    info = my_additional_info
    super(msg)
  end

  def message
    "#{to_s}: #{info}"
  end
end

As you can see from the Exception code, Ruby stores the message into instance variable called "mesg". Since this is native code, we are not able to access it. The only way to get it is to call to_s method. Please do not do this:

def to_s
  "#{message}: #{@info}"
end

This will end up with stack overflow. Special thanks to Ivan Nečas for helping me with this.

I have to admit this interface is not the best feature of Ruby. I can imagine an accessor for the message itself. And with a different name than "mesg". But that is life.

Nobody told us Ruby programming's gonna be easy. Take care.

0 comments:

Post a Comment