Wie hängen das Neuladen von require, require_dependency und Konstanten in Rails zusammen?

70

Wie sind requireund require_dependencyanders?
Wie können require_dependencyKlassen in der Entwicklung automatisch neu geladen werden, aber requirenicht?

Ich habe mich in den ActiveSupport::DependenciesCode von Rails und dispatcher.rb vertieft. Was ich in require_dependency's Code gesehen habe, ist, dass es im Grunde die Konstanten zu einem autoloaded_constantsArray hinzufügt . Es wird jedoch clear_applicationnach jeder Anforderung im Dispatcher gelöscht .

Kann jemand eine klare Erklärung geben oder mich auf einige Ressourcen hinweisen, die helfen werden?

wei
quelle
Zu Ihrer Information: Das Neuladen von Klassen wird automatisch in Engines (die nichts anderes als Plugins mit Apps / Ordnern sind) behandelt, genau wie in normalen Apps.
August Lilleaas
Danke, ja, das ist mir bewusst. Aber dann führt dies zu meiner anderen Frage: Kann ich andere Plugins in meinem Plugin verwenden? Angenommen, ich möchte ein act_as_xxx-Plugin in meinem eigenen Plugin verwenden. Kann ich das act_as_xxx einfach in das Vendor / Plugins-Verzeichnis meines Plugins einfügen und dann den Pfad zu $ ​​LOAD_PATH hinzufügen? Ich denke, ich sollte dies in einem anderen Thread fragen, obwohl es nichts mit meiner ursprünglichen Frage zu tun hat.
Wei

Antworten:

127

require(und sein Cousin load) sind Kernmethoden von Ruby. require_dependencyist eine Methode, mit der Rails das Problem des Abhängigkeitsmanagements lösen kann. Kurz gesagt, Rails kann Klassen im Entwicklungsmodus neu laden, sodass Sie den Server nicht jedes Mal neu starten müssen, wenn Sie eine Codeänderung vornehmen. Das Rails-Framework erstellt require_dependencyIhren Code, damit er ihn verfolgen und neu laden kann, wenn Änderungen vorgenommen werden. Der Standard Ruby requiremacht das nicht. Als App- (oder Plugin- / Engine-) Entwickler sollten Sie sich keine Sorgen machen müssen, require_dependencyda dies rein intern für Rails ist.

Die Magie des Ladevorgangs der Rails-Klasse liegt im ActiveSupport :: Dependencies-Modul. Dieser Code erweitert das Standardverhalten von Ruby, damit Code in Ihrer Rails-App Module (einschließlich Klassen, die von Module erben) automatisch unter Verwendung der Pfad- und Dateinamenkonventionen von Rails lädt. Dadurch muss der Programmierer seinen Code nicht mehr mit requireAufrufen verunreinigen, wie Sie es in einer einfachen Ruby-Anwendung tun würden.

Anders ausgedrückt: Auf diese Weise können Sie class Admin::Userinnerhalb der Datei definieren app/models/admin/user.rbund Rails wissen lassen, wovon Sie sprechen, wenn Sie Admin::User.newvon einem anderen Teil der Anwendung wie einem Controller aus aufrufen . Ohne ActiveSupport :: Dependencies müssten Sie alles, requirewas Sie benötigen , manuell ausführen .

Wenn Sie aus einer statisch typisierten Sprache wie C #, Java usw. stammen, ist dies möglicherweise eine Überraschung: Der Rails-Code wird erst geladen, wenn er benötigt wird. Beispielsweise ist eine UserModellklasse nicht definiert und user.rbwird erst geladen, nachdem Sie versucht haben, sie aufzurufen User.whatever_method_here. Rails verhindert, dass Ruby sich über diese fehlende Konstante beschwert, lädt Code für Userund ermöglicht Ruby, wie gewohnt fortzufahren.

Obwohl ich nicht für Ihr spezifisches Bedürfnis sprechen kann, wäre ich sehr überrascht, wenn Sie die require_dependencyMethode tatsächlich innerhalb eines Plugins oder einer Engine verwenden müssten . Wenn Sie die Rails-Konventionen befolgen, sollten Sie $ LOAD_PATH auch nicht von Hand anpassen müssen. Dies ist nicht "der Rails-Weg".

In der Welt von Ruby und auch Rails ist Einfachheit und Klarheit der Schlüssel. Wenn Sie nur ein Plugin oder eine Engine schreiben möchten und bereits tief in die Interna eintauchen, können Sie Ihr Problem aus einem anderen Blickwinkel betrachten. Mein Bauch sagt mir, dass Sie vielleicht versuchen, etwas zu tun, das unnötig kompliziert ist. Aber andererseits habe ich keine Ahnung, was Sie genau tun !! :) :)

MDaubs
quelle
24

require_dependencyist in einer Engine nützlich, wenn Sie eine Klasse, die nicht in Ihrer Engine definiert ist (z. B. in einer anderen Engine oder Rails-App), erneut öffnen und neu laden möchten. In diesem Fall funktioniert so etwas:

# app/controllers/my_engine/documents_controller.rb
require_dependency MyEngine::Engine.root.join('app', 'controllers', 'my_engine', 'documents_controller').to_s

module MyEngine
  class DocumentsController
    def show
      render :text => 'different'
    end
  end
end
Kris
quelle