ruby: wie zu verlangen, richtig (zur Vermeidung von zyklischen Abhängigkeiten)
heute war ich vor einem seltsamen problem:
habe einen "missing method" - Fehler auf einem Modul, aber die Methode war da, und die Datei, wo das Modul definiert wurde, war erforderlich. Nach einigem suchen fand ich eine zirkuläre Abhängigkeit, wo 2 Dateien erforderlich, jedes andere, und jetzt nehme ich an ruby leise bricht kreisförmigen erfordert.
Bearbeiten Beginnen: Beispiel
Datei 'ein.rb':
require './b.rb'
module A
def self.do_something
puts 'doing..'
end
end
Datei 'b.rb':
require './a.rb'
module B
def self.calling
::A.do_something
end
end
B.calling
Ausführung b.rb gibt b.rb:5:in 'calling': uninitialized constant A (NameError)
. Die verlangt haben, es gibt für beide Dateien, wie Sie ausgeführt werden sollen, auf Ihrer eigenen von der Kommandozeile aus (i entfallen, die code, es kurz zu halten).
So B. die Berufung hat, dort zu sein. Eine mögliche Lösung ist, wickeln Sie das erfordert in if __FILE__ == $0
, aber das scheint nicht der richtige Weg zu gehen.
Edit Ende
zur Vermeidung dieser schwer zu findende Fehler (wäre es nicht schöner, wenn das erforderlich ist, wirf eine Ausnahme, übrigens?), gibt es einige Richtlinien/Regeln bei der Strukturierung eines Projektes und, wo erforderlich, was? Zum Beispiel, wenn ich
module MainModule
module SubModule
module SubSubModule
end
end
end
wo sollte ich muss die Submodule? alle in der Haupt-oder nur den sub in der Haupt-und subsub in der sub?
jede Hilfe wäre sehr nett.
Zusammenfassung
Einer Erklärung, warum dies geschieht, ist diskutiert in forforfs Antwort und Kommentare.
Bisher beste Praxis (wie hingewiesen oder angedeutet durch gelegen) scheint Folgendes zu sein (bitte korrigiert mich wenn ich falsch Liege):
- jedem Modul oder die Klasse in die top-namespace in eine Datei benannt nach dem Modul/Klasse. in meinem Beispiel wären dies 1 Datei mit dem Namen 'main_module.rb.'
wenn es teilmodule oder-Unterklassen, erstellen Sie ein Verzeichnis, benannt nach dem Modul/Klasse (in meinem Beispiel ein Verzeichnis 'main_module', und legen Sie die Dateien für die Unterklassen/teilmodule dort (im Beispiel 1-Datei mit dem Namen 'sub_module.rb'). wiederholen Sie dies für jede Ebene des namespace. - benötigen-Schritt für Schritt (im Beispiel die
MainModule
erfordern würde, dieSubModule
, und dieSubmodule
erfordern würde, dieSubSubModule
) - separates "running" - code von 'Definition' code. in der Ausführung von code erfordern, sobald Sie Ihre top-level-Modul/Klasse, so wegen 2. alle Ihre-Bibliothek-Funktionen sollten nun verfügbar sein, und Sie können jeden definierten Methoden.
Dank an alle, die geantwortet/kommentiert, es hat mir sehr geholfen!
- Dein Beispiel produziert nicht ein Fehler; es funktioniert für mich. Sieht aus wie Sie haben vereinfacht, Ihr code zu dem Punkt, wo es nicht zu zeigen, Ihr problem. Meine Antwort auf Ihre Frage über die Reihenfolge des Ladens gewesen wäre deinem Beispiel-code! 🙂
- das ist merkwürdig, ich habe tatsächlich lief der code und es HAT zu produzieren, dass errror Meldung. ich bin mit ruby 1.9.2p180, was verwenden Sie?
- 1.8.7 und 1.9.3.
- gut, versuchte ich es mit ruby 1.8.7p330, die gibt mir auch den gleichen Fehler. vielleicht haben Sie nicht copy/paste richtig.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Nach zu Fragen, diese auf die Ruby-mailing-Liste eine Weile zurück, wenn ich eine Datei in meine Bibliotheken nur für die Dinge, die ich geändert, um diese zwei Regeln:
Wenn eine Datei muss der code von einem anderen in der gleichen Bibliothek, die ich verwenden
require_relative
in der Datei, muss der code.Wenn eine Datei muss der code von einer anderen Bibliothek, die ich verwenden
require
in der Datei, muss der code.Soweit ich es verstehe, Ruby verlangt, in der Reihenfolge, es wird gebeten, und also macht es nicht zu zyklischen Abhängigkeiten.
(Ruby-v1.9.2)
In Antwort auf das Kommentar über das Beispiel zeigt die zirkuläre Abhängigkeit Probleme:
eigentlich das problem mit dem Beispiel ist das nicht erforderlich sind kreisrund, sondern, dass
B.calling
aufgerufen wird, bevor der Bedarf abgeschlossen haben. Wenn Sie entfernen die B. Aufruf von b ist.rb funktioniert es einwandfrei. Zum Beispiel in irb-ohne B. Aufruf in den code-Datei, aber danach laufen:load
) mehrererequires
auf die gleiche Datei wird nur in einem einzigen laden der Datei.B.calling
hat, dort zu sein, (siehe edit). Oder ist das nur eine sehr schlechte Idee, die nur Schwierigkeiten bereitet?Ein paar grundlegende Dinge, die Sie hoffentlich bereits wissen:
Ruby ist interpretiert, nicht kompiliert, so dass Sie nicht durchführen kann, beliebigen code, der bisher nicht gesehen, durch den interpreter.
require
nur fügt den code aus der Datei in diesem Punkt des Programms, in anderen Worten, einerequire
an der Spitze des Programms interpretiert werden, bevor einrequire
an der Unterseite.(Hinweis: Bearbeitet berücksichtigen, erfordern Aussagen, Verhalten)
Also, wenn Sie waren zu tun:
ruby a.rb
dies ist, was Sie den ruby-interpreter würde sehen und ausführen:Wenn Sie stattdessen lief b zuerst
ruby b.rb
Dolmetscher sehe:Ich hoffe, dies erklärt die guten Antworten, die andere dir gegeben haben, und wenn Sie darüber nachdenken, warum es ist schwer, die Antwort auf Ihre Letzte Frage darüber, wo Sie erfordern Aussagen. Mit Ruby, Sie wollen, dass die Dateien nicht Module, also wo Sie die benötigen in Ihrem code, hängt davon ab, wie Sie Ihre Dateien organisiert sind.
Wenn Sie unbedingt brauchen, um in der Lage zu haben, die Module definiert und die Methoden ausführen, die in zufälliger Reihenfolge, dann könnte man etwas umsetzen wie diese zu sammeln, fordert Module, die noch nicht existieren, und dann rufen Sie, wenn Sie pop zu sein.
Sicher sein, definieren Sie die Verzögerung module zuerst und dann zu fordern, anstatt
B.calling
verwenden SieDelay.exec(:B, :calling, any_other_args)
. Also, wenn Sie diese nach dem Delay-Modul:Ergebnisse in:
Letzte Schritt ist, brechen Sie den code in Dateien. Ein Ansatz könnte sein, die haben drei Dateien
So lange, wie Sie sicher
require 'delay'
ist die erste Zeile der Modul-Dateien (eine.rb und b.rb) und die Verzögerung am Ende des Moduls, sollten die Dinge funktionieren.Letzte Anmerkung: Diese Implementierung macht nur Sinn, wenn Sie nicht entkoppeln Ihre definition code aus der Modul-Ausführung fordert.
require 'a.rb
und beginnt mit der Analyse einer.rb. dort trifft Sie aufrequire 'a.rb
und, da b.rb wurde nicht benötigt, dennoch beginnt die Analyse.b.rb. es ist wieder einerequire 'a.rb
, aber ein.rb hat bereits erforderlich, so ist es bewegt sich auf b -.rb. am Ende führt esB.calling
, die versucht ausführen::A.do_something
. dies schlägt fehl, da die Analyse ein.rb ist noch nicht abgeschlossen (oder abgebrochen wurde komplett?)require 'a.rb'
und beginnt mit der Analyse einer.rb. Das erste, was aufgetreten istrequire 'b.rb'
, sondern b.rb ist bereits in den parser, sorequire 'b.rb'
ignoriert. Den rest ein.rb definiert Modul Ein, dann gehen wir zu den übrigen b.rb definiert Modul B und läuft dann B. Aufruf (und die funktioniert). Aber, wenn Sie beginnen mit ein.rb "statt " b".rb, Dinge, die nicht funktionieren.puts 'c'; require './c.rb'
in eine Datei mit dem Namen c.rb. wenn Sie es ausführen, erhalten Sie "c", zweimal!), welches ist die wichtigste Ursache für dieses Problem ist.