Hi all,
I have been hacking away to create a simple library that adds
generics-like qualities to ruby. At the moment you can define methods
with type-matches, defaults if no match and different number of
argument matches. Currently thinking about adding pattern-matching
support from eigenclass blog. Depends on ruby facets.
Usage examples (Tests) are at the bottom.
Any suggestions, comments, flames
?
thanks,
Isaac Devine
P.S. BSD licensed. (don't know about the bit from why's guide though...)
--------------generics.rb--------------
# This file contains all you need to add generics to any ruby class 
# ie.
# class Sample
# include Generics
# generic_method :name, Types do
# CODE
# end
## Default case
# generic_method :name do
# CODE
# end
# end
require 'mega/inheritor'
# This Object stuff from Why's Metaprogramming guide
class Object
聽聽聽# The hidden singleton lurks behind everyone
聽聽聽def metaclass; class << self; self; end; end
聽聽聽def meta_eval &blk; metaclass.instance_eval &blk; end
聽聽聽# Adds methods to a metaclass
聽聽聽def meta_def name, &blk
聽聽聽聽聽meta_eval { define_method name, &blk }
聽聽聽end
聽聽聽# Defines an instance method within a class
聽聽聽def class_def name, &blk
聽聽聽聽聽class_eval { define_method name, &blk }
聽聽聽end
end
# End Why's Stuff.
module Generics
聽聽class_inherit do
聽聽# Get a metaclass for this class
聽聽def metaclass; class << self; self; end; end
聽聽聽# metaprogramming code for generic_method
聽聽def generic_method(method_name, *types, &blk )
聽聽聽聽# have a class instance hash which holds the following:
聽聽聽聽# { :method_name => { type_name => block, type_name => block } }
聽聽聽聽# initialize it here...
聽聽聽聽class_eval {
聽聽聽聽聽聽# define the class generic_signatures if they don't exist
聽聽聽聽聽聽@generic_signatures = Hash.new if not
defined?(@generic_signatures) # define the generic method's signatures
if they don't exist @generic_signatures[method_name] = Hash.new unless
@generic_signatures.has_key?(method_name)
聽聽聽聽聽聽def self.generic_signatures
聽聽聽聽聽聽聽聽return @generic_signatures
聽聽聽聽聽聽end
聽聽聽聽}
聽聽聽聽
聽聽聽聽# check to see if we are the default
聽聽聽聽if types.empty?
聽聽聽聽聽聽class_eval {
聽聽聽聽聽聽聽聽@generic_signatures[method_name].default = blk
聽聽聽聽聽聽}
聽聽聽聽else # got a typelist?
聽聽聽聽聽聽# create the type "string"
聽聽聽聽聽聽specific_method_name = types.join("_").to_sym
聽聽聽聽聽聽class_eval {
聽聽聽聽聽聽聽聽@generic_signatures[method_name][specific_method_name] = blk
聽聽聽聽聽聽}
聽聽聽聽end
聽聽聽聽
聽聽聽聽# define the class method that does the dispatch to
聽聽聽聽# the appropiate block.
聽聽聽聽class_def(method_name) do |*args|
聽聽聽聽聽聽type_sig_arr = args.collect { |a| a.class }
聽聽聽聽聽聽type_sig = type_sig_arr.join('_').to_sym
聽聽聽聽聽聽self.class.generic_signatures[method_name][type_sig].call(*args)
聽聽聽聽end
聽聽end
聽聽聽聽
聽聽end
end
class Test
include Generics
聽聽generic_method :get, String do |arg|
聽聽聽聽puts "In String... -- #{arg}"
聽聽end
聽聽
聽聽generic_method :get, Fixnum do |arg|
聽聽聽聽puts "In Fixnum... -- #{arg}"
聽聽end
end
class Test2
include Generics
聽聽聽generic_method :two_args, String, Fixnum do |arg1, arg2|
聽聽聽聽聽puts "got a String and Fixnum"
聽聽聽end
聽聽聽generic_method :two_args, String, String do |arg1, arg2|
聽聽聽聽聽puts "got two Strings"
聽聽聽end
end
# does having a method that accepts two different numbers
# of arguments work?
class TestVariable
聽聽include Generics
聽聽聽聽generic_method :test_method, String do |arg1|
聽聽聽聽聽聽puts "single argument"
聽聽聽聽end
聽聽聽聽
聽聽聽聽generic_method :test_method, String, Fixnum do |arg1,arg2|
聽聽聽聽聽聽puts "two arguments"
聽聽聽聽end
end
class TestDefault
聽聽include Generics
聽聽
聽聽generic_method :test, String do |arg|
聽聽聽聽puts "in String!"
聽聽end
聽聽
聽聽generic_method :test do |arg|
聽聽聽聽puts "The rest!"
聽聽end
end