Reload kode Clojure menggunakan (require … :reload)
dan :reload-all
adalah sangat bermasalah :
Jika Anda memodifikasi dua ruang nama yang saling bergantung, Anda harus ingat untuk memuatnya kembali dalam urutan yang benar untuk menghindari kesalahan kompilasi.
Jika Anda menghapus definisi dari file sumber dan kemudian memuatnya kembali, definisi tersebut masih tersedia dalam memori. Jika kode lain tergantung pada definisi-definisi itu, itu akan terus bekerja tetapi akan rusak ketika Anda memulai kembali JVM.
Jika memuat ulang namespace mengandung defmulti
, Anda juga harus memuat ulang semua defmethod
ekspresi terkait .
Jika memuat ulang namespace berisi defprotocol
, Anda juga harus memuat ulang catatan atau jenis yang menerapkan protokol itu dan mengganti setiap contoh yang ada dari catatan / jenis dengan contoh baru.
Jika ruang nama yang dimuat ulang berisi makro, Anda juga harus memuat ulang ruang nama yang menggunakan makro itu.
Jika program yang sedang berjalan berisi fungsi-fungsi yang menutup nilai-nilai dalam namespace yang dimuat kembali, nilai-nilai yang ditutup tidak diperbarui. (Ini biasa terjadi pada aplikasi web yang membangun "handler stack" sebagai komposisi fungsi.)
Pustaka clojure.tools.namespace meningkatkan situasi secara signifikan. Ini menyediakan fungsi refresh yang mudah yang melakukan reload cerdas berdasarkan grafik dependensi namespaces.
myapp.web=> (require '[clojure.tools.namespace.repl :refer [refresh]])
nil
myapp.web=> (refresh)
:reloading (myapp.web)
:ok
Sayangnya memuat ulang waktu kedua akan gagal jika namespace tempat Anda mereferensikan refresh
fungsi berubah. Ini karena fakta bahwa tools.namespace menghancurkan versi namespace saat ini sebelum memuat kode baru.
myapp.web=> (refresh)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: refresh in this context, compiling:(/private/var/folders/ks/d6qbfg2s6l1bcg6ws_6bq4600000gn/T/form-init819543191440017519.clj:1:1)
Anda bisa menggunakan nama var yang sepenuhnya memenuhi syarat sebagai solusi untuk masalah ini, tetapi secara pribadi saya lebih suka tidak harus mengetikkan itu pada setiap refresh. Masalah lain dengan hal di atas adalah bahwa setelah memuat kembali namespace utama fungsi pembantu REPL standar (seperti doc
dan source
) tidak lagi dirujuk di sana.
Untuk mengatasi masalah ini, saya lebih suka membuat file sumber aktual untuk namespace pengguna sehingga dapat dimuat ulang dengan andal. Saya memasukkan file sumber ~/.lein/src/user.clj
tetapi Anda dapat menempatkan di mana saja. File harus memerlukan fungsi refresh dalam deklarasi ns atas seperti ini:
(ns user
(:require [clojure.tools.namespace.repl :refer [refresh]]))
Anda dapat mengatur profil pengguna leiningen di ~/.lein/profiles.clj
lokasi yang Anda masukkan file ditambahkan ke jalur kelas. Profil akan terlihat seperti ini:
{:user {:dependencies [[org.clojure/tools.namespace "0.2.7"]]
:repl-options { :init-ns user }
:source-paths ["/Users/me/.lein/src"]}}
Perhatikan bahwa saya menetapkan namespace pengguna sebagai titik masuk saat meluncurkan REPL. Ini memastikan bahwa fungsi pembantu REPL mendapatkan referensi di namespace pengguna alih-alih namespace utama aplikasi Anda. Dengan begitu mereka tidak akan hilang kecuali Anda mengubah file sumber yang baru saja kita buat.
Semoga ini membantu!
(use 'foo.bar :reload-all)
selalu bekerja dengan baik untuk saya. Juga,(load-file)
seharusnya tidak perlu jika Anda mengatur classpath Anda dengan benar. Apa "efek yang diperlukan" yang tidak Anda dapatkan?