Forum: Ruby Creating objects without knowing their names

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
unknown (Guest)
on 2006-02-16 11:10
(Received via mailing list)
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
Joel VanderWerf (Guest)
on 2006-02-16 11:46
(Received via mailing list)
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.
Robert K. (Guest)
on 2006-02-16 12:50
(Received via mailing list)
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
(Guest)
on 2006-02-16 14:50
(Received via mailing list)
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
unknown (Guest)
on 2006-02-16 17:07
(Received via mailing list)
All very helpful answers. Many thanks indeed.
Paul
Joel VanderWerf (Guest)
on 2006-02-16 20:24
(Received via mailing list)
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.
This topic is locked and can not be replied to.