Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Ruby > code introspection

Reply
Thread Tools

code introspection

 
 
Daniel Cremer
Guest
Posts: n/a
 
      07-26-2004
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 .

thanks,
Daniel
 
Reply With Quote
 
 
 
 
gabriele renzi
Guest
Posts: n/a
 
      07-26-2004
il 26 Jul 2004 03:19:36 -0700, (Daniel Cremer)
ha scritto::

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


something like:

'Myclass' in 'Myclass.rb' or the file names are not related

>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:



you may use const_get:
>> class Foo
>> end

=> nil
>> Object::const_get('Foo').new

=> #<Foo:0x28b3eb8>


 
Reply With Quote
 
 
 
 
Lothar Scholz
Guest
Posts: n/a
 
      07-26-2004
Hello Daniel,

DC> Is there a solution to do code introspection rather than introspection
DC> at the level of the Module/Object ? I can't figure out if I'm looking

Yes but only from the C level. You need to create the "Node" tree and
travere it. Unfortuneatly there seems to be no visitor pattern class to do
this and you must expect that the Node structure can change even
between minor updates a lot. So read the code in eval.c and parse.c.

Look at the ExErb project for an application that does this.

Programming is easy if you are an experienced programmer but
maintainance is quite some work.


--
Best regards, emailto: scholz at scriptolutions dot com
Lothar Scholz http://www.ruby-ide.com
CTO Scriptolutions Ruby, PHP, Python IDE 's




 
Reply With Quote
 
Robert Klemme
Guest
Posts: n/a
 
      07-26-2004

"Lothar Scholz" <> schrieb im Newsbeitrag
news: m...
> Hello Daniel,
>
> DC> Is there a solution to do code introspection rather than

introspection
> DC> at the level of the Module/Object ? I can't figure out if I'm

looking
>
> Yes but only from the C level. You need to create the "Node" tree and
> travere it. Unfortuneatly there seems to be no visitor pattern class to

do
> this and you must expect that the Node structure can change even
> between minor updates a lot. So read the code in eval.c and parse.c.


Err, I think this is overkill. Daniel just wants to create an instance of
a class whose name he knows. So the already suggested Object.const_get(
class_name ) or ObjectSpace.const_get( class_name ) suffices.

If you don't know class names beforehand, you can work with an anonymous
module as wrapper like this:

mod = Module.new
mod.class_eval( File.read( "foo.rb" ) )
new_classes = mod.constants.inject([]) {|ar,c| cl=mod.const_get(c); ar <<
cl if Class === cl; ar}
p new_classes
include mod
p Foo

Or even do this

mod = Object
mod.class_eval( File.read( "foo.rb" ) )
classes = mod.constants.inject([]) {|ar,c| cl=mod.const_get(c); ar << cl
if Class === cl; ar}
p classes
p Foo


Regards

robert

 
Reply With Quote
 
Ara.T.Howard
Guest
Posts: n/a
 
      07-26-2004
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 .
>
> thanks,
> Daniel



~ > cat c.rb
files = %w(a.rb b.rb)

files.each do |file|
load file
name, klass = Common.introspect file
p name
p klass
klass.new
end


~ > ruby c.rb
"Foo"
Common::Foo
"foo"
"Bar"
Common::Bar
"bar"


~ > cat a.rb
module Common
class Foo
def initialize
p 'foo'
end
end

class << self
def introspect path
@introspect[File.basename(path)]
end
end
@introspect = {} unless defined? @introspect
@introspect[File.basename(__FILE__)] = 'Foo', Foo
end


~ > cat b.rb
module Common
class Bar
def initialize
p 'bar'
end
end

class << self
def introspect path
@introspect[File.basename(path)]
end
end
@introspect = {} unless defined? @introspect
@introspect[File.basename(__FILE__)] = 'Bar', Bar
end


you could obviously do this without the 'Common' module - but i think it's
cleaner/safer this way - provided you can manage to wrap all your modules this
way...

kind regards.

-a
--
================================================== =============================
| EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
| PHONE :: 303.497.6469
| A flower falls, even though we love it;
| and a weed grows, even though we do not love it.
| --Dogen
================================================== =============================
 
Reply With Quote
 
Daniel Cremer
Guest
Posts: n/a
 
      07-26-2004
On Mon, 2004-07-26 at 14:16, Robert Klemme wrote:
> "Lothar Scholz" <> schrieb im Newsbeitrag
> news: m...
> > Hello Daniel,
> >
> > DC> Is there a solution to do code introspection rather than

> introspection
> > DC> at the level of the Module/Object ? I can't figure out if I'm

> looking
> >
> > Yes but only from the C level. You need to create the "Node" tree and
> > travere it. Unfortuneatly there seems to be no visitor pattern class to

> do
> > this and you must expect that the Node structure can change even
> > between minor updates a lot. So read the code in eval.c and parse.c.

>
> Err, I think this is overkill. Daniel just wants to create an instance of
> a class whose name he knows. So the already suggested Object.const_get(
> class_name ) or ObjectSpace.const_get( class_name ) suffices.
>
> If you don't know class names beforehand, you can work with an anonymous
> module as wrapper like this:
>
> mod = Module.new
> mod.class_eval( File.read( "foo.rb" ) )
> new_classes = mod.constants.inject([]) {|ar,c| cl=mod.const_get(c); ar <<
> cl if Class === cl; ar}
> p new_classes
> include mod
> p Foo
>
> (...)


ahh... it looks like creating an anonymous module is the piece that is
really missing for me at the moment...
thanks for the ideas, I'm not sure how far I'll have to carry this so
they're all appreciated

-Daniel



 
Reply With Quote
 
Joel VanderWerf
Guest
Posts: n/a
 
      07-26-2004
> 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>:og
#<#<Script:0x401c4e3c>:og: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.



 
Reply With Quote
 
Joel VanderWerf
Guest
Posts: n/a
 
      07-26-2004
Joel VanderWerf wrote:
> --- cat.rb ---
>
>
> class Dog
> def initialize name
> @name = name
> end
> end
>
> CLASS = Dog
>
> def make_animal
> Dog.new "Rover"
> end


Ooops. Copy/paste error.

--- cat.rb ---


class Cat
def initialize name
@name = name
end
end

CLASS = Cat

def make_animal
Cat.new "Fuzzy"
end


 
Reply With Quote
 
gabriele renzi
Guest
Posts: n/a
 
      07-27-2004
il Mon, 26 Jul 2004 23:56:40 +0900, Daniel Cremer
<> ha scritto::

>> If you don't know class names beforehand, you can work with an anonymous
>> module as wrapper like this:
>>
>> mod = Module.new
>> mod.class_eval( File.read( "foo.rb" ) )
>> new_classes = mod.constants.inject([]) {|ar,c| cl=mod.const_get(c); ar <<
>> cl if Class === cl; ar}
>> p new_classes
>> include mod
>> p Foo
>>
>> (...)

>
>ahh... it looks like creating an anonymous module is the piece that is
>really missing for me at the moment...
>thanks for the ideas, I'm not sure how far I'll have to carry this so
>they're all appreciated
>


well, than take a look at the simple load:

load(filename, wrap=false) => true
-----------------------------------------------------------------------
Loads and executes the Ruby program in the file _filename_. If the
filename does not resolve to an absolute path, the file is
searched
for in the library directories listed in +$:+. If the optional
_wrap_ parameter is +true+, the loaded script will be executed
under an anonymous module, protecting the calling program's global
namespace. In no circumstance will any local variables in the
loaded file be propagated to the loading environment.
 
Reply With Quote
 
Robert Klemme
Guest
Posts: n/a
 
      07-29-2004

"gabriele renzi" <> schrieb im Newsbeitrag
news:...
> il Mon, 26 Jul 2004 23:56:40 +0900, Daniel Cremer
> <> ha scritto::
>
> >> If you don't know class names beforehand, you can work with an

anonymous
> >> module as wrapper like this:
> >>
> >> mod = Module.new
> >> mod.class_eval( File.read( "foo.rb" ) )
> >> new_classes = mod.constants.inject([]) {|ar,c| cl=mod.const_get(c);

ar <<
> >> cl if Class === cl; ar}
> >> p new_classes
> >> include mod
> >> p Foo
> >>
> >> (...)

> >
> >ahh... it looks like creating an anonymous module is the piece that is
> >really missing for me at the moment...
> >thanks for the ideas, I'm not sure how far I'll have to carry this so
> >they're all appreciated
> >

>
> well, than take a look at the simple load:
>
> load(filename, wrap=false) => true
> -----------------------------------------------------------------------
> Loads and executes the Ruby program in the file _filename_. If the
> filename does not resolve to an absolute path, the file is
> searched
> for in the library directories listed in +$:+. If the optional
> _wrap_ parameter is +true+, the loaded script will be executed
> under an anonymous module, protecting the calling program's global
> namespace. In no circumstance will any local variables in the
> loaded file be propagated to the loading environment.


Yes, but that doesn't help here since there's no way to find this
anonymous module in order to find all classes defined in the file. I
tried that initially, too, but the return value of load is not the module
but 'true' or 'false'. Or am I missing somethig?

Regards

robert

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Annotations processing + type introspection + code generation Alex J Java 7 09-12-2011 10:06 PM
Dynamic/runtime code introspection/compilation Thomas W Python 3 11-28-2006 03:04 PM
Help in using introspection to simplify repetitive code jsceballos@gmail.com Python 6 08-27-2006 08:07 PM
Synopsis: A Source-code Introspection Tool Steven T. Hatton C++ 2 07-06-2005 06:13 PM
Actual parameter introspection for error logging David Smith Java 0 07-11-2003 01:32 PM



Advertisments
 



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57