From: Isaac Devine Date: 2005-12-27T18:59:09+09:00 Subject: [ANN] (Real) Primitive Ruby Generics support 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