Method Namespace

I have need for a general purpose simple namespace construction. Not
selector namespaces, mind you, just a method namespace. I made the
following attempt. So close! But it fails b/c if a method is already
defined in the class and the namespace module being added also has a
method of the same name they will clash. I worked around it by aliasing
the method with a temporary name then realiasing it to the original
name. This words EXCEPT when super is called in the method (see the
test case a the bottom).

Anone have any better ways of implementing?

Thanks,
T.

  # namespace.rb

  require 'facet/functor'
  require 'facet/module/basename'

  class Module

    # Define a simple namespace.

···

#
    # class A
    # attr_writer :x
    # namespace :inside do
    # def x; @x; end
    # end
    # end
    #
    # a = A.new
    # a.x = 10
    # a.inside.x #=> 10
    # a.x # no method error

    def namespace( mod, &blk )

      # If block is given then create a module, othewise
      # get the name of the module.
      if block_given?
        name = mod.to_s
        mod = Module.new(&blk)
      else
        name = mod.basename.downcase
        mod = mod.dup
      end

      # We have to work around name clashes.
      nameclashes = mod.instance_methods & instance_methods
      nameclashes.each do |n|
        alias_method "#{n}:namespace", n
      end

      # Include the module. This is neccessary, otherwise
      # Ruby won't let us bind the instance methods.
      include mod

      # Undefine the instance methods of the module.
      mod.instance_methods.each{ |m| undef_method m }

      # Redefine the methods that clashed.
      nameclashes.each do |n|
        alias_method n, "#{n}:namespace"
        undef_method "#{n}:namespace"
      end

      # Add a method for the namespace that delegates
      # via the Functor to the module instance methods.
      define_method(name) do
        Functor.new(mod) do |op, base, *args|
          base.instance_method(op).bind(self).call(*args)
        end
      end
    end

  end

  # begin test

  require 'test/unit'

  class TestNamespace1 < Test::Unit::TestCase

    module M
      def x; "x"; end
    end

    class C
      namespace M
    end

    def test_01
      c = C.new
      assert_equal('x', c.m.x)
    end

    def test_02
      c = C.new
      assert_raises(NoMethodError){ c.x }
    end

  end

  class TestNamespace2 < Test::Unit::TestCase

    class B
      def x; 1; end
    end

    class C < B
      def x; super; end
      namespace :m do
        def x; "x"; end
      end
    end

    def test_01
      c = C.new
      assert_equal('x', c.m.x)
    end

    # THIS FAILS !!!
    def test_02
      c = C.new
      assert_equal(1, c.x)
    end

  end

Trans schrieb:

I have need for a general purpose simple namespace construction.
...
This words EXCEPT when super is called in the method (see the
test case a the bottom).
...

Tom, could you try this version?

   def namespace( mod, &blk )

     # If block is given then create a module, otherwise
     # get the name of the module.
     if block_given?
       name = mod.to_s
       mod = Module.new(&blk)
     else
       name = mod.basename.downcase
       mod = mod.dup
     end

     # Include the module. This is neccessary, otherwise
     # Ruby won't let us bind the instance methods.
     include mod

     # Save the instance methods of the module and
     # replace them with a "transparent" version.
     methods = {}
     mod.instance_methods(false).each do |m|
       methods[ m.to_sym ] = mod.instance_method(m)
       mod.instance_eval do
         define_method(m) do
           super
         end
       end
     end

     # Add a method for the namespace that delegates
     # via the Functor to the saved instance methods.
     define_method(name) do
       Functor.new(methods) do |op, mtab, *args|
         mtab[op].bind(self).call(*args)
       end
     end
   end

Regards,
Pit

Pit Capitain wrote:

Tom, could you try this version?

   def namespace( mod, &blk )

     # If block is given then create a module, otherwise
     # get the name of the module.
     if block_given?
       name = mod.to_s
       mod = Module.new(&blk)
     else
       name = mod.basename.downcase
       mod = mod.dup
     end

     # Include the module. This is neccessary, otherwise
     # Ruby won't let us bind the instance methods.
     include mod

     # Save the instance methods of the module and
     # replace them with a "transparent" version.
     methods = {}
     mod.instance_methods(false).each do |m|
       methods[ m.to_sym ] = mod.instance_method(m)
       mod.instance_eval do
         define_method(m) do
           super
         end
       end
     end

     # Add a method for the namespace that delegates
     # via the Functor to the saved instance methods.
     define_method(name) do
       Functor.new(methods) do |op, mtab, *args|
         mtab[op].bind(self).call(*args)
       end
     end
   end

Bloody hek, that's a clever solution. Works like a charm. Pit Captain,
you never cease to amaze! I'm putting this in Facets to replace the
weak SimpleDelegtor I had been using.

Thanks!
T.