[n00b] Reading a class-file and calling it at runtime

I'm trying to read-in a folder full of "plug-ins" and call each of them,
in turn. Once I get the class-name, I do something like:

            require "#{PLUG_IN_DIR}/#{one_plugin}"
            plugin_class=Object.const_get(plugin_class_name).new
            plugin_class.some_method

I *thought* that require worked a bit like the C pre-processor
"include", in that it would read and execute the named file at that
point, thereby defining my class and its methods. However, when I get
to the middle line, I get

     uninitialized constant PluginClassName

Since rails is mistaking my class-name for a constant, I'm guessing that
require didn't execute the way I think it does, so my class-name isn't
initialized.

...Or maybe I've completely mis-diagnosed the problem.

At any rate, can someone offer a suggestion for how to read a folder
full of class-definition-files and, once at a time,

  * Execute the class definition, so that my app knows about it
  * Instantiate an instance of the class (I think we have this part,
above)
  * Call a method on that class (should just be able to say
"a_class.a_method", right?)

Thanks!

···

--
Please take off your pants or I won't read your e-mail.
I will not, no matter how "good" the deal, patronise any business which sends
unsolicited commercial e-mail or that advertises in discussion newsgroups.

I'm not sure what you are trying to acomplish... but here is a trick i
use when rails mess with my requires.

def require_relative *args
  path= File.dirname(args.shift)
  args.each { |arg| path= File.join(path, arg) }
  require path
end

This will require the file with full file name. Now you can do

Dir[File.join(File.dirname(__FILE__), '/plugins/*.rb')].each do |file|
  require_relative __FILE__, file
end

That loads every file of directory plugins. Now, if you named the files
according to rails convetions, ie:

my_class_name.rb for MyClassName class, you can do something like:

plugin_classes= []

Dir[File.join(File.dirname(__FILE__), '/plugins/*.rb')].each do |file|
  require_relative __FILE__, file
  name= File.basename(file)
  plugin_classes << $1.camelize.constantize if name =~ /(.*)\.rb$/i
end

There... it sounds easy, no? :slight_smile:

···

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

Hi,

What you're doing here is right in principle. For example, in this code:

# ./myplugin.rb
class MyPlugin
  def some_method
    puts "Hi from #{self}!"
  end
end

module Plugins
  class AnotherPlugin
    def some_method
      puts "Hi from #{self}!"
    end
  end
end
__END__

# test-require-plugin.rb
one_plugin = "myplugin"
PLUG_IN_DIR = '.'
plugin_class_name = "MyPlugin"

require "#{PLUG_IN_DIR}/#{one_plugin}"
plugin_class = Object.const_get(plugin_class_name).new
plugin_class.some_method

plugin_class_name = "AnotherPlugin"
plugin_class = Plugins.const_get(plugin_class_name).new
plugin_class.some_method

# this will fail
plugin_class = Object.const_get(plugin_class_name).new
plugin_class.some_method

__END__
# output
Hi from #<MyPlugin:0xb7c25594>!
Hi from #<Plugins::AnotherPlugin:0xb7c23dfc>!
test-require-plugin.rb:14:in `const_get': uninitialized constant
AnotherPlugin (NameError)
        from test-require-plugin.rb:14

only the last call to some_method fails. Have you checked that the
name of the class is correct or if you are defining your plugin class
within a module?

Regards,
Sean

···

On 11/5/07, Miss Elaine Eos <Misc@your-pants.playnaked.com> wrote:

I'm trying to read-in a folder full of "plug-ins" and call each of them,
in turn. Once I get the class-name, I do something like:

            require "#{PLUG_IN_DIR}/#{one_plugin}"
            plugin_class=Object.const_get(plugin_class_name).new
            plugin_class.some_method

I *thought* that require worked a bit like the C pre-processor
"include", in that it would read and execute the named file at that
point, thereby defining my class and its methods. However, when I get
to the middle line, I get

     uninitialized constant PluginClassName

Since rails is mistaking my class-name for a constant, I'm guessing that
require didn't execute the way I think it does, so my class-name isn't
initialized.

...Or maybe I've completely mis-diagnosed the problem.

At any rate, can someone offer a suggestion for how to read a folder
full of class-definition-files and, once at a time,

  * Execute the class definition, so that my app knows about it
  * Instantiate an instance of the class (I think we have this part,
above)
  * Call a method on that class (should just be able to say
"a_class.a_method", right?)

Thanks!

--
Please take off your pants or I won't read your e-mail.
I will not, no matter how "good" the deal, patronise any business which sends
unsolicited commercial e-mail or that advertises in discussion newsgroups.

mmm i believe i messed up the things a bit, this should be the right
thing to do, if complex, anyway:

plugin_classes=

Dir[File.join(File.dirname(__FILE__), '/plugins/*.rb')].each do |file|

  file_name= File.basename(file)

  require_relative __FILE__, 'plugins', file_name

  plugin_classes << $1.camelize.constantize if file_name =~ /(.*)\.rb$/i
end

There, now it should work! :slight_smile:

···

plugin_classes=

Dir[File.join(File.dirname(__FILE__), '/plugins/*.rb')].each do |file|
  require_relative __FILE__, file
  name= File.basename(file)
  plugin_classes << $1.camelize.constantize if name =~ /(.*)\.rb$/i
end

There... it sounds easy, no? :slight_smile:

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

In article
<3736dd30711042349n7225229dw4af74110f2a52048@mail.gmail.com>,

···

sean.ohalpin@gmail.com wrote:

only the last call to some_method fails. Have you checked that the
name of the class is correct or if you are defining your plugin class
within a module?

I am NOT using "module." I guess I need to go read-up on modules --
Thanks! :slight_smile:

--
Please take off your pants or I won't read your e-mail.
I will not, no matter how "good" the deal, patronise any business which sends
unsolicited commercial e-mail or that advertises in discussion newsgroups.

Sean O'halpin wrote:

plugin_class = Object.const_get(plugin_class_name).new

I have a question about using Object as the receiver in the above line.
Is there any specific reason you are using Object? I find calling
const_get with Object very confusing because the Object class does not
define a const_get method--it's only tracking const_get through ruby's
extremely confusing circular inheritance:

Object <------+
  > >
  V |
Module |
--const_get |
               >
  > >
  V |
Class ---------+

that const_get lands in Object. Since calling const_get with any of the
class names Object, Module, or Class seems to work, why not just use
Module or Class to call const_get? If no Modules are involved, which
seems to be the op's case, then Class would seem like the least
confusing name to call const_get with.

#plugin.rb:

class Dog
  def speak
    puts "Woof woof"
  end
end

···

-----------

#my_program.rb:

require 'plugin'

dog_class = Class.const_get("Dog")
dog = dog_class.new
dog.speak

Of course calling const_get with Class might still result in some
confusion--looking up the methods for Class will not reveal a const_get
method.
--
Posted via http://www.ruby-forum.com/\.

In article
<3736dd30711042349n7225229dw4af74110f2a52048@mail.gmail.com>,

> I'm trying to read-in a folder full of "plug-ins" and call each of them,
> in turn. Once I get the class-name, I do something like:
>
> require "#{PLUG_IN_DIR}/#{one_plugin}"
> plugin_class=Object.const_get(plugin_class_name).new
> plugin_class.some_method

   [snip]

Ok, I tried your example, below, and it worked the same for me as for
you. But when I go back to my actual code, same problem. I don't
follow what's different about what your example is doing and what I'm
doing. Here are some differences:

* I'm running under rails. Can't see how that should matter -- this is
long before any of the rendering stuff, and no DB accesses. I'm just
triggering code in a controller. That's all ruby, right?

* My program works like your example, below, except that it walks a
directory and does the trick for every file in it. So, rather than call
"myplugin.rb", it walks the directory, finds my_plugin.rb, uses that as
a filename, converts to MyPlugin for class name, but the rest is the
same.

* My actual plugin is a sub-class of a master-plugin. This is how I
guarantee the interface. (There's probably a more Ruby-like way to do
this, but it's what I know...) So myplugin is mildly complicated in
that my 1st line looks more like this:

  class MyPlugin < MasterPlugin

But modifying the sample (below) to replicate that doesn't seem to
impact it's working-ness.

So I'm stuck trying to figure out how & where to trim back my original
to get it to act like the example. Does Rails actually matter?! Does
it matter if my require is inside a loop and/or inside another class (a
rails controller)?

The problem: I get

   uninitialized constant MyPlugin

at the line with const_get().

   [Example trimmed back to the minimal case that I'm using]

···

sean.ohalpin@gmail.com wrote:

On 11/5/07, Miss Elaine Eos <Misc@your-pants.playnaked.com> wrote:
What you're doing here is right in principle. For example, in this code:

# ./myplugin.rb
class MyPlugin
  def some_method
    puts "Hi from #{self}!"
  end
end
__END__

# test-require-plugin.rb
one_plugin = "myplugin"
PLUG_IN_DIR = '.'
plugin_class_name = "MyPlugin"

require "#{PLUG_IN_DIR}/#{one_plugin}"
plugin_class = Object.const_get(plugin_class_name).new
plugin_class.some_method

plugin_class_name = "AnotherPlugin"
plugin_class = Plugins.const_get(plugin_class_name).new
plugin_class.some_method
__END__
# output
Hi from #<MyPlugin:0xb7c25594>!
Hi from #<Plugins::AnotherPlugin:0xb7c23dfc>!

--
Please take off your pants or I won't read your e-mail.
I will not, no matter how "good" the deal, patronise any business which sends
unsolicited commercial e-mail or that advertises in discussion newsgroups.

Miss Elaine Eos wrote:

In article
<3736dd30711042349n7225229dw4af74110f2a52048@mail.gmail.com>,

only the last call to some_method fails. Have you checked that the
name of the class is correct or if you are defining your plugin class
within a module?

I am NOT using "module." I guess I need to go read-up on modules --
Thanks! :slight_smile:

Maybe you should post a simplified version of one of the files you are
requiring, e.g.

class Dog
  def speak
    puts "Woof woof"
  end
end

As this example shows:

Sean O'halpin wrote:

# ./myplugin.rb

class MyPlugin
  def some_method
    puts "Hi from #{self}!"
  end
end

---------------

# test-require-plugin.rb

one_plugin = "myplugin"
PLUG_IN_DIR = '.'
plugin_class_name = "MyPlugin"

require "#{PLUG_IN_DIR}/#{one_plugin}"
plugin_class = Object.const_get(plugin_class_name).new
plugin_class.some_method

# output
Hi from #<MyPlugin:0xb7c25594>!

...you don't need to have your class inside a module to get your code to
work. But the rest of the example showed that IF your class definition
is inside a module, then you have to alter your syntax a little to
retrieve the class with const_get.

Here is another example if it helps (all files are in the current
directory):

#plugin.rb:

class Dog
  def speak
    puts "Woof woof"
  end
end

#my_program.rb:

require 'plugin'

dog_class = Object.const_get("Dog")
dog = dog_class.new
dog.speak

···

sean.ohalpin@gmail.com wrote:

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

7stud -- wrote:

it's only tracking const_get through ruby's
extremely confusing circular inheritance:

Object <------+
  > >
  V |
Module |
--const_get |
               >
  > >
  V |
Class ---------+

This might be a clearer diagram:

Object
  >
  V
Module
--const_get

  >
  V
Class
  >
  V
Object (Object is a subclass of Class)

puts Class.kind_of?(Object)
-->true

puts Object.kind_of?(Class)
-->true

···

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

For me personally, having someone write Module.const_get versus
Class.const_get implies that the former will only return a Module
instance, and the latter would only return a Class instance. By using
Object.const_get you are clearly stating that there is no guarantee as
to what type of object the supplied constant points to.

···

On Nov 5, 11:43 am, bbxx789_0...@yahoo.com wrote:

Sean O'halpin wrote:

> plugin_class = Object.const_get(plugin_class_name).new

I have a question about using Object as the receiver in the above line.
Is there any specific reason you are using Object?

dog_class = Class.const_get("Dog")
dog = dog_class.new
dog.speak

Of course calling const_get with Class might still result in some
confusion--looking up the methods for Class will not reveal a const_get
method.

It's a way of looking up constants in the scope of the top level
binding (the special Object known as 'main').

Regards,
Sean

P.S. Thanks for clarifying my other post :slight_smile:

···

On 11/5/07, 7stud -- <bbxx789_05ss@yahoo.com> wrote:

Sean O'halpin wrote:
>
> plugin_class = Object.const_get(plugin_class_name).new
>

I have a question about using Object as the receiver in the above line.
Is there any specific reason you are using Object?

In article <Misc-4BC90C.22022205112007@newsclstr03.news.prodigy.net>,

···

Miss Elaine Eos <Misc@your-pants.PlayNaked.com> wrote:

Ok, I tried your example, below, and it worked the same for me as for
you. But when I go back to my actual code, same problem. I don't
follow what's different about what your example is doing and what I'm
doing. Here are some differences:

* I'm running under rails. Can't see how that should matter -- this is
long before any of the rendering stuff, and no DB accesses. I'm just
triggering code in a controller. That's all ruby, right?

It seems that my problems had to do with leaving rails (WEBrick) running
and changing my controller/plug-in files. Once I quit & restarted
WEBrick, everything started working just fine.

Not sure why -- I though in the past that redefining objects worked
correctly.

Anyway, thank you much for your help!

--
Please take off your pants or I won't read your e-mail.
I will not, no matter how "good" the deal, patronise any business which sends
unsolicited commercial e-mail or that advertises in discussion newsgroups.

i prefer Object.const_get too, although for different reasons: it *should* be well known by ruby programmers that the 'top' context is the Object instances called 'main'. another approach, suitable for some limited situations is simply

object = eval name, Binding::Top

BEGIN{
   Binding::Top = binding
}

which is less safe, of course, but works with effort for cases like

object = eval 'File::Stat', Binding::Top

which, for some reason, routinely confounds people on the list...

2 cts.

a @ http://codeforpeople.com/

···

On Nov 5, 2007, at 12:45 PM, Phrogz wrote:

For me personally, having someone write Module.const_get versus
Class.const_get implies that the former will only return a Module
instance, and the latter would only return a Class instance. By using
Object.const_get you are clearly stating that there is no guarantee as
to what type of object the supplied constant points to.

--
it is not enough to be compassionate. you must act.
h.h. the 14th dalai lama

Sean O'halpin wrote:

Sean O'halpin wrote:
>
> plugin_class = Object.const_get(plugin_class_name).new
>

I have a question about using Object as the receiver in the above line.
Is there any specific reason you are using Object?

It's a way of looking up constants in the scope of the top level
binding (the special Object known as 'main').

... so are Module.const_get and Class.const_get:

class Dog
end

module Stuff
  Greeting = "hello"
end

MyConst = 10

puts Module.const_get("Dog").new
puts Module.const_get("Stuff")::Greeting
puts Module.const_get("MyConst")
puts
puts Class.const_get("Dog").new
puts Class.const_get("Stuff")::Greeting
puts Class.const_get("MyConst")

--output:--
#<Dog:0x251c0>
hello
10

#<Dog:0x25148>
hello
10

···

On 11/5/07, 7stud -- <bbxx789_05ss@yahoo.com> wrote:

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

You can also use the predefined constant TOPLEVEL_BINDING, e.g.

  object = eval 'File::Stat', TOPLEVEL_BINDING

Regards,
Sean

···

On 11/5/07, ara.t.howard <ara.t.howard@gmail.com> wrote:

another approach, suitable for
some limited situations is simply

object = eval name, Binding::Top

BEGIN{
   Binding::Top = binding
}

which is less safe, of course, but works with effort for cases like

object = eval 'File::Stat', Binding::Top

You probably have a good reason for writing the above rather than the simpler

    eval('File::Stat', TOPLEVEL_BINDING) # => File::Stat

but I can't figure out what it is. So please explain.

Regards, Morton

···

On Nov 5, 2007, at 4:20 PM, ara.t.howard wrote:

object = eval name, Binding::Top

BEGIN {
  Binding::Top = binding
}

which is less safe, of course, but works with effort for cases like

object = eval 'File::Stat', Binding::Top

which, for some reason, routinely confounds people on the list...

2 cts.

Hi,

This illustrates what I mean:

Greeting = "goodbye"

class Module
Greeting = "hello"
end

class Class
Greeting = "hi!"
end

puts Module.const_get("Greeting")
puts Class.const_get("Greeting")
puts Object.const_get("Greeting")
puts eval( "Greeting", TOPLEVEL_BINDING ) # for Ara :wink:

--output--
hello
hi!
goodbye
goodbye

Regards,
Sean

···

On 11/5/07, 7stud -- <bbxx789_05ss@yahoo.com> wrote:

>> I have a question about using Object as the receiver in the above line.
>> Is there any specific reason you are using Object?
>
> It's a way of looking up constants in the scope of the top level
> binding (the special Object known as 'main').
>

... so are Module.const_get and Class.const_get:

class Dog
end

module Stuff
  Greeting = "hello"
end

MyConst = 10

puts Module.const_get("Dog").new
puts Module.const_get("Stuff")::Greeting
puts Module.const_get("MyConst")
puts
puts Class.const_get("Dog").new
puts Class.const_get("Stuff")::Greeting
puts Class.const_get("MyConst")

--output:--
#<Dog:0x251c0>
hello
10

#<Dog:0x25148>
hello
10

a) always forgetting what the constant is named
b) begin too lazy to look it when i was posting

TOPLEVEL_BINDING is, of course, the better way to go! :wink:

sorry for any confusion caused by my laziness.

ps. i *do* wish the top level binding was part of the Binding class, Binding.top, or something, because i can never remember where it lives...

a @ http://codeforpeople.com/

···

On Nov 5, 2007, at 3:50 PM, Morton Goldberg wrote:

You probably have a good reason for writing the above rather than the simpler

   eval('File::Stat', TOPLEVEL_BINDING) # => File::Stat

but I can't figure out what it is. So please explain.

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

laziness :wink: sorry.

a @ http://codeforpeople.com/

···

On Nov 5, 2007, at 3:50 PM, Morton Goldberg wrote:

but I can't figure out what it is. So please explain.

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama