Ruby for Newbies Fehlende Methoden

Ruby ist eine der beliebtesten Sprachen im Internet. Wir führen hier eine Session zu Nettuts + durch, die Sie in Ruby einführt, sowie die großartigen Frameworks und Tools, die mit der Ruby-Entwicklung einhergehen. In dieser Episode betrachten wir die zu coole Realität, mit der Ruby-Objekte mit Methoden umgehen, die nicht existieren.


Videoanleitung?


Ein Problem (und eine Lösung)

Nehmen wir an, Sie arbeiten mit einem Ruby-Objekt. Und sagen wir auch, dass Sie mit diesem Objekt nicht ganz vertraut sind. Und sagen wir auch, dass Sie eine Methode aufrufen, die auf dem Objekt nicht vorhanden ist.

o = Object.new o.some_method # NoMethodError: nicht definierte Methode 'some_method' für #

Dies ist weniger als wünschenswert, daher hat Ruby eine großartige Möglichkeit, uns davon zu retten. Überprüfen Sie dies heraus:

class OurClass def method_missing (method_name) setzt "es gibt keine Methode namens '# method_name'" end end o = OurClass.new o.some_method # => es gibt keine Methode namens 'some_method'

Wir können eine aufgerufene Methode erstellen method_missing in unserer Klasse. Wenn das Objekt, für das wir die Methode aufrufen, nicht über die Methode verfügt (und die Methode nicht von einer anderen Klasse oder einem anderen Modul erbt), gibt Ruby uns eine weitere Gelegenheit, etwas Nützliches zu tun: Wenn die Klasse über ein method_missing Methode geben wir die Informationen über die Methode cal an method_missing und lass es das Durcheinander sortieren.

Nun, das ist großartig. Wir erhalten keine Fehlermeldung mehr.


Eine bessere Verwendung

Aber halten Sie an und denken Sie eine Sekunde darüber nach. Zunächst einmal: Nein, wir bekommen keine Fehlermeldung mehr, aber wir bekommen nichts Nützliches. Es ist schwer zu sagen, was in diesem Fall nützlich wäre, da der Name unserer Methode nichts andeutet. Zweitens ist dies ziemlich leistungsfähig, da Sie grundsätzlich jede Methode an ein Objekt übergeben können und ein intelligentes Ergebnis erhalten.

Machen wir etwas, das mehr Sinn macht. fange damit an:

Klasse TutsSite attr_accessor: name,: Tutorials def initialize name = "", tuts = [] @name = name @tutorials = tuts end def get_tuts_about_javascript @ tutorials.select do | tut | tut [: tags] .include? "javascript" end end def get_tuts_by_jeffrey_way @ tutorials.select do | tut | tut [: author] == "Jeffrey Way" Ende Ende Ende

Hier sehen Sie eine kleine Klasse für eine Tutorial-Website. Wenn Sie ein neues Website-Objekt erstellen, übergeben wir ihm einen Namen und eine Reihe von Tutorials. Wir erwarten, dass die Tutorials Hashes in der folgenden Form sind:

title: "Some title", Autor: "der Autor", Tags: ["array", "von", "tags"] # Ruby 1.9 # OR : title => "Some title",: author => " der autor ",: tags => [" array "," von "," tags "] # Ruby 1.8

Wir erwarten Symbole als Schlüssel; Beachten Sie, dass Sie, wenn Sie nicht mit Ruby 1.9 arbeiten, das untere Format für Ihre Hashes verwenden müssen (beide funktionieren in 1.9).

Dann haben wir zwei Hilfsfunktionen, mit denen wir nur das Tutorial mit einem JavaScript-Tag oder nur die Tutorials von Jeffrey Way abrufen können. Diese sind nützlich zum Filtern der Tutorials? aber sie geben uns nicht zu viele Möglichkeiten. Natürlich könnten wir Methoden benennen get_tuts_with_tag und get_tuts_by_author die Parameter mit dem Tag- oder Autorennamen übernehmen. Wir gehen jedoch eine andere Route: method_missing.

Wie wir gesehen haben, method_missing Ruft den versuchten Methodennamen als Parameter ab. Was ich nicht erwähnt habe, ist, dass es ein Symbol ist. Die Parameter, die an die Methode übergeben werden, und der Block (falls vorhanden) sind ebenfalls verfügbar. Beachten Sie, dass die Parameter als individuelle Parameter an übergeben werden method_missing, Die übliche Konvention ist, den Splat-Operator zu verwenden, um sie alle in einem Array zu sammeln:

def method_missing name, * args & block end

Da wir also den Namen der versuchten Methode ermitteln können, können wir diesen Namen parsen und etwas Intelligentes damit tun. Wenn der Benutzer beispielsweise Folgendes anruft:

nettuts.get_tuts_by_jeffrey_way nettuts.get_tuts_about_html nettuts.get_tuts_about_canvas_by_rob_hawkes nettuts.get_tuts_by_jeremy_mcpeak_about_asp_net

Also, lasst uns dazu kommen; Verschrotten Sie diese früheren Methoden und ersetzen Sie diese durch Folgendes:

def method_missing name, * args & block tuts = @ tutorials.dup name = name.to_s.downcase if (md = / ^ get_tuts_ (von_ | über _) (\ w *?) ((_ durch_ | _über _) (\ w *) )? $ /. Übereinstimmungsname) wenn md [1] == 'by_' tuts.select! | tut | tut [: author] .downcase == md [2] .gsub ("_", "") tuts.select! | tut | tut [: tags] .include? md [5] .gsub ("_", "") wenn md [4] == '_about_' elsif md [1] == 'about_' tuts.select! | tut | tut [: tags] .include? md [2] .gsub ("_", "") tuts.select! | tut | tut [: author] .downcase == md [5] .gsub ("_", "") if md [4] == '_by_' end else tuts = "Dieses Objekt unterstützt das Objekt nicht '#  Name '"Ende endet Ende

Machen Sie sich keine Sorgen, wir gehen jetzt alles durch. Wir beginnen mit dem Duplizieren der @Tutorials Array; Jedes Ruby-Objekt hat eine dup Methode, die es kopiert; Wenn wir das nicht tun - und nur gesagt tuts = @Tutorial-wir würden mit dem ursprünglichen Array arbeiten, was wir nicht wollen; Wir möchten dieses Array so beibehalten, wie es ist. Dann filtern wir die Tutorial-Hashes heraus, die wir nicht möchten.

Wir müssen auch den Namen der Methode ermitteln; da ist es vorbei method_missing als symbol konvertieren wir es mit to_s und dann stellen Sie sicher, dass es in Kleinbuchstaben mit downcase.

Nun müssen wir überprüfen, ob die Methode dem gewünschten Format entspricht. Es ist ja möglich, dass jemand der Methode etwas anderes übergeben kann. Lassen Sie uns diesen Methodennamen analysieren. Wenn es passt, erarbeiten wir die Magie. Andernfalls wird eine Standardfehlermeldung zurückgegeben:

 if (md = /^get_tuts_(by_|about_)(\w*?)((_by_|_about_)(\w*)?$/.match name) #coming else tuts = "Dieses Objekt unterstützt das Methode '# Name' "Ende

Das sieht aus wie eine Enttäuschung, aber Sie sollten es verstehen: Im Grunde suchen wir? Get_tuts_? gefolgt von? by_? oder? about_ ?; dann haben wir den Namen eines Autors oder ein Tag, gefolgt von? _by_? oder? _about_? und einen Autor oder ein Tag. Wenn das stimmt, speichern wir die MatchData Objekt in md; sonst bekommen wir es Null zurück; In diesem Fall setzen wir tuts zur Fehlermeldung. Wir machen das so, dass wir zurückkehren können tuts.

Der reguläre Ausdruck stimmt also überein, wir erhalten eine MatchData Objekt. Wenn der verwendete Methodenname war get_tuts_by_andrew_burgess_about_html, Dies sind die Indizes, die Sie haben:

0. get_tuts_by_andrew_burgess_about_html 1. by_ 2. andrew_burgess 3. _about_html 4. _about_ 5. html

Wenn eine der optionalen Gruppen nicht gefüllt ist, hat der Index einen Wert von Null.

Die Daten, die wir wollen, befinden sich also bei den Indizes 2 und 5; Denken Sie daran, dass wir nur ein Tag, nur einen Autor oder beides (in beliebiger Reihenfolge) erhalten könnten. Als nächstes müssen wir die Tuts herausfiltern, die nicht unseren Kriterien entsprechen. Wir können das mit dem Array machen wählen Methode. Jedes Element wird einzeln an einen Block übergeben. Wenn der Block zurückkehrt wahr, der Artikel wird aufbewahrt; wenn es zurückkehrt falsch, Das Element wird aus dem Array geworfen. Beginnen wir damit:

Wenn md [1] == 'by_' tuts.select! | tut | tut [: author] .downcase == md [2] .gsub ("_", "") tuts.select! | tut | tut [: tags] .include? md [5] .gsub ("_", "") wenn md [4] == '_about_'

Ob md [1] ist? by_?, wir wissen, dass der Autor zuerst kam. Deshalb innerhalb des Blocks des ersten wählen anrufen, wir bekommen die tut Name des Hashers (downcase it) und vergleiche es mit md [2]. Ich verwende die globale Substitutionsmethode-gsub-alle Unterstriche durch ein Leerzeichen ersetzen. Wenn die Zeichenfolgen wahr sind, wird der Artikel beibehalten. sonst ist es nicht. In dieser Sekunde wählen anrufen, überprüfen wir das Tag (gespeichert in md [5]) in dem tut [: tags] Array. Das Array umfassen? Methode wird zurückkehren wahr Wenn sich das Element im Array befindet. Beachten Sie den Modifizierer am Ende dieser Zeile: Wir machen dies nur, wenn der vierte Index die Zeichenfolge ist. _About_?.

Beachten Sie, dass wir das Array tatsächlich verwenden wählen Methode: Wir verwenden wählen! (mit einem Knall / Ausrufezeichen). Dadurch wird kein neues Array nur mit den ausgewählten Elementen zurückgegeben. es funktioniert mit dem tatsächlichen tuts Array im Speicher.

Nachdem Sie das verstanden haben, sollten Sie in den nächsten Zeilen kein Problem haben:

elsif md [1] == 'about_' tuts.select! | tut | tut [: tags] .include? md [2] .gsub ("_", "") tuts.select! | tut | tut [: author] .downcase == md [5] .gsub ("_", "") wenn md [4] == '_by_' endet

Diese Zeilen verhalten sich wie oben, aber sie beziehen sich auf Methodennamen in umgekehrter Situation: tag first, optionaler author second.

Am Ende der Methode kehren wir zurück tuts; Dies ist entweder das gefilterte Array oder die Fehlernachricht.

Jetzt testen wir das:

tuts = [title: "So wechseln Sie ein Bild von B & W zu Farbe mit Canvas", Autor: "Jeffrey Way", Tags: ["javascript", "canvas"], title: "Node.js Schritt für Schritt : Blogging-Anwendung ", Autor:" Christopher Roach ", Tags: [" javascript "," node "], title:" Die 30 CSS-Selektoren, die Sie sich merken müssen ", Autor:" Jeffrey Way ", Tags: [" css "," Selektoren "], title:" Responsive Webdesign: Ein visueller Leitfaden ", Autor:" Andrew Gormley ", Tags: [" html "," responsive design "], title:" Webentwicklung von Scratch : Grundlegendes Layout ", Autor:" Jeffrey Way ", Tags: [" html "], title:" CodeIgniter-Anwendung vor CSRF schützen ", Autor:" Ian Murray ", Tags: [" php "," codeigniter " ], title: "Cron Jobs mit PHP verwalten", Autor: "Nikola Malich", Tags: ["php", "cron jobs"]] nettuts = TutsSite.new "Nettuts +", tuts p nettuts.get_tuts_by_ian_murray # [: title => "CodeIgniter-Anwendung vor CSRF schützen",: author => "Ian Murray",: tags => ["php", "codeigniter"]] p nettuts.get_tuts_about_html # [: ti tle => "Responsive Webdesign: Ein visueller Leitfaden",: author => "Andrew Gormley",: tags => ["html", "responsive design"], : title => "Webentwicklung aus Scratch: Basic Layout ",: author =>" Jeffrey Way ",: tags => [" html "]] p nettuts.get_tuts_by_jeffrey_way_about_canvas # [: title =>" So verschieben Sie ein Bild von B & W in Farbe mit Canvas ",: author => "Jeffrey Way",: tags => ["javascript", "canvas"]] p nettuts.get_tuts_about_php_by_nikola_malich # [: title => "Verwalten von Cron-Jobs mit PHP",: author => "Nikola Malich", : tags => ["php", "cron jobs"]] p nettuts.submit_an_article # Dieses Objekt unterstützt die Methode 'submit_an_article' nicht. "

Ich bin p-Um die Ergebnisse dieser Methoden auszudrucken, können Sie diese in einer Ruby-Datei in der Befehlszeile ausführen.


Eine Warnung

Ich sollte erwähnen, dass dies zwar ziemlich cool ist, aber nicht unbedingt die richtige Verwendung von ist method_missing. Es ist in erster Linie dazu da, um Sie vor Fehlern zu retten. Die Konvention ist jedoch nicht schlecht: Sie wird in der EU weit verbreitet Aktiver Rekord Klassen, die ein großer Teil von Ruby on Rails sind.


Ein Bonus

Sie wussten wahrscheinlich nicht, dass es ein ähnliches Feature in JavaScript gibt: es ist das __noSuchMethod__ Methode für Objekte. Soweit ich weiß, wird es nur in FireFox unterstützt, aber es ist eine interessante Idee. Ich habe das obige Beispiel in JavaScript neu geschrieben, und Sie können es an dieser JSBin überprüfen.


Fazit

Das ist eine Verpackung für heute! Ich habe ein paar interessante Ruby-Sachen im Ärmel und komme bald für dich. Behalten Sie Nettuts + im Auge, und wenn Sie etwas Bestimmtes wünschen, lassen Sie es mich in den Kommentaren wissen!