Yes and No.
If the bug is on the backtrace, this works fine. An exception is
thrown, the backtrace is printed, and you inspect each one and you
find the wrong code, and fix it.
End of story.
If however you are doing, as is quite often done, constructing an
object, passing in some of the instance variables as parameters to the
constructor...
The use that triggers the exception, is quite likely _not_ to have the
buggy line on the backtrace. The buggy line was on the call graph
heading towards the constructor, not the use of that instance.
The duck's backside is not watertight, the duck sinks.
Hence my static_type_check set of utilities...
So in...
class MyObject
def initialize( _a, _b, _c)
@a = _a.quacks_like( :qwaak, :waddle, :swim)
@b = _b.static_type_check String
@c = _c.polymorphic_type_check MyBaseClass
end
# Without the checks above, the exceptions would be thrown here, with the buggyline nowhere on the backtrace!
def my_use
@a.qwaak( @b) + @c
end
end
# Abstract base class for all the type check exceptions
class TypeCheckException < Exception
end
# This exception is thrown in event of a method being invoked with an
# object of the wrong duck type.
class DuckTypingException < TypeCheckException
end
# This exception is thrown in event of a method being invoked with a
# parameter of of the wrong static type.
class StaticTypeException < TypeCheckException
end
# This exception is thrown in event of a method being invoked with a
# parameter of of the wrong polymorphic type.
class PolymorphicTypeException < TypeCheckException
end
class Object
# Raise a DuckTypingException unless the object responds to all symbol.
def quacks_like( *symbols)
symbols.each do |symbol|
raise DuckTypingException, "Duck typing error, expected this object to respond to :#{symbol}, but found class #{self.class}\n\t\t#{symbol.inspect}" unless
respond_to? symbol
end
end
def static_type_check_boolean
raise StaticTypeException, "Static type check error, expected object to be a boolean, found '#{self.class}'
\t\t#{self.inspect}" unless
(self.class == TrueClass) ||
(self.class == FalseClass) ||
(self.class == NilClass)
self
end
def static_type_check( klass)
raise StaticTypeException, "Static type check error, expected object to be exactly class '#{klass}', found '#{self.class}'\n\t\t#{self.inspect}" unless
self.class == klass
self
end
def static_type_check_each( klass)
each do |object|
object.static_type_check klass
end
self
end
def polymorphic_type_check_each( klass)
each do |object|
object.polymorphic_type_check klass
end
self
end
def polymorphic_type_check( klass)
# @@static_caller[caller[1]]+=1
raise PolymorphicTypeException, "Polymorphic type check error, expected object to be a kind of '#{klass}', found '#{self.class}'\n\t\t#{self.inspect}" unless
self.kind_of? klass
self
end
end
John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : john.carter@tait.co.nz
New Zealand
···
On Thu, 29 May 2008, Jim Menard wrote:
What is the Duck way to check arguments? I have to because I'm passing
on to a rather fragile web service.
Don't bother. Use your arguments, assuming they are the correct type.
If they are not, then a runtime exception will be thrown.