Automatyczne ładowanie plików lib w Railsach 4

229

Używam następującego wiersza w inicjalizatorze, aby automatycznie załadować kod w moim /libkatalogu podczas programowania:

config / initializers / custom.rb:

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

(z podręcznika Rails 3: Automatyczne ładowanie folderów lib w trybie programowania )

Działa świetnie, ale jest zbyt nieefektywny w produkcji - Zamiast ładować biblioteki lib na każde żądanie, chcę je załadować przy uruchomieniu. Ten sam blog ma inny artykuł opisujący, jak to zrobić:

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/**/"]

Jednak po przejściu na to, nawet w fazie rozwoju, dostaję NoMethodErrors, gdy próbuję użyć funkcji lib.

Przykład jednego z moich plików lib:

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

Wywołanie Time.milli_stampspowoduje zgłoszenie NoMethodError

Zdaję sobie sprawę, że inni odpowiedzieli na podobne pytania dotyczące SO, ale wszystkie wydają się mieć do czynienia z konwencjami nazewnictwa i innymi zagadnieniami, o które nie musiałem się wcześniej martwić - Moje klasy lib już działały przy ładowaniu na żądanie, po prostu chcę to zmienić do per- startowego załadunku. Jak to zrobić?

Yarin
źródło
Czy folder config / initializers jest ładowany automatycznie po uruchomieniu aplikacji Rails?
Jwan622,

Odpowiedzi:

548

Myślę, że to może rozwiązać twój problem:

  1. w config / application.rb :

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

    i zachowaj właściwą konwencję nazewnictwa w lib .

    w lib / foo.rb :

    class Foo
    end

    w lib / foo / bar.rb :

    class Foo::Bar
    end
  2. jeśli naprawdę chcesz zrobić kilka łat małp w pliku, takich jak lib / extensions.rb , możesz ręcznie tego wymagać:

    w config / initializers / requ.rb :

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

PS

ifyouseewendy
źródło
1
@ ifyouseewendy - Masz rację - ponieważ extensions.rb nie przestrzegał konwencji nazewnictwa Railsów, Railsy nie uwzględniłyby tego w procesie ładowania. Uruchomiłem go ręcznie, wymagając go.
Yarin
@ifyouseewendy jak mogę dołączyć pliki przed załadowaniem modeli? dodaj ścieżkę do automatycznego ładowania, to fajne, ale jak kontrolować kolejność włączania? dzięki
Matrix
@Matrix „dołącz pliki przed załadowaniem modeli”, możesz ręcznie wymagać pliku bez korzystania z funkcji automatycznego ładowania.
ifyouseewendy
@ifyouseewendy, jeśli wymagam tego w inicjalizatorze, ale plik znajduje się w ścieżce autoload_path, zostanie ponownie załadowany (załadowany 2 razy) czy nie? czy jest to „Wymagany_przykład” jak w PHP?
Matryca
5
Wydaje się, że nie działa to w interfejsie API Rails 5 podczas produkcji (ale działa w fazie rozwoju). Uważam, że musisz użyć config.eager_load_paths << Rails.root.join('lib'). Ma to jednak poważną wadę, ponieważ powoduje eager_load_pathsobciążenie również wszystkich zadań. Myślę, że rozwiązanie Lulalala jest lepsze. Oto post na blogu z dalszymi
hirowatari
33

Chociaż to nie odpowiada bezpośrednio na pytanie, ale myślę, że to dobra alternatywa, aby całkowicie uniknąć pytania.

Aby uniknąć wszystkich autoload_pathslub eager_load_pathskłopotów, stworzyć „lib” lub „Różne” katalog w katalogu „APP”. Umieść kody w normalny sposób, a Railsy załadują pliki tak, jak ładują (i ponownie ładują) modele plików.

lulalala
źródło
3
Jestem w Rails 4.2. i nie ładuje automatycznie plików poniżej app, muszę to zrobić ręcznie ...... lub umieścić go w ścieżce automatycznego ładowania.
Arup Rakshit
6
Mylisz się, Arup, wszystkie podkatalogi katalogu aplikacji są automatycznie umieszczane w tablicy autoload_paths w Rails 4.2. Zobacz edgeguides.rubyonrails.org/…
Dr.Strangelove
Z wyjątkiem app/viewskatalogu, który nie został dodany; a raczej zostaje wyraźnie usunięty.
James B. Byrne,
1
Świetna odpowiedź. Jedyna rzecz, która działała dla mnie na szynach 5 / API.
jstafford
6
Pamiętaj tylko, że libjest przeznaczony do kodu, który można zastosować do wielu projektów i ewentualnie wyodrębnić do klejnotu. Jeśli nie, utwórz bardziej odpowiedni folder podczas wyszukiwania aplikacji jako services/lub presenters/nawet podkatalogi tych.
PhilT
6

To może pomóc komuś jak ja, który znajdzie tę odpowiedź, szukając rozwiązań, w jaki sposób Railsy radzą sobie z ładowaniem klasy ... Stwierdziłem, że musiałem zdefiniować, moduleczyja nazwa odpowiednio pasowała do mojej nazwy pliku, zamiast definiować klasę:

W pliku lib / development_mail_interceptor.rb (Tak, używam kodu z 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

działa, ale nie ładuje się, jeśli nie umieściłem klasy w module.

sami
źródło
1
W Rubim „odpowiednie dopasowanie” oznacza, że ​​plik znajduje się w systemie plików w LOAD_PATH/module/class.rb(podkreślony), gdzie LOAD_PATHznajduje się na ścieżkach ładowania używanych przez aplikację Ruby (autoload_paths w przypadku Railsów). libwahał się od automatycznego ładowania przez Railsy do braku automatycznego ładowania, aw ostatnich wersjach (> = Rails 3.x) nie jest automatycznie ładowany. Jakakolwiek magia sprawia, że ​​to działa dla ciebie, nie jest zalecane. Być może jest to stary Railscast?
Peter H. Boling
0

Użyj config.to_prepare, aby załadować małpkie łatki / rozszerzenia dla każdego żądania w trybie programowania.

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"

koniec

Madx
źródło