2010-08-04 20 views

Répondre

17

Le code affiché fonctionne très bien pour vérifier si la méthode est définie ou non. Module#method_defined? est exactement le bon choix. (Il ya aussi les variantes Module#public_method_defined?, Module#protected_method_defined? et Module#private_method_defined?.) Le problème est avec votre appel à def_method, qui n'existe pas. (Il s'appelle Module#define_method).

Cela fonctionne comme un charme:

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

Cependant, puisque vous connaissez déjà le nom à l'avance et ne pas utiliser toute fermeture, il n'y a pas besoin d'utiliser Module#define_method, vous pouvez simplement utiliser le mot-clé def à la place:

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

Ou ai-je mal compris votre question et vous êtes inquiet pour l'héritage? Dans ce cas, Module#method_defined? n'est pas le bon choix, car il parcourt toute la chaîne d'héritage. Dans ce cas, vous devrez utiliser Module#instance_methods ou l'un de ses cousins ​​Module#public_instance_methods, Module#protected_instance_methods ou Module#private_instance_methods, qui prennent un argument optionnel leur disant d'inclure ou non des méthodes de superclasses/mixins. (Notez que la documentation est erronée: si vous passez aucun argument, il sera inclure toutes les méthodes héritées.)

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

Voici une petite suite de test qui montre que ma suggestion fonctionne:

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

Le test passe en 1.9.2 mais 'test_that_the_method_cannot_be_redefined' et' test_that_the_method_does_exist_after_it_has_been_defined' échouent sous ruby ​​1.8.7. – mrm

1

La classe Object a la méthode des "méthodes": 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

Regardez le Ruby Object class. Il a une fonction methods pour obtenir une liste de méthodes et un respond_to? pour vérifier une méthode spécifique. Donc, vous voulez code comme ceci:

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, pour 4 raisons: 1) il n'aborde pas le problème des PO. Utiliser 'method_defined?' Est très bien, le problème est qu'il a mal orthographié 'define_method'. 2) 'respond_to?' Ne vérifie pas une méthode spécifique, il vérifie si un objet répond à un message spécifique. (Indice: le nom sorta le donne, n'est-ce pas?) Comprendre la différence entre les méthodes et les messages est * fondamental * pour comprendre Ruby et même OO en général. 3) Dans votre code, vous vérifiez si l'objet de classe 'C1' répond' ': hello', et sur cette base vous définissez une méthode' hello' pour * instances * de 'C1'. ... –

+0

... Encore: comprendre la différence entre * instances * et * classes * est fondamental pour comprendre Ruby et OO en classe en général. 4) Votre suite de tests ne teste pas réellement la chose sur laquelle le PO se soucie, à savoir que vous ne pouvez pas définir la méthode deux fois. Vous testez seulement que vous pouvez définir la méthode une fois, mais ce n'était pas la question. –

+0

@jorg, il met le 'respond_to?' Dans une méthode d'instance ('add_hello'), donc il vérifie l'instance (et non la classe). Aussi, juste par curiosité, quelle est la différence entre envoyer un message et invoquer une méthode dans Ruby? :) – horseyguy