2010-08-04 5 views

Antwort

17

Der Code, den Sie gebucht haben, funktioniert gut für die Überprüfung, ob die Methode definiert ist oder nicht. Module#method_defined? ist genau die richtige Wahl. (Es gibt auch die Varianten Module#public_method_defined?, Module#protected_method_defined? und Module#private_method_defined?.) Das Problem ist mit Ihrem Anruf zu def_method, die nicht existiert. (Es heißt Module#define_method).

Dies wirkt wie ein Zauber:

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

Da Sie bereits den Namen im Voraus wissen, und verwenden Sie keine Schließung, gibt es keine Notwendigkeit Module#define_method zu verwenden, können Sie nur verwenden, um das def Schlüsselwort stattdessen:

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

Oder habe ich Ihre Frage missverstanden und Sie sind besorgt über Vererbung? In diesem Fall ist Module#method_defined? nicht die richtige Wahl, da es die gesamte Vererbungskette durchläuft. In diesem Fall müssen Sie Module#instance_methods oder einen seiner Cousins ​​Module#public_instance_methods, Module#protected_instance_methods oder Module#private_instance_methods verwenden, die ein optionales Argument verwenden, das ihnen sagt, ob Methoden aus Superklassen/Mixins eingeschlossen werden sollen oder nicht. (Beachten Sie, dass die Dokumentation falsch ist: Wenn Sie keine Argumente übergeben, es wird umfassen alle geerbten Methoden.)

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

Hier ist eine kleine Test-Suite, die zeigt, dass mein Vorschlag funktioniert:

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

Der Test besteht in 1.9.2, aber "test_that_the_method_cannot_be_redefined" und "test_that_the_method_does_exist_after_it_has_been_defined" schlägt unter Ruby 1.8.7 fehl. – mrm

1

Die Object-Klasse hat die Methode "Methoden": 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

Blick auf die Ruby Object class. Es hat eine methods Funktion, um eine Liste von Methoden und eine respond_to? zu erhalten, um nach einer bestimmten Methode zu suchen. So wollen Sie Code wie folgt:

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, aus 4 Gründen: 1) Es adressiert das OP-Problem nicht. Die Verwendung von 'method_defined?' Ist in Ordnung, das Problem ist, dass er 'define_method' falsch geschrieben hat. 2) 'respond_to?' Prüft nicht auf eine bestimmte Methode, es prüft, ob ein Objekt auf eine bestimmte Nachricht reagiert. (Tipp: Der Name sorta gibt es weg, meinst du nicht?) Das Verständnis des Unterschieds zwischen Methoden und Nachrichten ist grundlegend für das Verständnis von Ruby und sogar OO im Allgemeinen. 3) In Ihrem Code überprüfen Sie, ob das Klassenobjekt 'C1' auf': Hallo' reagiert und basierend darauf definieren Sie eine 'Hallo'-Methode für * Instanzen * von' C1'. ... –

+0

... Nochmals: Das Verständnis des Unterschieds zwischen * Instanzen * und * Klassen * ist grundlegend für das Verständnis von Ruby und klassenbasierten OO im Allgemeinen. 4) Ihre Testsuite prüft nicht wirklich, was das OP kümmert, nämlich dass Sie die Methode nicht zweimal definieren können. Sie testen nur, dass Sie die Methode einmal definieren können, aber das war nicht die Frage. –

+0

@jorg, er legte das 'read_to?' In eine Instanzmethode ('add_hello'), so dass es die Instanz (und nicht die Klasse) überprüft. Aus Neugier, was ist der Unterschied zwischen dem Senden einer Nachricht und dem Aufrufen einer Methode in Ruby? :) – horseyguy

Verwandte Themen