Cara terbaik untuk memuat modul / kelas dari folder lib di Rails 3?

273

Karena rilis Rails 3 terbaru bukan memuat modul dan kelas dari lib lagi, apa cara terbaik untuk memuatnya?

Dari github:

A few changes were done in this commit:

Do not autoload code in *lib* for applications (now you need to explicitly 
require them). This makes an application behave closer to an engine 
(code in lib is still autoloaded for plugins);
Vincent
sumber

Jawaban:

251

Pada Rails 2.3.9 , ada pengaturan diconfig/application.rb di mana Anda dapat menentukan direktori yang berisi file yang ingin Anda muat secara otomatis.

Dari application.rb:

# Custom directories with classes and modules you want to be autoloadable.
# config.autoload_paths += %W(#{config.root}/extras)
Slobodan Kovacevic
sumber
7
Catatan @ terima kasih juga jika Anda mencari untuk memuat ulang seluruh subtree secara otomatis app/lib.
Tom Harrison
199
# Autoload lib/ folder including all subdirectories
config.autoload_paths += Dir["#{config.root}/lib/**/"]

Sumber: Rails 3 Quicktip: Direktori lib autoload termasuk semua subdirektori, hindari pemuatan yang malas

Harap diingat bahwa file yang terkandung dalam folder lib hanya dimuat ketika server dimulai. Jika Anda ingin kenyamanan untuk memuat ulang file-file tersebut secara otomatis, baca: Rails 3 Quicktip: Auto-reload folder lib dalam mode pengembangan . Ketahuilah bahwa ini tidak dimaksudkan untuk lingkungan produksi karena pemuatan ulang permanen memperlambat mesin.

berterimakasih
sumber
Tautan sudah mati
Besi
84

Keajaiban barang-barang autoloading

Saya pikir opsi mengendalikan folder dari mana hal autoloading dilakukan telah cukup tercakup dalam jawaban lain. Namun, jika ada orang lain yang mengalami kesulitan memuat barang meskipun mereka telah mengubah jalur muat otomatisnya sesuai kebutuhan, maka jawaban ini mencoba menjelaskan apa keajaiban di balik hal pengisian otomatis ini.

Jadi ketika datang untuk memuat barang dari subdirektori ada gotcha atau konvensi yang harus Anda ketahui. Terkadang sihir Ruby / Rails (saat ini sebagian besar Rails) dapat membuatnya sulit untuk memahami mengapa sesuatu terjadi. Modul apa pun yang dideklarasikan di jalur pengisian otomatis hanya akan dimuat jika nama modul sesuai dengan nama direktori induk. Jadi jika Anda mencoba memasukkan lib/my_stuff/bar.rbsesuatu seperti:

module Foo
  class Bar
  end
end

Itu tidak akan dimuat secara otomatis. Kemudian lagi jika Anda mengubah nama orang tua dir ke foosehingga tuan modul Anda di jalan: lib/foo/bar.rb. Itu akan ada untuk Anda. Pilihan lain adalah memberi nama file yang ingin Anda isi ulang secara otomatis dengan nama modul. Tentunya hanya ada satu file dengan nama itu. Jika Anda perlu membagi barang-barang Anda menjadi banyak file tentu saja Anda dapat menggunakan satu file untuk memerlukan file lain, tetapi saya tidak merekomendasikan itu, karena ketika ketika dalam mode pengembangan dan Anda memodifikasi file-file lain maka Rails tidak dapat secara otomatis muat ulang untuk Anda. Tetapi jika Anda benar-benar ingin, Anda dapat memiliki satu file dengan nama modul yang kemudian menentukan file aktual yang diperlukan untuk menggunakan modul. Jadi Anda dapat memiliki dua file: lib/my_stuff/bar.rbdan lib/my_stuff/foo.rbyang pertama sama seperti di atas dan yang terakhir berisi satu baris:require "bar"dan itu akan bekerja sama saja.

PS Saya merasa harus menambahkan satu hal penting lagi. Pada akhir-akhir ini, setiap kali saya ingin memiliki sesuatu di direktori lib yang perlu di-autoload, saya cenderung mulai berpikir bahwa jika ini adalah sesuatu yang sebenarnya saya kembangkan secara spesifik untuk proyek ini (yang biasanya, mungkin suatu hari nanti) berubah menjadi potongan "statis" kode yang digunakan dalam banyak proyek atau git submodule, dll. dalam hal ini pasti harus ada di folder lib) maka mungkin tempatnya tidak ada di folder lib sama sekali. Mungkin itu harus dalam subfolder di bawah folder aplikasi · Saya punya perasaan bahwa ini adalah cara rel baru dalam melakukan sesuatu. Tentunya, sihir yang sama sedang bekerja di mana pun di dalam jalur pengisian otomatis yang Anda letakkan di barang-barang Anda sehingga ada baiknya untuk hal-hal ini. Bagaimanapun, ini hanya pemikiran saya tentang masalah ini. Anda bebas untuk tidak setuju. :)


UPDATE: Tentang jenis sihir ..

Seperti yang ditunjukkan Severin dalam komentarnya, inti "autoload a module module" pasti adalah bagian dari Ruby, tetapi jalur autoload paths tidak. Anda tidak perlu menggunakan Railsautoload :Foo, File.join(Rails.root, "lib", "my_stuff", "bar"). Dan ketika Anda akan mencoba untuk referensi modul Foo untuk pertama kalinya maka itu akan dimuat untuk Anda. Namun apa yang dilakukan Rails adalah memberi kita cara untuk mencoba dan memuat barang secara otomatis dari folder terdaftar dan ini telah dilaksanakan sedemikian rupa sehingga perlu mengasumsikan sesuatu tentang konvensi penamaan. Jika belum diimplementasikan seperti itu, maka setiap kali Anda mereferensikan sesuatu yang saat ini tidak dimuat itu harus melalui semua file di semua folder autoload dan memeriksa apakah ada di antara mereka berisi apa yang Anda coba rujuk. Ini pada gilirannya akan mengalahkan gagasan autoloading dan autoreloading. Namun, dengan konvensi ini di tempat itu dapat mengurangi dari modul / kelas Anda mencoba memuat mana yang mungkin didefinisikan dan hanya memuat itu.

Timo
sumber
1
Mengapa sihir Ruby ini? Ruby hanya menyediakan fungsi Modul # autoload yang dapat Anda gunakan untuk memerintahkan file yang sedang dimuat saat mengakses konstanta (tidak terdefinisi) (lihat ruby-doc.org/core-1.9.3/Module.html#method-i-autoload ). Pencocokan nama modul / kelas ke direktori / file menurut saya dilakukan di Rails / ActiveSupport (misalnya di sini: github.com/rails/rails/blob/… ). Apakah aku salah?
severin
Ya, saya yakin Anda benar. Saya terlalu tergesa-gesa untuk "mengoreksi" jawaban asli saya ketika Zabba menunjukkan "cacatnya". Biarkan saya memperbarui jawaban saya sedikit lebih untuk memperjelas masalah ini.
Timo
1
Saya menghabiskan setengah jam untuk mengoceh. Saya perlu (ingin) untuk memuat otomatis Sprockets :: JSRender :: Processor. Path untuk itu dapat ditemukan dengan masuk ke rails console dan melakukan "Sprockets :: JSRender :: Processor" .underscore dan disvoering bahwa itu adalah "sprockets / js_render / processor" (dengan .rb ditambahkan) HTH seseorang.
pedz
Anda baru saja menyelamatkan kewarasan saya. ~ napas lega yang dalam ~ terima kasih banyak untuk berbagi :)
Brenden
Terima kasih atas komentar yang paling membantu ini. Saya tidak mengerti mengapa beberapa modul berperilaku seperti itu sampai saya membaca komentar Anda. Berkat kamu!
mjnissim
41

Peringatan: jika Anda ingin memuat 'monkey patch' atau 'open class' dari folder 'lib' Anda, jangan gunakan pendekatan 'autoload' !!!

  • " Config.autoload_paths " pendekatan: hanya bekerja jika Anda memuat sebuah kelas yang didefinisikan hanya dalam SATU tempat. Jika beberapa kelas telah didefinisikan di tempat lain, maka Anda tidak dapat memuatnya lagi dengan pendekatan ini.

  • " config / initializer / load_rb_file.rb ": selalu berhasil! apa pun kelas target adalah kelas baru atau "kelas terbuka" atau "tambalan monyet" untuk kelas yang ada, selalu berhasil!

Untuk detail lebih lanjut, lihat: https://stackoverflow.com/a/6797707/445908

Siwei Shen 申思维
sumber
6
Ini adalah perbedaan kritis untuk dipahami. Terima kasih untuk ini.
Tyler Collier
28

Sangat mirip, tapi saya pikir ini sedikit lebih elegan:

config.autoload_paths += Dir["#{config.root}/lib", "#{config.root}/lib/**/"]
Brian Armstrong
sumber
18

Dalam kasus saya, saya hanya mencoba memuat file langsung di bawah lib dir.

Dalam application.rb ...

require '/lib/this_file.rb' 

tidak berfungsi, bahkan di konsol dan kemudian ketika saya mencoba

require './lib/this_file.rb' 

dan rel memuat file dengan sempurna.

Saya masih sangat noob dan saya tidak yakin mengapa ini berhasil tetapi berhasil. Jika seseorang ingin menjelaskannya kepada saya, saya akan menghargainya: Saya harap ini membantu seseorang.

Nick Res
sumber
2
Itu karena ./lib/this_file.rb terlihat di direktori saat ini (di Rails console, itu akan menjadi root Rails Anda), dan /lib/this_file.rb mencari itu sebagai path absolut. Contoh: ./lib/this_file.rb = /var/www/myrailsapp/lib/this_file.rb, /lib/this_file.rb = /lib/this_file.rb
Jason
7

Saya memiliki masalah yang sama. Inilah cara saya menyelesaikannya. Solusinya memuat direktori lib dan semua subdirektori (tidak hanya direktori langsung). Tentu saja Anda dapat menggunakan ini untuk semua direktori.

# application.rb
config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]
hjuskewycz
sumber
5
Ini memiliki efek samping yang buruk dari konvensi namespacing Rails yang benar-benar musnah. Jika lib / bar / foo.rb mendefinisikan Bar :: Foo muncul sebelum lib / foo.rb mendefinisikan Foo di pencarian autoload, maka Anda akan mendapatkan kesalahan yang membingungkan seperti Expected lib/bar/foo.rb to define constant Foojika Anda mencoba memuat lib / foo.rb dengan merujuk ke Foo konstan.
Yakub
5

config.autoload_paths tidak berfungsi untuk saya. Saya menyelesaikannya dengan cara lain

Ruby on rails 3 tidak memuat kembali kode otomatis dari folder / lib. Saya menyelesaikannya dengan memasukkan ke dalamApplicationController

Dir["lib/**/*.rb"].each do |path|
  require_dependency path
end 
msa.im
sumber
4

Jika hanya file tertentu yang memerlukan akses ke modul di lib, tambahkan saja pernyataan perlu ke file yang membutuhkannya. Misalnya, jika satu model perlu mengakses satu modul, tambahkan:

require 'mymodule'

di bagian atas file model.rb.

Mike Fischer
sumber
50
Anda tidak boleh menggunakan di requiredalam aplikasi rel, karena mencegah ActiveSupport::Dependencies[un] memuat kode itu dengan benar. Sebaliknya Anda harus menggunakan config.autoload_pathsseperti jawaban di atas, dan kemudian sertakan / perluas sesuai kebutuhan.
ben_h
13
Terima kasih @Mike, saya akan melakukan apa yang Anda lakukan, senang melihat penjelasan mengapa itu buruk, terima kasih karena tidak menghapus jawabannya.
pupeno
bagaimana dengan menyertakan 'mymodule' jika Anda hanya ingin memuat satu modul?
Mike
1
@ben_h Haruskah Anda tidak requiredari mana pun di aplikasi Rails? Dalam tugas menyapu saya saat ini require-ing dan include-ing modul yang hidup lib/. Haruskah saya tidak melakukan itu?
Dennis
@ben_h Pencarian saya mengungkapkan bahwa itu umum untuk kode requireAnda lib/(misalnya posting blog ini , jawaban SO ini ). Saya masih tidak yakin tentang semuanya. Bisakah Anda memberikan lebih banyak bukti di balik klaim untuk tidak menggunakan require?
Dennis
2

Eja nama file dengan benar.

Serius. Saya bertarung dengan kelas selama satu jam karena kelasnya adalah Governance :: ArchitectureBoard dan file tersebut berada di lib / governance / architecture_baord.rb (ditransformasikan O dan A di "papan")

Tampak jelas dalam retrospeksi, tapi itu adalah iblis yang melacaknya. Jika kelas tidak didefinisikan dalam file yang diharapkan Rails berdasarkan pada munging nama kelas, itu sama sekali tidak akan menemukannya.

David Hempy
sumber
2

Sampai Rails 5, dianjurkan untuk menempatkan folder lib bawah direktori aplikasi atau malah membuat ruang nama yang bermakna lain untuk folder sebagai services, presenters, featuresdll dan meletakkannya di bawah direktori aplikasi untuk auto pemuatan oleh rel.

Silakan periksa Tautan Diskusi GitHub ini juga.

Ashik Salman
sumber