Back
with-closeable* (clj)
(source)macro
(with-closeable* bindings & body)
Take two arguments, a `bindings` vector and a `body`. Like a `let`,
support destructuring. Avoids partially open state when an exception
is thrown on evaluating closeable forms. Evaluate bindings
sequentially then return `body` and leave bindings open. When an
exception is thrown in a later binding or in the `body`, close
bindings already open in reverse order and finally bubble up the
exception. Do nothing for non-closeable bindings.
Use it if you need exception handling on the creation of a closeable
map, so no closeable objects are left open but with no references
because of an exception.
For instance, this form would throw an exception and leave the
server open and the port locked:
``` clojure
(closeable-map {:server (http/start-server (api config))
:kafka {:consumer (kafka-consumer config)
:producer (throw (ex-info "Exception" {}))}})
;; `consumer` and `server` stay open but with no reference. Kafka
;; messages are consumed and the port is locked.
;; => (ex-info "Exception" {})
```
`with-closeable*` prevents that kind of broken, partially open
states for its bindings:
``` clojure
(with-closeable* [server (http/start-server (api config))
consumer (kafka-consumer config)
producer (throw (ex-info "Exception" {}))]
;; Your code goes here.
)
;; Close consumer,
;; close server,
;; finally throw `(ex-info "Exception" {})`.
```
You now have the guarantee that your code will only be executed if
all these closeable are open. In the latter example an exception is
thrown when `producer` is evaluated, so `consumer` is closed, then
`server` is closed, and finally the exception is bubbled up. Your
code is not evaluated. In the next example the body is evaluated,
but throws an exception: all bindings are closed.
``` clojure
(with-closeable* [server (http/start-server (api config))
consumer (kafka-consumer config)
producer (kafka-producer config)]
;; Your code goes here.
(throw (ex-info "Exception" {})))
;; Close producer,
;; close consumer,
;; close server,
;; finally throw `(ex-info "Exception" {})`.
```
When no exception is thrown, leave bindings open and return like a
normal `let` form.
``` clojure
(with-closeable* [server (http/start-server (api config))
consumer (kafka-consumer config)
producer (kafka-producer config)]
;; Your code goes here.
)
;; All closeable in bindings stay open.
;; => result
```