Kernel.load() behaviour

I want to wrap a pile of classes, which are defined in their own files, inside
a wrapper module. Basically, I want this to work:

glibc.rb:
class Glibc
end

test.rb:
module Packages
  load 'glibc.rb'
end

Packages::Glibc.new

But it doesn't do what I expected. Glibc gets added to the global namespace,
rather than the Packages namespace.

I.e. Glibc.new works; Packages::Glibc.new doesn't

Now, I see Kernel.load() is documented as

Kernel.load(filename,wrap=false)

If wrap==true, then "the loaded script will be executed under an anonymous
module, protecting the calling program's global namespace"

I guess I need the equivalent of
Kernel.load(filename,wrapper_module=nil)
So I can do
load('glibc.rb',Packages)

Or is there another way?

Suggestions appreciated

Andrew Walrond

PS Explicitly putting Packages:: on each class definition is possible, but
there are 000s of packages so I was looking for a better way....

"Andrew Walrond" <andrew@walrond.org> schrieb im Newsbeitrag
news:200504011701.25701.andrew@walrond.org...

I want to wrap a pile of classes, which are defined in their own files,

inside

a wrapper module. Basically, I want this to work:

glibc.rb:
class Glibc
end

test.rb:
module Packages
  load 'glibc.rb'
end

Packages::Glibc.new

But it doesn't do what I expected. Glibc gets added to the global

namespace,

rather than the Packages namespace.

I.e. Glibc.new works; Packages::Glibc.new doesn't

Now, I see Kernel.load() is documented as

Kernel.load(filename,wrap=false)

If wrap==true, then "the loaded script will be executed under an

anonymous

module, protecting the calling program's global namespace"

I guess I need the equivalent of
Kernel.load(filename,wrapper_module=nil)
So I can do
load('glibc.rb',Packages)

Or is there another way?

This has come up recently. Unfortunately there's no chance of getting at
the anonymous module other than from within the loaded file. I'd wish it
was changed like this:

Kernel.load(file, mode=nil)

Where "mode" is interpreted like this:

- if it's nil or false, no wrapping takes place, return value is the
parameter

- if it's a module, that is used for wrapping, return value is the module

- if it's anything else (i.e. equivalent to true) an anonymous module is
created for wrapping, return value is the anonymous module

You might find more on this in the archives.

Suggestions appreciated

Andrew Walrond

PS Explicitly putting Packages:: on each class definition is possible,

but

there are 000s of packages so I was looking for a better way....

You don't need that: simply wrap the file you want to load with

module Packages
  # all 000s class definitions here
end

As easy as that. :slight_smile:

Kind regards

    robert

This has come up recently. Unfortunately there's no chance of getting at
the anonymous module other than from within the loaded file. I'd wish it
was changed like this:

Kernel.load(file, mode=nil)

Where "mode" is interpreted like this:

- if it's nil or false, no wrapping takes place, return value is the
parameter

- if it's a module, that is used for wrapping, return value is the module

- if it's anything else (i.e. equivalent to true) an anonymous module is
created for wrapping, return value is the anonymous module

You might find more on this in the archives.

Thanks - I'll do a search.

You don't need that: simply wrap the file you want to load with

module Packages
  # all 000s class definitions here
end

As easy as that. :slight_smile:

Not quite; Each of the 000s of packages is in it's own file.... :wink:

Andrew

···

On Friday 01 April 2005 17:24, Robert Klemme wrote:

Not quite; Each of the 000s of packages is in it's own file.... :wink:

eval "module Packages\n" << `cat *.rb` << "\nend"

:wink:

"Andrew Walrond" <andrew@walrond.org> schrieb im Newsbeitrag news:200504011904.16688.andrew@walrond.org...

···

On Friday 01 April 2005 17:24, Robert Klemme wrote:

This has come up recently. Unfortunately there's no chance of getting at
the anonymous module other than from within the loaded file. I'd wish it
was changed like this:

Kernel.load(file, mode=nil)

Where "mode" is interpreted like this:

- if it's nil or false, no wrapping takes place, return value is the
parameter

- if it's a module, that is used for wrapping, return value is the module

- if it's anything else (i.e. equivalent to true) an anonymous module is
created for wrapping, return value is the anonymous module

You might find more on this in the archives.

Thanks - I'll do a search.

You don't need that: simply wrap the file you want to load with

module Packages
  # all 000s class definitions here
end

As easy as that. :slight_smile:

Not quite; Each of the 000s of packages is in it's own file.... :wink:

Then I probably misunderstood you. Care to reveal some more details of the design and its rationale?

Kind regards

    robert

You are a _very_ twisted individual :wink:

···

On Friday 01 April 2005 19:48, Aredridel wrote:

> Not quite; Each of the 000s of packages is in it's own file.... :wink:

eval "module Packages\n" << `cat *.rb` << "\nend"

:wink:

module Packages
end

Packages.module_eval( `cat glibc.rb`, 'glibc.rb`) # A little more elegant.

Brian.

···

On Apr 1, 2005 11:48 AM, Aredridel <aredridel@gmail.com> wrote:

>
> Not quite; Each of the 000s of packages is in it's own file.... :wink:

eval "module Packages\n" << `cat *.rb` << "\nend"

:wink:

Essentially, I have lots of package files in a directory, each with a single
class derived from Package; eg class Glibc < Package

Now rather than polluting the global namespace (one of the packages is called
'File' which is an obvious clash) I just wanted to load them all into the
namespace of a module, without having to add <module Packages> ... <end>
around the class(es) in every one of the thousands of package files.

Very static and very boring :wink:

Andrew

···

On Friday 01 April 2005 22:24, Robert Klemme wrote:

Then I probably misunderstood you. Care to reveal some more details of the
design and its rationale?

Almost perfect, but this avoids the external 'cat' dependency:

Packages.module_eval( IO.readlines('glibc.rb').join, 'glibc.rb')

I learned some more ruby today. Thanks all!

Andrew

···

On Friday 01 April 2005 22:37, Brian Mitchell wrote:

On Apr 1, 2005 11:48 AM, Aredridel <aredridel@gmail.com> wrote:
> > Not quite; Each of the 000s of packages is in it's own file.... :wink:
>
> eval "module Packages\n" << `cat *.rb` << "\nend"
>
> :wink:

module Packages
end

Packages.module_eval( `cat glibc.rb`, 'glibc.rb`) # A little more elegant.

One more idea that lets one use load and allows anonymous modules:

glibc.rb:
class Glibc
end

some_file.rb:
module Packages
end

class Package
  def self.inherited(sub)
    Packages.const_set(sub.name.split("::").last, sub)
  end
end

load 'glibc.rb', true

# Packages.constants == ["Glibc"]

I also have an idea on how to do this with load my appending the
anonymous module BUT I couldn't get Module.nesting to work and it
seems ruby take some shortcuts in creation of the anon Module. If I
can fix this then we'll all be able to load into specific modules
which would be nice.

Brian.

···

On Apr 1, 2005 2:44 PM, Andrew Walrond <andrew@walrond.org> wrote:

On Friday 01 April 2005 22:37, Brian Mitchell wrote:
> On Apr 1, 2005 11:48 AM, Aredridel <aredridel@gmail.com> wrote:
> > > Not quite; Each of the 000s of packages is in it's own file.... :wink:
> >
> > eval "module Packages\n" << `cat *.rb` << "\nend"
> >
> > :wink:
>
> module Packages
> end
>
> Packages.module_eval( `cat glibc.rb`, 'glibc.rb`) # A little more elegant.
>

Almost perfect, but this avoids the external 'cat' dependency:

Packages.module_eval( IO.readlines('glibc.rb').join, 'glibc.rb')

I learned some more ruby today. Thanks all!

* Andrew Walrond <andrew@walrond.org> [2005-04-02 06:44:53 +0900]:

Almost perfect, but this avoids the external 'cat' dependency:

Packages.module_eval( IO.readlines('glibc.rb').join, 'glibc.rb')

Packages.module_eval( File.read('glibc.rb'), 'glibc.rb')

···

--
Jim Freeze
Code Red. Code Ruby

Con fecha 1/4/2005, "Brian Mitchell" <binary42@gmail.com> escribió:

> > > Not quite; Each of the 000s of packages is in it's own file.... :wink:
> >
> > eval "module Packages\n" << `cat *.rb` << "\nend"
> >
> > :wink:
>
> module Packages
> end
>
> Packages.module_eval( `cat glibc.rb`, 'glibc.rb`) # A little more elegant.
>

Almost perfect, but this avoids the external 'cat' dependency:

Packages.module_eval( IO.readlines('glibc.rb').join, 'glibc.rb')

I learned some more ruby today. Thanks all!

One more idea that lets one use load and allows anonymous modules:

glibc.rb:
class Glibc
end

some_file.rb:
module Packages
end

class Package
def self.inherited(sub)
   Packages.const_set(sub.name.split("::").last, sub)
end
end

load 'glibc.rb', true

# Packages.constants == ["Glibc"]

This will still pollute the namespace, though, at
the initial loading. That seemed to be Andrew's
concern.

I also have an idea on how to do this with load my appending the
anonymous module BUT I couldn't get Module.nesting to work and it
seems ruby take some shortcuts in creation of the anon Module. If I
can fix this then we'll all be able to load into specific modules
which would be nice.

I submitted an RCR about this type of load behaviour (specifically
to enable the client programmer define a namespace for an imported
module rather than having the library author do it).

While the solutions here are likely suitable for this particular
situation, the more general problem is trying to load a file like
this:

class String
  def my_string_extension()
    # ...
  end
end

module MyModule
  class MyClass
    # ...
  end
end

The presumed extension of the standard String class will instead
create a new String class in the enveloping module.

Brian.

E

···

On Apr 1, 2005 2:44 PM, Andrew Walrond <andrew@walrond.org> wrote:

On Friday 01 April 2005 22:37, Brian Mitchell wrote:
> On Apr 1, 2005 11:48 AM, Aredridel <aredridel@gmail.com> wrote:

Not with
load 'glibc.rb',true
surely?

On a related note, if glibc.rb does require(filename) or load(filename,false),
this second level load breaks through and will pollute the top level global
namespace. I suppose this is the obvious, though unwanted behavior.

Andrew

···

On Friday 01 April 2005 23:49, Saynatkari wrote:

Con fecha 1/4/2005, "Brian Mitchell" <binary42@gmail.com> escribió:
>
>One more idea that lets one use load and allows anonymous modules:
>
>glibc.rb:
>class Glibc
>end
>
>some_file.rb:
>module Packages
>end
>
>class Package
> def self.inherited(sub)
> Packages.const_set(sub.name.split("::").last, sub)
> end
>end
>
>load 'glibc.rb', true
>
># Packages.constants == ["Glibc"]

This will still pollute the namespace, though, at
the initial loading. That seemed to be Andrew's
concern.

Con fecha 1/4/2005, "Saynatkari" <ruby-ml@magical-cat.org> escribió:

Con fecha 1/4/2005, "Brian Mitchell" <binary42@gmail.com> escribió:

> > > Not quite; Each of the 000s of packages is in it's own file.... :wink:
> >
> > eval "module Packages\n" << `cat *.rb` << "\nend"
> >
> > :wink:
>
> module Packages
> end
>
> Packages.module_eval( `cat glibc.rb`, 'glibc.rb`) # A little more elegant.
>

Almost perfect, but this avoids the external 'cat' dependency:

Packages.module_eval( IO.readlines('glibc.rb').join, 'glibc.rb')

I learned some more ruby today. Thanks all!

One more idea that lets one use load and allows anonymous modules:

glibc.rb:
class Glibc
end

some_file.rb:
module Packages
end

class Package
def self.inherited(sub)
   Packages.const_set(sub.name.split("::").last, sub)
end
end

load 'glibc.rb', true

# Packages.constants == ["Glibc"]

This will still pollute the namespace, though, at
the initial loading. That seemed to be Andrew's
concern.

Meh. Obviously ignore that.

I also have an idea on how to do this with load my appending the
anonymous module BUT I couldn't get Module.nesting to work and it
seems ruby take some shortcuts in creation of the anon Module. If I
can fix this then we'll all be able to load into specific modules
which would be nice.

I submitted an RCR about this type of load behaviour (specifically
to enable the client programmer define a namespace for an imported
module rather than having the library author do it).

While the solutions here are likely suitable for this particular
situation, the more general problem is trying to load a file like
this:

class String
def my_string_extension()
   # ...
end
end

module MyModule
class MyClass
   # ...
end
end

The presumed extension of the standard String class will instead
create a new String class in the enveloping module.

Brian.

E

···

On Apr 1, 2005 2:44 PM, Andrew Walrond <andrew@walrond.org> wrote:

On Friday 01 April 2005 22:37, Brian Mitchell wrote:
> On Apr 1, 2005 11:48 AM, Aredridel <aredridel@gmail.com> wrote:

My final solution, taking the above into account:

c.rb
class Glibc
end

b.rb
inline 'c.rb'

class Gcc
end

a.rb
module Packages
   def Packages.inline(filename)
     eval(IO.readlines(filename).join,nil,filename)
   end
   inline('c.rb')
end

Packages::Gcc.new
Packages::Glibc.new

Nice and neat :slight_smile:

···

On Friday 01 April 2005 23:57, Andrew Walrond wrote:

On a related note, if glibc.rb does require(filename) or
load(filename,false), this second level load breaks through and will
pollute the top level global namespace. I suppose this is the obvious,
though unwanted behavior.

My final puzzle before sleep; How to throw an exception if any of the package
files try to use require() or load() instead of inline() ? (My package
developers might forget and pollute the global namespace)

Is there a neat way?

Andrew

···

On Saturday 02 April 2005 00:34, Andrew Walrond wrote:

My final solution, taking the above into account:

c.rb
class Glibc
end

b.rb
inline 'c.rb'

class Gcc
end

a.rb
module Packages
   def Packages.inline(filename)
     eval(IO.readlines(filename).join,nil,filename)
   end
   inline('c.rb')
end

Packages::Gcc.new
Packages::Glibc.new

This should give you the general idea if you want to prevent it.

module Kernel
  alias _load load
  def load(file, wrap)
    raise "..." if check_if_f_is_a_package_file
    _load(file, wrap)
  end
  
  alias _require require
  def require(lib)
    raise '...' if check_if_lib_plus_rb_is_a_package_file
    _require(lib)
  end
end

not sure if this covers everything you want. You may want to disable
require and load completely while you are in your eval:

# Not yet thread safe but can be made so.

module Packages
  def Packages.disable_loading(&block)
    Kernel.module_eval do
      alias _load load
      def load(*); raise '??' end
      alias _require require
      def require(*); raise '??' end
    end
    ret = yield
    Kernel.module_eval do
      alias load _load
      alias require _require
    end
    ret
  end

  def Packages.inline(filename)
    disable_loading {
      eval(IO.readlines(filename).join, nil, filename)
    }
  end

  inline('c.rb')
end

Brian.

···

On Apr 1, 2005 4:41 PM, Andrew Walrond <andrew@walrond.org> wrote:

On Saturday 02 April 2005 00:34, Andrew Walrond wrote:
> My final solution, taking the above into account:
>
> c.rb
> class Glibc
> end
>
> b.rb
> inline 'c.rb'
>
> class Gcc
> end
>
> a.rb
> module Packages
> def Packages.inline(filename)
> eval(IO.readlines(filename).join,nil,filename)
> end
> inline('c.rb')
> end
>
> Packages::Gcc.new
> Packages::Glibc.new
>

My final puzzle before sleep; How to throw an exception if any of the package
files try to use require() or load() instead of inline() ? (My package
developers might forget and pollute the global namespace)

Is there a neat way?

Andrew

"Andrew Walrond" <andrew@walrond.org> schrieb im Newsbeitrag news:200504020041.35224.andrew@walrond.org...

My final solution, taking the above into account:

c.rb
class Glibc
end

b.rb
inline 'c.rb'

class Gcc
end

a.rb
module Packages
   def Packages.inline(filename)
     eval(IO.readlines(filename).join,nil,filename)

You can shorten that to
eval( File.read(filename), nil, filename )

   end
   inline('c.rb')
end

Packages::Gcc.new
Packages::Glibc.new

My final puzzle before sleep; How to throw an exception if any of the package
files try to use require() or load() instead of inline() ? (My package
developers might forget and pollute the global namespace)

Is there a neat way?

I posted an approach for temporarily exchanging methods a while ago which can be used for this under subject "Mocking new and other class methods":

module Kernel
private
  def mock(obj, sym, mock)
    cl = class <<obj; self; end
    old = obj.method sym
    cl.class_eval { define_method(sym, &mock) }

    begin
      yield
    ensure
      cl.class_eval { define_method(sym, &old) }
    end
  end
end

Something along these lines:

mock Kernel, :require, lambda {|f1| inline f} do
  mock Kernel, :load, lambda {|f2,*| inline f2} do
    require ...
  end
end

Kind regards

    robert

···

On Saturday 02 April 2005 00:34, Andrew Walrond wrote:

Andrew Walrond wrote:

My final solution, taking the above into account:

c.rb
class Glibc
end

b.rb
inline 'c.rb'

class Gcc
end

a.rb
module Packages
  def Packages.inline(filename)
    eval(IO.readlines(filename).join,nil,filename)
  end
  inline('c.rb')
end

Packages::Gcc.new
Packages::Glibc.new

My final puzzle before sleep; How to throw an exception if any of the package files try to use require() or load() instead of inline() ? (My package developers might forget and pollute the global namespace)

Is there a neat way?

If you can arrange that the context in which those nested require/load calls occur is an object of your own choosing (rather than ruby's top-level object), then you can make require and load do whatever you want.

Actually, this could be a way to unify your #inline with #load/#require in such a way that it does the right thing, depending on where the referenced file is located. That way, package developers are insulated from the issue, and can still require standard libraries if they need to. I can explain better with some code ...

In my "script" library (findable on RAA), there is a subclass of Module called Script (probably too bland a name, but oh well). Script has a module method Script.load that creates a new Script instance:

   require 'script'
   script = Script.load("scripts/simple-script.rb")

The script object returned from Script.load can be used to access all the constants and top-level methods defined in the file. (A top level method is one defined like "def foo ... end" without an explicit class or module context.)

For example, if scripts/simple-script.rb has:

   VALUE = [1,2,3]
   def foo; "FOO"; end

Then the main program (with the Script.load line) can call

   script.foo
   script::VALUE

This is a convenient way to construct objects in a script and return them to the program that loaded the script.

The underlying approach is to use module_eval, as others have suggested on this thread, to load the definitions in the given file into a module and return them, encapsulated into an instance of Script.

Here's the twist: instances of Script have their own #load and #require implementation that augments Kernel's, and this affects nested loading. Script#load behaves like Script.load: definitions go into the Script instance (a module), rather than into the global namespace. Script#require first tries the local dir (where the original script file--in this case "scripts/simple-script.rb"--is located). Then it falls back to Kernel#require. It maintains its own equivalent of $LOADED_FEATURES so that you have the usual require semantics on the local dir. So, as long as you doing it from the the top level, requiring a local file puts the definitions into the script instance's scope, but requiring a normal library file puts the definitions into the global scope, as usual.

For example, if scripts/simple-script.rb might have this code:

   require 'rbtree' # not found in scripts/, so look in usual
                          # places.
   require 'other-script' # if found locally, load it in to the
                          # current Script context

That's really about all Script does, though there are some bells and whistles: a way of passing in arguments to the loaded script, correct reporting of file name and line number in exceptions generated by the loaded files, a #to_s method, #autoscript (like #autoload).

If the script lib is not exactly what you want, you might still want to take a look at the source (it's short) to see if it can be adapted. You could change the require definition to raise an exception (note that this only affects the top level, not require called dynamically from within methods defined inside classes or modules other than the script itself).

HTH.

···

On Saturday 02 April 2005 00:34, Andrew Walrond wrote:

This is useful. Thanks!

···

On Sunday 03 April 2005 21:52, Joel VanderWerf wrote:

If the script lib is not exactly what you want, you might still want to
take a look at the source (it's short) to see if it can be adapted. You
could change the require definition to raise an exception (note that
this only affects the top level, not require called dynamically from
within methods defined inside classes or modules other than the script
itself).