Creating objects without knowing their names

P

paul.p.carey

Hi

I'd like to load an arbitary number of Ruby files from a directory and
create their associated objects. However, I don't know the class names
of the files that have just been loaded. Is it possible to determine a
file's class name once that file has been loaded? (Without resorting to
parsing, or enforcing a mapping between the filename and class name.)

What I want to do is something like the following:

converter_names = Array.new
Find.find("./converters") do |filename|
converter_names.push(filename) if filename =~ /rb$/
end

converters = Array.new
converter_names.each do |converter_name|
load converter_name
klassName = ...?
converters.push(Object.const_get(klassName).new)
end

Many thanks

Paul
 
J

Joel VanderWerf

Hi

I'd like to load an arbitary number of Ruby files from a directory and
create their associated objects. However, I don't know the class names
of the files that have just been loaded. Is it possible to determine a
file's class name once that file has been loaded? (Without resorting to
parsing, or enforcing a mapping between the filename and class name.)

What I want to do is something like the following:

converter_names = Array.new
Find.find("./converters") do |filename|
converter_names.push(filename) if filename =~ /rb$/
end

converters = Array.new
converter_names.each do |converter_name|
load converter_name
klassName = ...?
converters.push(Object.const_get(klassName).new)
end

You probably know this already, but for the benefit of bystanders: there
isn't in general one-to-one mapping between classes and files. A class
can be defined across several files; a file can contain several class
definitions. (Maybe in the case of the files you are using there is
always a 1-1 map, though.)

I like to use module_eval to solve this problem by reading the file and
evaling its definitions in the context of a new module. It's all wrapped
up neatly in my "script" library on RAA.

For example:

$ cat cl.rb
class MyWeirdClass
def foo
end
end

SOME_CONSTANT = 3

$ cat script-ex.rb
require 'script'

script = Script.load "cl.rb"

p script
p script.constants
script_objects = script.constants.map {|k| script.const_get(k)}
script_classes = script_objects.grep(Class)
p script_classes


$ ruby script-ex.rb
#<Script:/home/vjoel/ruby/misc/cl.rb>
["SOME_CONSTANT", "MyWeirdClass"]
[#<Script:0xb7d794a4>::MyWeirdClass]


This also has the benefit of keeping every name from the external file
in a new module's namespace. The return value of Script.load is that module.
 
R

Robert Klemme

You probably know this already, but for the benefit of bystanders:
there isn't in general one-to-one mapping between classes and files.
A class
can be defined across several files; a file can contain several class
definitions. (Maybe in the case of the files you are using there is
always a 1-1 map, though.)

There are other options. Some of them:

- Make sure all classes on those files inherit a base class which
overrides #inherited so it gets notified every time a sub class is
created. You can then store the set of sub classes in a member of the
base class and use that for whatever purposes.

- Make classes in those files register with some arbitrary registry you
create (that's basically the same as the first just more explicit).

- use set_trace_func to trance execution of those files and notice
whenever a new class occurs. (Not sure whether that works though).

Kind regards

robert
 
M

m4rkusha

Robert said:
There are other options. Some of them:

- Make sure all classes on those files inherit a base class which
overrides #inherited so it gets notified every time a sub class is
created. You can then store the set of sub classes in a member of the
base class and use that for whatever purposes.

- Make classes in those files register with some arbitrary registry you
create (that's basically the same as the first just more explicit).

- use set_trace_func to trance execution of those files and notice
whenever a new class occurs. (Not sure whether that works though).

Kind regards

robert

Or you can compare the classes you had before loading to classes you
had after loading. Something like this:

def defined_classes
classes = []
ObjectSpace.each_object(Class){|c| classes << c}
classes
end

initial_classes = defined_classes
require 'net/http'
p defined_classes - initial_classes
 

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
473,767
Messages
2,569,570
Members
45,045
Latest member
DRCM

Latest Threads

Top