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
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