From 9b36a264c135c9693525c525ca89d49e9c987ec7 Mon Sep 17 00:00:00 2001 From: Mikhail Kuzmin Date: Tue, 15 Oct 2024 21:45:44 +0400 Subject: [PATCH] Add di/with-open --- src/darkleaf/di/core.clj | 23 +++++++++++++++++- .../di/tutorial/q_starting_many_keys_test.clj | 24 +++++++++---------- 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/darkleaf/di/core.clj b/src/darkleaf/di/core.clj index ff3296c3..011bac8c 100644 --- a/src/darkleaf/di/core.clj +++ b/src/darkleaf/di/core.clj @@ -9,7 +9,7 @@ ;; **********************************************************************/ (ns darkleaf.di.core - (:refer-clojure :exclude [ref key ns-publics derive]) + (:refer-clojure :exclude [ref key ns-publics derive with-open]) (:require [clojure.core :as c] [clojure.set :as set] @@ -759,3 +759,24 @@ (update-keys deps #(-> % name keyword))) (demolish [_ _]))) (registry key))))) + +(defmacro with-open + "A `c/with-open` variant that supports destructuring in bindings. + + bindings => [name init ...] + Evaluates body in a try expression with names bound to the values + of the inits, and a finally clause that calls (.close name) on each + name in reverse order." + [bindings & body] + {:pre [(vector? bindings) + (even? (count bindings))]} + (if (zero? (count bindings)) + `(do ~@body) + (let [[binding-form init-expr] (subvec bindings 0 2)] + `(let [resource# ~init-expr] + (try + (let [~binding-form resource#] + (with-open ~(subvec bindings 2) + ~@body)) + (finally + (.close resource#))))))) diff --git a/test/darkleaf/di/tutorial/q_starting_many_keys_test.clj b/test/darkleaf/di/tutorial/q_starting_many_keys_test.clj index 1004f40e..8bce7e5c 100644 --- a/test/darkleaf/di/tutorial/q_starting_many_keys_test.clj +++ b/test/darkleaf/di/tutorial/q_starting_many_keys_test.clj @@ -7,29 +7,29 @@ [darkleaf.di.core :as di] [clojure.test :as t])) +;; The standard `with-open` does not support destructuring in bindings. +;; Use `di/with-open` to handle resources with destructuring support. + (def a :a) (def b :b) (t/deftest verbose-test - (with-open [root (di/start ::root {::root (di/template [(di/ref `a) (di/ref `b)])})] - (let [[a b] @root] - (t/is (= :a a)) - (t/is (= :b b))))) + (di/with-open [[a b] (di/start ::root {::root (di/template [(di/ref `a) (di/ref `b)])})] + (t/is (= :a a)) + (t/is (= :b b)))) ;; The root container implements `clojure.lang.Indexed` ;; so you can use destructuring without derefing the root. (t/deftest indexed-test - (with-open [root (di/start [`a `b])] - (let [[a b] root] - (t/is (= :a a)) - (t/is (= :b b))))) + (di/with-open [[a b] (di/start [`a `b])] + (t/is (= :a a)) + (t/is (= :b b)))) ;; The root container implements `clojure.lang.ILookup` ;; so you can use destructuring without derefing the root. (t/deftest lookup-test - (with-open [root (di/start {:a `a :b `b})] - (let [{:keys [a b]} root] - (t/is (= :a a)) - (t/is (= :b b))))) + (di/with-open [{:keys [a b]} (di/start {:a `a :b `b})] + (t/is (= :a a)) + (t/is (= :b b))))