# import-module.rb ... create scope of methods by module
# by Shin-ichiro Hara
#
# This library is based on the algorithm of `scope-in-state'
# by Keiju ISHIZUKA
#
# Ver. 0.78
# beta1: adopt ruby-1.8.0
# beta2:
# beta3:
# beta4:
# beta5:
#
# 2003.04.30
#
# Example:
#
# require "import-module"
#
# class Foo
# def hello
# puts 'hello'
# end
# end
#
# module Bar
# def hello
# puts 'bye'
# end
# end
#
# module Baz
# def hello
# puts 'good-bye'
# end
# end
#
# foo = Foo.new
# foo.hello #=> hello
# Foo.import_module(Bar) do
# foo.hello #=> bye
# Foo.import_module(Baz) do
# foo.hello #=> good-bye
# end
# foo.hello #=> bye
# end
# foo.hello #=> hello
#
# Foo.import(Bar) do
# Foo.hello #=> bye
# end
#$IMPORT_MODULE_debug = true
################################################################ kk
module Import_Module
IMPORT_MODULE_Version = "0.78beta1"
module API
def import_module(mod)
scope = adopt_module(mod) #not upper compatible
if block_given?
begin
yield
ensure
scope.pop
end
end
end
def adopt_module(mod)
sw = Thread.critical or Thread.critical = true # this 'or' is safe
unless scope = (@__IMPORT_MODULE_PREFIX_scopes ||= {})[mod.object_id]
scope = Scope.create(self, mod)
@__IMPORT_MODULE_PREFIX_scopes[mod.object_id] = scope
end
scope.push
Thread.critical = sw
scope
end
def set_orig_method(meth, orig)
meth = meth.id2name if meth.is_a? Symbol
orig = orig.id2name if orig.is_a? Symbol
meth0 = Import_Module.name(orig, :orig)
module_eval "def #{meth}(*a, &b); #{meth0}(*a, &b); end\n"
end
end
def self.name(meth, s, prefix = true)
name = s.to_s.gsub(/_|::|[^\w]/){|c| "_%03d" % [c[0]]}
if meth =~ /^[_a-zA-Z][_\w]*$/
meth = "__" + meth
else
meth = "_op" + meth.gsub(/[^\w]/){|c| "%03d" % [c[0]]} + "__"
end
(prefix ? "__IMPORT_MODULE_PREFIX_" : "") + name + meth
end
################################################################ kk
class Scope
attr_reader :target, :klass, :source, :mod
@@cm_no = -1
def self.create(klass, mod)
target = Target.enclose(klass)
source = Source.enclose(mod)
scope = enclose(target, source)
scope.set_methods
scope
end
def self.enclose(target, source)
target.scopes[source.mod] ||= new(target, source)
end
def initialize(target, source)
@target = target
@klass = target.klass
@source = source
@mod = source.mod
end
def activate
push
begin
yield
ensure
pop
end
end
def set_methods
meths = @target.get_orig_methods(@source)
@target.def_orig_methods(meths)
set_meth_no
def_methods
mod = @mod
@klass.class_eval do include mod end
end
def inspect
"Scope(#{@target.inspect}, #{@source.inspect})"
end
def push()
c = Thread.current.__IMPORT_MODULE_PREFIX_stack.current
Thread.current.__IMPORT_MODULE_PREFIX_stack.push(update(c))
end
def pop
Thread.current.__IMPORT_MODULE_PREFIX_stack.pop
end
# private ### this declearation makes it slow
def set_meth_no
@source.methods.__each__ do |meth|
@target.meth_no[meth] || @target.meth_no[meth] = (@@cm_no += 1)
end
end
def update(c)
d = c.dup
@source.methods.__each__ do |meth|
d[@target.meth_no[meth]] = @mod.object_id
end
d
end
def def_methods
# Define new methods in the target class
t = ""
@source.methods.__each__ do |meth|
puts "#{@klass}> def #{meth}(#{@source.param(meth)})" if $IMPORT_MODULE_debug
s, = method_code(meth)
t << s
end
@klass.module_eval t
end
def method_code(meth)
param = @source.param(meth)
no = @target.meth_no[meth]
line_no = __LINE__ + 1
s = "def #{meth}(#{param})\n"
s << " modid = Thread.current.__IMPORT_MODULE_PREFIX_proxy[#{no}]\n"
i = 0
@target.scopes.each_key do |mod|
s << (i == 0 ? " " : " els") << "if modid == #{mod.object_id}\n"
s << " #{Import_Module.name(meth, mod)}(#{param})\n"
i += 1
end
s << " else\n" if i > 0
s << " #{Import_Module.name(meth, :orig)}(#{param})\n"
s << " end\n" if i > 0
s << "end\n"
s << "protected(:#{meth})\n" if @target.protecteds.include?(meth)
s << "private(:#{meth})\n" if @target.privates.include?(meth)
[s, __FILE__, line_no]
end
end
################################################################ kk
class Target
attr_reader :scopes, :meth_no
attr_accessor :klass, :stack
attr_accessor :orig_methods, :saved_methods
attr_accessor :publics, :privates, :protecteds
def self.enclose(klass)
s = self
klass.instance_eval do
@__IMPORT_MODULE_PREFIX_target ||= s.new(klass)
end
end
def initialize(klass)
@scopes = {}
@meth_no = {}
@klass = klass
@publics = @klass.public_instance_methods(true)#.find_all{|m| @klass.public_method_defined? m}
@privates = @klass.private_instance_methods(true)#.find_all{|m| @klass.private_method_defined? m}
@protecteds = @klass.protected_instance_methods(true)#.find_all{|m| @klass.protected_method_defined? m}
@orig_methods = {}
@saved_methods = {}
resist_orig_methods
end
def inspect
"Target(#{@klass})"
end
def def_orig_methods(meths, pub_sw = false)
# Store original methods of the root class
@klass.module_eval do
meths.__each__ do |meth|
meth0 = Import_Module.name(meth, :orig)
alias_method meth0, meth
public meth0 if pub_sw
end
end
end
def get_orig_methods(source)
meths = []
source.methods.__each__ do |meth|
if @orig_methods[meth] && !@saved_methods[meth]
@saved_methods[meth] = true
meths.push meth
end
end
meths
end
private
def resist_orig_methods
@publics.__each__ do |x|
@orig_methods[x] = true
end
@protecteds.__each__ do |x|
@orig_methods[x] = true
end
@privates.__each__ do |x|
@orig_methods[x] = true
end
end
end
################################################################ kk
class Source
attr_reader :mod, :methods
def self.enclose(mod)
s = self
mod.instance_eval do
@__IMPORT_MODULE_PREFIX_source or
@__IMPORT_MODULE_PREFIX_source = s.new(mod)
end
end
def initialize(mod)
@mod = mod
@methods = _methods
store
end
def inspect
"Source(#{@mod})"
end
def param(meth)
s = ""
if (n = @mod.instance_method(meth).arity) >= 0
n.times do |i| s << "x#{i}, " end
s << "&b"
else
(-n-1).times do |i| s << "x#{i}, " end
s << "*a, &b"
end
end
def param0(meth)
s = ""
if (n = @mod.instance_method(meth).arity) >= 0
n.times do |i| s << "x#{i}, " end
s << "b"
else
(-n-1).times do |i| s << "x#{i}, " end
s << "a, b"
end
end
private
def _methods
meths = @mod.instance_methods(true)
meths.concat @mod.protected_instance_methods(true)
meths.concat @mod.private_instance_methods(true)
meths.reject!{|f| f =~ /^__IMPORT_MODULE_PREFIX_/ }
meths
end
def store
# Store Soruce methods
methods = @methods
@mod.module_eval do
methods.__each__ do |meth|
meth0 = Import_Module.name(meth, self)
unless method_defined? meth0
alias_method meth0, meth
public meth0 if true
end
end
end
end
end
################################################################ kk
class Stack
def initialize(a)
@stack = a
export_current
end
def dup
self.class.new(@stack.dup)
end
def current
@stack.last
end
def push(c)
@stack.push c
export_current
end
def nop(scope)
NO USE
end
def pop
c = @stack.pop
export_current
c
end
private
def export_current
Thread.current.__IMPORT_MODULE_PREFIX_proxy = current
end
end
end
################################################################ kk
Module.module_eval do
include Import_Module::API
end
class Thread
attr_accessor :__IMPORT_MODULE_PREFIX_proxy
attr_accessor :__IMPORT_MODULE_PREFIX_stack
end
class << Thread
alias new_org new
def new(*opts, &b)
Thread.new_org(Thread.current, *opts) do |parent, *opts|
Thread.current.__IMPORT_MODULE_PREFIX_stack =
parent.__IMPORT_MODULE_PREFIX_stack.dup
yield *opts
end
end
alias start new
alias fork new
end
Thread.current.__IMPORT_MODULE_PREFIX_stack =
Import_Module::Stack.new([Array.new])
# Import_Module::Stack.new([Hash.new])
class Object
def import(mod)
(class << self; self; end).import_module(mod) do
yield self
end
end
def adopt(mod)
(class << self; self; end).adopt_module(mod)
self
end
end
class Array
alias __each__ each
end
class Hash
alias __each__ each
end
################################################################ kk
if $IMPORT_MODULE_debug
class Module
alias include_orig include
def include(mod)
puts "#{self}> includes #{mod}"
include_orig mod
end
alias alias_method_orig alias_method
def alias_method(a, b)
a0 = a.sub(/__IMPORT_MODULE_PREFIX_/, "@").sub(/\d+(\d{3})/, '\1')
b0 = b.sub(/__IMPORT_MODULE_PREFIX_/, "@").sub(/\d+(\d{3})/, '\1')
puts "#{self}> #{a0} = #{b0}"
alias_method_orig a, b
end
alias remove_method_orig remove_method
def remove_method(a)
a0 = a.sub(/__IMPORT_MODULE_PREFIX_/, "@").sub(/\d+(\d{3})/, '\1')
puts "#{self}> remove #{a0}"
remove_method_orig a
end
alias undef_method_orig undef_method
def undef_method(a)
a0 = a.sub(/__IMPORT_MODULE_PREFIX_/, "@").sub(/\d+(\d{3})/, '\1')
puts "#{self}> undef #{a0}"
undef_method_orig a
end
end
end
################################################################ kk
if $0 == __FILE__
class Foo
def hello
puts 'hello'
end
end
module Bar
def hello
puts 'bye'
end
end
module Baz
def hello
puts 'good-bye'
end
end
foo = Foo.new
foo.hello #=> hello
Foo.import_module(Bar) do
foo.hello #=> bye
Foo.import_module(Baz) do
foo.hello #=> good-bye
end
foo.hello #=> bye
end
foo.hello #=> hello
puts
Foo.import(Bar) do
Foo.hello #=> bye
end
end
syntax highlighted by Code2HTML, v. 0.9.1