1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
|
require 'facets/functor'
require 'facets/module/basename'
class Module
# Create method namespaces, allowing for method
# chains but still accessing the object's instance.
#
# class A
# attr_writer :x
# method_space :inside do
# def x; @x; end
# end
# end
#
# a = A.new
# a.x = 10
# a.inside.x #=> 10
#
# expect NoMethodError do
# a.x
# end
#
# NOTE: This is not (presently) a common core extension and is not
# loaded automatically when using <code>require 'facets'</code>.
#
# CREDIT: Pit Captain
def method_space(name, mod=nil, &blk)
## If block is given then create a module, otherwise
## get the name of the module.
if block_given?
name = name.to_s
raise ArgumentError if mod
mod = Module.new(&blk)
else
if Module === name
mod = name
name = mod.basename.downcase
end
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.module_eval %{
def #{m}(*a,&b)
super(*a,&b)
end
}
##mod.instance_eval do
## define_method(m)
## super
## end
##end
end
## Add a method for the namespace that delegates
## via the Functor to the saved instance methods.
define_method(name) do
mtab = methods
Functor.new do |op, *args|
if meth = mtab[op.to_sym]
meth.bind(self).call(*args)
else
#self.__send__(op, *args)
raise NoMethodError, "undefined method `#{m}'"
end
end
end
end
# Include a module via a specified space.
#
# module T
# def t ; "HERE" ; end
# end
#
# class X
# include_as :test => T
# def t ; test.t ; end
# end
#
# X.new.t #=> "HERE"
#
# NOTE: This is not (presently) a common core extension and is not
# loaded automatically when using <code>require 'facets'</code>.
def include_as(h)
h.each{ |name, mod| method_space(name, mod) }
end
end
|