# = as.rb

# LOG  2006-11-04  trans@gmail.com  Use own delegator instead of Functor.
# LOG  2006-11-04  trans@gmail.com  Now #as can take a block.

module Kernel

  class As
    # Privatize all methods except #binding an operators.
    private *instance_methods.select { |m| m !~ /(^__|^\W|^binding$)/ }

    def initialize(subject, ancestor)
      @subject = subject
      @ancestor = ancestor
    end

    def method_missing(sym, *args, &blk)
      @ancestor.instance_method(sym).bind(@subject).call(*args,&blk)
    end
  end

  # Returns a As-functor that allows one to call any
  # ancestor's method directly of the given object.
  #
  #   class A
  #     def x ; 1 ; end
  #   end
  #
  #   class B < A
  #     def x ; 2 ; end
  #   end
  #
  #   class C < B
  #     def x ; as(A).x ; end
  #   end
  #
  #   C.new.x  #=> 1

  def as( ancestor, &blk )
    @__as ||= {}
    unless r = @__as[ancestor]
      r = (@__as[ancestor] = As.new(self, ancestor))
    end
    r.instance_eval(&blk) if block_given?
    #yield(r) if block_given?
    r
  end
  #alias_method :__as__, :as

  def send_as( ancestor, sym, *args, &blk )
    ancestor.instance_method(sym).bind(self).call(*args,&blk)
  end

#--
#   def as( role, &blk )
#     unless role.is_a?(Class)
#       role = self.class.const_get( role )
#     end
#     @roles ||= {}
#     @roles[role] ||= role.allocate
#     robj = @roles[role]
#     robj.assign_from( self )
#     result = robj.instance_eval( &blk )
#     self.assign_from( robj )  # w/o this much like namespaces
#     result
#   end
#++

end


#  _____         _
# |_   _|__  ___| |_
#   | |/ _ \/ __| __|
#   | |  __/\__ \ |_
#   |_|\___||___/\__|
#
=begin test

  require 'test/unit'

  class TCKernel < Test::Unit::TestCase
    class A
      def x; "A.x"; end
      def y; "A.y"; end
    end
    class B < A
      def x; "B.x" end
      def y; "B.y" end
    end
    class C < B
      def x; "C.x"; end
      def y; as(B).x ; end
    end

    def setup
      @c = C.new
    end

    def test_as
      assert_equal("B.x", @c.y)
      assert_equal("C.x", @c.x)
    end
  end

=end
