Project

General

Profile

Feature #5856 ยป trunk-raise-any.diff

kstephens (Kurt Stephens), 01/07/2012 06:52 AM

View differences:

eval.c
break;
}
if (argc > 0) {
if (!rb_obj_is_kind_of(mesg, rb_eException))
rb_raise(rb_eTypeError, "exception object expected");
if (argc > 2)
set_backtrace(mesg, argv[2]);
}
test/ruby/test_raise_any.rb
=begin
Feature: Raise any object
= Proposal
The ability to raise any object that conforms to the protocol of Exception.
= 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
* ruby/eval.c: Remove make_exception() assertion rb_obj_is_kind_of(mesg, rb_mRaiseable).
Sample Usage:
=end
require 'test/unit'
class TestRaiseable < Test::Unit::TestCase
# Example: mixin implementation of Exception protocol methods:
module Raiseable
def exception *args # error.c: exc_exception()
case args.size
when 0
return self
when 1
return self if self.object_id == args[0].object_id
end
clone.initialize *args
end
def initialize mesg = nil # error.c: exc_initialize()
@mesg = mesg
@bt = nil
self
end
def == other # error.c: exc_equal()
return true if self.object_id == other.object_id
if self.class == other.class
mesg = other.message
backtrace = other.backtrace
else
mesg = other.instance_variable_get(:'@mesg')
backtrace = other.backtrace
end
@mesg == mesg && self.backtrace == backtrace
end
def to_s # error.c: exc_to_s()
@mesg || self.class.name
end
def message; to_s; end
def inspect # error.c: exc_inspect()
"#<#{self.class}: #{self}>"
end
def backtrace; @bt; end
def set_backtrace v # error.c: exc_set_backtrace()
v = [ v ] if String === v
raise TypeError, "backtrace must be Array of String" unless Array === v || v.all?{|e| String === e}
@bt = v
end
def self.included target
super
target.extend ModuleMethods
end
module ModuleMethods
def exception *args; new *args; end
end
end
def test_raiseable
raiseable = Class.new do
include Raiseable
end
begin
raise raiseable, "this must be handled"
assert(false)
rescue Exception
$stderr.puts "#{__FILE__}:#{__LINE__}: Unexpected Exception: #{$!.inspect}"
assert(false)
rescue Raiseable
assert(true)
end
end
class SomeLibraryFromSomebodyElse
def do_it
something_that_takes_too_long
rescue ::Exception => exc
$stderr.puts "\n#{__FILE__}:#{__LINE__}: Something failed: #{exc.inspect}"
42
end
def something_that_takes_too_long
sleep 10
end
end
class MyTimeoutError
include Raiseable
end
def test_MyTimeoutError
raise MyTimeoutError
assert(false)
rescue ::Exception
$stderr.puts "#{__FILE__}:#{__LINE__}: Unexpected Exception: #{$!.inspect}"
assert(false)
rescue MyTimeoutError
assert(true)
end
def test_raiseable_timeout
require 'timeout'
begin
Timeout.timeout(1, MyTimeoutError) do
SomeLibraryFromSomebodyElse.new.do_it
end
assert(false)
rescue MyTimeoutError
assert(true)
rescue Exception
$stderr.puts "#{__FILE__}:#{__LINE__}: Unexpected Exception: #{$!.inspect}"
assert(false)
end
end
end
    (1-1/1)