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