Say you have a class definition in a string -
str = <<EOF
module A
class B
def b
puts "hello"
end
end
end
EOF
What is the easiest way to find out the fully qualified name of the class
there, namely "A::B"
Now I could do a thing like
module EmptyModule
end
EmptyModule.module_eval(str)
def self.find_class mod
mod.constants.each do |str|
m = mod.const_get(str)
if m.class == Module
find_class(m)
else
cname = m.to_s
puts cname.match(/EmptyModule::/).post_match
break
end
end
end
and call this method
find_class(EmptyModule)
which prints "A::B"
This works but it seems too much work to do something simple.
Is there a better/simpler way to do this?
Thanks
Nasir
Interesting. I thought that in general you couldn't:
m = Module.new
Foo = m
Bar = m
module m
# what's the *name* of the module we are in now??
end
But actually Ruby rejects that, so you must say 'module Foo' or 'module Bar'
···
On Sat, Mar 31, 2007 at 05:54:42AM +0900, Nasir Khan wrote:
Say you have a class definition in a string -
str = <<EOF
module A
class B
def b
puts "hello"
end
end
end
EOF
What is the easiest way to find out the fully qualified name of the class
there, namely "A::B"
I don't think that's what he's looking for.
He has a string containing Ruby source code which presumably defines a
class (or classes?) which might be contained within a module. He
want's to find the fully qualified name of this unknown class (or
classes?).
The A::B is really just an example.
WHY he wants to do this is beyond me.
···
On 3/30/07, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> wrote:
On Sat, 31 Mar 2007, Nasir Khan wrote:
> Say you have a class definition in a string -
>
> str = <<EOF
> module A
> class B
> def b
> puts "hello"
> end
> end
> EOF
>
> What is the easiest way to find out the fully qualified name of the class
> there, namely "A::B"
>
> Now I could do a thing like
>
> module EmptyModule
> end
>
> EmptyModule.module_eval(str)
>
> def self.find_class mod
> mod.constants.each do |str|
> m = mod.const_get(str)
> if m.class == Module
> find_class(m)
> else
> cname = m.to_s
> puts cname.match(/EmptyModule::/).post_match
> break
> end
> end
> end
>
> and call this method
>
> find_class(EmptyModule)
>
> which prints "A::B"
>
> This works but it seems too much work to do something simple.
>
> Is there a better/simpler way to do this?
>
> Thanks
> Nasir
harp:~ > cat a.rb
#
# a better const_get
#
def constant_get(hierachy)
ancestors = hierachy.split(%r/::/)
parent = Object
while((child = ancestors.shift))
klass = parent.const_get child
parent = klass
end
klass
end
c = constant_get 'A::B'
--
Rick DeNatale
My blog on Ruby
http://talklikeaduck.denhaven2.com/
I think this illustrates your point:
m = Module.new do
attr_accessor :name
end
B = Class.new do
include m
end
p B.ancestors
# [B, #<Module:0xb7c3bc2c>, Object, Kernel]
Regards,
Sean
···
On 3/31/07, Brian Candler <B.Candler@pobox.com> wrote:
On Sat, Mar 31, 2007 at 05:54:42AM +0900, Nasir Khan wrote:
> Say you have a class definition in a string -
>
> str = <<EOF
> module A
> class B
> def b
> puts "hello"
> end
> end
> end
> EOF
>
> What is the easiest way to find out the fully qualified name of the class
> there, namely "A::B"
Interesting. I thought that in general you couldn't:
m = Module.new
Foo = m
Bar = m
module m
# what's the *name* of the module we are in now??
end
But actually Ruby rejects that, so you must say 'module Foo' or 'module Bar'
I don't think that's what he's looking for.
Thank you.
He has a string containing Ruby source code which presumably defines a
class (or classes?) which might be contained within a module. He
want's to find the fully qualified name of this unknown class (or
classes?).
Yes.
The A::B is really just an example.
WHY he wants to do this is beyond me.
Huh! Then ask me.
Anyway, there could be several uses for such a thing. Simplest use case
would be a RAILS like (but not RAILS) situation where a controller is
deployed on a running server which (controller) optionally comes as a string
enveloped in a protocol message, this string is evaled and the controller is
instatiated but the system also needs to maintain meta information including
the name of the controller just deployed, which defaults to the fully
qualified name of the class.
My use case is similar.
- Nasir
···
--
Rick DeNatale
My blog on Ruby
http://talklikeaduck.denhaven2.com/
Here's one way to capture that info as it's evaled. Not thread-safe
as-is. Not even sure if it's useful at all, but I was a bit curious.
There's more ways to do this, of course, especially depending on when
you want to capture this info.
str = <<EOF
module A
class B
def b
puts "hello"
end
end
end
EOF
set_trace_func proc {|event, file, line, id, binding, classname|
return unless event == 'class'
name = eval('self.class == Class && Module.nesting[0]', binding)
if name
# Do something with name
end
}
eval(str)
set_trace_func(nil)
···
On 3/31/07, Nasir Khan <rubylearner@gmail.com> wrote:
Anyway, there could be several uses for such a thing. Simplest use case
would be a RAILS like (but not RAILS) situation where a controller is
deployed on a running server which (controller) optionally comes as a string
enveloped in a protocol message, this string is evaled and the controller is
instatiated but the system also needs to maintain meta information including
the name of the controller just deployed, which defaults to the fully
qualified name of the class.
My use case is similar.
--
Kristoffer Lundén
kristoffer.lunden@gmail.com
kristoffer.lunden@gamemaker.nu
http://www.gamemaker.nu/
0704 48 98 77
I like his idea of evaluating the string in the context of a 'sandbox'
module, here's a refinement which does that. It strips off the
throwaway module since I assume that he's doing this to pre-flight
check actually re-running the code again after scanning it.
I also check to see if the trace function is running on the
originating thread, there are still thread safety issues since another
thread might also be using set_trace_func. Another way to approach
this would be to set and reset Thread.critical, but it doesn't solve
the second problem since there doesn't seem to be a way to stack trace
functions.
rick@bill:/public/rubyscripts$ cat find_classes.rb
def classes_defined_in(str)
begin
this_thread = Thread.current
result = []
set_trace_func(lambda do |event, file, line, id, binding, classname|
return unless Thread.current == this_thread
return unless event == 'class'
name = eval('self.class == Class && Module.nesting[0]',binding)
result << name.to_s.split(/::/,2)[1] if name
end)
ensure
Module.new.module_eval(str)
set_trace_func(nil)
end
result
end
str = <<EOF
module M1
class C1
def c
puts "hello"
end
end
module M2
class C2
end
end
end
module M3
class C3
end
end
EOF
p classes_defined_in(str)
rick@bill:/public/rubyscripts$ ruby find_classes.rb
["M1::C1", "M1::M2::C2", "M3::C3"]
···
On 3/30/07, Kristoffer Lundén <kristoffer.lunden@gmail.com> wrote:
On 3/31/07, Nasir Khan <rubylearner@gmail.com> wrote:
> Anyway, there could be several uses for such a thing. Simplest use case
> would be a RAILS like (but not RAILS) situation where a controller is
> deployed on a running server which (controller) optionally comes as a string
> enveloped in a protocol message, this string is evaled and the controller is
> instatiated but the system also needs to maintain meta information including
> the name of the controller just deployed, which defaults to the fully
> qualified name of the class.
> My use case is similar.
>
Here's one way to capture that info as it's evaled. Not thread-safe
as-is. Not even sure if it's useful at all, but I was a bit curious.
There's more ways to do this, of course, especially depending on when
you want to capture this info.
str = <<EOF
module A
class B
def b
puts "hello"
end
end
end
EOF
set_trace_func proc {|event, file, line, id, binding, classname|
return unless event == 'class'
name = eval('self.class == Class && Module.nesting[0]', binding)
if name
# Do something with name
end
}
eval(str)
set_trace_func(nil)
--
Rick DeNatale
My blog on Ruby
http://talklikeaduck.denhaven2.com/
Here is an abridged (out of context) version of my original solution. (which
I am using now)
···
-----
nasir@sparkle:misc>cat find_class_name.rb
module EmptyModule
def EmptyModule.clear_all
constants.each {|x| remove_const(x.to_sym)}
end
end
@my_classes = []
def class_from_string(str)
EmptyModule.clear_all # clean the slate
EmptyModule.module_eval(str)
end
cstr = <<EOF
module B
class A
def a
puts "hello"
end
end
end
module B
class C
end
class D
end
end
EOF
# This string will usually come from some external invocation of this
function
class_from_string(cstr)
def find_class mod
mod.constants.each do |str|
m = mod.const_get(str)
if m.class == Module
find_class(m)
else
cname = m.to_s
cname = cname.match(/EmptyModule::/).post_match
@my_classes << cname
end
end
end
find_class( EmptyModule )
puts @my_classes.uniq
---------------
And the result -
nasir@sparkle:misc>ruby find_class_name.rb
B:
B::C
B::A
----------------
I guess I will stick with this solution. set_trace_func usage is very
interesting but as I could have potentially several such evaluations going
on in parallel, I would go with the solution above.
Comments criticisms welcome.
~ nasir
On 3/31/07, Rick DeNatale <rick.denatale@gmail.com> wrote:
On 3/30/07, Kristoffer Lundén <kristoffer.lunden@gmail.com> wrote:
> On 3/31/07, Nasir Khan <rubylearner@gmail.com> wrote:
> > Anyway, there could be several uses for such a thing. Simplest use
case
> > would be a RAILS like (but not RAILS) situation where a controller is
> > deployed on a running server which (controller) optionally comes as a
string
> > enveloped in a protocol message, this string is evaled and the
controller is
> > instatiated but the system also needs to maintain meta information
including
> > the name of the controller just deployed, which defaults to the fully
> > qualified name of the class.
> > My use case is similar.
> >
>
> Here's one way to capture that info as it's evaled. Not thread-safe
> as-is. Not even sure if it's useful at all, but I was a bit curious.
> There's more ways to do this, of course, especially depending on when
> you want to capture this info.
>
> str = <<EOF
> module A
> class B
> def b
> puts "hello"
> end
> end
> end
> EOF
>
> set_trace_func proc {|event, file, line, id, binding, classname|
> return unless event == 'class'
> name = eval('self.class == Class && Module.nesting[0]', binding)
> if name
> # Do something with name
> end
> }
>
> eval(str)
>
> set_trace_func(nil)
I like his idea of evaluating the string in the context of a 'sandbox'
module, here's a refinement which does that. It strips off the
throwaway module since I assume that he's doing this to pre-flight
check actually re-running the code again after scanning it.
I also check to see if the trace function is running on the
originating thread, there are still thread safety issues since another
thread might also be using set_trace_func. Another way to approach
this would be to set and reset Thread.critical, but it doesn't solve
the second problem since there doesn't seem to be a way to stack trace
functions.
rick@bill:/public/rubyscripts$ cat find_classes.rb
def classes_defined_in(str)
begin
this_thread = Thread.current
result = []
set_trace_func(lambda do |event, file, line, id, binding, classname|
return unless Thread.current == this_thread
return unless event == 'class'
name = eval('self.class == Class && Module.nesting[0]',binding)
result << name.to_s.split(/::/,2)[1] if name
end)
ensure
Module.new.module_eval(str)
set_trace_func(nil)
end
result
end
str = <<EOF
module M1
class C1
def c
puts "hello"
end
end
module M2
class C2
end
end
end
module M3
class C3
end
end
EOF
p classes_defined_in(str)
rick@bill:/public/rubyscripts$ ruby find_classes.rb
["M1::C1", "M1::M2::C2", "M3::C3"]
--
Rick DeNatale
My blog on Ruby
http://talklikeaduck.denhaven2.com/
This is still not thread safe since if you are doing this on two
different threads they are sharing the EmptyModule.
Here's my variation using your approach but with an anonymous module
to do the module_eval:
rick@frodo:/public/rubyscripts$ cat find_classes2.rb
class String
def all_class_names
mod = Module.new
mod.module_eval(self)
mod.all_class_names
end
end
class Module
def all_class_names
class_names = []
constants.each do |const_name|
const = const_get(const_name)
case const
when Class
class_names << const.to_s.split(/::/,2)[1]
when Module
class_names += const.all_class_names
end
end
class_names.uniq
end
end
cstr = <<EOF
module B
class A
def a
puts "hello"
end
end
end
module B
class C
end
class D
end
NonClassConst = :non_class_const
end
EOF
puts cstr.all_class_names
rick@frodo:/public/rubyscripts$ ruby find_classes2.rb
B::C
B::A
B:
···
On 3/31/07, Nasir Khan <rubylearner@gmail.com> wrote:
Here is an abridged (out of context) version of my original solution. (which
I am using now)
-----
nasir@sparkle:misc>cat find_class_name.rb
module EmptyModule
def EmptyModule.clear_all
constants.each {|x| remove_const(x.to_sym)}
end
end
@my_classes = []
def class_from_string(str)
EmptyModule.clear_all # clean the slate
EmptyModule.module_eval(str)
end
cstr = <<EOF
module B
class A
def a
puts "hello"
end
end
end
module B
class C
end
class D
end
end
EOF
# This string will usually come from some external invocation of this
function
class_from_string(cstr)
def find_class mod
mod.constants.each do |str|
m = mod.const_get(str)
if m.class == Module
find_class(m)
else
cname = m.to_s
cname = cname.match(/EmptyModule::/).post_match
@my_classes << cname
end
end
end
find_class( EmptyModule )
puts @my_classes.uniq
---------------
And the result -
nasir@sparkle:misc>ruby find_class_name.rb
B:
B::C
B::A
----------------
I guess I will stick with this solution. set_trace_func usage is very
interesting but as I could have potentially several such evaluations going
on in parallel, I would go with the solution above.
Comments criticisms welcome.
--
Rick DeNatale
My blog on Ruby
http://talklikeaduck.denhaven2.com/