2010-08-04 8 views

Respuesta

17

El código que ha publicado funciona bien para comprobar si el método está definido o no. Module#method_defined? es exactamente la elección correcta. (También están las variantes Module#public_method_defined?, Module#protected_method_defined? y Module#private_method_defined?). El problema es con su llamada al def_method, que no existe. (Se llama Module#define_method).

Esto funciona como un encanto:

class C1  
    define_method(:hello) do 
    puts 'Hi Everyone' 
    end unless method_defined? :hello 
end 

Sin embargo, puesto que ya conoce el nombre de antemano y no se usa ninguna de cierre, no hay necesidad de usar Module#define_method, sólo puede utilizar la palabra clave def en su lugar:

class C1 
    def hello 
    puts 'Hi Everyone' 
    end unless method_defined? :hello 
end 

¿O he entendido mal su pregunta y le preocupa la herencia? En ese caso, Module#method_defined? no es la elección correcta, ya que recorre toda la cadena de herencia. En ese caso, tendrá que usar Module#instance_methods o uno de sus primos Module#public_instance_methods, Module#protected_instance_methods o Module#private_instance_methods, que toman un argumento opcional diciéndoles si deben incluir métodos de superclases/mixins o no. (Tenga en cuenta que la documentación está mal: si no pasa argumentos, que se incluir todos los métodos heredados.)

class C1 
    unless instance_methods(false).include? :hello 
    def hello 
     puts 'Hi Everyone' 
    end 
    end 
end 

aquí hay un pequeño conjunto de pruebas que demuestra que mi sugerencia funciona:

require 'test/unit' 
class TestDefineMethodConditionally < Test::Unit::TestCase 
    def setup 
    @c1 = Class.new do 
     def self.add_hello(who) 
     define_method(:hello) do 
      who 
     end unless method_defined? :hello 
     end 
    end 

    @o = @c1.new 
    end 

    def test_that_the_method_doesnt_exist_when_it_hasnt_been_defined_yet 
    assert [email protected]_defined?(:hello) 
    assert [email protected]_methods.include?(:hello) 
    assert [email protected]?(:hello) 
    assert [email protected]_to?(:hello) 
    assert_raise(NoMethodError) { @o.hello } 
    end 

    def test_that_the_method_does_exist_after_it_has_been_defined 
    @c1.add_hello 'one' 

    assert @c1.method_defined?(:hello) 
    assert @c1.instance_methods.include?(:hello) 
    assert @o.methods.include?(:hello) 
    assert_respond_to @o, :hello 
    assert_nothing_raised { @o.hello } 
    assert_equal 'one', @o.hello 
    end 

    def test_that_the_method_cannot_be_redefined 
    @c1.add_hello 'one' 

    assert @c1.method_defined?(:hello) 
    assert @c1.instance_methods.include?(:hello) 
    assert @o.methods.include?(:hello) 
    assert_respond_to @o, :hello 
    assert_nothing_raised { @o.hello } 
    assert_equal 'one', @o.hello 

    @c1.add_hello 'two' 

    assert @c1.method_defined?(:hello) 
    assert @c1.instance_methods.include?(:hello) 
    assert @o.methods.include?(:hello) 
    assert_respond_to @o, :hello 
    assert_nothing_raised { @o.hello } 
    assert_equal 'one', @o.hello, 'it should *still* respond with "one"!' 
    end 
end 
+0

La prueba pasa en 1.9.2 pero 'test_that_the_method_cannot_be_redefined' y' test_that_the_method_does_exist_after_it_has_been_defined' fallan bajo ruby ​​1.8.7. – mrm

1

La clase de objetos tiene el método de "métodos": docs

class Klass 
    def kMethod() 
    end 
end 
k = Klass.new 
k.methods[0..9] #=> ["kMethod", "freeze", "nil?", "is_a?", 
        # "class", "instance_variable_set", 
        # "methods", "extend", "__send__", "instance_eval"] 
k.methods.length #=> 42 
2

Mira el Ruby Object class. Tiene una función methods para obtener una lista de métodos y un respond_to? para verificar un método específico. Entonces quiere un código como este:

class C1 
    def add_hello 
    unless self.respond_to? "hello" 
     def hello 
     puts 'Hi Everyone' 
     end 
    end 
    end 
end 

cone.hello  #This would fail 
cone.add_hello 
cone.hello  #This would work 
+0

-1, por 4 razones: 1) no aborda el problema de OPs. Usar 'method_defined?' Está bien, el problema es que escribió mal 'define_method'. 2) 'responder_a?' No verifica un método específico, sino que verifica si un objeto responde a un mensaje específico. (Sugerencia: el nombre sorta lo delata, ¿no cree?) Comprender la diferencia entre los métodos y los mensajes es * fundamental * para comprender Ruby e incluso OO en general. 3) En su código, compruebe si el objeto de clase 'C1' responde a': hello', y en función de eso define un método 'hello' para * instances * de' C1'. ... –

+0

... De nuevo: entender la diferencia entre * instancias * y * clases * es fundamental para entender Ruby y OO basado en clases en general. 4) Su banco de pruebas en realidad no prueba lo que le importa al OP, es decir, que no puede definir el método dos veces. Solo prueba que puede definir el método una vez, pero esa no era la pregunta. –

+0

@jorg, puso el 'responder_to?' En un método de instancia ('add_hello'), por lo que comprueba la instancia (y no la clase). Además, solo por curiosidad, ¿cuál es la diferencia entre enviar un mensaje e invocar un método en Ruby? :) – horseyguy

Cuestiones relacionadas