Custom Exceptions

Discussion in 'Ruby' started by Leslie Viljoen, Apr 6, 2010.

  1. [Note: parts of this message were removed to make it a legal post.]

    Hi everyone

    I want to make a custom exception like so:

    class BillRowError < StandardError
    def initialize(field, index)
    @field = field
    @index = index
    end
    end

    I'll call this like so:
    raise(BillRowError.new:)roamingcalls, @index), "Roaming Calls field
    missing") if n.length == 0

    But now I'd like to be able to modify the string that Ruby prints when the
    exception is not rescue'd. I thought I could add this method to the
    BillRowError class:

    def message
    @message + " field: #{@field}, row: #{@index}"
    end

    That almost works but I get a "instance variable @message not initialized"
    warning, which means Ruby is not setting @message in my object like I
    expected. Making my own message= method doesn't help.

    Can an exception object access and modify the message that gets passed in
    the "raise"?
    Leslie Viljoen, Apr 6, 2010
    #1
    1. Advertising

  2. Leslie Viljoen

    Chris Hulan Guest

    On Apr 6, 10:44 am, Leslie Viljoen <> wrote:
    > [Note:  parts of this message were removed to make it a legal post.]
    >
    > Hi everyone
    >
    > I want to make a custom exception like so:
    >
    > class BillRowError < StandardError
    >     def initialize(field, index)
    >         @field = field
    >         @index = index
    >     end
    > end
    >
    > I'll call this like so:
    > raise(BillRowError.new:)roamingcalls, @index), "Roaming Calls field
    > missing") if n.length == 0
    >
    > But now I'd like to be able to modify the string that Ruby prints when the
    > exception is not rescue'd. I thought I could add this method to the
    > BillRowError class:
    >
    >     def message
    >         @message + " field: #{@field}, row: #{@index}"
    >     end
    >
    > That almost works but I get a "instance variable @message not initialized"
    > warning, which means Ruby is not setting @message in my object like I
    > expected. Making my own message= method doesn't help.
    >
    > Can an exception object access and modify the message that gets passed in
    > the "raise"?


    The default Exception:initialize is defined to take 1 parameter, a
    string containing the error message
    You define different initialize parameters so @message is not getting
    set as expected

    Maybe something like:
    class BillRowError < StandardError
    def initialize(msg, field, index)
    super(msg)
    @field = field
    @index = index
    end

    def message
    @message + " field: #{@field}, row: #{@index}"
    end

    end
    ....
    raise(BillRowError.new("Roaming Calls field missing",:roamingcalls,
    @index), ) if n.length == 0

    cheers
    Chris Hulan, Apr 6, 2010
    #2
    1. Advertising

  3. [Note: parts of this message were removed to make it a legal post.]

    On Tue, Apr 6, 2010 at 9:44 AM, Leslie Viljoen <>wrote:

    > Hi everyone
    >
    > I want to make a custom exception like so:
    >
    > class BillRowError < StandardError
    > def initialize(field, index)
    > @field = field
    > @index = index
    > end
    > end
    >
    > I'll call this like so:
    > raise(BillRowError.new:)roamingcalls, @index), "Roaming Calls field
    > missing") if n.length == 0
    >
    > But now I'd like to be able to modify the string that Ruby prints when the
    > exception is not rescue'd. I thought I could add this method to the
    > BillRowError class:
    >
    > def message
    > @message + " field: #{@field}, row: #{@index}"
    > end
    >
    > That almost works but I get a "instance variable @message not initialized"
    > warning, which means Ruby is not setting @message in my object like I
    > expected. Making my own message= method doesn't help.
    >
    > Can an exception object access and modify the message that gets passed in
    > the "raise"?
    >


    You could try rewriting the to_s method and take advantage of the
    inheritance:

    def to_s
    super + " field: #{@field}, row: #{@index}"
    end

    That worked for me. I'm not sure if this is the recommended solution.
    Mario Antonetti, Apr 6, 2010
    #3
  4. Judging by the C source, Exception uses hidden(*) instance variables
    "mesg" and "bt" for message and backtrace respectively. However, the
    accessor #message internally calls #to_s, which you can override.

    class BillRowError < StandardError
    def initialize(field, index)
    @field = field
    @index = index
    end

    alias :eek:rig_to_s :to_s
    def to_s
    "#{orig_to_s} field: #{@field}, row: #{@index}"
    end
    end

    begin
    raise BillRowError.new(5, 7), "bar"
    rescue BillRowError => e
    p e
    p e.instance_variables
    p e.message
    p e.backtrace
    end

    Produces:

    #<BillRowError: bar field: 5, row: 7>
    ["@index", "@field"]
    "bar field: 5, row: 7"
    ["ert.rb:14"]

    Admittedly this behaviour doesn't seem to be well documented, so may be
    fragile. Use at your own risk.

    HTH,

    Brian.

    (*) It uses instance variables whose names don't begin with '@' and so
    cannot be accessed by normal code
    --
    Posted via http://www.ruby-forum.com/.
    Brian Candler, Apr 6, 2010
    #4
  5. Chris Hulan wrote:
    > The default Exception:initialize is defined to take 1 parameter, a
    > string containing the error message


    But 'raise' also lets you pass an error message, in addition to the
    exception instance.

    Note that even the default constructor doesn't create an instance
    variable called @message.

    class Foo < StandardError
    end

    begin
    raise Foo, "bar"
    rescue Foo => e
    p e
    p e.instance_variables
    p e.instance_variable_get:)@message)
    p e.message
    p e.backtrace
    end

    #<Foo: bar>
    [] # << No instance variables!
    nil # << No @message!
    "bar"
    ["ert.rb:5"]
    --
    Posted via http://www.ruby-forum.com/.
    Brian Candler, Apr 6, 2010
    #5
  6. Leslie Viljoen

    Chris Hulan Guest

    On Apr 6, 11:56 am, Brian Candler <> wrote:
    > Chris Hulan wrote:
    > > The default Exception:initialize is defined to take 1 parameter, a
    > > string containing the error message

    >
    > But 'raise' also lets you pass an error message, in addition to the
    > exception instance.
    >
    > Note that even the default constructor doesn't create an instance
    > variable called @message.
    >
    > class Foo < StandardError
    > end
    >
    > begin
    >   raise Foo, "bar"
    > rescue Foo => e
    >   p e
    >   p e.instance_variables
    >   p e.instance_variable_get:)@message)
    >   p e.message
    >   p e.backtrace
    > end
    >
    > #<Foo: bar>
    > []              # << No instance variables!
    > nil             # << No @message!
    > "bar"
    > ["ert.rb:5"]
    > --
    > Posted viahttp://www.ruby-forum.com/.


    I should stop answering questions based on reading the docs at ruby-
    docs.org, they are just not very clear...plus sloppy reading on my
    part doesn't help ;)
    So message is not an attribute so using @message is wrong.
    It looks like overriding to_s, or maybe using super in the override of
    message to get the parents input.

    cheers
    Chris Hulan, Apr 6, 2010
    #6
  7. [Note: parts of this message were removed to make it a legal post.]

    On Tue, Apr 6, 2010 at 7:10 PM, Chris Hulan <> wrote:

    >
    > I should stop answering questions based on reading the docs at ruby-
    > docs.org, they are just not very clear...plus sloppy reading on my
    > part doesn't help ;)
    > So message is not an attribute so using @message is wrong.
    > It looks like overriding to_s, or maybe using super in the override of
    > message to get the parents input.
    >
    >

    Yip, these surprising revelations were the reason for my post!
    Leslie Viljoen, Apr 6, 2010
    #7
  8. [Note: parts of this message were removed to make it a legal post.]

    On Tue, Apr 6, 2010 at 5:24 PM, Brian Candler <> wrote:

    > Judging by the C source, Exception uses hidden(*) instance variables
    > "mesg" and "bt" for message and backtrace respectively. However, the
    > accessor #message internally calls #to_s, which you can override.
    >
    > class BillRowError < StandardError
    > def initialize(field, index)
    > @field = field
    > @index = index
    > end
    >
    > alias :eek:rig_to_s :to_s
    > def to_s
    > "#{orig_to_s} field: #{@field}, row: #{@index}"
    > end
    > end
    >
    > begin
    > raise BillRowError.new(5, 7), "bar"
    > rescue BillRowError => e
    > p e
    > p e.instance_variables
    > p e.message
    > p e.backtrace
    > end
    >
    > Produces:
    >
    > #<BillRowError: bar field: 5, row: 7>
    > ["@index", "@field"]
    > "bar field: 5, row: 7"
    > ["ert.rb:14"]
    >
    > Admittedly this behaviour doesn't seem to be well documented, so may be
    > fragile. Use at your own risk.
    >
    >

    Wow thanks for this, I didn't think of aliasing the old to_s. I find it
    strange that exceptions don't just use a plain old @message instance
    variable but I suppose there's some good reason for it.


    --
    Do not support Microsoft:
    http://www.vanwensveen.nl/rants/microsoft/IhateMS.html
    Leslie Viljoen, Apr 6, 2010
    #8
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Chris Dunaway

    Custom Exceptions with Web Services

    Chris Dunaway, Jan 13, 2004, in forum: ASP .Net
    Replies:
    1
    Views:
    405
    Shiv Kumar
    Jan 13, 2004
  2. Ahmed Moustafa
    Replies:
    5
    Views:
    29,993
    Chris Smith
    Jul 14, 2004
  3. Paul Miller
    Replies:
    3
    Views:
    1,010
    Alex Martelli
    Nov 12, 2003
  4. Replies:
    3
    Views:
    601
    Sherm Pendley
    Apr 16, 2007
  5. Lie
    Replies:
    3
    Views:
    602
Loading...

Share This Page