to_s and to_i follow a naming convention that is not extensible (third party users write to_class, while the standard library methods write to_c), lack error checking, and pollute the method namespaces of classes that provide these conversions.String() and Integer(), which don't match the usual convention, and require the use of a user-defined dispatch mechanism to dispatch based on the source type.
Object (possible names: #to, #to_type, #as -- we'll use #to in the RCR) accepting a parameter wich represents the target type of the transformation.#to method, say:
a=some_string.to(:CapitalizedString)
a=3.to :Odd #=> 3
a=4.to :Odd #=> TypeError
is_a?, kind_of? andrespond_to? may be factored in a
def foo a,b
a=a.to Bla
b=b.to Boo
..do stuff
end
def sum a,b
a=a.as Numeric; b=b.as Numeric
a+b
end
sum 1,2
false, nil,NilClass, allowing users to write their own boolean#to_bool) without changing existing
foo= bla.to(Integer) rescue 0
Integer conversions) can be passed to the (#to,#as, #to_type) method, and
'200'.to Integer, 16
Integer considering it encoded inSortedCollection class. Converting anEnumerable object would be easy:
ConvRegistry[Enumerable,SortedCollection]= proc do |enum|
sca=SortedCollection.new
enum.each {|i| sc.insert i }
sc
end
Enumerable to a pseudoclass :Bool can be as easy as
ConvRegistry[Enumerable,:Bool]=proc { |x| not x.empty? }
ConvRegistry[Enumerable,:Bool]=proc { ... }
ConvRegistry[String,Integer]= proc { ... }
ConvRegistry[Class,Enumerable]= proc ... }
rb_define_conversion(rb_cEnumerable, rb_intern("Bool"),enum_to_bool);
rb_define_conversion(rb_cString, rb_cInteger, string_to_int);
rb_define_conversion(rb_cClass, rb_cEnumerable, class_to_enum);
class ConvRegistry
@@reg=Hash.new
def self.[] from_type,to_type
if res=@@reg[[from_type,to_type]]
res
elsif res= find_in_ancestors(from_type,to_type)
res
else
raise TypeError.new("no conversion for #{from_type},#{to_type}")
end
end
def self.find_in_ancestors from_type,to_type
from_type.ancestors[1..-1].each do |anc|
if res2= @@reg[[anc,to_type]]
return res2
end
end
nil
end
def self.[]= from_type,to_type,func
@@reg[[from_type,to_type]]=func
end
end
class Object
def to(something,*args,&blk)
if something.is_a? Module and self.is_a? something
return self
end
ConvRegistry[self.class,something].call(self, *args,&blk)
end
end
if __FILE__== $0
require 'test/unit'
require 'stringio'
class MyTest < Test::Unit::TestCase
def setup
ConvRegistry[Enumerable,:Bool]=proc { |x| not x.empty? }
ConvRegistry[String,Integer]= proc { |x,*args| x.to_i *args }
ConvRegistry[Class,Enumerable]= proc { |x| x.send
:include,Enumerable }
ConvRegistry[Object,Enumerable]= proc {|x| x.send :extend,
Enumerable }
ConvRegistry[Object,:Frozen]= proc do |x|
if x.frozen?
x
else
raise TypeError.new
end
end
ConvRegistry[Integer,:Odd]= proc do |x|
if x%2==1
x
else
raise TypeError.new
end
end
ConvRegistry[Object,:Readable]= proc do |x|
if x.respond_to? :read
x
else
raise TypeError.new
end
end
end
def test_class_class
assert_equal '5'.to(Integer),5
end
def test_subclass_class
my=Class.new String
m=my.new '5'
assert_equal m.to(Integer),5
end
def test_class_module
f_class=Class.new(Object)
f_class.class_eval do
def each
yield 1
end
end
f_obj=f_class.to(Enumerable).new
assert_equal f_obj.find_all {|x| x==1}, [1]
end
def test_more_arguments
a='aa'
assert_equal 170,a.to(Integer, 16)
end
def test_instance_module
f_class=Class.new Object
f_class.class_eval do
def each
yield 1
end
end
f_obj=f_class.new
f_obj=f_obj.to Enumerable
assert_equal f_obj.find_all {|x| x==1}, [1]
end
def test_singleton_module
f=Object.new
def f.each
yield 1
end
f=f.to(Enumerable)
assert_equal f.find_all {|x| x==1}, [1]
end
def test_property_pseudoclass_ok
a= 'ciao'
a.freeze
a=a.to(:Frozen)
assert_equal a, 'ciao'
end
def test_property_pseudoclass_fail
assert_raises(TypeError) {'ciao'.to :Frozen}
end
def test_property_pseudoclass_ok2
a=5
a=a.to :Odd
assert_equal a,5
end
def test_property_pseudoclass_fail2
assert_raises(TypeError) {6.to :Odd}
end
def test_object_superclass
a=42
assert_equal a, a.to(Integer)
end
def test_object_mixin_ok
a=[]
assert_equal a,a.to(Enumerable)
end
def test_object_mixin_fail
a=5
assert_raises(TypeError) {a.to Enumerable}
end
def test_methodbag_pseudoclass_ok
a=StringIO.new
assert_equal a,a.to(:Readable)
end
def test_methodbag_pseudoclass_fail
a=24
assert_raises(TypeError) {a.to :Readable}
end
def test_enumerable_bool
a=''
assert_equal false, a.to( :Bool)
a << 1
assert_equal true , a.to( :Bool)
a=[]
assert_equal false, a.to( :Bool)
a << 1
assert_equal true , a.to( :Bool)
end
end
end
Back to RCRchive.
RCR Submission page and RCRchive powered by Ruby, Apache, RuWiki (modified), and RubLog