Sinatra definiert eine Anzahl von Methoden, die scheinbar im aktuellen Gültigkeitsbereich liegen, d. h. nicht innerhalb einer Klassendeklaration. Diese sind im Sinatra-Juwel definiert.
Ich möchte in der Lage sein, ein Juwel zu schreiben, das eine Funktion erstellt, die ich aus dem globalen Geltungsbereich aufrufen kann, z. B.
%Vor%Dies würde dann die Funktion my_blog im globalen Bereich aufrufen.
Offensichtlich könnte ich die Object-Klasse im Gem mit der Funktion add_blog vergleichen, aber das scheint wie ein Overkill zu sein, da es jedes Objekt erweitern würde.
TL; DR extend
-ing ein Modul auf der obersten Ebene fügt seine Methoden der obersten Ebene hinzu, ohne sie zu jedem Objekt hinzuzufügen.
Dafür gibt es drei Möglichkeiten:
Das wird funktionieren, aber es ist nicht der beste Weg, es zu tun: Es ist unmöglich, dein Juwel zu verlangen, ohne die Methode auf die oberste Ebene zu bringen. Einige Benutzer möchten vielleicht Ihren Edelstein ohne diese verwenden.
Idealerweise haben wir eine Möglichkeit, diese auf bestimmte Weise ODER auf der obersten Ebene verfügbar zu machen, je nach Präferenz des Benutzers. Dazu definieren wir Ihre Methode (n) innerhalb eines Moduls:
%Vor% Jetzt ist unser Code gut abgegrenzt. Die Frage ist, wie wir es von der obersten Ebene zugänglich machen, also müssen wir nicht immer MyGem::TopLevel.add_blog
aufrufen?
Schauen wir uns an, was die oberste Ebene in Ruby eigentlich bedeutet. Ruby ist eine stark objektorientierte Sprache. Das bedeutet unter anderem, dass alle Methoden an ein Objekt gebunden sind. Wenn Sie eine scheinbar globale Methode wie puts oder require aufrufen, rufen Sie tatsächlich eine Methode unter ein" Standard "-Objekt namens main
.
Wenn wir also eine Methode zum Top-Level-Bereich hinzufügen möchten, müssen wir sie zu main
hinzufügen. Es gibt ein paar Möglichkeiten, wie wir das tun können.
main
ist eine Instanz der Klasse Object
. Wenn wir die Methoden aus unserem Modul zu Object
hinzufügen (dem Affen-Patch , auf den das OP Bezug nimmt), werden wir es sein in der Lage, sie von der Haupt- und somit der obersten Ebene zu verwenden. Wir können das tun, indem Sie unser Modul als mixin verwenden:
Wir können jetzt add_blog von der obersten Ebene aus aufrufen. Dies ist jedoch auch keine perfekte Lösung (wie das OP hervorhebt), da wir unsere neuen Methoden nicht nur zu main
hinzugefügt haben, sondern sie zu jeder Instanz von% hinzugefügt haben. Code%! Leider ist fast alles in Ruby ein Nachkomme von Object
, also haben wir es gerade möglich gemacht, add_blog auf irgendwas zu setzen! Zum Beispiel Object
.
Dies ist eindeutig unerwünscht, aber konzeptionell ziemlich nah an dem, was wir wollen. Lassen Sie uns das verfeinern, damit wir nur Methoden zu main hinzufügen können.
Wenn also 1.add_blog("what does it mean to add a blog to a number?!")
die Methoden eines Moduls zu einer Klasse hinzufügt, könnten wir es direkt auf main aufrufen? Denken Sie daran, wenn eine Methode ohne einen expliziten Empfänger ("besitzendes Objekt") aufgerufen wird, wird sie am include
aufgerufen.
Sieht vielversprechend aus, ist aber immer noch nicht perfekt - es stellt sich heraus, dass main
der Klasse des Empfängers Methoden hinzufügt, nicht nur dem Empfänger, also könnten wir immer noch seltsame Dinge wie include
machen.
Um das zu beheben, brauchen wir eine Methode, die dem empfangenden Objekt nur Methoden hinzufügt, nicht seine Klasse. 1.add_blog("still not a good thing!")
ist diese Methode.
Diese Version fügt die Methoden unseres Edelsteins dem Top-Level-Bereich hinzu, ohne mit anderen Objekten zu verwirren:
%Vor%Ausgezeichnet! Die letzte Stufe besteht darin, unser Gem einzurichten, damit ein Benutzer unsere Top-Level-Methoden zu main hinzufügen kann, ohne dass er sich selbst anrufen muss. Wir sollten Benutzern auch die Möglichkeit bieten, unsere Methoden vollständig zu nutzen.
%Vor%Auf diese Weise können Benutzer Ihre Methoden automatisch zur obersten Ebene hinzufügen lassen:
%Vor%Oder Sie können wählen, ob Sie auf die Methoden im Bereich zugreifen möchten:
%Vor% Ruby erreicht dies durch ein bisschen Magie namens die Eigenklasse . Jedes Ruby-Objekt, das eine Instanz einer Klasse ist, hat eine eigene Klasse - seine Eigenklasse. Wir verwenden tatsächlich extend
, um die extend
-Methoden zur Eigenklasse des main hinzuzufügen.
Dies ist die Lösung, die ich verwenden würde. Es ist auch das Muster, das Sinatra verwendet. Sinatra wendet es etwas komplizierter an, aber es ist im Wesentlichen dasselbe:
Wenn Sie
anrufen %Vor%sinatra.rb wird Ihrem Skript effektiv vorangestellt. Diese Datei ruft
auf %Vor%sinatra / main.rb ruft
an %Vor% MyGem::TopLevel
ist das Äquivalent des oben beschriebenen Moduls Sinatra::Delegator
- es bewirkt, dass main über Sinatra-spezifische Methoden Bescheid weiß.
Hier unterscheidet sich Delgator leicht - anstatt seine Methoden direkt zu main hinzuzufügen, veranlasst Delegator main, bestimmte Methoden an eine designierte Zielklasse weiterzuleiten - in diesem Fall MyGem::TopLevel
.
Das können Sie im Sinatra-Code selbst sehen. Eine Suche durch sinatra / base.rb zeigt, dass, wenn das Modul Sinatra::Application
erweitert oder eingeschlossen wird, der aufrufende Bereich (in diesem Fall das Objekt "main") die folgenden Methoden an Delegator
:
Tags und Links ruby