Menambahkan direktori ke $ LOAD_PATH (Ruby)

96

Saya telah melihat dua teknik yang umum digunakan untuk menambahkan direktori file yang saat ini sedang dijalankan ke $ LOAD_PATH (atau $ :). Saya melihat keuntungan melakukan ini jika Anda tidak bekerja dengan permata. Yang satu tampak lebih bertele-tele daripada yang lain, jelas, tetapi adakah alasan untuk memilih salah satu dari yang lain?

Pertama, metode verbose (bisa berlebihan):

$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__))) unless $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__)))

dan yang lebih lugas, cepat-dan-kotor:

$:.unshift File.dirname(__FILE__)

Ada alasan untuk memilih salah satu dari yang lain?

Mark W.
sumber
2
Sebuah sedikit versi kurang verbose dari verbose satu adalah:File.expand_path(File.dirname(__FILE__)).tap {|pwd| $LOAD_PATH.unshift(pwd) unless $LOAD_PATH.include?(pwd)}
Nathan panjang
bagaimana dengan klausa "kecuali"? Bagaimana kedua hal di atas bisa setara?
Dering
Sebagai seseorang yang datang ke sini untuk mencoba memahami cara menggunakan ini, ini sangat samar. Saya tidak melihat dari mana asal nama direktori pada contoh. Saya akan sangat menghargai jika seseorang bisa menjelaskan ini.
SlySherZ
1
Menggunakan __dir__(seperti pada Ruby 2.0) dapat membuat semua ini lebih ringkas.
Nathan Long

Jawaban:

52

Saya akan mengatakan pergi dengan $:.unshift File.dirname(__FILE__)yang lain, hanya karena saya telah melihat lebih banyak penggunaannya dalam kode daripada yang $LOAD_PATHsatu, dan itu juga lebih pendek!

Ryan Bigg
sumber
Ketika saya pertama kali mulai menggunakan Ruby, saya jelas berpikir bahwa $ LOAD_PATH lebih baik. Tetapi setelah Anda lulus dari status pemula, saya hanya akan menggunakan $ LOAD_PATH jika saya mencoba membuat kode saya lebih mudah dibaca oleh seorang pemula. Meh itu trade off. Itu tergantung pada seberapa "publik" kode tersebut, selama penggunaan memori sama untuk masing-masing, yang saya asumsikan pada dasarnya demikian.
boulder_ruby
9
Tergantung pada panduan gaya yang Anda ikuti untuk proyek Anda. Panduan Gaya Ruby yang populer mengatakan untuk "Hindari menggunakan variabel khusus gaya Perl (seperti $ :, $ ;, dll.). Variabel tersebut sangat samar dan penggunaannya dalam apa pun kecuali skrip satu baris tidak disarankan."
bobmagoo
153

Jalur pemuatan Ruby sangat sering terlihat ditulis sebagai $:, tetapi hanya karena pendek, tidak membuatnya lebih baik. Jika Anda lebih suka kejelasan daripada kepintaran, atau jika singkatnya hanya membuat Anda gatal, Anda tidak perlu melakukannya hanya karena orang lain begitu. Menyapa ...

$LOAD_PATH

... dan ucapkan selamat tinggal pada ...

# I don't quite understand what this is doing...
$:

sumber
29
Selain itu, jauh lebih sulit bagi Google untuk string seperti "$:" yang hanya berisi simbol.
DSimon
23

Saya tidak terlalu menyukai cara 'cepat-dan-kotor'. Siapa pun yang baru mengenal Ruby akan memikirkan apa $:.itu Ruby .

Saya menemukan ini lebih jelas.

libdir = File.dirname(__FILE__)
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)

Atau jika saya peduli tentang memiliki jalan yang lengkap ...

libdir = File.expand_path(File.dirname(__FILE__))
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)

UPDATE 2009/09/10

Akhir-akhir ini saya telah melakukan yang berikut:

$:.unshift(File.expand_path(File.dirname(__FILE__))) unless
    $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))

Saya telah melihatnya di sejumlah besar proyek ruby ​​yang berbeda saat menjelajahi GitHub.

Sepertinya konvensi?

Luke Antins
sumber
@ LukeAntins, ini benar-benar hebat tetapi di mana saya harus "bootstrap" load_path dalam aplikasi?
gaussblurinc
@gaussblurinc Di suatu tempat 'di dekat bagian atas' lib / aplikasi Anda, tetapi itu sangat tergantung. Jika Anda memiliki binfile yang selalu relatif dengan Anda codedan itu hanya pernah dijalankan oleh binfile ... bootstrap di tempat sampah. Jika Anda memiliki perpustakaan, kemudian bootstrap di bagian atas kode perpustakaan Anda seperti di lib/code.rbuntuk mendapatkan akses ke semua yang ada di bawah lib/code/. Semoga ocehan ini membantu!
Luke Antins
1
RuboCop memberi tahu saya bahwa itu __dir__bisa digunakan untuk mendapatkan jalur ke direktori file saat ini.
Raphael
8

Jika Anda mengetik script/consoledi proyek Rails Anda dan masuk $:, Anda akan mendapatkan sebuah array yang menyertakan semua direktori yang diperlukan untuk memuat Ruby. Hal yang dapat diambil dari latihan kecil ini $:adalah sebuah array. Dengan demikian, Anda dapat menjalankan fungsi di dalamnya seperti menambahkan direktori lain dengan unshiftmetode atau <<operator. Seperti yang Anda tersirat dalam pernyataan Anda $:dan $LOAD_PATHadalah sama.

Kerugian melakukannya dengan cara cepat dan kotor seperti yang Anda sebutkan adalah: jika Anda sudah memiliki direktori di jalur boot Anda, itu akan terulang kembali.

Contoh:

Saya memiliki plugin yang saya buat bernama todo. Direktori saya terstruktur seperti ini:

/---penjaja
  |
  | --- / plugins
        |
        | --- / todo
              |
              | --- / lib
                    |
                    | --- / app
                          |
                          | --- / model
                          | --- / pengontrol
              |
              | --- / rel
                    |
                    | --- init.rb

Di file init.rb saya memasukkan kode berikut:

## In vendor/plugins/todo/rails/init.rb
    %w{ models controllers models }.each do |dir|
      path = File.expand_path(File.join(File.dirname(__FILE__), '../lib', 'app', dir))
      $LOAD_PATH << path
      ActiveSupport::Dependencies.load_paths << path
      ActiveSupport::Dependencies.load_once_paths.delete(path)
    end 

Perhatikan bagaimana saya memberi tahu blok kode untuk melakukan tindakan di dalam blok ke string 'model', 'pengontrol', dan 'model', di mana saya mengulangi 'model'. (FYI, %w{ ... }hanyalah cara lain untuk memberi tahu Ruby untuk menyimpan array string). Ketika saya menjalankan script/console, saya mengetik yang berikut ini:

>> puts $:

Dan saya mengetik ini agar lebih mudah membaca konten dalam string. Output yang saya dapatkan adalah:

...
...
./Users/Me/mySites/myRailsApp/vendor/plugins/todo/lib/app/models
./Users/Me/mySites/myRailsApp/vendor/plugins/todo/lib/app/controllers
./Users/Me/mySites/myRailsApp/vendor/plugins/todo/lib/app/models

Seperti yang Anda lihat, meskipun ini adalah contoh sederhana yang dapat saya buat saat menggunakan proyek yang sedang saya kerjakan, jika Anda tidak berhati-hati, cara cepat dan kotor akan mengarah ke jalur yang berulang. Cara yang lebih panjang akan memeriksa jalur yang berulang dan memastikan jalur tersebut tidak terjadi.

Jika Anda seorang programmer Rails yang berpengalaman, Anda mungkin memiliki gagasan yang sangat baik tentang apa yang Anda lakukan dan kemungkinan besar tidak membuat kesalahan dengan mengulangi jalur. Jika Anda seorang pemula, saya akan melanjutkan dengan cara yang lebih panjang sampai Anda benar-benar memahami apa yang Anda lakukan.

Dyba
sumber
tanggapan Anda sangat membantu dan juga dijelaskan dengan baik. Pengeditan yang disarankan: metode load_pathsdan load_once_paths.deletesudah tidak digunakan lagi. Akan terbantu untuk memperbarui baris yang merujuk pada mereka seperti: ActiveSupport::Dependencies.autoload_paths << path ActiveSupport::Dependencies.autoload_once_paths.delete(path)
Uzzar
8

Terbaik yang saya temui untuk menambahkan dir melalui jalur relatif saat menggunakan Rspec. Saya merasa cukup bertele-tele tetapi juga masih satu liner yang bagus.

$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
Dave Robertson
sumber
1

Ada permata yang memungkinkan Anda mengatur jalur pemuatan Anda dengan kode yang lebih bagus dan lebih bersih. Lihat ini: https://github.com/nayyara-samuel/load-path .

Ini juga memiliki dokumentasi yang bagus

Rubyist
sumber
-2

Saya tahu sudah lama sejak pertanyaan ini pertama kali diajukan, tetapi saya memiliki jawaban tambahan yang ingin saya bagikan.

Saya memiliki beberapa aplikasi Ruby yang dikembangkan oleh programmer lain selama beberapa tahun, dan mereka menggunakan kembali kelas yang sama di aplikasi yang berbeda meskipun mereka mungkin mengakses database yang sama. Karena ini melanggar aturan KERING, saya memutuskan untuk membuat perpustakaan kelas untuk digunakan bersama oleh semua aplikasi Ruby. Saya bisa saja meletakkannya di pustaka Ruby utama, tetapi itu akan menyembunyikan kode khusus dalam basis kode umum yang tidak ingin saya lakukan.

Saya mengalami masalah ketika saya mengalami konflik nama antara nama "profile.rb" yang sudah ditentukan, dan kelas yang saya gunakan. Konflik ini bukanlah masalah sampai saya mencoba membuat pustaka kode umum. Biasanya, Ruby mencari lokasi aplikasi terlebih dahulu, lalu pergi ke lokasi $ LOAD_PATH.

Application_controller.rb tidak dapat menemukan kelas yang saya buat, dan memberikan kesalahan pada definisi asli karena ini bukan kelas. Karena saya menghapus definisi kelas dari bagian app / models pada aplikasi, Ruby tidak dapat menemukannya di sana dan mencarinya di jalur Ruby.

Jadi, saya memodifikasi variabel $ LOAD_PATH untuk memasukkan jalur ke direktori perpustakaan yang saya gunakan. Ini dapat dilakukan di file environment.rb pada saat inisialisasi.

Bahkan dengan direktori baru yang ditambahkan ke jalur pencarian, Ruby memberikan kesalahan karena Ruby lebih memilih mengambil file yang ditentukan sistem terlebih dahulu. Jalur pencarian dalam variabel $ LOAD_PATH secara istimewa mencari jalur Ruby terlebih dahulu.

Jadi, saya perlu mengubah urutan pencarian sehingga Ruby menemukan kelas tersebut di perpustakaan umum saya sebelum mencari di perpustakaan bawaan.

Kode ini melakukannya di file environment.rb:

Rails::Initializer.run do |config|

* * * * *

path = []
path.concat($LOAD_PATH)
$LOAD_PATH.clear
$LOAD_PATH << 'C:\web\common\lib'
$LOAD_PATH << 'C:\web\common'
$LOAD_PATH.concat(path)

* * * * *

end

Saya tidak berpikir Anda dapat menggunakan salah satu konstruksi pengkodean lanjutan yang diberikan sebelumnya pada tingkat ini, tetapi berfungsi dengan baik jika Anda ingin menyiapkan sesuatu pada waktu inisialisasi di aplikasi Anda. Anda harus mempertahankan urutan asli variabel $ LOAD_PATH asli ketika ditambahkan kembali ke variabel baru, jika tidak, beberapa kelas Ruby utama akan hilang.

Di file application_controller.rb, saya cukup menggunakan file

require 'profile'
require 'etc' #etc

dan ini memuat file pustaka kustom untuk seluruh aplikasi, yaitu, saya tidak perlu menggunakan perintah require di setiap pengontrol.

Bagi saya, ini adalah solusi yang saya cari, dan saya pikir saya akan menambahkannya ke jawaban ini untuk menyampaikan informasi.

Timothy Dooling
sumber