Feature #5818 ยป raiseable.diff
error.c | ||
---|---|---|
}
|
||
/* exception classes */
|
||
VALUE rb_mRaiseable;
|
||
VALUE rb_eException;
|
||
VALUE rb_eSystemExit;
|
||
VALUE rb_eInterrupt;
|
||
... | ... | |
void
|
||
Init_Exception(void)
|
||
{
|
||
rb_eException = rb_define_class("Exception", rb_cObject);
|
||
rb_mRaiseable =
|
||
rb_eException = rb_define_module("Raiseable");
|
||
// rb_eException = rb_define_class("Exception", rb_cObject);
|
||
rb_define_singleton_method(rb_eException, "exception", rb_class_new_instance, -1);
|
||
rb_define_method(rb_eException, "exception", exc_exception, -1);
|
||
rb_define_method(rb_eException, "initialize", exc_initialize, -1);
|
||
... | ... | |
rb_define_method(rb_eException, "backtrace", exc_backtrace, 0);
|
||
rb_define_method(rb_eException, "set_backtrace", exc_set_backtrace, 1);
|
||
rb_eException = rb_define_class("Exception", rb_cObject);
|
||
rb_define_singleton_method(rb_eException, "exception", rb_class_new_instance, -1);
|
||
rb_include_module(rb_eException, rb_mRaiseable);
|
||
rb_eSystemExit = rb_define_class("SystemExit", rb_eException);
|
||
rb_define_method(rb_eSystemExit, "initialize", exit_initialize, -1);
|
||
rb_define_method(rb_eSystemExit, "status", exit_status, 0);
|
eval.c | ||
---|---|---|
break;
|
||
}
|
||
if (argc > 0) {
|
||
if (!rb_obj_is_kind_of(mesg, rb_eException))
|
||
extern VALUE rb_mRaiseable;
|
||
if (!rb_obj_is_kind_of(mesg, rb_mRaiseable))
|
||
rb_raise(rb_eTypeError, "exception object expected");
|
||
if (argc > 2)
|
||
set_backtrace(mesg, argv[2]);
|
test/ruby/test_raiseable.rb | ||
---|---|---|
=begin
|
||
Feature: Raiseable
|
||
= Proposal
|
||
The ability to raise any object that is a Raiseable.
|
||
= Problem
|
||
* The Exception subclass hierarchy is well-established.
|
||
* CRuby does not allow any object that behaves as an Exception to be raised, it must be a subclass of Exception.
|
||
* 3rd-party code often rescues Exception; e.g. for error recovery, retry and/or logging.
|
||
* Users need the ability to raise objects that would not normally be rescued by *any* code;
|
||
e.g.: hard timeouts or custom signal handlers in an application.
|
||
= Solution
|
||
* A "Raiseable" module implements all of the methods currently defined in Exception.
|
||
* Exception class includes Raiseable module.
|
||
* ruby/eval.c: make_exception() asserts rb_obj_is_kind_of(mesg, rb_mRaiseable),
|
||
instead of rb_obj_is_kind_of(mesg, rb_cException).
|
||
* Users should avoid "rescue Raiseable" in usual circumstances.
|
||
= Other Ideas not implemented here:
|
||
* Remove the obj_is_kind_of(mesg, rb_mRaiseable) restriction to allow pure duck-typing.
|
||
* Clean up the ivar names (@bt, @mesg) and method names (set_backtrace).
|
||
Sample Usage:
|
||
=end
|
||
require 'test/unit'
|
||
class TestRaiseable < Test::Unit::TestCase
|
||
def test_raiseable
|
||
raiseable = Class.new do
|
||
include Raiseable
|
||
def self.exception *args; new *args; end
|
||
end
|
||
begin
|
||
raise raiseable, "this must be handled"
|
||
assert(false)
|
||
rescue Exception
|
||
assert(false)
|
||
rescue Raiseable
|
||
assert(true)
|
||
end
|
||
end
|
||
class SomeLibraryFromSomebodyElse
|
||
def do_it
|
||
something_that_takes_too_long
|
||
rescue ::Exception
|
||
$stderr.puts "Something failed"
|
||
42
|
||
end
|
||
def something_that_takes_too_long
|
||
sleep 10
|
||
end
|
||
end
|
||
class MyTimeoutError
|
||
include Raiseable
|
||
def self.exception *args; new *args; end
|
||
end
|
||
def test_raiseable_timeout
|
||
require 'timeout'
|
||
begin
|
||
Timeout.timeout(1, MyTimeoutError) do
|
||
SomeLibraryFromSomebodyElse.new.do_it
|
||
end
|
||
assert(false)
|
||
rescue MyTimeoutError
|
||
# $stderr.puts "TIMEOUT: #{$!.inspect}"
|
||
assert(true)
|
||
end
|
||
end
|
||
end
|
||