Como achar o método específico de um instância na Eigenclass?

Para algum Ruby gurú:

class Object
    def eigenclass
        class << self; self; end
    end
end

def find_method(klass, name)
    m = klass.methods.grep(/^#{name}/)
    if m.empty?
        puts "Could not find #{name}"
    else
        puts "Found #{name}: " + m.join(", ")
    end
end

class Pela; end

p = Pela.new

# adicionando um método específico apenas para essa instância

def p.bye
    puts "BYE!"
end

p.bye

find_method(p.eigenclass, "bye")

puts

# adicionando um método para a classe Pela (método estático)

def Pela.hi
    puts "HI!"
end

Pela.hi

find_method(p.eigenclass, "hi")

Output:

c:\rubycode>ruby crazyeigen.rb
BYE!
Could not find bye

HI!
Found hi: hi

Pra onde foi o danado do método bye, ou seja, em que classe ele está definido?

Pensando aqui acho que o que eu quero é impossível em Ruby.

Apesar de internamente em Ruby classes armazenarem métodos, uma classe só vai te dar o nome do método se o método for um método de classe. Por isso que quando temos uma metaclasse, ela pode nos mostrar o nome dos métodos de classe.

Agora quando temos métodos específicos de uma instância, apesar deles estarem internamente armazenados na classe (eigenclass ou singleton class), a classe não tem como lhe fornecer essa informação, apenas a instância através do método methods.

É interessante notar que cada instância tem sua própria eigenclass, que pode ser modificado on-the-fly para adicionar métodos específicos da instância.

A classe de dois Objects são iguais (Object) mas as suas eigenclasses são diferentes, conforme o código abaixo mostra.


class Object
    def eigenclass
        class << self; self; end
    end
end

def find_method(klass, name)
    m = klass.methods.grep(/^#{name}/)
    if m.empty?
        puts "Could not find #{name}"
    else
        puts "Found #{name}: " + m.join(", ")
    end
end

class Pela; end

p = Pela.new

# adding a instance method

def p.bye
    puts "BYE!"
end

p.bye

find_method(p.eigenclass, "bye")
find_method(p, "bye")

puts

# check if they are the same...

p1 = Pela.new
p2 = Pela.new

def p1.bye
    puts "BYE!!!"
end

puts p1.class, p2.class, p1.class == p2.class

puts p1.eigenclass, p2.eigenclass, p1.eigenclass == p2.eigenclass

find_method(p1, "bye")
find_method(p2, "bye")
puts
find_method(p1.eigenclass, "bye")
find_method(p2.eigenclass, "bye")

Output:


c:\rubycode>ruby crazyeigen.rb
BYE!
Could not find bye
Found bye: bye

Pela
Pela
true
#<Class:#><Pela:0x2b81670>>
#<Class:#><Pela:0x2b811c0>>
false
Found bye: bye
Could not find bye

Could not find bye
Could not find bye

Putz! Meu erro era que eu estava usando o método “methods” de uma classe, quando na verdade eu deveria ter usado o “public_instance_methods”.

Uma instancia qualquer possui o método “methods” mas apenas instâncias do tipo classe possui o método “public_instance_methods”.