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