Forum: Ruby Ruby namespaces and require

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Alex Y. (Guest)
on 2006-05-19 11:26
(Received via mailing list)
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 :-) ) to fix exactly the sort of collision problems
that are already occurring.
dave (Guest)
on 2006-05-19 11:47
(Received via mailing list)
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
Alex Y. (Guest)
on 2006-05-19 12:04
(Received via mailing list)
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.
Gregory S. (Guest)
on 2006-05-19 19:03
(Received via mailing list)
On Fri, May 19, 2006 at 04:23:45PM +0900, Alex Y. 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
unknown (Guest)
on 2006-05-19 19:03
(Received via mailing list)
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.
Alex Y. (Guest)
on 2006-05-19 20:00
(Received via mailing list)
removed_email_address@domain.invalid wrote:
> 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 Y. (Guest)
on 2006-05-19 20:10
(Received via mailing list)
Gregory S. wrote:
> On Fri, May 19, 2006 at 04:23:45PM +0900, Alex Y. 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 :-)

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

> ...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.
Austin Z. (Guest)
on 2006-05-20 07:34
(Received via mailing list)
On 5/19/06, Alex Y. <removed_email_address@domain.invalid> 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'

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
Alex Y. (Guest)
on 2006-05-20 12:46
(Received via mailing list)
Austin Z. wrote:
> On 5/19/06, Alex Y. <removed_email_address@domain.invalid> 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 :-)  Bear in mind, though, that this offers a way to fix a
current problem without breaking anything.
dave (Guest)
on 2006-05-20 13:32
(Received via mailing list)
| Alternative, certainly.  I'm not so sure about better or more elegant.

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


  | 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
dave (Guest)
on 2006-05-20 13:35
(Received via mailing list)
  | 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
unknown (Guest)
on 2006-05-20 19:57
(Received via mailing list)
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.
Alex Y. (Guest)
on 2006-05-21 16:43
(Received via mailing list)
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 :)
There's more than one way to skin *that* cat :-)

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

>   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 Y. (Guest)
on 2006-05-21 16:55
(Received via mailing list)
removed_email_address@domain.invalid 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.

>     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"]
Austin Z. (Guest)
on 2006-05-24 20:50
(Received via mailing list)
On 5/20/06, removed_email_address@domain.invalid 
<removed_email_address@domain.invalid> wrote:
> But perhaps Austin you have a better solution?

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

This is a solution looking for a real problem.

-austin
unknown (Guest)
on 2006-05-24 21:28
(Received via mailing list)
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.
Austin Z. (Guest)
on 2006-05-24 21:32
(Received via mailing list)
On 5/24/06, removed_email_address@domain.invalid 
<removed_email_address@domain.invalid> wrote:
> namespaces will arrive soon and provide the proper solution.
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
This topic is locked and can not be replied to.