Custom exceptions

(J-Van) #1

Hi,

When should you define your own exceptions? Any rules of thumb?

I'm writing an application that runs on a computer and listens for
requests to start, kill, and view log files for other applications on
that computer. Say I get a request to start an application on that
computer and the requested executable doesn't exist. Should I throw a
custom exception then? Or, say an application dies unexpectedly.
What should I use for an exception then?

Thanks,
Joe

(J-Van) #2

The start function follows:

class Application
...
  def start
    raise "No executable given!" if not @executable
    if @pid = fork
      Process.detach @pid
      @status = :running
    else
      @options.each do |option_name, option_value|
        ENV[option_name] = option_value
      end
      args = @arguments.join " "
      exec "#{ basedir }/#{ version }/#{ executable } #{ args }"
    end
  end
...
end

So, if there's no valid executable, then the exec call will fail.
What's the best way of transmitting that information to the rest of
the system?

···

On 8/30/05, Joe Van Dyk <joevandyk@gmail.com> wrote:

Hi,

When should you define your own exceptions? Any rules of thumb?

I'm writing an application that runs on a computer and listens for
requests to start, kill, and view log files for other applications on
that computer. Say I get a request to start an application on that
computer and the requested executable doesn't exist. Should I throw a
custom exception then? Or, say an application dies unexpectedly.
What should I use for an exception then?

(Eric Hodel) #3

When should you define your own exceptions? Any rules of thumb?

I define custom exceptions when I need to differentiate them from the built-in exceptions.

I'm writing an application that runs on a computer and listens for
requests to start, kill, and view log files for other applications on
that computer. Say I get a request to start an application on that
computer and the requested executable doesn't exist. Should I throw a
custom exception then? Or, say an application dies unexpectedly.
What should I use for an exception then?

Sometimes an exception with a message is enough. Other times I need to know the difference between two RuntimeErrors with different messages.

I create my own exceptions for the second case because rescue FooError is easier than reading exception messages.

···

On 30 Aug 2005, at 10:52, Joe Van Dyk wrote:

--
Eric Hodel - drbrain@segment7.net - http://segment7.net
FEC2 57F1 D465 EB15 5D6E 7C11 332A 551C 796C 9F04

(J-Van) #4

Eeek. So, I was trying to do a test like:

  def test_start_bad_application
    a = Application.new invalid_application_executable
    assert_raise(RuntimeError) { a.start }
    assert !a.running?
    assert_equal :failed, a.status
  end

But since it forks into a new process, I don't catch the exception. I
could check to see if the executable exists (and is executable) before
going into the fork, and if it's not valid, throw an exception. Is
that my best bet?

···

On 8/30/05, Joe Van Dyk <joevandyk@gmail.com> wrote:

On 8/30/05, Joe Van Dyk <joevandyk@gmail.com> wrote:
> Hi,
>
> When should you define your own exceptions? Any rules of thumb?
>
> I'm writing an application that runs on a computer and listens for
> requests to start, kill, and view log files for other applications on
> that computer. Say I get a request to start an application on that
> computer and the requested executable doesn't exist. Should I throw a
> custom exception then? Or, say an application dies unexpectedly.
> What should I use for an exception then?

The start function follows:

class Application
...
  def start
    raise "No executable given!" if not @executable
    if @pid = fork
      Process.detach @pid
      @status = :running
    else
      @options.each do |option_name, option_value|
        ENV[option_name] = option_value
      end
      args = @arguments.join " "
      exec "#{ basedir }/#{ version }/#{ executable } #{ args }"
    end
  end
...
end

So, if there's no valid executable, then the exec call will fail.
What's the best way of transmitting that information to the rest of
the system?

(J-Van) #5

Should the custom exception be in its own class? Or in a module?

Say I have the following class

class NodeManager
  def start_application args
  end
end

There are a few different ways start_application could fail:
  - bad arguments
  - user attempted to start the same application twice (which is an
error for us)
  - not enough resources available

I want to distinguish between those different reasons. So, would I
want to have something like this:

module JoesExceptions # figure out some name
  class BadArgumentsForApplication < Exception
  end

  class ApplicationStartedTwice < Exception
  end

  class NotEnoughResourcesForApplication < Exception
  end
end

And then, in start_application:

def start_application args
  # if arguments are bad
  raise BadArgumentsForApplication
  # and so on
end

···

On 8/30/05, Eric Hodel <drbrain@segment7.net> wrote:

On 30 Aug 2005, at 10:52, Joe Van Dyk wrote:

> When should you define your own exceptions? Any rules of thumb?

I define custom exceptions when I need to differentiate them from the
built-in exceptions.

> I'm writing an application that runs on a computer and listens for
> requests to start, kill, and view log files for other applications on
> that computer. Say I get a request to start an application on that
> computer and the requested executable doesn't exist. Should I throw a
> custom exception then? Or, say an application dies unexpectedly.
> What should I use for an exception then?

Sometimes an exception with a message is enough. Other times I need
to know the difference between two RuntimeErrors with different
messages.

I create my own exceptions for the second case because rescue
FooError is easier than reading exception messages.

(Robert) #6

Joe Van Dyk wrote:

Hi,

When should you define your own exceptions? Any rules of thumb?

I'm writing an application that runs on a computer and listens for
requests to start, kill, and view log files for other applications
on that computer. Say I get a request to start an application on
that computer and the requested executable doesn't exist. Should I
throw a custom exception then? Or, say an application dies
unexpectedly. What should I use for an exception then?

I'd go with Eric's rule of thumb. It's not an exact science but you there
is a minimal criterium: if you need to catch this exception separately
from others you should introduce a new exception type.

Btw, here's a nice short script to print a class hierarchy of all
exceptions:

require 'pp'
tree = (cr = lambda {|h,k| h[k] = Hash.new &cr})[{},nil]
ObjectSpace.each_object(Class) {|cl| if cl.ancestors.include? Exception
then cl.ancestors.reverse.inject(tree){|tr,cl| tr[cl]} end}
pp tree

The start function follows:

class Application
...
  def start
    raise "No executable given!" if not @executable
    if @pid = fork
      Process.detach @pid
      @status = :running
    else
      @options.each do |option_name, option_value|
        ENV[option_name] = option_value
      end
      args = @arguments.join " "
      exec "#{ basedir }/#{ version }/#{ executable } #{ args }"
    end
  end
...
end

So, if there's no valid executable, then the exec call will fail.
What's the best way of transmitting that information to the rest of
the system?

Eeek. So, I was trying to do a test like:

  def test_start_bad_application
    a = Application.new invalid_application_executable
    assert_raise(RuntimeError) { a.start }
    assert !a.running?
    assert_equal :failed, a.status
  end

But since it forks into a new process, I don't catch the exception. I
could check to see if the executable exists (and is executable) before
going into the fork, and if it's not valid, throw an exception. Is
that my best bet?

I guess so.

Kind regards

    robert

···

On 8/30/05, Joe Van Dyk <joevandyk@gmail.com> wrote:

On 8/30/05, Joe Van Dyk <joevandyk@gmail.com> wrote:

(J-Van) #7

>
> > When should you define your own exceptions? Any rules of thumb?
>
> I define custom exceptions when I need to differentiate them from the
> built-in exceptions.
>
> > I'm writing an application that runs on a computer and listens for
> > requests to start, kill, and view log files for other applications on
> > that computer. Say I get a request to start an application on that
> > computer and the requested executable doesn't exist. Should I throw a
> > custom exception then? Or, say an application dies unexpectedly.
> > What should I use for an exception then?
>
> Sometimes an exception with a message is enough. Other times I need
> to know the difference between two RuntimeErrors with different
> messages.
>
> I create my own exceptions for the second case because rescue
> FooError is easier than reading exception messages.

Should the custom exception be in its own class? Or in a module?

Say I have the following class

class NodeManager
  def start_application args
  end
end

There are a few different ways start_application could fail:
  - bad arguments
  - user attempted to start the same application twice (which is an
error for us)
  - not enough resources available

I want to distinguish between those different reasons. So, would I
want to have something like this:

module JoesExceptions # figure out some name
  class BadArgumentsForApplication < Exception
  end

  class ApplicationStartedTwice < Exception
  end

  class NotEnoughResourcesForApplication < Exception
  end
end

And then, in start_application:

def start_application args
  # if arguments are bad
  raise BadArgumentsForApplication

I guess that would be:
    raise JoesApplication::BadArgumentsForApplication

···

On 9/1/05, Joe Van Dyk <joevandyk@gmail.com> wrote:

On 8/30/05, Eric Hodel <drbrain@segment7.net> wrote:
> On 30 Aug 2005, at 10:52, Joe Van Dyk wrote:

  # and so on
end