Moduły Rails / lib i

83

Piszę niestandardowy wrapper dla open_flash_chartwtyczki. Jest umieszczany /libi ładowany jako moduł ApplicationController.

Jednak mam jakiś problem z hierarchią klas lub czymś.

Z dowolnego sterownika mogę uzyskać dostęp do open_flash_chartfunkcji jak OpenFlashChart, Lineetc

Jednak w klasie w /libmodule to nie działa!

Jakieś pomysły?

Mantas
źródło
mam nadzieję, że to pomoże ci stackoverflow.com/questions/17304110/…
S.Yadav

Odpowiedzi:

147

Pliki są ładowane do Railsów na dwa sposoby:

  • Jest rejestrowany w procesie automatycznego ładowania, a Ty odwołujesz się do stałej, która odpowiada nazwie pliku. Na przykład, jeśli masz app/controllers/pages_controller.rbi odwołujesz się do PagesController, app/controllers/pages_controller.rbzostanie automatycznie załadowany. Dzieje się tak w przypadku wstępnie ustawionej listy katalogów w ścieżce ładowania. Jest to cecha Railsów i nie jest częścią normalnego procesu ładowania Rubiego.
  • Pliki są wyraźnie required. Jeśli plik jest required, Ruby przegląda całą listę ścieżek w twoich ścieżkach ładowania i znajduje pierwszy przypadek, w którym plik, który required, znajduje się na ścieżce ładowania. Możesz zobaczyć całą ścieżkę ładowania, sprawdzając $ LOAD_PATH (alias dla $ :).

Ponieważ libznajduje się w twojej ścieżce ładowania, masz dwie opcje: albo nazwij swoje pliki tymi samymi nazwami, co stałe, aby Railsy automatycznie je pobierały, gdy odwołasz się do danej stałej, lub wyraźnie zażądają modułu.

Zauważyłem również, że możesz być zdezorientowany co do innej rzeczy. ApplicationController nie jest głównym obiektem w systemie. Przestrzegać:

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>

Będziesz musiał dołączyć moduł do dowolnej klasy, w której chcesz go używać.

class AnotherClass
  include MyModule
end

AnotherClass.new.im_awesome
# AnotherClass is so awesome

Oczywiście, aby móc dołączyć moduł w pierwszej kolejności, musisz go mieć (używając jednej z powyższych technik).

Yehuda Katz
źródło
2
Chciałem tylko dodać: Jeśli jeden z modułów w / lib (lub w jednym z katalogów automatycznego ładowania) jest już zdefiniowany; przykład przeciążasz ActiveRecord lub String, będziesz musiał jawnie tego wymagać lub nie zostanie załadowany
Mike
1
o dziwo, otrzymuję: niezainicjowaną stałą GaClient (NameError), chyba że wcześniej wymagam 'ga_client' (klasa jest zdefiniowana w lib / ga_client.rb). Czy istnieje dokumentacja dotycząca schematu nazewnictwa automatycznego ładowania?
mkirk
87

W Railsach 3 / lib moduły nie są ładowane automatycznie.

Dzieje się tak, ponieważ linia:

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

wewnątrz config / application.rb jest komentowane.

Możesz spróbować odkomentować tę linię lub (dla mnie zadziałało to nawet lepiej), zostawić to skomentowane (do przyszłego wykorzystania) i dodać te dwie linie:

config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]
diegopau
źródło
Spowoduje to zduplikowanie ../libścieżki w ApplicationName::Application.config.autoload_pathstablicy.
jibiel
1
@jibiel, więc jaka jest tutaj rozdzielczość?
yluminate
4
dlaczego ta druga opcja działała lepiej dla Ciebie? Ponadto, dlaczego zmieniono ustawienie domyślne ... musi być jakiś powód, mimo że wiele osób szukało pracy.
ckarbass
@ylluminate Więc single config.autoload_paths += Dir["#{config.root}/lib/**/"]powinno wystarczyć. @ckarbass Druga linia daje swobodę organizowania narzędzi w podfoldery, a tym samym ujednolicenia modułów za pomocą przestrzeni nazw . To prawie wszystko. A oto dlaczego wartość domyślna została zmieniona. Lepiej późno niż wcale :)
jibiel
Wydaje mi się to trochę nieuporządkowane. Czy nie byłoby to niepotrzebne automatyczne ładowanie zadań rake?
Dennis
22

Pomijając odkomentowanie config.autoload_paths (jestem na Railsach 3.1.3), zadziałało dla mnie stworzenie takiego inicjalizatora:

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

W ten sposób mogę wywoływać metody mymodule z dowolnego miejsca i jako metody klasowe Model.mymodule_methodlub jako metody instancjimymodel.mymodule_method

Może jakiś ekspert wyjaśni konsekwencje tego. Teraz używaj go na własne ryzyko.

Edycja: Myślę, że później lepszym rozwiązaniem byłoby:

utwórz inicjalizator w następujący sposób:

#config/initializers/myapp_init.rb
require ‘my_module’

W razie potrzeby dołącz moduł, na przykład:

1) jeśli chcesz używać go jako „Metody klasowe”, użyj opcji „rozszerz”:

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

2) jeśli chcesz używać go jako „Metody instancji”, umieść go w definicji klasy:

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

3) pamiętaj, że include MyModuleodnosi się do pliku my_module.rbw ścieżce ładowania, który musi być wymagany jako pierwszy

Fernando Fabreti
źródło
3
Stworzyłem mój moduł na libfolderze, więc dodałem config.autoload_paths += %W(#{config.root}/lib)na config/application.rbpliku. Następnie postąpiłem zgodnie z twoją sugestią, aby dodać config/initializers/myapp_init.rbplik i jego zawartość. Wszystko jest dobrze.
Wielkie
Mimo że requiredziała dla mnie, a automatyczne ładowanie nie działa (niezdefiniowana metoda modułu), ten komentarz mówi, że nie powinieneś używaćrequire .
Dennis
3

Aby użyć modułu lib/my_module.rbw swoich modelach i kontrolerach:

W config/application.rb:

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

W Twoim modelu (podobny pomysł na kontroler):

require_dependency 'my_module'

class MyModel < ActiveRecord::Base
  include MyModule

  MyModule.some_method
end

Ta metoda jest opisana bardziej szczegółowo pod adresem http://hakunin.com/rails3-load-paths

Dennis
źródło
0

Może się zdarzyć, że chcesz jawnie załadować plik (i) do katalogu lib w czasie inicjalizacji aplikacji.
W moim config / application.rb mam wpis jako,
config.autoload_paths += %W(#{config.root}/lib)

Może się tak zdarzyć, że nazwa / hierarchia modułu nie jest taka sama, jak w pliku lub lokalizacja / nazwa pliku nie jest taka sama jak ta hierarchia, więc automatyczne ładowanie tego pliku również nie jest możliwe. Więc kiedy dodałem wpis na dole config / application.rb as,
require "./lib/file_name_without_extention
działał dobrze.

Swapnil Chincholkar
źródło