Код, который вы разместили, прекрасно работает для проверки того, определен метод или нет. Module#method_defined?
- правильный выбор. (Есть также варианты Module#public_method_defined?
, Module#protected_method_defined?
и Module#private_method_defined?
.) Проблема с вашим звонком на def_method
, который не ' не существует. (Это называется Module#define_method
).
Это работает как шарм:
class C1
define_method(:hello) do
puts 'Hi Everyone'
end unless method_defined? :hello
end
Однако, так как вы уже знаете имя заранее и не используете закрытие, нет необходимости использовать Module#define_method
, вы можете просто использовать ключевое слово def
вместо:
class C1
def hello
puts 'Hi Everyone'
end unless method_defined? :hello
end
Или я неправильно понял ваш вопрос, и вы беспокоитесь о наследовании? В этом случае Module#method_defined?
- неправильный выбор, потому что он охватывает всю цепочку наследования. В этом случае вам придется использовать Module#instance_methods
или одного из его двоюродных братьев Module#public_instance_methods
, Module#protected_instance_methods
или Module#private_instance_methods
, который принимает необязательный аргумент, сообщающий им, включать ли методы из суперклассов / миксинов или нет. (Обратите внимание, что документация неверна: если вы не передадите аргументы, будет включать все унаследованные методы.)
class C1
unless instance_methods(false).include? :hello
def hello
puts 'Hi Everyone'
end
end
end
Вот небольшой набор тестов, который показывает, что мое предложение работает:
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 !@c1.method_defined?(:hello)
assert !@c1.instance_methods.include?(:hello)
assert !@o.methods.include?(:hello)
assert !@o.respond_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