En Ruby, ¿cómo verifico si el método "foo = ()" está definido?

81

En Ruby, puedo definir un método foo = (bar):

irb(main):001:0> def foo=(bar)
irb(main):002:1>   p "foo=#{bar}"
irb(main):003:1> end
=> nil

Ahora me gustaría comprobar si se ha definido

irb(main):004:0> defined?(foo=)
SyntaxError: compile error
(irb):4: syntax error, unexpected ')'
 from (irb):4
 from :0

¿Cuál es la sintaxis adecuada para usar aquí? Supongo que debe haber una forma de escapar "foo =" de modo que se analice y pase correctamente al definido. operador.

Alex Boisvert
fuente

Respuestas:

131

El problema es que el foo=método está diseñado para usarse en asignaciones. Puede usar defined?de la siguiente manera para ver qué está pasando:

defined? self.foo=()
#=> nil
defined? self.foo = "bar"
#=> nil

def foo=(bar)
end

defined? self.foo=()
#=> "assignment"
defined? self.foo = "bar"
#=> "assignment"

Compare eso con:

def foo
end

defined? foo
#=> "method"

Para probar si el foo=método está definido, debe usar respond_to?en su lugar:

respond_to? :foo=
#=> false

def foo=(bar)
end

respond_to? :foo=
#=> true
molf
fuente
¡Gracias! Esto resuelve mi problema. Todavía tengo curiosidad por saber si hay una forma de escapar de foo = tal que se pueda alimentar a definido. pero al menos ahora puedo seguir adelante.
Alex Boisvert
1
El problema aquí es que foo=siempre se usa en las asignaciones, por lo que Ruby regresará "assignment"si realiza la prueba defined? foo()(consulte la respuesta actualizada).
molf
41

Puede comprobar si existe un método utilizando el respond_to?método y pasarle un símbolo, por ejemplo, bar.respond_to?(:foo=)para ver si el objeto bartiene un método foo=. Si desea saber si las instancias de una clase responden a un método que puede usar method_defined?en la clase (o módulo), por ejemplo Foo.method_defined?(:bar=).

defined?no es un método, sino un operador que devuelve una descripción del operando (o nulo si no está definido, razón por la cual se puede usar en una declaración if). El operando puede ser cualquier expresión, es decir, una constante, una variable, una asignación, un método, una llamada a un método, etc. La razón por la que no funciona cuando lo haces defined?(foo=)es por los paréntesis, omítelos y debería funcionar más o menos de lo esperado. Dicho esto, defined?es un operador bastante extraño y nadie lo usa para probar la existencia de métodos.

Theo
fuente
¿Responde_a ?, ¿no responde_a?
0x4a6f4672
3
Ah, la ortografía de la biblioteca central de Ruby ... respond_to?, start_with?, end_with?.
Theo
3
stackoverflow.com/questions/5280556/… tiene una buena explicación de la elección detrás de la denominación TL; DRyou.knock if you.respond_to?(:knock)
drewish