(require … :reload)
andを使用してClojureコードをリロードすること:reload-all
は非常に問題があります:
相互に依存する 2 つの名前空間を変更する場合は、コンパイル エラーを回避するために正しい順序で再読み込みすることを忘れないでください。
ソース ファイルから定義を削除して再ロードした場合、それらの定義は引き続きメモリ内で使用できます。他のコードがそれらの定義に依存している場合、そのコードは引き続き機能しますが、次に JVM を再起動したときに壊れます。
再ロードされた名前空間に が含まれている場合defmulti
は、関連するすべてのdefmethod
式も再ロードする必要があります。
再ロードされた名前空間に が含まれている場合はdefprotocol
、そのプロトコルを実装するレコードまたはタイプも再ロードし、それらのレコード/タイプの既存のインスタンスを新しいインスタンスに置き換える必要があります。
再ロードされた名前空間にマクロが含まれている場合は、それらのマクロを使用する名前空間も再ロードする必要があります。
実行中のプログラムに、再ロードされた名前空間の値を閉じる関数が含まれている場合、それらの閉じられた値は更新されません。(これは、関数の構成として「ハンドラ スタック」を構築する Web アプリケーションでは一般的です。)
clojure.tools.namespace ライブラリーは状況を大幅に改善します。名前空間の依存グラフに基づいてスマートなリロードを行う簡単な更新機能を提供します。
myapp.web=> (require '[clojure.tools.namespace.repl :refer [refresh]])
nil
myapp.web=> (refresh)
:reloading (myapp.web)
:ok
refresh
残念ながら、関数を参照した名前空間が変更された場合、2 回目のリロードは失敗します。これは、tools.namespace が新しいコードをロードする前に現在のバージョンの名前空間を破棄するためです。
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)
この問題の回避策として完全修飾 var 名を使用できますが、個人的には更新のたびに入力する必要がないことを好みます。上記の別の問題は、メイン名前空間をリロードした後、標準の REPL ヘルパー関数 (doc
や などsource
) が参照されなくなることです。
これらの問題を解決するには、確実にリロードできるように、ユーザー名前空間の実際のソース ファイルを作成することを好みます。ソースファイルを入れました~/.lein/src/user.clj
が、どこにでも入れて構いません。ファイルは、次のようにトップ ns 宣言で更新機能を必要とする必要があります。
(ns user
(:require [clojure.tools.namespace.repl :refer [refresh]]))
ファイルを配置した場所がクラスパスに追加されるように、 leiningen ユーザープロファイルをセットアップできます。~/.lein/profiles.clj
プロファイルは次のようになります。
{:user {:dependencies [[org.clojure/tools.namespace "0.2.7"]]
:repl-options { :init-ns user }
:source-paths ["/Users/me/.lein/src"]}}
REPL を起動するときに、ユーザーの名前空間をエントリ ポイントとして設定していることに注意してください。これにより、アプリケーションのメインの名前空間ではなく、ユーザーの名前空間で REPL ヘルパー関数が参照されるようになります。そうすれば、作成したばかりのソース ファイルを変更しない限り、それらが失われることはありません。
お役に立てれば!