私がバインディングを使用する理由は 2 つあります。
- 他のシンボルの定数または他の値をオーバーライドするテストの実行
- データベース接続やメッセージ ブローカー チャネルなどの「グローバル」リソースを使用する
テスト
私は、メッセージ交換を介してメッセージを送信することによって通信するいくつかのコンポーネントを備えた分散システムに取り組んでいます。これらの取引所にはグローバルな名前があり、私は次のように定義しました:
(ns const)
(def JOB-EXCHANGE "my.job.xchg")
(def CRUNCH-EXCHANGE "my.crunch.xchg")
;; ... more constants
これらの定数は、メッセージを適切な場所に送信するために多くの場所で使用されます。コードをテストするために、テスト スイートの一部で実際のメッセージ交換を使用するコードを実行します。ただし、テストが実際のシステムに干渉することは望ましくありません。
これを解決するために、binding
これらの定数をオーバーライドする呼び出しでテスト コードをラップします。
;; in my testing code:
(binding [const/CRUNCH-EXCHANGE (str const/CRUNCH-EXCHANGE (gensym "-TEST-"))
const/CRUNCH-TASK-QUEUE (str const/CRUNCH-TASK-QUEUE (gensym "-TEST-"))]
;; tests here
)
このbinding
関数内では、定数を使用する任意のコードを呼び出すことができ、オーバーライドされた値が使用されます。
グローバル リソースの使用
バインディングを使用するもう 1 つの方法は、特定のスコープ内のグローバルまたはシングルトン リソースの値を「修正」することです。以下は、私が作成した RabbitMQ ライブラリの例です。RabbitMQ の値をConnection
シンボルにバインドし*amqp-connection*
て、コードで使用できるようにしています。
(with-connection (make-connection opts)
;; code that uses a RabbitMQ connection
)
の実装with-connection
は非常に簡単です。
(def ^{:dynamic true} *amqp-connection* nil)
(defmacro with-connection
"Binds connection to a value you can retrieve
with (current-connection) within body."
[conn & body]
`(binding [*amqp-connection* ~conn]
~@body))
私の RabbitMQ ライブラリ内のすべてのコードで接続を使用でき、*amqp-connection*
それが有効な open であると想定できますConnection
。または(current-connection)
、RabbitMQ 呼び出しを でラップするのを忘れた場合に説明的な例外をスローする関数を使用しwith-connection
ます。
(defn current-connection
"If used within (with-connection conn ...),
returns the currently bound connection."
[]
(if (current-connection?)
*amqp-connection*
(throw (RuntimeException.
"No current connection. Use (with-connection conn ...) to bind a connection."))))