Dada una clase, ver si la instancia tiene método (Ruby)


Sé en Ruby que puedo usar respond_to? para comprobar si un objeto tiene cierto método.

Pero, dada la clase, ¿cómo puedo comprobar si la instancia tiene un cierto método?

Es decir, algo como

Foo.new.respond_to?(:bar)

Pero siento que tiene que haber una mejor manera que instanciar una nueva instancia.

Author: Arslan Ali, 2010-07-22

10 answers

No se porque todo el mundo esta sugiriendo que deberías usar instance_methods y include? cuando method_defined? hace el trabajo.

class Test
  def hello; end
end

Test.method_defined? :hello #=> true
 319
Author: horseyguy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-09 14:56:46

Puedes usar method_defined? de la siguiente manera:

String.method_defined? :upcase # => true

Mucho más fácil, portátil y eficiente que el instance_methods.include? todo el mundo parece estar sugiriendo.

Tenga en cuenta que no sabrá si una clase responde dinámicamente a algunas llamadas con method_missing, por ejemplo redefiniendo respond_to?, o desde Ruby 1.9.2 definiendo respond_to_missing?.

 43
Author: Marc-André Lafortune,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-21 23:27:22

En realidad esto no funciona tanto para Objetos como para Clases.

Esto hace:

class TestClass
  def methodName
  end
end

Así que con la respuesta dada, esto funciona:

TestClass.method_defined? :methodName # => TRUE

Pero esto NO funciona:

t = TestClass.new
t.method_defined? : methodName  # => ERROR!

Así que uso esto tanto para clases como para objetos:

Clases:

TestClass.methods.include? 'methodName'  # => TRUE

Objetos:

t = TestClass.new
t.methods.include? 'methodName'  # => TRUE
 21
Author: Alex V,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-10 20:34:38

La respuesta a " Dada una clase, ver si la instancia tiene método (Ruby)" es mejor. Aparentemente Ruby tiene esto incorporado, y de alguna manera me lo perdí. Mi respuesta se deja como referencia, a pesar de todo.

Ruby clases responder a los métodos instance_methods y public_instance_methods. En Ruby 1.8, la primera lista todos los nombres de métodos de instancia en una matriz de cadenas, y la segunda lo restringe a métodos públicos. El segundo comportamiento es lo que probablemente querrías, ya que respond_to? se restringe a sí mismo a los métodos públicos por defecto, también.

Foo.public_instance_methods.include?('bar')

En Ruby 1.9, sin embargo, esos métodos devuelven matrices de símbolos.

Foo.public_instance_methods.include?(:bar)

Si está planeando hacer esto a menudo, es posible que desee extender Module para incluir un método abreviado. (Puede parecer extraño asignar esto a Module en lugar de Class, pero ya que es donde viven los métodos instance_methods, es mejor mantenerse en línea con ese patrón.)

class Module
  def instance_respond_to?(method_name)
    public_instance_methods.include?(method_name)
  end
end

Si desea soportar Ruby 1.8 y Ruby 1.9, ese sería un lugar conveniente para agregar la lógica para buscar tanto cadenas como símbolos.

 7
Author: Matchu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:50

No estoy seguro de si esta es la mejor manera, pero siempre puedes hacer esto:

Foo.instance_methods.include? 'bar'
 2
Author: dbyrne,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-21 20:10:23

Creo que hay algo malo con method_defined? en Rails. Puede ser inconsistente o algo así, por lo que si usa Rails, es mejor usar algo de attribute_method?(attribute).

"testing for method_defined? en ActiveRecord las clases no funcionan hasta que una instanciación " es una pregunta sobre la inconsistencia.

 2
Author: NoDisplayName,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:41

Si está comprobando si un objeto puede responder a una serie de métodos, podría hacer algo como:

methods = [:valid?, :chase, :test]

def has_methods?(something, methods)
  methods & something.methods == methods
end

El methods & something.methods unirá los dos arrays en sus elementos comunes/coincidentes. algo.methods incluye todos los métodos que está comprobando, igualará los métodos. Por ejemplo:

[1,2] & [1,2,3,4,5]
==> [1,2]

So

[1,2] & [1,2,3,4,5] == [1,2]
==> true

En esta situación, querrás usar símbolos, porque cuando llamas .métodos, devuelve una matriz de símbolos y si se utiliza ["my", "methods"], devolvería false.

 2
Author: TalkativeTree,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-06 21:41:28

Intenta Foo.instance_methods.include? :bar

 2
Author: imightbeinatree at Cloudspace,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-06 21:50:40

klass.instance_methods.include :method_name o "method_name", dependiendo de la versión de Ruby creo.

 1
Author: yxhuvud,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-09 00:49:37
class Foo
 def self.fclass_method
 end
 def finstance_method
 end
end

foo_obj = Foo.new
foo_obj.class.methods(false)
=> [:fclass_method] 

foo_obj.class.instance_methods(false)
=> [:fclass_method] 

¡Espero que esto te ayude!

 1
Author: Rajeev Kannav Sharma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-09 00:50:40