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!

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:

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:

On 11/5/07, Miss Elaine E. [email protected] wrote:

to the middle line, I get
full of class-definition-files and, once at a time,
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.

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

In article
[email protected],
[email protected] 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:

Miss Elaine E. wrote:

In article
[email protected],
[email protected] 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:

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

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

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.

On Nov 5, 11:43 am, [email protected] 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.

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 11/5/07, 7stud – [email protected] 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’).

Regards,
Sean

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

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.

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 11/5/07, ara.t.howard [email protected] wrote:

object = eval ‘File::Stat’, Binding::Top

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

object = eval ‘File::Stat’, TOPLEVEL_BINDING

Regards,
Sean

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

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

2 cts.

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 3:50 PM, Morton G. 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.

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 G. wrote:

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

laziness :wink: sorry.

a @ http://codeforpeople.com/

Sean O’halpin wrote:

On 11/5/07, 7stud – [email protected] 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

In article [email protected],
Miss Elaine E. [email protected] 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!

In article
[email protected],
[email protected] wrote:

On 11/5/07, Miss Elaine E. [email protected] 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

[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]

On 11/5/07, 7stud – [email protected] wrote:

end
puts Module.const_get(“MyConst”)
#Dog:0x25148
hello
10

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