From: Gregory Seidman Date: 2006-05-20T00:00:08+09:00 Subject: Re: Ruby namespaces and require On Fri, May 19, 2006 at 04:23:45PM +0900, Alex Young wrote: } There have been a few threads recently both here and on the Rails list } where the problem of namespace collisions has come up. It seems to my } untutored eye that they've come about simply because the original } authors didn't apply their namespacing universally. From memory, an } example is htmltools and ActionPack both defining an HTML::Tag class. } } A possible solution sprung to mind: how does the following syntax look? } } require 'foo', :into => 'Bar' } require 'qux', :into => 'Wibble::Spang' [...] } Any thoughts? Has this been discussed to death before my time? Is it } obviously stupid? I don't care for the :into => syntax, myself. } It's just struck me that if we're getting help from the interpreter } anyway, this might be possible: } } require 'foo' into Bar [...] Nicer, but I consider the whole into thing superfluous. Consider the following implementation: def qualified_const_get(str) path = str.to_s.split("::") from_root = path[0].empty? if from_root path = path[1..-1] else from_root = (self == Object) || (self.class == Object) end if not from_root begin start_ns = (Class === self) ? self : self.class return path.inject(start_ns) { |ns,name| ns.const_get(name) } rescue NameError #noop end end path.inject(Object) { |ns,name| ns.const_get(name) } end module RequireWithinNamespace AlreadyRequired = Hash.new { |h,k| h[k] = [] } def self.find_requirable_file(filename) filename = "#{filename}.rb" unless /\.rb$/ === filename $LOAD_PATH.each { |dir| path = File.join(dir, filename) return path if File.exists?(path) } raise "Could not find #{filename} in #{$LOAD_PATH.inspect}" end def self.require_within(filename, context = nil, base_require = :require) if context if not ((Module === context) || (Class === context)) context = Object::qualified_const_get(context) end filename = find_requirable_file(filename) context_list = AlreadyRequired[filename] if not (context_list.index context) context_list << context context.instance_eval { eval File.read(filename) } return true else return false end else send(base_require, filename) end end def require(filename, context = nil) RequireWithinNamespace.require_within(filename, context) end end You can then use... RequireWithinNamespace::require 'qux', 'Wibble::Spang' ...or even... # no need for quotes around the namespace RequireWithinNamespace::require_within 'qux', Wibble::Spang ...or, since it is a drop-in replacement for require, you can add... module Kernel alias :require_no_namespace :require def require(filename, context = nil) RequireWithinNamespace.require_within(filename, context, :require_no_namespace) end end ...so you can use it for your normal requires or to require in a namespace. By the way, I decided that it was time to take the Ruby tricks and techniques I've discovered or developed and make them publicly available. I am starting to post them on a blog for people to take and use freely. See http://redcorundum.blogspot.com/ (There is only oe post so far, but I have a bunch of stuff that will gradually go up there.) Once I've done a bit more testing of this, it will go up there as well. } Alex --Greg