Setting namespace in loaded files

Hi all,

I’m dynamically loading some ruby files, and those files need to call
a method on a module of mine. E.g., in this case, the method is
Puppet::Parser::Functions.newfunction, and the files are autoloaded
from ‘puppet/parser/functions/.rb’.

I’d like to load the files in a way that ‘newfunction’ could be
called without the full path to Puppet::Parser::Functions.; they call
‘newfunction’, and it correctly resolves to the Functions module.

Is there a way to load a file within an existing namespace, so that
the method search path started at the scope doing the loading?

Thanks,
Luke

On Tue, 22 Aug 2006, Luke K. wrote:

Is there a way to load a file within an existing namespace, so that the
method search path started at the scope doing the loading?

If you read the file, and then eval it in the desired scope, it has the
effect of loading it into that scope.

Kirk H.

unknown wrote:

On Tue, 22 Aug 2006, Luke K. wrote:

Is there a way to load a file within an existing namespace, so that the
method search path started at the scope doing the loading?

If you read the file, and then eval it in the desired scope, it has the
effect of loading it into that scope.

There is some complexity/problem with this but
the details elude me. Came up when implementing
the require ‘foo’, MyNamespace thingy.

Kirk H.

Eero S. wrote:

unknown wrote:

On Tue, 22 Aug 2006, Luke K. wrote:

Is there a way to load a file within an existing namespace, so that the
method search path started at the scope doing the loading?

If you read the file, and then eval it in the desired scope, it has the
effect of loading it into that scope.

There is some complexity/problem with this but
the details elude me. Came up when implementing
the require ‘foo’, MyNamespace thingy.

Note to self, Enter does not do the same thing
as Ctrl-z.

The problem was that extending existing classes
etc. would cause issues since they would be also
enveloped in the new namespace. So this is only
an issue for general-purpose programming.

Kirk H.

On Tue, 22 Aug 2006, Luke K. wrote:

Is there a way to load a file within an existing namespace, so that the
method search path started at the scope doing the loading?

Thanks,
Luke

food for thought:

harp:~ > cat a.rb
def context_load path, &context
o = Object.new
o.instance_eval &context
o.instance_eval IO.read(path)
end

context_load ‘b.rb’ do
def m() p ‘foo’ end
end

context_load ‘b.rb’ do
def m() p ‘bar’ end
end
harp:~ > cat b.rb
m()

harp:~ > ruby a.rb
“foo”
“bar”

related

http://codeforpeople.com/lib/ruby/dynaload/

regards.

-a

On Aug 21, 2006, at 7:36 PM, [email protected] wrote:

On Tue, 22 Aug 2006, Luke K. wrote:

If you read the file, and then eval it in the desired scope, it has
the effect of loading it into that scope.

Hmmm. That complicates things a little, since I need to iterate
through $: myself, but it seems like about the only way.

Thanks.

On Aug 21, 2006, at 7:42 PM, [email protected] wrote:

context_load ‘b.rb’ do
harp:~ > ruby a.rb
“foo”
“bar”

Yeah, if I decide to go this route (rather than just taking the easy,
already-working route of requiring a receiver for the method), it
definitely makes sense to pull it into a separate method.

related

http://codeforpeople.com/lib/ruby/dynaload/

I’ll look into that more closely. I’ve already used some of the
ideas in it, but I can’t seem to understand the general idea behind
it. What problems are you solving with this?

Most of my dynamic loading is based on the assumption that I’m
loading someone else’s code, so I want to do as much as possible for
the user. I don’t want them to have to know how I’m automatically
loading their files, for instance.

I think for now, I’m just going to leave it as is; people can specify
the appropriate module as the receiver.

On Wed, 23 Aug 2006, Luke K. wrote:

Thanks.
check out my dynaload lib for a way to accomplish the same without eval.
it
requires some action on the part of the loaded files - but it is ‘safe’.

cheers.

-a

On Wed, 23 Aug 2006, Luke K. wrote:

If you read the file, and then eval it in the desired scope, it has the
effect of loading it into that scope.

Hmmm. That complicates things a little, since I need to iterate through $:
myself, but it seems like about the only way.

It can be made to work. I do a lot of this sort of dynamic loading with
no problems. Write yourself a method that iterates through your search
path, and write one that loads and evals a file into a specific scope,
and
it should come together all right for you.

Kirk H.

On Wed, 23 Aug 2006, Luke K. wrote:

http://codeforpeople.com/lib/ruby/dynaload/

I’ll look into that more closely. I’ve already used some of the ideas in
it, but I can’t seem to understand the general idea behind it. What
problems are you solving with this?

problems:

  • harp:~ > cat a.rb
    class C
    def m() p ‘important!’ end
    end

    load ‘b.rb’

    C.new.m

    harp:~ > cat b.rb
    class C
    def m() p ‘clobbered!’ end
    end

    harp:~ > ruby a.rb
    “clobbered!”

  • harp:~ > cat a.rb
    module M; end

    load ‘b.rb’

    harp:~ > cat b.rb
    class M; end

    harp:~ > ruby a.rb
    ./b.rb:1: M is not a class (TypeError)
    from a.rb:3

  • harp:~ > cat a.rb
    load ‘b.rb’

    p m

    harp:~ > cat b.rb

    m = Module.new do
    def self.m() 42 end
    end

    harp:~ > ruby a.rb
    a.rb:3: undefined local variable or method `m’ for main:Object
    (NameError)

solutions:

  • harp:~ > cat a.rb
    require ‘dynaload’

    class C
    def m() p ‘important!’ end
    end

    loaded = Dynaload.dynaload ‘b.rb’

    C.new.m

    c, metadata = loaded.classes.first

    c.new.m

    harp:~ > cat b.rb
    require ‘dynaload’

    class C
    def m() p ‘clobbered!’ end
    end

    Dynaload.export C, ‘the other C’

    harp:~ > ruby a.rb
    “important!”
    “clobbered!”

  • harp:~ > cat a.rb
    require ‘dynaload’

    module M; end

    loaded = Dynaload.dynaload ‘b.rb’

    m, metadata = loaded.modules.first

    p(M == m)

    harp:~ > cat b.rb
    require ‘dynaload’

    module M; end

    Dynaload.export M, ‘the other M’

    harp:~ > ruby a.rb
    false

Most of my dynamic loading is based on the assumption that I’m loading
someone else’s code, so I want to do as much as possible for the user. I
don’t want them to have to know how I’m automatically loading their files,
for instance.

my too. you can skin that cat many ways. for instance, here’s a
distilled
technique to make it totally painless for users:

let’s say you write a library that does this:

 harp:~ > cat lib.rb
 require 'dynaload'

 def m &b
   c = Class.new{
     def self.bar() p 'forty-two' end

     module_eval &b
   }

   Dynaload.export c, 'c' => true
 end

now, client code can use this lib like so:

 harp:~ > cat b.rb
 require 'lib'

 m{
   bar

   def foo() p 42 end
 }

note that ‘bar’ has been pre-defined for them and that no explicit
exporting is
going on.

now, your code can load this client code, with no danger of namespace
pollution, and use whatever class was defined in it using

 harp:~ > cat a.rb
 require 'dynaload'

 loaded = Dynaload.dynaload 'b.rb'

 c, meta = loaded.classes.select{|c,meta| meta.has_key? 'c'}.first

 c.new.foo

running shows

 harp:~ > ruby a.rb
 "forty-two"
 42

so the client could call ‘bar’ and you could call ‘foo’.

food for thought.

cheers.

-a