Search code examples
rubyexceptionintrospection

Custom fields in a Ruby exception object not shown when inspecting


If I create a class in Ruby, I can generally "see" its fields when using inspect:

>> class C; def initialize; @x=1; end; end
=> :initialize
>> C.new.inspect
=> "#<C:0x007fd5b9119c20 @x=1>"

This does not seem to hold true for exceptions:

>> class E < StandardError; def initialize; super("hello"); @y=2; end; end
=> :initialize
>> begin; raise E.new; rescue E => e; puts e.inspect; end 
#<E: hello>
=> nil

I would have expected this to show #<E: hello @y=2>! So naturally I went to the documentation and saw that the Error class specifically overrides inspect to "return this exception’s class name and message."

This leads me to believe that either (1) adding extra descriptive fields to an error object is a bad thing, or (2) the author of the Ruby Error class made a mistake, or (3) there is something inherently weird about error objects that an explicit override of inspect was needed!

I don't mean for this to be an opinion question. My programming question here is:

  • If the answer is (1), and it is bad practice to add fields to an error object, what should I do instead?
  • If the answer is (2), how does one get around this mess? Override inspect in one's custom error subclass?

Solution

  • If the answer is (2), how does one get around this mess? Override inspect in one's custom error subclass?

    I do not know why would you want to have instance variables in error class, but if

    how does one get around this mess?

    means

    where are the instance variables I have defined for the object?

    the answer is as follows:

    begin; raise E.new; rescue E => e; puts e.instance_variables; end
    #=> @y
    

    To have instance variables shown within inspect you will for sure have to override inspect method:

    class E < StandardError
      def initialize
        super('hello')
        @y = 2
      end
    
      def inspect
        "<#{self.class}: #{message}, #{instance_variables.map { |v| "#{v}=#{instance_variable_get(v)}"}.join(', ')}>"
      end
    end
    begin; raise E.new; rescue E => e; puts e.inspect; end
    #=> <E: hello, @y=2>
    

    I do not see any mess here :)