Never been to DZone Snippets before?

Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

Overload for Ruby (See related posts)

An module let you overload functions

# 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.


Click here to browse all 5140 code snippets

Related Posts