Memuat file lib secara otomatis di Rails 4

229

Saya menggunakan baris berikut dalam initializer untuk memuat kode secara otomatis di /libdirektori saya selama pengembangan:

config / initializers / custom.rb:

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

(dari Rails 3 Quicktip: Auto reload folder lib dalam mode pengembangan )

Ini berfungsi dengan baik, tetapi terlalu tidak efisien untuk digunakan dalam produksi - Alih-alih memuat lib pada setiap permintaan, saya hanya ingin memuatnya saat start up. Blog yang sama memiliki artikel lain yang menjelaskan cara melakukan ini:

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

Namun, ketika saya beralih ke itu, bahkan dalam pengembangan, saya mendapatkan NoMethodErrors ketika mencoba menggunakan fungsi lib.

Contoh salah satu file lib saya:

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

Memanggil Time.milli_stampakan melempar NoMethodError

Saya menyadari orang lain telah menjawab pertanyaan serupa pada SO tetapi mereka semua tampaknya berurusan dengan konvensi penamaan dan masalah lain yang saya tidak perlu khawatirkan sebelumnya - kelas lib saya sudah berfungsi untuk pemuatan per permintaan, saya hanya ingin mengubahnya untuk memuat per startup . Apa cara yang benar untuk melakukan ini?

Yarin
sumber
Apakah folder config / initializers dimuat secara otomatis ketika aplikasi Rails dijalankan?
Jwan622

Jawaban:

548

Saya pikir ini dapat menyelesaikan masalah Anda:

  1. di config / application.rb :

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

    dan simpan konvensi penamaan yang tepat di lib .

    di lib / foo.rb :

    class Foo
    end

    di lib / foo / bar.rb :

    class Foo::Bar
    end
  2. jika Anda benar-benar ingin melakukan beberapa tambalan monyet di file seperti lib / extensions.rb , Anda mungkin memerlukannya secara manual:

    di config / initializers / require.rb :

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

PS

ifyouseewendy
sumber
1
@ ifyouseewendy- Anda benar sekali - karena extensions.rb tidak mengikuti konvensi penamaan Rails, Rails tidak akan memasukkannya dalam proses pemuatan. Saya membuatnya bekerja dengan secara manual membutuhkannya.
Yarin
@ifyouseewendy bagaimana saya bisa memasukkan file sebelum model dimuat? tambahkan path ke autoload itu keren, tetapi bagaimana mengontrol urutan penyertaan? thx
Matrix
@ Matrix "termasuk file sebelum model dimuat", Anda dapat secara manual memerlukan file Anda tanpa menggunakan fitur pengisian otomatis.
ifyouseewendy
@ifyouseewendy jika saya memerlukannya di inisialisasi tetapi file dalam autoload_path, itu akan dimuat ulang (dimuat 2 kali) atau tidak? mereka adalah "wajib_ sekali" seperti di php?
Matriks
5
Ini sepertinya tidak berfungsi di Rails 5 API dalam produksi (tetapi tidak dalam pengembangan). Saya percaya bahwa Anda perlu menggunakannya config.eager_load_paths << Rails.root.join('lib'). Namun, itu memiliki kelemahan utama dalam eager_load_pathsmemuat semuanya dalam tugas juga. Saya pikir solusi lulalala lebih baik. Berikut adalah posting blog dengan rincian lebih lanjut: blog.arkency.com/2014/11/...
hirowatari
33

Meskipun ini tidak langsung menjawab pertanyaan, tetapi saya pikir itu adalah alternatif yang baik untuk menghindari pertanyaan itu sama sekali.

Untuk menghindari semua autoload_pathsatau eager_load_pathskerumitan, membuat "lib" atau "misc" direktori di bawah direktori "app". Tempatkan kode seperti yang biasanya Anda lakukan di sana, dan Rails akan memuat file seperti bagaimana memuat (dan memuat kembali) file model.

lalalala
sumber
3
Saya di Rails 4.2. dan itu tidak secara otomatis memuat file di bawah app, saya harus melakukannya secara manual ...... atau perlu meletakkannya di jalur autload ..
Arup Rakshit
6
Anda salah, Arup, subdirektori direktori aplikasi secara otomatis ada di dalam array autoload_paths di Rails 4.2.Lihat edgeguides.rubyonrails.org/…
Dr.Strangelove
Kecuali app/viewsdirektori yang tidak ditambahkan; atau lebih tepatnya dihapus secara eksplisit.
James B. Byrne
1
Jawaban yang bagus Satu-satunya hal yang bekerja untuk saya di rel 5 / api.
jstafford
6
Hanya ingat bahwa libini dimaksudkan untuk kode yang dapat diterapkan ke beberapa proyek dan mungkin dapat diekstraksi menjadi permata. Jika tidak membuat folder yang lebih tepat di bawah pencarian aplikasi sebagai services/atau presenters/dan bahkan menundukkannya.
PhilT
6

Ini mungkin membantu seseorang seperti saya yang menemukan jawaban ini ketika mencari solusi untuk bagaimana Rails menangani pemuatan kelas ... Saya menemukan bahwa saya harus mendefinisikan modulenama yang cocok dengan nama file saya dengan tepat, daripada hanya mendefinisikan kelas:

Dalam file lib / development_mail_interceptor.rb (Ya, saya menggunakan kode dari 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

berfungsi, tetapi tidak dimuat jika saya tidak meletakkan kelas di dalam modul.

sama
sumber
1
Dalam ruby, "cocok dengan tepat" berarti bahwa file tersebut terletak di sistem file di LOAD_PATH/module/class.rb(garis bawah) di mana LOAD_PATHberada di jalur pemuatan yang digunakan oleh aplikasi Ruby (autoload_paths dalam kasus Rails). libtelah berfluktuasi dari yang dimuat secara otomatis oleh Rails ke tidak dimuat secara otomatis, dan dalam versi terbaru (> = Rails 3.x) itu tidak dimuat secara otomatis. Sihir apa pun yang membuat ini bekerja untuk Anda tidak disarankan. Mungkin itu adalah Railscast lama?
Peter H. Boling
0

Gunakan config.to_prepare untuk memuat tambalan / ekstensi monyet Anda untuk setiap permintaan dalam mode pengembangan.

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"

akhir

Madx
sumber