> On Mon, 26 Jul 2004, Daniel Cremer wrote:
> 
>> Hi,
>>
>> Is there a solution to do code introspection rather than introspection
>> at the level of the Module/Object ? I can't figure out if I'm looking
>> for something that isn't there in Ruby since I've been reading C# code
>> or if I'm missing something. Let me explain my problem.
>> I would like to load ruby source files and dynamically create objects
>> from classes located inside these sources. It's one class/object per
>> source file and I can  make it so that a string from a configuration
>> file holds the name of the class. However I can't figure out a good
>> way to use that string to get to the class in the source file.
>>
>> I could use eval but am really uncomfortable with that for obvious
>> reasons (people keep saying it's evil so I don't play with him). The
>> other solution I came up with was to add a method in the same source
>> but outside the class to return the desired object:
>>
>> --------------source file---------
>> class MyNewClass
>> ...
>> end
>>
>> def create_loaded_class()
>>  return MyNewClass.new()
>> end
>> ---------------------------------
>>
>> Then as I require the source files I can successively invoke the
>> create_loaded_class method. This works but can get quite messy as
>> there are a lot of things to consider if you need to reload sources
>> and create new objects etc.
>> Please tell me I'm missing something obvious :).

Using module_eval in the context of a wrapper module is one way to go. 
(I know you said no eval, but load/require end up eval-ing, anyway. I 
guess you could do something with $SAFE if you are worried about mischief.)

This has gotten so common for me that I put it in a small lib: 
http://redshift.sourceforge.net/script. Here's a way of using it for 
what you want to do:

--- program.rb ---


require 'script'

files = [ "dog.rb", "cat.rb" ]

files.each do |file|
   animal_wrapper = Script.load(file)
     # animal_wrapper is a Module in whose context the top-level
     # constants and methods are defined.

   p animal_wrapper.main_file # should be same as the file local var
   p animal_wrapper::CLASS    # top-level constant in the script
   p animal_wrapper.make_animal # top-level meth in the script
end


--- dog.rb ---


class Dog
   def initialize name
     @name = name
   end
end

CLASS = Dog

def make_animal
   Dog.new "Rover"
end


--- cat.rb ---


class Dog
   def initialize name
     @name = name
   end
end

CLASS = Dog

def make_animal
   Dog.new "Rover"
end


--- output of program.rb ---


"/home/vjoel/tmp/script-example/dog.rb"
#<Script:0x401c4e3c>::Dog
#<#<Script:0x401c4e3c>::Dog:0x401c4964 @name="Rover">
"/home/vjoel/tmp/script-example/cat.rb"
#<Script:0x401c4928>::Cat
#<#<Script:0x401c4928>::Cat:0x401c4450 @name="Fuzzy">

--------

(You might want Dog and Cat to inherit from some base class that has a 
self.to_s method to avoid the hex junk.)

You could simplify the picture by defining the _same_ class in each 
script file:

class Animal
   def initialize;...;end
end

and then you can reference it by

   animal_wrapper::Animal.new

in your main program. The fact that dog.rb and cat.rb are loaded into 
different wrapper contexts keeps these classes distinct, even though 
their names appear the same.