Beste Möglichkeit, Modul / Klasse aus dem lib-Ordner in Rails 3 zu laden?

273

Da die neueste Version von Rails 3 keine Module und Klassen mehr automatisch aus lib lädt, wie lassen Sie sie am besten laden?

Vom Github:

A few changes were done in this commit:

Do not autoload code in *lib* for applications (now you need to explicitly 
require them). This makes an application behave closer to an engine 
(code in lib is still autoloaded for plugins);
Vincent
quelle

Antworten:

250

Ab Rails 2.3.9 gibt es eine Einstellung, in config/application.rbder Sie Verzeichnisse angeben können, die Dateien enthalten, die automatisch geladen werden sollen .

Aus application.rb:

# Custom directories with classes and modules you want to be autoloadable.
# config.autoload_paths += %W(#{config.root}/extras)
Slobodan Kovacevic
quelle
7
Beachten Sie die Antwort von @ dankbar auch, wenn Sie den gesamten Teilbaum von automatisch laden möchten app/lib.
Tom Harrison
199
# Autoload lib/ folder including all subdirectories
config.autoload_paths += Dir["#{config.root}/lib/**/"]

Quelle: Rails 3 Quicktip: Autoload lib-Verzeichnis einschließlich aller Unterverzeichnisse. Vermeiden Sie verzögertes Laden

Bitte beachten Sie, dass die im lib-Ordner enthaltenen Dateien nur beim Starten des Servers geladen werden. Wenn Sie möchten, dass diese Dateien bequem automatisch geladen werden, lesen Sie: Rails 3 Quicktip: Lib-Ordner im Entwicklungsmodus automatisch neu laden . Beachten Sie, dass dies nicht für eine Produktionsumgebung gedacht ist, da das permanente Nachladen die Maschine verlangsamt.

dankbar
quelle
Die Links sind tot
Besi
84

Die Magie des automatischen Ladens von Sachen

Ich denke, die Option, die Ordner zu steuern, aus denen das automatische Laden durchgeführt wird, wurde in anderen Antworten ausreichend behandelt. Falls jedoch jemand anderes Probleme beim Laden hat, obwohl die Autoload-Pfade nach Bedarf geändert wurden, versucht diese Antwort zu erklären, was die Magie hinter diesem Autoload-Ding ist.

Wenn es darum geht, Inhalte aus Unterverzeichnissen zu laden, gibt es eine Gotcha oder eine Konvention, die Sie beachten sollten. Manchmal kann die Ruby / Rails-Magie (diesmal meistens Rails) es schwierig machen zu verstehen, warum etwas passiert. Jedes in den Autoload-Pfaden deklarierte Modul wird nur geladen, wenn der Modulname dem Namen des übergeordneten Verzeichnisses entspricht. Für den Fall, dass Sie versuchen, lib/my_stuff/bar.rbetwas zu tun wie:

module Foo
  class Bar
  end
end

Es wird nicht automatisch geladen. Wenn Sie das übergeordnete Verzeichnis fooso umbenennen, dass Ihr Modul unter folgendem Pfad gehostet wird : lib/foo/bar.rb. Es wird für Sie da sein. Eine andere Möglichkeit besteht darin, die Datei, die automatisch geladen werden soll, anhand des Modulnamens zu benennen. Offensichtlich kann es dann nur eine Datei mit diesem Namen geben. Falls Sie Ihre Daten in viele Dateien aufteilen müssen, können Sie diese eine Datei natürlich verwenden, um andere Dateien zu benötigen. Ich empfehle dies jedoch nicht, da Rails im Entwicklungsmodus und wenn Sie diese anderen Dateien ändern, nicht in der Lage ist, automatisch zu arbeiten lade sie für dich neu. Wenn Sie jedoch wirklich möchten, können Sie eine Datei mit dem Modulnamen haben, die dann die tatsächlichen Dateien angibt, die für die Verwendung des Moduls erforderlich sind. Sie könnten also zwei Dateien haben: lib/my_stuff/bar.rbund lib/my_stuff/foo.rbdie erste ist dieselbe wie oben und die zweite enthält eine einzelne Zeile:require "bar"und das würde genauso funktionieren.

PS Ich fühle mich gezwungen, noch eine wichtige Sache hinzuzufügen. Wenn ich in letzter Zeit etwas im lib-Verzeichnis haben möchte, das automatisch geladen werden muss, neige ich dazu zu denken, dass es eines Tages etwas sein könnte, das ich speziell für dieses Projekt entwickle (was es normalerweise ist) verwandeln Sie sich in einen "statischen" Codeausschnitt, der in vielen Projekten oder in einem Git-Submodul usw. verwendet wird. In diesem Fall sollte er sich definitiv im lib-Ordner befinden. Dann befindet sich sein Platz möglicherweise überhaupt nicht im lib-Ordner. Vielleicht sollte es sich in einem Unterordner unter dem App-Ordner befinden. · Ich habe das Gefühl, dass dies die neue Art und Weise ist, Dinge zu tun. Offensichtlich ist die gleiche Magie überall dort wirksam, wo Sie in Ihren Autoload-Pfaden Ihre Sachen ablegen, damit es gut für diese Dinge ist. Jedenfalls sind dies nur meine Gedanken zu diesem Thema. Es steht Ihnen frei, anderer Meinung zu sein. :) :)


UPDATE: Über die Art der Magie ..

Wie Severin in seinem Kommentar betonte, ist der Kern "Autoload a Module Mechanism" sicher ein Teil von Ruby, aber das Autoload Paths-Zeug ist es nicht. Sie brauchen keine Rails zu tunautoload :Foo, File.join(Rails.root, "lib", "my_stuff", "bar"). Und wenn Sie zum ersten Mal versuchen würden, auf das Modul Foo zu verweisen, wird es für Sie geladen. Rails bietet uns jedoch die Möglichkeit, Inhalte automatisch aus registrierten Ordnern zu laden. Dies wurde so implementiert, dass etwas über die Namenskonventionen vorausgesetzt werden muss. Wenn es nicht so implementiert worden wäre, müsste es jedes Mal, wenn Sie auf etwas verweisen, das gerade nicht geladen ist, alle Dateien in allen Autoload-Ordnern durchsuchen und prüfen, ob eine von ihnen das enthält, worauf Sie verweisen wollten. Dies würde wiederum die Idee des automatischen Ladens und automatischen Ladens zunichte machen. Mit diesen Konventionen kann es jedoch von dem Modul / der Klasse abziehen, dass Sie versuchen, dort zu laden, wo dies definiert sein könnte, und dies einfach laden.

Timo
quelle
1
Warum ist das Ruby Magie? Ruby bietet lediglich die Funktion Modul # Autoload, mit der Sie eine Datei befehlen können, die beim Zugriff auf eine (undefinierte) Konstante geladen wird (siehe ruby-doc.org/core-1.9.3/Module.html#method-i-autoload ). Der Abgleich von Modul- / Klassennamen mit Verzeichnissen / Dateien erfolgt meiner Meinung nach in Rails / ActiveSupport (zB hier: github.com/rails/rails/blob/… ). Liege ich falsch?
Severin
Ja, ich glaube du hast recht. Ich war zu voreilig, um meine ursprüngliche Antwort zu "korrigieren", als Zabba auf seinen "Fehler" hinwies. Lassen Sie mich meine Antwort etwas weiter aktualisieren, um dieses Problem zu klären.
Timo
1
Ich verbrachte ungefähr eine halbe Stunde damit, herumzuspielen. Ich musste (wollte) Sprockets :: JSRender :: Processor automatisch laden. Der Pfad dafür kann gefunden werden, indem Sie in die Rails-Konsole gelangen und "Sprockets :: JSRender :: Processor" ausführen. Dabei wird festgestellt, dass es sich um "sprockets / js_render / processor" (mit hinzugefügtem .rb) handelt.
Pedz
Du hast gerade meine geistige Gesundheit gerettet. ~ tiefes Seufzen der Erleichterung ~ vielen Dank für das Teilen :)
Brenden
Vielen Dank für diesen äußerst hilfreichen Kommentar. Ich habe nicht verstanden, warum sich einige Module so verhalten, bis ich Ihren Kommentar gelesen habe. Segen für dich!
mjnissim
41

Warnung: Wenn Sie den 'Monkey Patch' oder die 'Open Class' aus Ihrem 'lib'-Ordner laden möchten, verwenden Sie nicht den ' Autoload'-Ansatz !!!

  • Ansatz " config.autoload_paths ": Funktioniert nur, wenn Sie eine Klasse laden, die nur an EINER Stelle definiert ist. Wenn eine Klasse bereits an einer anderen Stelle definiert wurde, können Sie sie mit diesem Ansatz nicht erneut laden.

  • Ansatz " config / initializer / load_rb_file.rb ": funktioniert immer! Was auch immer die Zielklasse eine neue Klasse oder eine "offene Klasse" oder ein "Affen-Patch" für eine vorhandene Klasse ist, es funktioniert immer!

Weitere Informationen finden Sie unter: https://stackoverflow.com/a/6797707/445908

Siwei Shen 申思维
quelle
6
Dies ist eine wichtige Unterscheidung zu verstehen. Danke dafür.
Tyler Collier
28

Sehr ähnlich, aber ich finde das etwas eleganter:

config.autoload_paths += Dir["#{config.root}/lib", "#{config.root}/lib/**/"]
Brian Armstrong
quelle
18

In meinem Fall habe ich versucht, einfach eine Datei direkt unter das lib-Verzeichnis zu laden.

Innerhalb von application.rb ...

require '/lib/this_file.rb' 

funktionierte nicht, auch nicht in der Konsole und dann, als ich es versuchte

require './lib/this_file.rb' 

und Rails lädt die Datei perfekt.

Ich bin immer noch ziemlich noob und ich bin nicht sicher, warum das funktioniert, aber es funktioniert. Wenn jemand es mir erklären möchte, würde ich es begrüßen: DI hoffe, das hilft jemandem so oder so.

Nick Res
quelle
2
Dies liegt daran, dass ./lib/this_file.rb im aktuellen Verzeichnis sucht (in der Rails-Konsole wäre dies Ihr Rails-Stammverzeichnis) und /lib/this_file.rb dies als absoluten Pfad sucht. Beispiel: ./lib/this_file.rb = /var/www/myrailsapp/lib/this_file.rb, /lib/this_file.rb = /lib/this_file.rb
Jason
7

Ich hatte das gleiche Problem. Hier ist, wie ich es gelöst habe. Die Lösung lädt das lib-Verzeichnis und alle Unterverzeichnisse (nicht nur das direkte). Natürlich können Sie dies für alle Verzeichnisse verwenden.

# application.rb
config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]
hjuskewycz
quelle
5
Dies hat den bösen Nebeneffekt, dass Rails-Namespace-Konventionen völlig durcheinander geraten. Wenn lib / bar / foo.rb, das Bar :: Foo definiert, vor lib / foo.rb erscheint, das Foo in der Autoload-Suche definiert, werden verwirrende Fehler Expected lib/bar/foo.rb to define constant Fooangezeigt , z. B. wenn Sie versuchen, lib / foo.rb zu laden, indem Sie sich auf Foo beziehen Konstante.
Jacob
5

config.autoload_paths funktioniert bei mir nicht. Ich löse es auf andere Weise

Ruby on Rails 3 lädt den Code nicht automatisch aus dem Ordner / lib neu. Ich löse es, indem ich hineinsteckeApplicationController

Dir["lib/**/*.rb"].each do |path|
  require_dependency path
end 
msa.im
quelle
4

Wenn nur bestimmte Dateien Zugriff auf die Module in lib benötigen, fügen Sie einfach eine require-Anweisung zu den Dateien hinzu, die sie benötigen. Wenn beispielsweise ein Modell auf ein Modul zugreifen muss, fügen Sie Folgendes hinzu:

require 'mymodule'

oben in der Datei model.rb.

Mike Fischer
quelle
50
Sie sollten nicht requirein einer Rails-App verwenden, da dies verhindert, ActiveSupport::Dependenciesdass der Code ordnungsgemäß [ent] geladen wird. Stattdessen sollten Sie config.autoload_pathswie die obige Antwort verwenden und dann nach Bedarf einschließen / erweitern.
ben_h
13
Danke @Mike, ich wollte tun, was du getan hast. Es war gut zu sehen, warum es schlecht ist. Danke, dass du die Antwort nicht entfernt hast.
Pupeno
Was ist mit 'mymodule', wenn Sie nur ein Modul laden möchten?
Mike
1
@ben_h Solltest du nicht requirevon irgendwo in einer Rails-App? In einer Harke Aufgabe bin ich zur Zeit require-ing und include-ing ein Modul , das in lebt lib/. Sollte ich das nicht tun?
Dennis
@ben_h Meine Suche zeigt, dass es requireIhrem lib/Code gemeinsam ist (z. B. dieser Blog-Beitrag , diese SO-Antwort ). Ich bin mir immer noch nicht sicher. Können Sie mehr Beweise für die Behauptung liefern, dass Sie nicht verwendet haben require?
Dennis
2

Schreiben Sie den Dateinamen richtig.

Ernsthaft. Ich habe eine Stunde lang mit einer Klasse gekämpft, weil die Klasse Governance :: ArchitectureBoard war und sich die Datei in lib / Governance / Architecture_baord.rb befand (transponierte O und A in "board").

Scheint im Nachhinein offensichtlich, aber es war der Teufel, der das aufspürte. Wenn die Klasse nicht in der Datei definiert ist, in der Rails sie erwartet, basierend auf dem Munging des Klassennamens, wird sie sie einfach nicht finden.

David Hempy
quelle
2

Ab Rails 5, wird empfohlen , den Ordner lib unter App - Verzeichnis zu setzen oder stattdessen erstellen andere sinnvolle Namensräume für den Ordner wie services, presenters,features usw. und es für die automatische Beladung von Schienen unter App Verzeichnis.

Bitte überprüfen Sie auch diesen GitHub-Diskussionslink .

Ashik Salman
quelle