Setting namespace in loaded files

L

Luke Kanies

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/<funcname>.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
 
K

khaines

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 Haines
 
E

Eero Saynatkari

unknown said:
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.
 
A

ara.t.howard

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

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
 
E

Eero Saynatkari

Eero said:
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.
 
L

Luke Kanies

On Tue, 22 Aug 2006, Luke Kanies 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.
 
L

Luke Kanies

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"

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.

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

khaines

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 Haines
 
A

ara.t.howard

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

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
 
A

ara.t.howard

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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,210
Messages
2,571,091
Members
47,691
Latest member
Jenny-jane

Latest Threads

Top