13

Getting a list of all modules is easy in Ruby:

ObjectSpace.each_object(Module).to_a

However, is it possible to get a list of all eigenclasses (also known as singleton classes or metaclasses)? Or are eigenclasses invisible?

I tried

str = "foo"
my_metaclass = class << str; self; end
my_metaclass.class == Class # my_metaclass' class is Class
ObjectSpace.each_object(Class).include?(my_metaclass) # false
ObjectSpace.each_object.include?(my_metaclass) # still false
# Just to show each_object works
ObjectSpace.each_object(Class).include?(String) # true

I'm trying to get eigenclasses because I'm wanting to list all the methods that are defined within a script. I could look for all the instance methods defined by modules and classes, and then look for singleton methods of modules and classes (or of all objects, if I want to chew up CPU), but that seems a little hackish.

Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
  • Did you end up finding an answer to this? nice question ;) – 2potatocakes Oct 19 '11 at 22:45
  • I couldn't find a way to do this without calling each_object on each class. One thing I did notice though was that ObjectSpace.count_objects() :T_CLASS count increments with each eigenclass created. So it may be possible to do this in C land? – Dan Healy Oct 21 '11 at 02:50
  • @DanHealy: What do you mean by `each_object` on each class? As in `classes = ObjectSpace.each_object(Class).to_a; objects = classes.map{|klass| ObjectSpace.each_object(klass).to_a}.flatten`? – Andrew Grimm Oct 21 '11 at 02:55
  • I was basically referring to the method you suggested in your question: iterating over all objects. – Dan Healy Oct 21 '11 at 03:39
  • woohoo! I love this question. – Igbanam Oct 25 '11 at 07:38
  • You must be doing something seriously meta. =P – thomasfedb Nov 10 '11 at 04:54
  • @thomasfedb: I'm working on something where, if there's a method_missing that doesn't get handled, it says something like ":foo isn't implemented in YourFirstClass, but it's implemented in YourOtherClass". I need to put it on github soon. – Andrew Grimm Nov 10 '11 at 05:16

3 Answers3

2

If you mean objects that have singleton methods, this should work.

eigens = []
ObjectSpace.each_object do |object|
  eigens << object unless object.singleton_methods.empty?
end

If not, could you clarify? I used this discussion as a reference:

http://www.ruby-forum.com/topic/77046

sheldonh
  • 2,684
  • 24
  • 31
  • The main disadvantage of this approach is that iterating through each object seems inefficient. – Andrew Grimm Nov 06 '11 at 22:46
  • It is. :-) Is this something you need to do frequently in your program? I'm keen to see what you're up to, if you're able to share. – sheldonh Nov 07 '11 at 20:16
1

I doubt this is what you want, but it should return all eigenclasses:

eigens = ObjectSpace.each_object.collect { |obj| class << obj; self; end }

That will indeed assign an array of all the eigenclasses to the variable eigens. The thing is, Ruby implementations likely don't actually create an eigenclass unless there is a need for it, and this code (I believe) will actually create the eigen classes even for the objects where one wasn't needed.

If finding a better way is important, I'd tweet the question to one of the implementors of any of the Ruby implementations (@yukihiro_matz, @evanphx, @headius to name a few that come to mind). If anybody would know, they would.

Ryan
  • 2,073
  • 1
  • 19
  • 33
1

As of MRI 1.9, eigenclass enumeration does NOT seem to be supported. As a (semi-)consequence, there is no 100%-reliable way to iterate over all methods. The best approximation for an overall method enumerator is as follows

methods = []

ObjectSpace.each_object { |x|
  if x.kind_of?(Module)
    methods += x.public_instance_methods(false) +
               x.protected_instance_methods(false) +
               x.private_instance_methods(false)
  end
  methods +=   x.singleton_methods(false)
}

However, this code will NOT enumerate

  • private methods owned by 1st eigenclasses,
  • methods owned by 2nd, 3rd, ... eigenclasses.
paon
  • 378
  • 3
  • 12