Rails / lib-Module und

83

Ich schreibe einen benutzerdefinierten Wrapper für das open_flash_chartPlugin. Es wird platziert /libund als Modul geladen ApplicationController.

Ich habe jedoch ein Klassenhierarchie- oder SMTH-Problem.

Von jedem Controller kann ich Zugriff auf open_flash_chartFunktionen wie OpenFlashChart, Lineetc

In einer Klasse in einem /libModul funktioniert es jedoch nicht!

Irgendwelche Ideen?

Mantas
quelle
Ich
S.Yadav

Antworten:

147

Es gibt zwei Möglichkeiten, wie Dateien in Rails geladen werden:

  • Es wird im Autoload-Prozess registriert und Sie verweisen auf eine Konstante, die dem Dateinamen entspricht. Wenn Sie beispielsweise app/controllers/pages_controller.rbPagesController haben und darauf verweisen, app/controllers/pages_controller.rbwird dieser automatisch geladen. Dies geschieht für eine voreingestellte Liste von Verzeichnissen im Ladepfad. Dies ist eine Funktion von Rails und nicht Teil des normalen Ruby-Ladevorgangs.
  • Dateien sind explizit required. Wenn eine Datei required ist, durchsucht Ruby die gesamte Liste der Pfade in Ihren Ladepfaden und findet den ersten Fall, in dem sich die Datei required im Ladepfad befindet. Sie können den gesamten Ladepfad anzeigen, indem Sie $ LOAD_PATH (ein Alias ​​für $ :) überprüfen.

Da libsich in Ihrem Ladepfad zwei Optionen befinden: Sie können Ihre Dateien entweder mit denselben Namen wie die Konstanten benennen, sodass Rails sie automatisch aufnimmt, wenn Sie auf die betreffende Konstante verweisen, oder das Modul explizit anfordern.

Ich bemerke auch, dass Sie über eine andere Sache verwirrt sein könnten. ApplicationController ist nicht das Stammobjekt im System. Beobachten:

module MyModule
  def im_awesome
    puts "#{self} is so awesome"
  end
end

class ApplicationController < ActionController::Base
  include MyModule
end

class AnotherClass
end

AnotherClass.new.im_awesome
# NoMethodError: undefined method `im_awesome' for #<AnotherClass:0x101208ad0>

Sie müssen das Modul in jede Klasse aufnehmen, in der Sie es verwenden möchten.

class AnotherClass
  include MyModule
end

AnotherClass.new.im_awesome
# AnotherClass is so awesome

Um das Modul überhaupt einbinden zu können, muss es natürlich verfügbar sein (mit einer der oben genannten Techniken).

Yehuda Katz
quelle
2
Ich wollte nur hinzufügen: Wenn eines Ihrer Module in / lib (oder in einem der Autoload-Verzeichnisse) bereits definiert ist; Wenn Sie beispielsweise ActiveRecord oder String überladen, müssen Sie dies explizit verlangen, sonst wird es nicht geladen
Mike
1
Seltsamerweise erhalte ich: nicht initialisierte Konstante GaClient (NameError), es sei denn, ich benötige vorher 'ga_client' (die Klasse ist in lib / ga_client.rb definiert). Gibt es eine Dokumentation für das Benennungsschema für das automatische Laden?
Mkirk
86

In Rails 3 / lib werden Module nicht automatisch geladen.

Dies liegt daran, dass die Zeile:

# config.autoload_paths += %W(#{config.root}/extras)

in config / application.rb wird kommentiert.

Sie können versuchen, diese Zeile zu kommentieren oder (für mich hat es noch besser funktioniert) diesen Kommentar zu hinterlassen (zum späteren Nachschlagen) und diese beiden Zeilen hinzufügen:

config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]
diegopau
quelle
Dadurch wird der ../libPfad im ApplicationName::Application.config.autoload_pathsArray dupliziert .
Jibiel
1
@jibiel also wie ist die Auflösung hier?
ylluminate
4
Warum hat die letztere Option für Sie besser funktioniert? Warum wurde die Standardeinstellung geändert? Es muss einen Grund geben, obwohl viele Leute Arbeit finden.
Karbass
@ylluminate Also Single config.autoload_paths += Dir["#{config.root}/lib/**/"]sollte reichen. @ckarbass In der zweiten Zeile haben Sie die Freiheit, Ihre Dienstprogramme in Unterordnern zu organisieren und so Ihre Module mit Namespaces zu versehen . Das wars so ziemlich. Und hier ist, warum die Standardeinstellung geändert wurde. Besser spät als nie :)
Jibiel
Das scheint mir ein bisschen chaotisch. Würde dies nicht unnötig automatisch Rechenaufgaben laden?
Dennis
22

Neben dem Auskommentieren von config.autoload_paths (I'm on Rails 3.1.3) funktionierte für mich die Erstellung eines Initialisierers wie folgt:

#config/initializers/myapp_init.rb
require 'my_module'    
include MyModule

Auf diese Weise kann ich mymodule-Methoden von überall und als Klassenmethoden Model.mymodule_methododer als Instanzmethoden aufrufenmymodel.mymodule_method

Vielleicht kann ein Experte die Auswirkungen erklären. Verwenden Sie es jetzt auf eigenes Risiko.

Edit: Danach denke ich, ein besserer Ansatz wäre:

Erstellen Sie einen Initialisierer wie folgt:

#config/initializers/myapp_init.rb
require my_module

Fügen Sie das Modul bei Bedarf wie folgt hinzu:

1) Wenn Sie es als "Klassenmethoden" verwenden möchten, verwenden Sie "Erweitern":

class Myclass < ActiveRecord::Base
   extend MyModule
   def self.method1
      Myclass.my_module_method
   end
end

2) Wenn Sie es als "Instanzmethoden" verwenden möchten, fügen Sie es in die Klassendefinition ein:

class Myclass < ActiveRecord::Base
include MyModule
   def method1
      self.my_module_method 
   end
end

3) Denken Sie daran, dass include MyModulesich dies auf eine Datei my_module.rbin Ihrem Ladepfad bezieht, die zuerst benötigt werden muss

Fernando Fabreti
quelle
3
Ich habe meine Modul erstellt libOrdner, also ich habe hinzugefügt config.autoload_paths += %W(#{config.root}/lib)auf config/application.rbDatei. Danach folgte ich Ihrem Vorschlag, eine config/initializers/myapp_init.rbDatei und deren Inhalt hinzuzufügen . Alles ist gut. Vielen Dank :)
SMMousavi
Obwohl dies requirefür mich funktioniert und das automatische Laden nicht funktioniert (undefinierte Modulmethode), heißt es in diesem Kommentar, dass Sie es nicht verwenden solltenrequire .
Dennis
3

So verwenden Sie das Modul lib/my_module.rbin Ihren Modellen und Steuerungen:

In config/application.rb:

config.watchable_dirs['lib'] = [:rb]

In Ihrem Modell (ähnliche Idee für Ihren Controller):

require_dependency 'my_module'

class MyModel < ActiveRecord::Base
  include MyModule

  MyModule.some_method
end

Diese Methode wird unter http://hakunin.com/rails3-load-paths ausführlicher beschrieben

Dennis
quelle
0

Möglicherweise möchten Sie zum Zeitpunkt der Anwendungsinitialisierung Dateien explizit im Verzeichnis lib laden.
In meiner config / application.rb habe ich einen Eintrag als.
config.autoload_paths += %W(#{config.root}/lib)

Dies kann auch der Fall sein, dass der Name / die Hierarchie des Moduls nicht mit dem in der Datei übereinstimmt oder dass der Speicherort / Name der Datei nicht mit dieser Hierarchie übereinstimmt. Laden Sie sie daher automatisch dieser Datei ist auch nicht möglich. Als ich also unten in config / application.rb as einen Eintrag hinzufügte,
require "./lib/file_name_without_extention
funktionierte das einwandfrei.

Swapnil Chincholkar
quelle