Automatisches Laden von lib-Dateien in Rails 4

229

Ich verwende die folgende Zeile in einem Initialisierer, um Code in meinem /libVerzeichnis während der Entwicklung automatisch zu laden :

config / initializers / custom.rb:

RELOAD_LIBS = Dir[Rails.root + 'lib/**/*.rb'] if Rails.env.development?

(aus Rails 3 Quicktip: Lib-Ordner im Entwicklungsmodus automatisch neu laden )

Es funktioniert gut, ist aber zu ineffizient, um es in der Produktion zu verwenden. Anstatt bei jeder Anforderung Bibliotheken zu laden, möchte ich sie nur beim Start laden. Im selben Blog gibt es einen weiteren Artikel, in dem beschrieben wird, wie das geht:

config / application.rb:

# Custom directories with classes and modules you want to be autoloadable.
config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]

Wenn ich jedoch auch in der Entwicklung dazu wechsle, erhalte ich NoMethodErrors, wenn ich versuche, die lib-Funktionen zu verwenden.

Beispiel einer meiner lib-Dateien:

lib / extensions.rb:

Time.class_eval do
  def self.milli_stamp
    Time.now.strftime('%Y%m%d%H%M%S%L').to_i
  end
end

Beim Aufruf Time.milli_stampwird NoMethodError ausgelöst

Mir ist klar, dass andere ähnliche Fragen zu SO beantwortet haben, aber alle scheinen sich mit Namenskonventionen und anderen Problemen zu befassen, über die ich mir vorher keine Sorgen machen musste. Meine lib-Klassen haben bereits für das Laden pro Anfrage funktioniert , ich möchte sie nur ändern zum Laden pro Start . Was ist der richtige Weg, um dies zu tun?

Yarin
quelle
Wird der Ordner config / initializers beim Start einer Rails-App automatisch geladen?
Jwan622

Antworten:

548

Ich denke, das könnte Ihr Problem lösen:

  1. in config / application.rb :

    config.autoload_paths << Rails.root.join('lib')

    und halten Sie die richtige Namenskonvention in lib .

    in lib / foo.rb :

    class Foo
    end

    in lib / foo / bar.rb :

    class Foo::Bar
    end
  2. Wenn Sie wirklich einige Affen-Patches in einer Datei wie lib / extensions.rb erstellen möchten , benötigen Sie diese möglicherweise manuell:

    in config / initializers / require.rb :

    require "#{Rails.root}/lib/extensions" 

PS

ifyouseewendy
quelle
1
@ ifyouseewendy- Sie haben genau Recht, da extensions.rb die Namenskonventionen von Rails nicht befolgte und Rails sie nicht in den Ladevorgang einbezog. Ich habe es zum Laufen gebracht, indem ich es manuell benötigt habe.
Yarin
@ifyouseewendy Wie kann ich Dateien einfügen, bevor Modelle geladen werden? Fügen Sie den Pfad zum automatischen Laden hinzu. Es ist cool, aber wie können Sie die Reihenfolge der Aufnahme steuern? thx
Matrix
@Matrix "Dateien einschließen, bevor Modelle geladen werden". Möglicherweise benötigen Sie Ihre Datei manuell, ohne die Autoload-Funktion zu verwenden.
ifyouseewendy
@ifyouseewendy Wenn ich es im Initialisierer benötige, die Datei sich jedoch in autoload_path befindet, wird es neu geladen (zweimal geladen) oder nicht? ihr ist ein "require_once" wie in php?
Matrix
5
Dies scheint in der Rails 5-API in der Produktion nicht zu funktionieren (aber in der Entwicklung). Ich glaube, dass Sie verwenden müssen config.eager_load_paths << Rails.root.join('lib'). Dies hat jedoch einen großen Nachteil darin, dass auch eager_load_pathsbei Aufgaben alles geladen wird. Ich denke, dass die Lösung von Lulalala besser ist. Hier ist ein Blog-Beitrag mit weiteren Details: blog.arkency.com/2014/11/…
hirowatari
33

Dies beantwortet zwar nicht direkt die Frage, aber ich denke, es ist eine gute Alternative, um die Frage insgesamt zu vermeiden.

Um all das autoload_pathsoder eager_load_pathsÄrger zu vermeiden , erstellen Sie ein "lib" - oder ein "misc" -Verzeichnis unter "app" -Verzeichnis. Platzieren Sie Codes wie gewohnt dort, und Rails lädt Dateien genauso wie Modelldateien (und lädt sie neu).

lulalala
quelle
3
Ich bin in Rails 4.2. und es lädt keine Dateien automatisch unter app, ich muss es manuell tun ...... oder muss es in den Autload-Pfad setzen ..
Arup Rakshit
6
Sie liegen falsch, Arup, alle Unterverzeichnisse des App-Verzeichnisses befinden sich automatisch im Array autoload_paths in Rails 4.2. Siehe edgeguides.rubyonrails.org/…
Dr.Strangelove
Mit Ausnahme des app/viewsVerzeichnisses, das nicht hinzugefügt wird; oder vielmehr wird explizit entfernt.
James B. Byrne
1
Gute Antwort. Einzige Sache, die bei mir auf Rails 5 / API funktioniert hat.
Jstafford
6
Denken Sie daran, dass libdies für Code gedacht ist, der auf mehrere Projekte angewendet und möglicherweise in ein Juwel extrahiert werden kann. Wenn nicht, erstellen Sie unter App-Suche einen passenderen Ordner als services/oder presenters/und unterschreiben Sie diese sogar.
PhilT
6

Dies könnte jemandem wie mir helfen, der diese Antwort findet, wenn er nach Lösungen sucht, wie Rails mit dem Laden von Klassen umgeht ... Ich stellte fest, dass ich eine definieren musste module dessen Name meinem Dateinamen angemessen entspricht, anstatt nur eine Klasse zu definieren:

In der Datei lib / development_mail_interceptor.rb (Ja, ich verwende Code aus einem Railscast :))

module DevelopmentMailInterceptor
  class DevelopmentMailInterceptor
    def self.delivering_email(message)
      message.subject = "intercepted for: #{message.to} #{message.subject}"
      message.to = "[email protected]"
    end
  end
end

funktioniert, wird aber nicht geladen, wenn ich die Klasse nicht in ein Modul eingefügt habe.

Sameers
quelle
1
In Ruby bedeutet "Matching entsprechend", dass sich die Datei im Dateisystem unter LOAD_PATH/module/class.rb(unterstrichen) befindet, wo sie LOAD_PATHsich in den von der Ruby-App verwendeten Ladepfaden befindet (autoload_paths im Fall von Rails). libhat sich vom automatischen Laden durch Rails zum nicht automatischen Laden verändert und wird in neueren Versionen (> = Rails 3.x) nicht automatisch geladen. Was auch immer Magie diese Arbeit für Sie macht, wird nicht empfohlen. Vielleicht ist es ein alter Railscast?
Peter H. Boling
0

Verwenden Sie config.to_prepare, um Ihre Affen-Patches / -Erweiterungen für jede Anforderung im Entwicklungsmodus zu laden.

config.to_prepare do |action_dispatcher|
 # More importantly, will run upon every request in development, but only once (during boot-up) in production and test.
 Rails.logger.info "\n--- Loading extensions for #{self.class} "
 Dir.glob("#{Rails.root}/lib/extensions/**/*.rb").sort.each do |entry|
   Rails.logger.info "Loading extension(s): #{entry}"
   require_dependency "#{entry}"
 end
 Rails.logger.info "--- Loaded extensions for #{self.class}\n"

Ende

Madx
quelle