JVMを再起動することなく、プロジェクト全体のソースコードのリロードを実現するツールtools.namespaceを紹介します。
ClojureにはREPLが付属しており、動作を確かめながらソフトウェアを開発することができます。
修正後のソースコードをREPL上に読み込むには通常、
(use 'namespace :reload または :reload-all)
を利用します。例)
1
| |
しかし、:reload、reload-allを利用したリロードでは、
- ソースコードから削除した定義がJVM上に残ってしまう
- 依存するファイルのリロード漏れがあると、REPL上で古いコードと新しいコードが混在してしまう
といった不便な点があります。
リロードがうまくいかない場合は、JVMごと再起動するのが常套手段ですが、プロジェクトが大きくなるほど時間がかかります。
tools.namespaceを利用するとJVMを再起動することなく、プロジェクト全体のソースコードのリロードを実現できます。
tools.namespaceの使い方
project.cljのdependenciesに以下を追記します。
1
| |
開発中にソースコードを変更した際は、以下をREPL上で実行することで、ソースコードのリロードが行われます。
1 2 | |
リロードを活用するために
tools.namespaceによるリロードをスムーズに行うためには、ソフトウェアが以下のルールに従っている必要があります。
1. グローバルな状態を持たない
動作中に変化するアプリケーションの状態をグローバルVarに格納している場合、(refresh)は状態をクリアしてしまいます。
リロードをスムーズに行うためには、アプリケーションの動作中にグローバルVarの状態を参照しない設計(状態を表すデータ構造はパラメータとして必要な関数に渡す設計)が求められます。
2. ライフサイクルを管理できる
REPL上からアプリケーションの起動、停止を行う関数を用意しておくことが求められます。
起動関数では、アプリケーションの動作に必要なリソースを確保し、内部状態を表すデータ構造に組み立てて、必要な関数に渡します。
終了関数では、全てのリソースを開放し、まっさらな状態に戻します。
これらの関数とtools.namespace利用した開発の流れは以下のようになります。
Step 1. REPL起動
Step 2. アプリケーション起動
1 2 3 4 5 | |
Step 3. 動作確認
Step 4. ソースコード修正
Step 5. アプリケーション再起動
1 2 3 4 5 | |
※上記でグローバルVaruser/my-appを利用していますが、アプリケーション内からは直接user/my-appを参照せず、ローカルパラメータとしてのみ参照されるため、リロードによって不都合が発生することはありません。
状態管理のフレームワーク
リロード可能なアプリケーションを実現するために、アプリケーションのライフサイクル管理の統一的なインターフェースを定義し、内部状態を適切に管理するための仕組みとして、Componentフレームワークがtools.namespaceと同じ作者によって提供されています。
注意点
- AOTコンパイル非対応
- 一部のライブラリ(ring-develなど)は機能が衝突する
(reresh)を実行した名前空間はクリアされる(REPL上で定義した内容は削除される)