Я использую привязки по двум причинам:
- запуск тестов, которые переопределяют константы или другие значения других символов
- с использованием "глобальных" ресурсов, таких как соединения с базой данных или каналы брокера сообщений
тестирование
Я работаю в распределенной системе с несколькими компонентами, которые обмениваются данными, отправляя сообщения через обмен сообщениями.Эти обмены имеют глобальные имена, которые я определил примерно так:
(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
функции я могу вызывать любой код, который использует константы, и он будет использовать переопределенные значения.
с использованием глобальных ресурсов
Другой способЯ использую привязки, чтобы «исправить» значение глобального или одноэлементного ресурса в определенной области видимости.Вот пример библиотеки 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*
и считать его действительным, открытым 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."))))