Setting namespace in loaded files

(Luke A. Kanies) #1

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

···

--
Luke Kanies
http://madstop.com | http://reductivelabs.com | 615-594-8199

(Kirk Haines) #2

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

···

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

(Ara.T.Howard) #3

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 Tue, 22 Aug 2006, Luke Kanies wrote:

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

--
to foster inner awareness, introspection, and reasoning is more efficient than
meditation and prayer.
- h.h. the 14th dali lama

(E S) #4

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

···

On Tue, 22 Aug 2006, Luke Kanies wrote:

Kirk Haines

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

(Luke A. Kanies) #5

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:36 PM, khaines@enigo.com wrote:

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.

--
Luke Kanies
http://madstop.com | http://reductivelabs.com | 615-594-8199

(Luke A. Kanies) #6

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.

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 Aug 21, 2006, at 7:42 PM, ara.t.howard@noaa.gov wrote:

--
Luke Kanies
http://madstop.com | http://reductivelabs.com | 615-594-8199

(E S) #7

Eero Saynatkari wrote:

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

···

On Tue, 22 Aug 2006, Luke Kanies wrote:

Kirk Haines

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

(Kirk Haines) #8

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

···

On Wed, 23 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.

(Ara.T.Howard) #9

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 Kanies wrote:

On Aug 21, 2006, at 7:36 PM, khaines@enigo.com wrote:

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.

--
to foster inner awareness, introspection, and reasoning is more efficient than
meditation and prayer.
- h.h. the 14th dali lama

(Ara.T.Howard) #10

problems:

···

On Wed, 23 Aug 2006, Luke Kanies 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?

   -
     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
--
to foster inner awareness, introspection, and reasoning is more efficient than
meditation and prayer.
- h.h. the 14th dali lama