Forum: Ruby nifty one-liner to search ObjectSpace?

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.
Ce8b03e5750097942c58e12b46724312?d=identicon&s=25 Giles Bowkett (Guest)
on 2007-03-06 01:22
(Received via mailing list)
Say I've got a class and I know it has subclasses. I'm absolutely sure
there must be an irb ObjectSpace one-liner to give me all the
subclasses of an arbitrary class.

The best I have right now is based on a solution to something else, a
Scriptaculous inline autocompleter for Rails apps that gives you Ajaxy
autocomplete search of all class names within your Rails app.

http://gilesbowkett.com/blog_code_samples/123006_s...
http://gilesbowkett.blogspot.com/search?q=cleverness

I've been able to find the Class instance method #inherited(), which
takes another class and tells you yay or nay, but a simple one-liner
to return a class's subclasses has so far proved out of my reach.

I know it exists. I just don't know what it is. I'm also kind of
stymied because it appears ObjectSpace has #each_object but not #each.
That seems so weird to me.
90a73d9875462aaa9fab2feffafbffe7?d=identicon&s=25 Ben Bleything (Guest)
on 2007-03-06 01:29
(Received via mailing list)
On Tue, Mar 06, 2007, Giles Bowkett wrote:
> Say I've got a class and I know it has subclasses. I'm absolutely sure
> there must be an irb ObjectSpace one-liner to give me all the
> subclasses of an arbitrary class.

Yup :)

> I've been able to find the Class instance method #inherited(), which
> takes another class and tells you yay or nay, but a simple one-liner
> to return a class's subclasses has so far proved out of my reach.

ObjectSpace.each_object( Class ) {|klass| puts klass if klass < IO }

...will give you any class that is a subclass of IO.  Could pretty
easily be wrapped into a method you could call on a class.  In fact:

class Class
  def subclasses
    out = []
    ObjectSpace.each_object( Class ) {|k| out << k if k < self }
    return out
  end
end

Stick that in your .irbrc and call IO.subclasses.  YEAH.

> I know it exists. I just don't know what it is. I'm also kind of
> stymied because it appears ObjectSpace has #each_object but not #each.
> That seems so weird to me.

What would #each do that #each_object doesn't?

Ben
42172acdf3c6046f84d644cb0b94642c?d=identicon&s=25 Pat Maddox (pergesu)
on 2007-03-06 01:44
(Received via mailing list)
On 3/5/07, Giles Bowkett <gilesb@gmail.com> wrote:
>
> http://www.gilesgoatboy.org
> http://gilesbowkett.blogspot.com
> http://giles.tumblr.com/
>
>

How about
ObjectSpace.each_object {|o| puts o.name if o.is_a?(Class) && o <
BaseClass}

irb(main):005:0> ObjectSpace.each_object {|o| puts o.name if
o.is_a?(Class) && o < Numeric}
Bignum
Float
Fixnum
Integer

Pat
90a73d9875462aaa9fab2feffafbffe7?d=identicon&s=25 Ben Bleything (Guest)
on 2007-03-06 01:55
(Received via mailing list)
On Tue, Mar 06, 2007, Pat Maddox wrote:
> How about
> ObjectSpace.each_object {|o| puts o.name if o.is_a?(Class) && o < BaseClass}

ObjectSpace.each_object takes an optional argument that restricts it to
a given class/module, so you can take out the #is_a? check by saying
ObjectSpace.each_object( Class ) {}

Ben
Ce8b03e5750097942c58e12b46724312?d=identicon&s=25 Giles Bowkett (Guest)
on 2007-03-06 02:35
(Received via mailing list)
On 3/5/07, Ben Bleything <ben@bleything.net> wrote:
>
>   end
> Ben
>
>



#each enables #collect, doesn't it? It's the core iterator for all
those other iterator methods.

I wanted to throw the horns all metal-style when I first saw this, but
it doesn't work in irb:

>> class Class
>>    def subclasses
>>        out = []
>>        ObjectSpace.each_object( Class ) {|k| out << k if k < self }
>>        return out
>>      end
>>   end
=> nil
>> Event.subclasses
NoMethodError: protected method `subclasses' called for Event:Class
        from
/opt/local/lib/ruby/gems/1.8/gems/activerecord-1.15.2/lib/active_record/base.rb:1236:in
`method_missing'
        from (irb):9
>> Event.new.subclasses
NoMethodError: undefined method `subclasses' for #<Event:0x3556b50>
        from
/opt/local/lib/ruby/gems/1.8/gems/activerecord-1.15.2/lib/active_record/base.rb:1861:in
`method_missing'
        from (irb):10
>>

I thought maybe I needed to use Class.class_eval instead:

Class.class_eval do
   def subclasses
       out = []
       ObjectSpace.each_object( Class ) {|k| out << k if k < self }
       return out
     end
  end

but I got identical error output when I tried that. In fact I think I
just used a synonym for "class Class" by using "Class.class_eval" and
the whole thing was very cargo cult of me.

Anyway, that < operator for inheritance is insanely cool, but I'm
missing something getting this to work.

Couldn't get Pat's solution to work either!

>> ObjectSpace.each_object {|o| puts o.name if o.is_a?(Class) && o < Event}
=> 55128

Maybe just molasses in the brainpan today.
Ce8b03e5750097942c58e12b46724312?d=identicon&s=25 Giles Bowkett (Guest)
on 2007-03-06 02:42
(Received via mailing list)
>> muppet = []
=> []
>> ObjectSpace.each_object {|o| muppet.push(o.name) if o.is_a?(Class)
&& o < Event}
=> 57244
>> muppet
=> []
90a73d9875462aaa9fab2feffafbffe7?d=identicon&s=25 Ben Bleything (Guest)
on 2007-03-06 03:20
(Received via mailing list)
On Tue, Mar 06, 2007, Giles Bowkett wrote:
> #each enables #collect, doesn't it? It's the core iterator for all
> those other iterator methods.

Good call, hadn't considered that.

> I wanted to throw the horns all metal-style when I first saw this, but
> it doesn't work in irb:

WORKSFORME:

>> class Class
>>   def subclasses
>>     out = []
>>     ObjectSpace.each_object( Class ) {|k| out << k if k < self }
>>     return out
>>   end
>> end
=> nil
>> IO.subclasses
=> [File, Socket, UNIXServer, UNIXSocket, UDPSocket, TCPServer,
TCPSocket, IPSocket, BasicSocket]

I just tried this on a Rails model, and it bombed.  I think that's
what's happening to you.  You can do Event.send :subclasses, but that's
sort of ghetto.

> but I got identical error output when I tried that. In fact I think I
> just used a synonym for "class Class" by using "Class.class_eval" and
> the whole thing was very cargo cult of me.

hehehe

> Couldn't get Pat's solution to work either!
>
> >>ObjectSpace.each_object {|o| puts o.name if o.is_a?(Class) && o < Event}
> => 55128

This doesn't make sense to me, but without being in your environment
it's useless to try to help :)

Ben
Ce8b03e5750097942c58e12b46724312?d=identicon&s=25 Giles Bowkett (Guest)
on 2007-03-06 03:23
(Received via mailing list)
You know what, I'm sorry, I figured out why this didn't work.
ObjectSpace only contains the classes loaded, not the classes which
could be loaded. The subclasses of the class I was examining hadn't
been loaded yet.

>> ObjectSpace.each_object(Class){|k| out << k if k < ActiveRecord::Base}
=> 1355
>> out
=> [CGI::Session::ActiveRecordStore::Session, Event, Tag, Tagging]

The Fixnum returned by #each_object() still baffles me, but I think
for this to be useful I'd have to autoload all the classes defined in
the app; I was doing this in Rails console and apparently the console
lazily loads only the things it needs at any given time.
Ce8b03e5750097942c58e12b46724312?d=identicon&s=25 Giles Bowkett (Guest)
on 2007-03-06 03:26
(Received via mailing list)
The Rails problem is due to some Rails internals thing, I think. I
just got it to work using #subklasses instead of #subclasses for the
method name. Doesn't fix the lazy loading, though.
90a73d9875462aaa9fab2feffafbffe7?d=identicon&s=25 Ben Bleything (Guest)
on 2007-03-06 03:30
(Received via mailing list)
On Tue, Mar 06, 2007, Giles Bowkett wrote:
> The Fixnum returned by #each_object() still baffles me, but I think
> for this to be useful I'd have to autoload all the classes defined in
> the app; I was doing this in Rails console and apparently the console
> lazily loads only the things it needs at any given time.

I'm pretty sure it's the total number of objects inspected by the call
to each_object.  So that'd mean it looked at 1355 classes to find the
tasty nuggets it was after.

Ben
Ce8b03e5750097942c58e12b46724312?d=identicon&s=25 Giles Bowkett (Guest)
on 2007-03-06 03:32
(Received via mailing list)
Sorry, that was my assumption too, I'm just puzzled that it's doing
that, rather than returning an array or something.
93d566cc26b230c553c197c4cd8ac6e4?d=identicon&s=25 Pit Capitain (Guest)
on 2007-03-06 09:07
(Received via mailing list)
Giles Bowkett schrieb:
> Say I've got a class and I know it has subclasses. I'm absolutely sure
> there must be an irb ObjectSpace one-liner to give me all the
> subclasses of an arbitrary class.
> (...)
> I know it exists. I just don't know what it is. I'm also kind of
> stymied because it appears ObjectSpace has #each_object but not #each.
> That seems so weird to me.

Giles, here's something for both of your wishes:

   require "enumerator"

   s = Exception  # substitute with your superclass
   puts ObjectSpace.to_enum(:each_object, class<<s; self; end).to_a

Note that you can replace "to_a" with any method of Enumerable.

Regards,
Pit
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2007-03-06 09:26
(Received via mailing list)
On 06.03.2007 01:21, Giles Bowkett wrote:
>
> I've been able to find the Class instance method #inherited(), which
> takes another class and tells you yay or nay, but a simple one-liner
> to return a class's subclasses has so far proved out of my reach.
>
> I know it exists. I just don't know what it is. I'm also kind of
> stymied because it appears ObjectSpace has #each_object but not #each.
> That seems so weird to me.

Additional note: if you need the subclass info often you may want to
look into #inherited.  With that method you can record all subclasses of
a class when they are created and do not need to revisit all classes.

Kind regards

  robert
This topic is locked and can not be replied to.