Method Namespace

T

Trans

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
 
P

Pit Capitain

Trans said:
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
 
T

Trans

Pit said:
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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top