Ruby namespaces and require

Hi all,

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'

What that would do is require the named file such that all its code was executed in the context of the named module. My meta-fu isn't quite up to giving even a simple example, but I'd envisage the result looking something like this:

   $ cat bar.rb
   class Bar
   end
   $ cat test.rb
   require 'bar', :into => 'Foo'
   p Foo.constants
   $ ruby test.rb
   ["Bar"]

With the htmltools and ActionPack example, this:

   require 'htmltools', :into => 'MyHtmltoolsModule'
   require 'actionpack', :into => 'MyCollisionModule'

would result in MyHtmlToolsModule::HTML::Tag and MyCollisionModule::HTML::Tag existing side-by-side.

Would this require help from the interpreter? require and load both execute the code at the top level of the namespace (unless I've missed something) and without overloading them to actually eval() the code differently, I'm not sure what's best.

Any thoughts? Has this been discussed to death before my time? Is it obviously stupid?

It's just struck me that if we're getting help from the interpreter anyway, this might be possible:

   require 'foo' into Bar

which I think is quite neat. In case I haven't quite made it clear, the main advantage I can see from this is that modules can be protected from each other without the original author needing to care how their module is going to be used, and it can be retrospectively applied to existing code (give or take any meta- or cleverer-than-me-programming that makes this void, of course :slight_smile: ) to fix exactly the sort of collision problems that are already occurring.

···

--
Alex

It should be better and more elegant to have a STD NS like in Java
  or in C#.

  Then use for all non official applications an ext/<org>/<name>
  prefix.

  Like:

    require "skp/ifc/users"

···

--
Upper reality >oftware.
Dave - Skp Core (skp-it.eu).

--
Email.it, the professional e-mail, gratis per te: http://www.email.it/f

Sponsor:
Refill srl il paradiso della tua stampante - cartucce e toner compatibili, inchiostri e accessori per la ricarica, carta speciale. Tutto a prezzi scontatissimi!
Clicca qui: http://adv.email.it/cgi-bin/foclick.cgi?mid=5187&d=19-5

} 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

···

On Fri, May 19, 2006 at 04:23:45PM +0900, Alex Young wrote:

In Rolls (roll.rubyforge.org) I added the import method which works
like this:

  module Bar
    import 'foo'
  end

I can't say which sytax is better, but either works, but there are some
gotchyas in doing this that one must be careful about. For instance,
importing any code with extensions to built in classes will not work
unless the class name is prefixed with '::'.

T.

Awful and unworkable. Remember that in Ruby, a filename does not
necessarily correspond to a created or loaded class. Furthermore, with
dynamic code generation, that would end up meaning nothing at all in
the end.

So, I would oppose this heartily.

-austin

···

On 5/19/06, Alex Young <alex@blackkettle.org> 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'

--
Austin Ziegler * halostatue@gmail.com
               * Alternate: austin@halostatue.ca

dave wrote:

  It should be better and more elegant to have a STD NS like in Java
  or in C#.

Alternative, certainly. I'm not so sure about better or more elegant.
Having a standard namespace and having a system that applies it are two separate concerns. It's semantics versus syntax.

  Then use for all non official applications an ext/<org>/<name>
  prefix.

  Like:

    require "skp/ifc/users"

There's nothing stopping that from happening as well, except for the large body of code that already exists without it. In fact, this would be quite a nice way of adapting the existing codebase to a standard namespace if that was what Matz wanted, without needing an extensive rewrite.

To take your example:
   require 'users', :into => 'Skp::Ifc'
would have a similar effect, without the requirement that the 'users.rb' file was written with a namespace in mind, or a directory structure to mirror it.

···

--
Alex

transfire@gmail.com wrote:

In Rolls (roll.rubyforge.org) I added the import method which works
like this:

  module Bar
    import 'foo'
  end

I can't say which sytax is better, but either works, but there are some
gotchyas in doing this that one must be careful about. For instance,
importing any code with extensions to built in classes will not work
unless the class name is prefixed with '::'.

I can't say I like the syntax (I came up with :into for precisely that reason - your way is more natural, but rather verbose), but it's different strokes for different folks. I'll take a look at your implementation... It'd be good to have something standard to point to as a solution.

···

--
Alex

Gregory Seidman wrote:
<snip>

} 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.

Fair enough :slight_smile:

} 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:

<snip implementation I'll have to go away and Study...>

I've got a bit of a soft spot for natural language-ish syntax, but that's just personal preference talking.

You can then use...

RequireWithinNamespace::require 'qux', 'Wibble::Spang'

Given that my plan was to alias away (or somehow supplement) the existing Kernel#require, I didn't want to hijack the second parameter because I don't know what's going on with require_gem. As I understand it, require_gem is being deprecated in favour of plain-old require, but require_gem handles version requirements which (I imagine) could get tacked on to require. Passing named parameters with a hash seems to be the cleanest way to please everyone :slight_smile:

...or even...

# no need for quotes around the namespace
RequireWithinNamespace::require_within 'qux', Wibble::Spang

That's a neat trick - the only reason I had the quotes there in the first place was because I wasn't sure it was possible to get rid of them without triggering warnings.

...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

And *that* is what I think I'll be using from here on in. Very cool.

<snip>

See
http://redcorundum.blogspot.com/

<snip>
Bookmarked!

Thanks for that, good to know I'm not entirely barking.

···

On Fri, May 19, 2006 at 04:23:45PM +0900, Alex Young wrote:

--
Alex

Austin Ziegler wrote:
<snip>

A possible solution sprung to mind: how does the following syntax look?

   require 'foo', :into => 'Bar'
   require 'qux', :into => 'Wibble::Spang'

Awful and unworkable.

Really? There's already been one implementation posted that could support exactly this. Is it the concept you object to, or how it looks in this case? Would you prefer the:
   module Bar
     import('foo')
   end
syntax that was also suggested? Or maybe
   require 'foo', Bar
?

Remember that in Ruby, a filename does not
necessarily correspond to a created or loaded class.

I'm not assuming that. I'm just supplying a module context within which arbitrary code can be imported and executed. I'd want this system to be circumventable too, by the way - as long as the author knew they were doing it.

Furthermore, with
dynamic code generation, that would end up meaning nothing at all in
the end.

This is where it gets interesting. Can you give me an example?

So, I would oppose this heartily.

Fair enough :slight_smile: Bear in mind, though, that this offers a way to fix a current problem without breaking anything.

···

On 5/19/06, Alex Young <alex@blackkettle.org> wrote:

--
Alex

Austin may be right that there are too many problems with doing this
effectively as a standard. But I'm not yet sure of that. All I have
seen for sure is the problem of core extensions, which like I said can
be resolved using a prefix '::'. Of course one should be careful not to
reference things in the absolute but rather in the relative. For
instance, never do:

  class X
    def X.foo

but always

  class X
    def self.foo

Following some simple guidelines should make importing feasible. NAd of
course, it's okay if something is not importable --one can decument
that fact too.

But perhaps Austin you have a better solution?

T.

Alternative, certainly. I'm not so sure about better or more elegant.

  It is a necessary evolution, i think. To control the entropy :slight_smile:

  > Having a standard namespace and having a system that applies it are two
  > separate concerns.

  A good and adpreciated standard would help users to:
  
    - Find projects they need.
      (like "ext/devel/deploy/gem" or "ext/net/http/rails")
    - Understand the nature of the project.
    - Avoid collisions.
    - Keep order.

  Long paths can be avoided with and intelligent optimization
  on require:

  If the find returns just one matching file in the repository
  like:

  require "rails" it becomes -> require "ext/net/http/rails".

  Then the file is sourced.

  So if there is a Conflict:

  require "http/rails" would be fine.

···

--
Upper reality >oftware.
Dave - Skp Core (skp-it.eu).

--
Email.it, the professional e-mail, gratis per te: http://www.email.it/f

Sponsor:
Refill s.r.l. - Prodotti per TUTTE le stampanti sul mercato a prezzi sempre convenienti. Dal 1993, leader nel compatibile di qualità in Italia.
Clicca qui: http://adv.email.it/cgi-bin/foclick.cgi?midQ88&d -5

> require 'foo', Bar

  What about:
  
    require_in Bar, "foo" ?

···

--
Upper reality >oftware.
Dave - Skp Core (skp-it.eu).

--
Email.it, the professional e-mail, gratis per te: http://www.email.it/f

Sponsor:
Cerchi l’amore? Allora iscriviti gratis a Meetic: troverai migliaia di single in linea e potrai chattare con loro.
Clicca qui: http://adv.email.it/cgi-bin/foclick.cgi?mid@49&d -5

transfire@gmail.com wrote:

Austin may be right that there are too many problems with doing this
effectively as a standard. But I'm not yet sure of that. All I have
seen for sure is the problem of core extensions, which like I said can
be resolved using a prefix '::'.

That does feel clumsy. It's a simple fix, I know, but it's still clumsy.

Of course one should be careful not to
reference things in the absolute but rather in the relative. For
instance, never do:

  class X
    def X.foo

but always

  class X
    def self.foo

Why?

irb(main):001:0> class X; end
=> nil
irb(main):002:0> module Y; class X; def X.foo; end; end; end
=> nil
irb(main):003:0> Y::X.methods - X.methods
=> ["foo"]

···

--
Alex

Yes. Complain to developers who step on each others classes/namespaces.

This is a solution looking for a real problem.

-austin

···

On 5/20/06, transfire@gmail.com <transfire@gmail.com> wrote:

But perhaps Austin you have a better solution?

--
Austin Ziegler * halostatue@gmail.com
               * Alternate: austin@halostatue.ca

dave wrote:

  > Alternative, certainly. I'm not so sure about better or more elegant.

  It is a necessary evolution, i think. To control the entropy :slight_smile:

There's more than one way to skin *that* cat :slight_smile:

  > Having a standard namespace and having a system that applies it are two > separate concerns.

  A good and adpreciated standard would help users to:
      - Find projects they need. (like "ext/devel/deploy/gem" or "ext/net/http/rails")
    - Understand the nature of the project.
    - Avoid collisions.
    - Keep order.

Yes. However, we don't have that, nor is there any project to impose such a structure on the community, which would need to take into account the large amounts of existing code.

  Long paths can be avoided with and intelligent optimization
  on require:

  If the find returns just one matching file in the repository
  like:

  require "rails" it becomes -> require "ext/net/http/rails".

  Then the file is sourced.

  So if there is a Conflict:

  require "http/rails" would be fine.

That doesn't actually solve the original problem, although it does reduce it: what happens when http/rails and http/roads both define a top-level Wheel class?

···

--
Alex

Austin wrote:

Yes. Complain to developers who step on each others classes/namespaces.

This is a solution looking for a real problem.

The problem has been expirenced many times. I think your just looking
at the one piece of it. The whole issue includes the practice of core
extensions and that's come up a good a lot. If extensions never occured
then it would be easy enough to ecapsulate libraries at load time w/o
issue. But with extensions, not even being cooperative with regards to
class/namspaces can't completely avoid the problems. Hopefully true
namespaces will arrive soon and provide the proper solution.

T.

True namespaces exist, Trans. Selector namespaces are something
different entirely.
I'm not convinced that selector namespaces are useful, but Matz is.
I'm absolutely convinced that *this* isn't useful.

-austin

···

On 5/24/06, transfire@gmail.com <transfire@gmail.com> wrote:

Austin wrote:
> Yes. Complain to developers who step on each others classes/namespaces.
>
> This is a solution looking for a real problem.
The problem has been expirenced many times. I think your just looking
at the one piece of it. The whole issue includes the practice of core
extensions and that's come up a good a lot. If extensions never occured
then it would be easy enough to ecapsulate libraries at load time w/o
issue. But with extensions, not even being cooperative with regards to
class/namspaces can't completely avoid the problems. Hopefully true
namespaces will arrive soon and provide the proper solution.

--
Austin Ziegler * halostatue@gmail.com
               * Alternate: austin@halostatue.ca