Creating objects without knowing their names


#1

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


#2

removed_email_address@domain.invalid wrote:

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.


#3

Robert K. wrote:

overrides #inherited so it gets notified every time a sub class is

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


#4

Joel VanderWerf wrote:

removed_email_address@domain.invalid wrote:

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

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

#5

All very helpful answers. Many thanks indeed.
Paul


#6

removed_email_address@domain.invalid wrote:

require ‘net/http’
p defined_classes - initial_classes

Unreliable if your “script” files also require libraries. You still have
to pick out the classes you are looking for.