Identfying classes in a file dynamically


#1

Hi,

I was trying to identify the classes in a give file at the run time. the
following is the code i used.
sc, ec = [], []
ObjectSpace.each_object(Class) { |c| sc << c }
require ‘Roman.rb’
ObjectSpace.each_object(Class) { |c| ec << c }
new_classes = ec - sc
puts new_classes
sc, ec = [], []
ObjectSpace.each_object(Class) { |c| sc << c }
require ‘Roman1.rb’
ObjectSpace.each_object(Class) { |c| ec << c }
new_classes = ec - sc
puts new_classes

Both roman.rb and roman1.rb has the same class Roman.
the first time i print the new_classes i can see the Roman class in the
output but the second one does not print because the class Roman is
arleady read previously.

is there a way i can over come this problem and identify the Roman class
twice.

Thanks,
Navya.


#2

Navya A. wrote:

ObjectSpace.each_object(Class) { |c| sc << c }
require ‘Roman1.rb’
ObjectSpace.each_object(Class) { |c| ec << c }
new_classes = ec - sc
puts new_classes

Both roman.rb and roman1.rb has the same class Roman.
the first time i print the new_classes i can see the Roman class in the output but the second one does not print because the class Roman is arleady read previously.

is there a way i can over come this problem and identify the Roman class twice.

One way is to wrap the file in a module, so that the constants defined
in it will not collide with other constants.

file = “some/file”
m = Module.new
m.module_eval(IO.read(file), File.expand_path(file))
p m.constants

But then the Roman module won’t be available except as m::Roman. To fix
this, you could include m into any class or module that needs to use
Roman.


#3

Hi –

On Tue, 11 Apr 2006, Joel VanderWerf wrote:

sc, ec = [], []

One way is to wrap the file in a module, so that the constants defined
in it will not collide with other constants.

file = “some/file”
m = Module.new
m.module_eval(IO.read(file), File.expand_path(file))
p m.constants

But then the Roman module won’t be available except as m::Roman. To fix
this, you could include m into any class or module that needs to use Roman.

Also I think that if the read-in file uses the ::Const construct,
those classes won’t get counted.

David


David A. Black (removed_email_address@domain.invalid)
Ruby Power and Light, LLC (http://www.rubypowerandlight.com)

“Ruby for Rails” coming in PDF April 15, and in paper May 5!
http://www.manning.com/black


#4

Hi –

On Tue, 11 Apr 2006, Joel VanderWerf wrote:

$ ruby -e ‘load “t.rb”; p Foo’
Foo
$ ruby -e ‘load “t.rb”; ObjectSpace.each_object(Class) {|c| p c if
c.name == “Foo”}’
Foo

So it doesn’t look like that works, unless I’m misunderstanding…

Yes, that’s what I meant; if you have ::Foo in your file, then you
won’t get a Foo class in the module you wrap in, so you won’t get the
right count of modules in the file.

David


David A. Black (removed_email_address@domain.invalid)
Ruby Power and Light, LLC (http://www.rubypowerandlight.com)

“Ruby for Rails” coming in PDF April 15, and in paper May 5!
http://www.manning.com/black


#5

removed_email_address@domain.invalid wrote:

Also I think that if the read-in file uses the ::Const construct,
those classes won’t get counted.

You mean like this:

$ cat t.rb
class ::Foo
end
$ ruby -e ‘load “t.rb”; p Foo’
Foo
$ ruby -e ‘load “t.rb”; ObjectSpace.each_object(Class) {|c| p c if
c.name == “Foo”}’
Foo

So it doesn’t look like that works, unless I’m misunderstanding…


#6

removed_email_address@domain.invalid wrote:

Yes, that’s what I meant; if you have ::Foo in your file, then you
won’t get a Foo class in the module you wrap in, so you won’t get the
right count of modules in the file.

Oh, I see–I thought you meant it was a workaround, but it’s actually a
limitation of the module wrapping trick. Good point.


#7

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Navya A. wrote:

ObjectSpace.each_object(Class) { |c| sc << c }

Way #1, store the new classes in a hash. This way you won’t be
overriding any classes you’ve previously loaded.

Way #2, use one array to store new classes, and then each time you
compute the “new_classes” after you load a file, merge those
into your one authoriatize array. This way you won’t be overriding any
classes you’ve previously loaded.

Way #3, pass a string into a new ruby process which loads the files and
outputs the new classes. Read these in and then use Way #1
or Way #2 to store the classes. This is a bit hackish, but it works and
it leaves your toplevel ruby process namespace alone.

Here is the idea of Way #3

str =<<-‘EOT’
def count_classes( arg )
arr = []
ObjectSpace.each_object( arg ){ |c| arr << c }
arr
end

orig_classes = count_classes( Class )
require "load"
new_classes = count_classes( Class ) - orig_classes
puts new_classes.join( "\n" )

EOT

child = IO.popen( “ruby -e ‘#{str}’”, “r” )
new_classes = child.readlines.map!{ |s| s.chomp! }
puts new_classes

Zach
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD8DBQFEO7aRMyx0fW1d8G0RAjPQAJ0YvkXA1bRmS7VCWlxw/n5CeFUD9ACcD100
MN9Ii+gFYzpg56Veh4YFd2g=
=vGHj
-----END PGP SIGNATURE-----