Loading a module without polluting my namespace

Hey folks!

In my current project I try to load a module dynamically. Which is
basically no problem due to 'require` accepting filenames as well. My
problem is that I'd totally pollute my namespace. I thought of something
like the following:

def load_module(filename)
  module NamespaceGuard # Just a random name
    require filename
    # Take a care of the loaded module
  end
  # My namespace is clean again
end

But Ruby's syntax apparently forbids module definitions in methods. I'd
be very glad if anyone could help me with this problem.

···

--
Posted via http://www.ruby-forum.com/.

Usually the file you are loading has the "protective" namespace. Eg.

  # namespace_guard.rb
  module NamespaceGuard
    ...
  end

  # main.rb
  require 'namespace_guard'

···

On Jun 13, 2:33 pm, Hagbard Celine <sin3141...@gmail.com> wrote:

Hey folks!

In my current project I try to load a module dynamically. Which is
basically no problem due to 'require` accepting filenames as well. My
problem is that I'd totally pollute my namespace. I thought of something
like the following:

def load_module(filename)
module NamespaceGuard # Just a random name
require filename
# Take a care of the loaded module
end
# My namespace is clean again
end

But Ruby's syntax apparently forbids module definitions in methods. I'd
be very glad if anyone could help me with this problem.

Hagbard Celine wrote:

Hey folks!

In my current project I try to load a module dynamically. Which is
basically no problem due to 'require` accepting filenames as well. My
problem is that I'd totally pollute my namespace. I thought of something
like the following:

def load_module(filename)
  module NamespaceGuard # Just a random name
    require filename
    # Take a care of the loaded module
  end
  # My namespace is clean again
end

But Ruby's syntax apparently forbids module definitions in methods. I'd
be very glad if anyone could help me with this problem.

You certainly can define modules dynamically:

   def make_mod
     Module.new do
       def self.foo; p "FOO"; end
       def bar; p "BAR"; end
     end
   end

   m = make_mod

   p m.methods(false) # ==> ["foo"]
   p m.instance_methods(false) # ==> ["bar"]

   m.foo # ==> "FOO"
   x=
   x.extend m
   x.bar # ==> "BAR"

Also, the #load method takes an optional argument that causes it to wrap the loaded definitions in

$ ri Kernel#load | cat
------------------------------------------------------------ Kernel#load
      load(filename, wrap=false) => true

···

------------------------------------------------------------------------
      Loads and executes the Ruby program in the file filename. If the
      filename does not resolve to an absolute path, the file is
      searched for in the library directories listed in $:. If the
      optional wrap parameter is true, the loaded script will be
      executed under an anonymous module, protecting the calling
      program's global namespace. In no circumstance will any local
      variables in the loaded file be propagated to the loading
      environment.

You can use this like so:

$ cat b.rb
def foo
   puts "foo in b"
end

$ cat a.rb
load "b.rb", true # try this without the true

begin
   foo
rescue => e
   puts e
end

def foo
   puts "foo in a"
end

foo

$ ruby a.rb
undefined local variable or method `foo' for main:Object
foo in a

However, you don't get easy access to the anonymous module. If you want that, I have a little library that may be helpful:

http://redshift.sourceforge.net/script/

Hagbard Celine wrote:

In my current project I try to load a module dynamically. Which is
basically no problem due to 'require` accepting filenames as well. My
problem is that I'd totally pollute my namespace.

Kernel.load(filename, true) might help. But AFAIK that doesn't prevent
the source code from doing

class ::Object
  def override_something_important
    ..
  end
end

If you need to protect against untrusted code, have a look at _why's
sandbox.

···

--
Posted via http://www.ruby-forum.com/\.

Kernel#require does not namespace anything, no matter how you use it.

Properly written Rubby libraries namespace their classes and modules. If you own the code you're requiring, fix it. If not, find an alternative to the code in question (which I find suspect based on this lack of namespacing) or perhaps you may find some luck with Kernel#load.

···

--
Rein Henrichs
http://puppetlabs.com
http://reinh.com

Also, properly written, it's "Ruby". Oops.

···

On 2010-06-13 18:35:25 -0700, Rein Henrichs said:

Kernel#require does not namespace anything, no matter how you use it.

Properly written Rubby libraries namespace their classes and modules. If you own the code you're requiring, fix it. If not, find an alternative to the code in question (which I find suspect based on this lack of namespacing) or perhaps you may find some luck with Kernel#load.

--
Rein Henrichs

http://reinh.com

Thanks for your reply but I'm afraid that doesn't solve my problem. I
stumbled upon the optional argument of `load' as well but it prevents me
from accessing the loaded module. I'm sorry if I wasn't clear enough. I
would need something like this:

# foo.rb
module Foo
  def method_a
  end

  def method_b
  end
end

# bar.rb

def load_module
  load "foo.rb"
  puts Foo.methods
end
# `Foo' isn't known anymore

If I use `load "foo.rb", true' I can't access the loaded module or am I
mistaken?

Security is a minor problem for the moment as it's just a hobby project.
But I take a look at _why's sandbox anyway. Thanks for the hint.

···

--
Posted via http://www.ruby-forum.com/.

Rein Henrichs wrote:

Properly written Rubby libraries namespace their classes and modules.
If you own the code you're requiring, fix it. If not, find an
alternative to the code in question (which I find suspect based on this
lack of namespacing) or perhaps you may find some luck with Kernel#load.

I happen to be in control of the loaded modules but I think you didn't
understand my problem correctly. If I use namespace guards (what I
actually do at the moment) the loaded modules remain in existence and
pollute my namespace. So, namespace guards don't really help me.

@Joel:
The `Script' library seems to fit my needs quite perfectly. Thank you
for the hint.

···

--
Posted via http://www.ruby-forum.com/\.

Joel VanderWerf wrote:

However, you don't get easy access to the anonymous module. If you want
that, I have a little library that may be helpful:

Script

This is great, but seems to blows up when requiring other files

I noticed that there is a redefinition of require in script.rb. Is that
method meant to be handling that?

http://redshift.sourceforge.net/script/doc/classes/Script.html#M000003

Best regards

dreamcat4
dreamcat4@gmail.com

···

--
Posted via http://www.ruby-forum.com/\.

Hagbard Celine wrote:

Thanks for your reply but I'm afraid that doesn't solve my problem. I
stumbled upon the optional argument of `load' as well but it prevents me
from accessing the loaded module.

I wish 'load' would simply return the anonymous module it has created,
but there are nasty workarounds. For example:

$ cat foo.rb
module Foo
  def bar
    puts "hello"
  end
  module_function :bar
end
$res = Foo

$ irb --simple-prompt

load "foo.rb", true

=> true

$res

=> #<Module:0xb741b218>::Foo

$res.bar

hello
=> nil

···

--
Posted via http://www.ruby-forum.com/\.

Hagbard Celine wrote:

Thanks for your reply but I'm afraid that doesn't solve my problem. I stumbled upon the optional argument of `load' as well but it prevents me from accessing the loaded module. I'm sorry if I wasn't clear enough. I would need something like this:

...

If I use `load "foo.rb", true' I can't access the loaded module or am I mistaken?

That's correct (you can't access it without playing tricks like global vars or searching ObjectSpace).

If security is not your concern, take a look at the script lib I mentioned before:

http://redshift.sourceforge.net/script

Unlike load(..., true) , Script.load returns the wrapper module.

Hagbard Celine wrote:

Thanks for your reply but I'm afraid that doesn't solve my problem. I stumbled upon the optional argument of `load' as well but it prevents me from accessing the loaded module. I'm sorry if I wasn't clear enough. I would need something like this:

# foo.rb
module Foo
  def method_a
  end

  def method_b
  end
end

# bar.rb

def load_module
  load "foo.rb"
  puts Foo.methods
end
# `Foo' isn't known anymore

Your example is not quite right.

$ cat foo.rb
module Foo
   def method_a
   end

   def method_b
   end
end

$ cat bar.rb
def load_module
   load "foo.rb"
   puts Foo.instance_methods
end

load_module

$ ruby bar.rb
method_a
method_b

Dreamcat Four wrote:

Joel VanderWerf wrote:

However, you don't get easy access to the anonymous module. If you want
that, I have a little library that may be helpful:

Script

This is great, but seems to blows up when requiring other files

plist4r/lib/plist4r/backend/c_f_property_list.rb at master · dreamcat4/plist4r · GitHub

script_trace.rb · GitHub

Is it possible to simplify the example a bit? I can't tell where the missing const is actually defined.

I noticed that there is a redefinition of require in script.rb. Is that method meant to be handling that?

Class: Script

There's an example of using require with script in examples/program2.rb. Maybe that helps?

Sorry not to be more helpful...

Yes. The problem is in the way your script references the other files.
You've got absolute paths (which makes sense in the no-wrapper case),
but the Script class is expecting relative. The problem goes away
(AFAICT) with this patch:

--- /home/vjoel/tmp/dc2/examples/scripts/script.rb 2010-07-13
17:13:47.673562875 -0700
+++ - 2010-07-13 17:15:18.375246675 -0700
@@ -1,6 +1,6 @@
puts "in #{__FILE__}, line #{__LINE__}"

-load File.dirname(__FILE__)+"/sub-script.rb"
+load "sub-script.rb"

OUTPUT = ["input was #{INPUT}"]

@@ -9,10 +9,10 @@
   end
end

-require File.dirname(__FILE__)+'/lib/a-class'
+require 'lib/a-class'

-require File.dirname(__FILE__)+'/lib/x-accessor'
-require File.dirname(__FILE__)+'/lib/x-accessor' # only loaded once
+require 'lib/x-accessor'
+require 'lib/x-accessor' # only loaded once

# Falls back to Kernel.load, since "benchmark.rb" isn't in the current dir.
load "benchmark.rb" unless $LOADED_FEATURES.include?("benchmark.rb")

···

On Sun, Jul 11, 2010 at 3:07 AM, Dreamcat Four <dreamcat4@gmail.com> wrote:

Joel VanderWerf wrote:

However, you don't get easy access to the anonymous module. If you want
that, I have a little library that may be helpful:

Script

This is great, but seems to blows up when requiring other files

plist4r/lib/plist4r/backend/c_f_property_list.rb at master · dreamcat4/plist4r · GitHub

script_trace.rb · GitHub

I noticed that there is a redefinition of require in script.rb. Is that
method meant to be handling that?

Class: Script

Brian Candler wrote:

I wish 'load' would simply return the anonymous module it has created,
but there are nasty workarounds.

Exactly this behavior I would have loved and needed. What a bummer that
there are just workarounds which are indeed nasty. I guess I have to
live with it. Thanks for your help.

···

--
Posted via http://www.ruby-forum.com/\.

Thanks for replying,
Will try to come back soon with an example for reproducing this.

Joel VanderWerf wrote:

···

Dreamcat Four wrote:

script_trace.rb · GitHub

Is it possible to simplify the example a bit? I can't tell where the
missing const is actually defined.

I noticed that there is a redefinition of require in script.rb. Is that
method meant to be handling that?

http://redshift.sourceforge.net/script/doc/classes/Script.html#M000003

There's an example of using require with script in examples/program2.rb.
Maybe that helps?

Sorry not to be more helpful...

--
Posted via http://www.ruby-forum.com/\.

Joel VanderWerf wrote:

Dreamcat Four wrote:

script_trace.rb · GitHub

Is it possible to simplify the example a bit? I can't tell where the

Here is the example2.rb, but modified to reproduce this scenario.

When we run the same ruby code directly (example2-no-wrapper.rb), there
is no error. So the script wrapper is doing something differently than
ruby would normally.

My question:
Can find a way to get around this without touching the loaded source
code?

···

missing const is actually defined.

I noticed that there is a redefinition of require in script.rb. Is that
method meant to be handling that?

Class: Script

There's an example of using require with script in examples/program2.rb.
Maybe that helps?

Sorry not to be more helpful...

--
Posted via http://www.ruby-forum.com/\.

Here is a fix to the require() and load() methods. Its easier to patch
it there and just not to assume those methods are being given relative
paths.

Joel VanderWerf wrote:

···

On Sun, Jul 11, 2010 at 3:07 AM, Dreamcat Four <dreamcat4@gmail.com> > wrote:

script_trace.rb · GitHub

I noticed that there is a redefinition of require in script.rb. Is that
method meant to be handling that?

Class: Script

Yes. The problem is in the way your script references the other files.
You've got absolute paths (which makes sense in the no-wrapper case),
but the Script class is expecting relative. The problem goes away
(AFAICT) with this patch:

--- /home/vjoel/tmp/dc2/examples/scripts/script.rb 2010-07-13
17:13:47.673562875 -0700
+++ - 2010-07-13 17:15:18.375246675 -0700
@@ -1,6 +1,6 @@
puts "in #{__FILE__}, line #{__LINE__}"

-load File.dirname(__FILE__)+"/sub-script.rb"
+load "sub-script.rb"

OUTPUT = ["input was #{INPUT}"]

@@ -9,10 +9,10 @@
   end
end

-require File.dirname(__FILE__)+'/lib/a-class'
+require 'lib/a-class'

-require File.dirname(__FILE__)+'/lib/x-accessor'
-require File.dirname(__FILE__)+'/lib/x-accessor' # only loaded once
+require 'lib/x-accessor'
+require 'lib/x-accessor' # only loaded once

# Falls back to Kernel.load, since "benchmark.rb" isn't in the current
dir.
load "benchmark.rb" unless $LOADED_FEATURES.include?("benchmark.rb")

--
Posted via http://www.ruby-forum.com/\.

Dreamcat Four wrote:

Here is a fix to the require() and load() methods. Its easier to patch
it there and just not to assume those methods are being given relative
paths.

Fix load and require for absolute paths · dreamcat4/script@e7d585b · GitHub

--- a/lib/script.rb
+++ b/lib/script.rb
@@ -52,7 +52,11 @@ class Script < Module
   # from those sub files.

   def load(file, wrap = false)
- load_in_module(File.join(@__dir, file))
+ if file =~ /^\//
+ load_in_module(file)
+ else
+ load_in_module(File.join(@__dir, file))
+ end
     true
   rescue MissingFile
     super
@@ -70,7 +74,11 @@ class Script < Module
   def require(feature)
     unless @__loaded_features[feature]
       @__loaded_features[feature] = true
- file = File.join(@__dir, feature)
+ if feature =~ /^\//
+ file = feature
+ else
+ file = File.join(@__dir, feature)
+ end
       file += ".rb" unless /\.rb$/ =~ file
       load_in_module(file)
     end

···

--
1.6.6.1

Joel VanderWerf wrote:

On Sun, Jul 11, 2010 at 3:07 AM, Dreamcat Four <dreamcat4@gmail.com> >> wrote:

script_trace.rb · GitHub

I noticed that there is a redefinition of require in script.rb. Is that
method meant to be handling that?

Class: Script

Yes. The problem is in the way your script references the other files.
You've got absolute paths (which makes sense in the no-wrapper case),
but the Script class is expecting relative. The problem goes away
(AFAICT) with this patch:

--- /home/vjoel/tmp/dc2/examples/scripts/script.rb 2010-07-13
17:13:47.673562875 -0700
+++ - 2010-07-13 17:15:18.375246675 -0700
@@ -1,6 +1,6 @@
puts "in #{__FILE__}, line #{__LINE__}"

-load File.dirname(__FILE__)+"/sub-script.rb"
+load "sub-script.rb"

OUTPUT = ["input was #{INPUT}"]

@@ -9,10 +9,10 @@
   end
end

-require File.dirname(__FILE__)+'/lib/a-class'
+require 'lib/a-class'

-require File.dirname(__FILE__)+'/lib/x-accessor'
-require File.dirname(__FILE__)+'/lib/x-accessor' # only loaded once
+require 'lib/x-accessor'
+require 'lib/x-accessor' # only loaded once

# Falls back to Kernel.load, since "benchmark.rb" isn't in the current
dir.
load "benchmark.rb" unless $LOADED_FEATURES.include?("benchmark.rb")

--
Posted via http://www.ruby-forum.com/\.

Dreamcat Four wrote:

Here is a fix to the require() and load() methods. Its easier to patch it there and just not to assume those methods are being given relative paths.

Sure, that makes sense. I'll merge that in the next release. Thanks!