[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Method Namespace

Trans

12/5/2006 3:08:00 AM

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


2 Answers

Pit Capitain

12/5/2006 9:07:00 PM

0

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

Trans

12/6/2006 5:02:00 AM

0


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.