# = overload.rb
#
# == Copyright (c) 2005 Thomas Sawyer
#
#   Ruby License
#
#   This module is free software. You may use, modify, and/or redistribute this
#   software under the same terms as Ruby.
#
#   This program is distributed in the hope that it will be useful, but WITHOUT
#   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
#   FOR A PARTICULAR PURPOSE.
#
# == Author(s)
#
# * Thomas Sawyer

# Author::    Thomas Sawyer
# Copyright:: Copyright (c) 2006 Thomas Sawyer
# License::   Ruby License

# = Overload
#
# Overload methods.
#
#   class X
#     def x
#       "hello"
#     end
#
#     overload :x, Integer do |i|
#       i
#     end
#
#     overload :x, String, String do |s1, s2|
#       [s1, s2]
#     end
#   end
#

class Module

  def method_overloads
    @method_overloads ||= {}
  end

  def overload( name, *signiture, &block )
    name = name.to_sym

    if method_overloads.key?( name )
      method_overloads[name][signiture] = block

    else
      method_overloads[name] = {}
      method_overloads[name][signiture] = block

      if method_defined?( name )
        #method_overloads[name][nil] = instance_method( name ) #true
        alias_method( "#{name}Generic", name )
        has_generic = true
      else
        has_generic = false
      end

      define_method( name ) do |*args|
        ovr = self.class.method_overloads["#{name}".to_sym]
        sig = args.collect{ |a| a.class }
        hit = nil
        faces = ovr.keys.sort { |a,b| b.size <=> a.size }
        faces.each do |cmp|
          next unless cmp.size == sig.size
          cmp.size.times { |i|
            next unless cmp[i] < sig[i]
          }
          hit = cmp
        end

        if hit
          ovr[hit].call(*args)
        else
          if has_generic #ovr[nil]
            send( "#{name}Generic", *args )
            #ovr[nil].bind(self).call(*args)
          else
            raise NoMethodError
          end
        end

      end

    end

  end

end


#  _____         _
# |_   _|__  ___| |_
#   | |/ _ \/ __| __|
#   | |  __/\__ \ |_
#   |_|\___||___/\__|
#

=begin test

require 'test/unit'

class TCOverload < Test::Unit::TestCase

  class X

    def x
      "hello"
    end

    overload :x, Integer do |i|
      i
    end

    overload :x, String, String do |s1, s2|
      [s1, s2]
    end

  end

  def setup
    @x = X.new
  end

  def test_x
    assert_equal( "hello", @x.x )
  end

  def test_i
    assert_equal( 1, @x.x(1) )
  end

  def test_s
    assert_equal( ["a","b"], @x.x("a","b") )
  end

end

=end

