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.