# Example: # # class Test1 # include Overload # # def foo # puts "foo # Original one" # end # # overload :foo, :foo_str, String do |str, x| # puts "foo_str(String str, x)" # end # # overload :foo, Integer do |i| # puts "foo(Integer i) # no specific name" # end # end module Overload def self.included(klass) class << klass include Overload::Feature end end def self.spec_to_cond(spec) code = "args.length == #{spec[0]}" spec[1..-1].each_with_index { |s, i| code << " && #{s.inspect} === args[#{i}]" } code end module Feature private def overload(method_id, *spec, &block) if spec[0].instance_of? Symbol specific_method_id = spec.shift define_method(specific_method_id, block) if block block = instance_method(specific_method_id) elsif block define_method(method_id, block) block = instance_method(method_id) remove_method(method_id) else raise "You must give a block or a name of existing method!" end spec.unshift(block.arity) add_method_to_dispatch_table(method_id, spec, block) update_dispatcher(method_id) end def add_method_to_dispatch_table(method_id, key, block) if !dispatch_table[method_id] dispatch_table[method_id] = Hash.new end dispatch_table[method_id][key] = block end def update_dispatcher(method_id) remove_method(method_id) if method_defined?(method_id) class_eval dispatcher_code(method_id), "#{method_id}_dispatcher", 0 end def dispatcher_code(method_id) method_body = <<-EOS def #{method_id.to_s}(*args, &blk) dt = DispatchTable[#{method_id.inspect}] EOS sorted_dispatch_table_of(method_id).each_with_index do |(spec, block), i| method_body << <<-EOS #{(i==0) ? "if" : "elsif"} #{Overload::spec_to_cond(spec)} dt[#{spec.inspect}].bind(self).call(*args, &blk) EOS end method_body << <<-EOS else raise "Couldn't find method for #{method_id}(\#{args.inspect[1..-2]})" end end EOS end def sorted_dispatch_table_of(method_id) dispatch_table[method_id].sort do |a,b| a[0].length <=> b[0].length end end def dispatch_table const_set("DispatchTable", Hash.new) if !const_defined?("DispatchTable") const_get("DispatchTable") end end end
You need to create an account or log in to post comments to this site.