From: Paul Brannan Date: 2005-10-20T02:16:31+09:00 Subject: namespaces implemented in ruby Rich and Nathaniel and I were standing around at the conference this weekend talking about Nathaniel's idea for implementing namespaces in pure ruby, just so ruby users could play around with them until we get them in ruby 2.0 (or maybe 1.9.1). Here's my first pass at an implementation of that idea. It uses a slightly modified version of Florian Gross's Binding.of_caller method. The idea is pretty simple; each scope contains two local variables called __ns (which namespace is currently being modified) and __use (which namespaces are searched during method invocation). When a method is added, a proxy method is created that does the method searching. No method caching is done, but that could be implemented. The global namespace is known as :__global. The syntax is something like this: namespace :namespace do # methods created in this scope will be added to :namespace end use :namespace do # methods called in this scope will be searched in :namespace first, # then :__global. end Hopefully someone can take this and extend it to properly handle singleton methods and inheritance. There is also one bug that I don't know how to address: class Foo; def foo; puts "global namespace"; end; end namespace :n { class Foo; def foo; puts "namespace n"; end; end } p = proc { Foo.new.foo } use :n { p.call } This should print "global namespace" but currently prints "namespace n". The problem is that both the outer scope and the inner scope share the same __use variable, and I don't know any good way to actually make the variable truly block-local. I'm all ears if anyone knows how to fix this. Paul --- require 'thread' def caller_binding(&block) old_critical = Thread.critical Thread.critical = true count = 0 result, error = callcc do |cc| tracer = lambda do |*args| type, context = args[0], args[4] if type == "return" count += 1 if count == 2 set_trace_func(nil) cc.call(eval("binding", context), nil) end elsif type != "line" set_trace_func(nil) cc.call(nil, lambda { raise(ArgumentError, "unable to get binding" ) }) end end set_trace_func(tracer) return nil end Thread.critical = false error.call if error yield result end def method_table h = {} methods.each do |name| h[name] = method(name) end return h end def namespace(ns) caller_binding do |b| old_ns = eval("defined?(__ns) ? __ns : nil", b) eval("__ns = #{ns.inspect}", b) eval("__mt ||= []; __mt << method_table", b) begin use(ns) do yield end ensure if old_ns then eval("__ns = #{old_ns.inspect}", b) else eval("__ns = nil", b) end eval("__mt.pop", b) end end end def use(ns) caller_binding do |b| eval("__use ||= []; __use << #{ns.inspect}", b) begin yield ensure eval("__use.pop", b) end end end class Object # TODO: need singleton_method_added too def self.method_added(name) caller_binding do |b| in_ma = eval("defined?(__in_ma) ? true : false", b) return if in_ma ns = eval("defined?(__ns) ? __ns : nil", b) ns ||= :__global mt = class << self; @mt ||= {}; end mt[ns] = instance_method(name) undef_method name self.class_eval <<-END __in_ma = true def #{name}(*args, &block) caller_binding do |b| use = eval("defined?(__use) ? __use : nil", b) use ||= [] use = use.reverse + [:__global] use.each do |ns| # TODO: also search up the inheritance hierarchy mt = class << self.class; @mt ||= {}; end u = mt[ns] next if not u m = u.bind(self) return m.call(*args, &block) end raise NoMethodError, "undefined method `#{name}' for TODO" end end END end end end class Foo namespace :n do def foo; 42; end end namespace :m do def foo; 10; end end def foo; 1; end end def bar p Foo.new.foo end use :n do p Foo.new.foo #=> 42 (namespace n) use :m do p Foo.new.foo #=> 10 (namespace m) end use :z do p Foo.new.foo #=> 42 (namespace n) end bar #=> 1 (global namespace) end use :z do p Foo.new.foo #=> 1 (global namespace) end p Foo.new.foo #=> 1 (global namespace)