# Ruby Treasures 0.4
# Copyright (C) 2002 Paul Brannan <paul@atdesk.com>
# 
# You may distribute this software under the same terms as Ruby (see the file
# COPYING that was distributed with this library).
# 
##
# A hack to allow a mixin to be used like Java's "interfaces".
#
# E.g.:
# <pre>
#   module Foo
#     interface_method :foo
#     interface_method :bar
#   end
#   
#   class XYZ
#     def bar
#     end
#   end
#   
#   class Bar < XYZ
#     include Foo
#     def foo
#       puts "foo"
#     end
#   end
#   
#   b = Bar.new
#   assert_object_includes_complete(b, Foo)
# </pre>
#
class Module
public
  ##
  # Determine if a class includes a module, in O(n) time.
  #
  # @return true if the class includes the module, false otherwise.
  #
  def includes_module?(mod)
    return included_modules.index(mod)
  end

private
  ##
  # Declare an abstract method.
  #
  # @param symbol the name of the method.
  #
  def interface_method(symbol)
    self.class_eval %{
      if not defined?(@@__interface_methods__) then
        @@__interface_methods__ = Array.new
      end
      @@__interface_methods__.push(symbol)
    }
  end
end

##
# Determine if a class includes a module, in O(n) time.
#
# @return true if the class includes the module, false otherwise.
#
def assert_class_includes(klass, mod)
  if not klass.includes_module?(mod) then
    raise TypeError, "foo_obj should have the Foo interface"
  end
end

##
# Determine if an object's class includes a module, in O(n) time.
#
# @return true if the object's class includes the module, false otherwise.
#
def assert_object_includes(obj, mod)
  assert_class_includes(obj.class, mod)
end

##
# Determine if a class includes a module and implements all the module's
# abstract methods, in O(n+m) time.
#
# @return true if the class includes the module, false otherwise.
#
def assert_class_includes_complete(klass, mod)
  assert_class_includes(klass, mod)
  imethods = mod.class_eval("@@__interface_methods__")
  imethods.each do |meth|
    if not klass.method_defined?(meth) then
      raise TypeError, "Class #{klass} should have method #{meth} from module #{mod} defined"
    end
  end
end

##
# Determine if an object's class includes a module and implements all the
# module's abstract methods, in O(n+m) time.
#
# @return true if the object's class includes the module, false otherwise.
#
def assert_object_includes_complete(obj, mod)
  assert_class_includes_complete(obj.class, mod)
end