Back

ref (clj)

(source)

function

(ref key)
Create a reference to a top-level key in a config map.

Examples

integrant
(ns integrant.core-test
  (:require #?(:clj  [clojure.test :refer [are deftest is testing]]
               :cljs [cljs.test :refer-macros [are deftest is testing]])
            [integrant.core :as ig]
            [weavejester.dependency :as dep]))

(defmethod ig/prep-key ::p [_ v]
  (merge {:a (ig/ref ::a)} v))

(deftest ref-test
  (is (ig/ref? (ig/ref ::foo)))
  (is (ig/ref? (ig/ref [::foo ::bar])))
  (is (ig/reflike? (ig/ref ::foo)))
  (is (ig/reflike? (ig/ref [::foo ::bar]))))

(deftest refset-test
  (is (ig/refset? (ig/refset ::foo)))
  (is (ig/refset? (ig/refset [::foo ::bar])))
  (is (ig/reflike? (ig/refset ::foo)))
  (is (ig/reflike? (ig/refset [::foo ::bar]))))

#?(:clj
   (deftest read-string-test
     (is (= (ig/read-string "{:foo/a #ig/ref :foo/b, :foo/b 1}")
            {:foo/a (ig/ref :foo/b), :foo/b 1}))
     (is (= (ig/read-string "{:foo/a #ig/refset :foo/b, :foo/b 1}")
            {:foo/a (ig/refset :foo/b), :foo/b 1}))
     (is (= (ig/read-string {:readers {'test/var find-var}}
                            "{:foo/a #test/var clojure.core/+}")
            {:foo/a #'+}))))

     (testing "some namespaces"
       (remove-lib 'integrant.test.foo)
       (remove-lib 'integrant.test.bar)
       (remove-lib 'integrant.test.baz)
       (remove-lib 'integrant.test.quz)
       (is (= (set (ig/load-namespaces
                    {:integrant.test/foo 1
                     :integrant.test/bar (ig/ref :integrant.test/foo)
                     :integrant.test/baz 3}
                    [:integrant.test/bar]))
              '#{integrant.test.foo
                 integrant.test.bar}))
       (is (some? (find-ns 'integrant.test.foo)))
       (is (some? (find-ns 'integrant.test.bar)))
       (is (nil?  (find-ns 'integrant.test.baz))))

(deftest dependency-graph-test
  (let [m {::a (ig/ref ::p), ::b (ig/refset ::ppp) ::p 1, ::pp 2}]
    (testing "graph with refsets"
      (let [g (ig/dependency-graph m)]
        (is (dep/depends? g ::a ::p))
        (is (dep/depends? g ::b ::p))
        (is (dep/depends? g ::b ::pp))))

    (testing "graph without refsets"
      (let [g (ig/dependency-graph m {:include-refsets? false})]
        (is (dep/depends? g ::a ::p))
        (is (not (dep/depends? g ::b ::p)))
        (is (not (dep/depends? g ::b ::pp)))))))

(deftest key-comparator-test
  (let [graph (ig/dependency-graph {::a (ig/ref ::ppp) ::p 1, ::b 2})]
    (is (= (sort (ig/key-comparator graph) [::b ::a ::p])
           [::p ::a ::b]))))

  (testing "custom prep-key"
    (is (= (ig/prep {::p {:b 2}, ::a 1})
           {::p {:a (ig/ref ::a), :b 2}, ::a 1})))

(deftest expand-test
  (testing "default expand"
    (is (= (ig/expand {::unique 1})
           {::unique 1})))
  (testing "empty map values"
    (is (= (ig/expand {::unique {}})
           {::unique {}}))
    (is (= (ig/expand {::a {}, ::mod-a {:x 1}})
           {::a {:x 1}}))
    (is (= (ig/expand {::z {}, ::mod-z {:x 1}})
           {::z {:x 1}})))
  (testing "single expand"
    (is (= (ig/expand {::mod 1})
           {::a 1, ::b {:v 1}})))
  (testing "expand with unrelated keys"
    (is (= (ig/expand {::mod 1, ::b {:x 1}, ::c 2})
           {::a 1, ::b {:v 1, :x 1}, ::c 2})))
  (testing "expand with direct override"
    (is (= (ig/expand {::mod {:x 1}, ::a ^:override {:x 2}})
           {::a {:x 2}, ::b {:v {:x 1}}})))
  (testing "expand with nested override"
    (is (= (ig/expand {::mod 1, ::b ^:override {:v 2}})
           {::a 1, ::b {:v 2}}))
    (is (= (ig/expand {::mod-c 1, ::c ^:override {:x {:y {:z 2}}}})
           {::c {:x {:y {:z 2}}}})))
  (testing "unresolved conflicting index"
    (is (thrown-with-msg?
         #?(:clj clojure.lang.ExceptionInfo :cljs cljs.core.ExceptionInfo)
         (re-pattern (str "^Conflicting values at index "
                          "\\[:integrant\\.core-test/a\\] "
                          "for expansions: :integrant\\.core-test/mod, "
                          ":integrant\\.core-test/mod-a\\. Use the "
                          "\\^:override metadata to set the preferred "
                          "value\\.$"))
         (ig/expand {::mod 1, ::mod-a 2}))))
  (testing "unresolved conflicting nested index"
    (is (thrown-with-msg?
         #?(:clj clojure.lang.ExceptionInfo :cljs cljs.core.ExceptionInfo)
         (re-pattern (str "^Conflicting values at index "
                          "\\[:integrant\\.core-test/b :v\\] "
                          "for expansions: :integrant\\.core-test/mod, "
                          ":integrant\\.core-test/mod-b\\. Use the "
                          "\\^:override metadata to set the preferred "
                          "value\\.$"))
         (ig/expand {::mod 1, ::mod-b 2}))))
  (testing "resolved conflict"
    (is (= (ig/expand {::mod {:x 1}, ::mod-a {:x 2}, ::a ^:override {:x 3}})
           {::a {:x 3}, ::b {:v {:x 1}}})))
  (testing "resolved nested conflict"
    (is (= (ig/expand {::mod 1, ::mod-b 2, ::b ^:override {:v 3}})
           {::a 1, ::b {:v 3}}))
    (is (= (ig/expand {[::one ::mod-c] 1
                       [::two ::mod-c] 2
                       ::c ^:override {:x {:y {:z 3}}}})
           {::c {:x {:y {:z 3}}}})))
  (testing "expand with refs"
    (let [m {::a (ig/ref ::b) ::b 1}]
      (is (= m (ig/expand m))))
    (let [m {::a (ig/refset ::b) ::b 1}]
      (is (= m (ig/expand m))))))

(deftest init-test
  (testing "without keys"
    (reset! log [])
    (let [m (ig/init {::a (ig/ref ::b), ::b 1})]
      (is (= m {::a [[1]], ::b [1]}))
      (is (= @log [[:init ::b 1]
                   [:init ::a [1]]]))))

  (testing "with keys"
    (reset! log [])
    (let [m (ig/init {::a (ig/ref ::b), ::b 1, ::c 2} [::a])]
      (is (= m {::a [[1]], ::b [1]}))
      (is (= @log [[:init ::b 1]
                   [:init ::a [1]]]))))

  (testing "with inherited keys"
    (reset! log [])
    (let [m (ig/init {::p (ig/ref ::a), ::a 1} [::pp])]
      (is (= m {::p [[1]], ::a [1]}))
      (is (= @log [[:init ::a 1]
                   [:init ::p [1]]]))))

  (testing "with composite keys"
    (reset! log [])
    (let [m (ig/init {::a (ig/ref ::b), [::x ::b] 1})]
      (is (= m {::a [:x], [::x ::b] :x}))
      (is (= @log [[:init [::x ::b] 1]
                   [:init ::a :x]]))))

  (testing "with composite refs"
    (reset! log [])
    (let [m (ig/init {::a (ig/ref [::b ::c]), [::b ::c ::e] 1, [::b ::d] 2})]
      (is (= m {::a [[1]], [::b ::c ::e] [1], [::b ::d] [2]}))
      (is (or (= @log [[:init [::b ::c ::e] 1]
                       [:init ::a [1]]
                       [:init [::b ::d] 2]])
              (= @log [[:init [::b ::d] 2]
                       [:init [::b ::c ::e] 1]
                       [:init ::a [1]]])))))

  (testing "with failing composite refs"
    (reset! log [])
    (is (thrown-with-msg?
         #?(:clj clojure.lang.ExceptionInfo :cljs cljs.core.ExceptionInfo)
         (re-pattern (str "^Invalid composite key: "
                          "\\[:integrant.core-test/a :b\\]. "
                          "Every keyword must be namespaced.$"))
         (ig/init {[::a :b] :anything}))))

  (testing "with custom resolve-key"
    (let [m (ig/init {::a (ig/ref ::r), ::r 1})]
      (is (= m {::a [1], ::r {:v 1}}))))

  (testing "with refsets"
    (reset! log [])
    (let [m (ig/init {::a (ig/refset ::ppp), ::p 1, ::pp 2})]
      (is (= m {::a [#{[1] [2]}], ::p [1], ::pp [2]}))
      (is (= @log [[:init ::p 1]
                   [:init ::pp 2]
                   [:init ::a #{[1] [2]}]]))))

  (testing "with refsets and keys"
    (reset! log [])
    (let [m {::a (ig/refset ::ppp), ::p 1, ::pp 2}]
      (is (= (ig/init m [::a])      {::a [#{}]}))
      (is (= (ig/init m [::a ::p])  {::a [#{[1]}] ::p [1]}))
      (is (= (ig/init m [::a ::pp]) {::a [#{[1] [2]}] ::p [1] ::pp [2]}))))

  (testing "large config"
    (is (= (ig/init {:a/a1 {} :a/a2 {:_ (ig/ref :a/a1)}
                     :a/a3 {} :a/a4 {} :a/a5 {}
                     :a/a6 {} :a/a7 {} :a/a8 {}
                     :a/a9 {} :a/a10 {}})
           {:a/a1 [{}] :a/a2 [{:_ [{}]}]
            :a/a3 [{}] :a/a4 [{}] :a/a5 [{}]
            :a/a6 [{}] :a/a7 [{}] :a/a8 [{}]
            :a/a9 [{}] :a/a10 [{}]})))

  (testing "with passing specs"
    (let [m (ig/init {::n (ig/ref ::k), ::k 1})]
      (is (= m {::n 2, ::k 1}))))

  (testing "with failing asserts"
    (is (thrown-with-msg?
         #?(:clj clojure.lang.ExceptionInfo :cljs cljs.core.ExceptionInfo)
         (re-pattern (str "Assertion failed on key " ::n
                          " when building system"))
         (ig/init {::n (ig/ref ::k), ::k 1.1}))))

(deftest halt-test
  (testing "without keys"
    (reset! log [])
    (let [m (ig/init {::a (ig/ref ::b), ::b 1})]
      (ig/halt! m)
      (is (= @log [[:init ::b 1]
                   [:init ::a [1]]
                   [:halt ::a [[1]]]
                   [:halt ::b [1]]]))))

  (testing "with keys"
    (reset! log [])
    (let [m (ig/init {::a (ig/ref ::b), ::b (ig/ref ::c), ::c 1})]
      (ig/halt! m [::a])
      (is (= @log [[:init ::c 1]
                   [:init ::b [1]]
                   [:init ::a [[1]]]
                   [:halt ::a [[[1]]]]]))
      (reset! log [])
      (ig/halt! m [::c])
      (is (= @log [[:halt ::a [[[1]]]]
                   [:halt ::b [[1]]]
                   [:halt ::c [1]]]))))

  (testing "with partial system"
    (reset! log [])
    (let [m (ig/init {::a 1, ::b (ig/ref ::a)} [::a])]
      (ig/halt! m)
      (is (= @log [[:init ::a 1]
                   [:halt ::a [1]]]))))

  (testing "with inherited keys"
    (reset! log [])
    (let [m (ig/init {::a (ig/ref ::p), ::p 1} [::a])]
      (ig/halt! m [::pp])
      (is (= @log [[:init ::p 1]
                   [:init ::a [1]]
                   [:halt ::a [[1]]]
                   [:halt ::p [1]]]))))

  (testing "with composite keys"
    (reset! log [])
    (let [m (ig/init {::a (ig/ref ::b), [::x ::b] 1})]
      (ig/halt! m)
      (is (= @log [[:init [::x ::b] 1]
                   [:init ::a :x]
                   [:halt ::a [:x]]
                   [:halt [::x ::b] :x]])))))

(deftest suspend-resume-test
  (testing "same configuration"
    (reset! log [])
    (let [c {::a (ig/ref ::b), ::b 1}
          m (ig/init c)]
      (ig/suspend! m)
      (ig/resume c m)
      (is (= @log [[:init ::b 1]
                   [:init ::a [1]]
                   [:suspend ::a [[1]]]
                   [:suspend ::b [1]]
                   [:resume ::b 1 1 [1]]
                   [:resume ::a [1] [1] [[1]]]]))))

  (testing "missing keys"
    (reset! log [])
    (let [c {::a (ig/ref ::b), ::b 1}
          m (ig/init c)]
      (ig/suspend! m)
      (ig/resume (dissoc c ::a) m)
      (is (= @log [[:init ::b 1]
                   [:init ::a [1]]
                   [:suspend ::a [[1]]]
                   [:suspend ::b [1]]
                   [:halt ::a [[1]]]
                   [:resume ::b 1 1 [1]]]))))

  (testing "missing refs"
    (reset! log [])
    (let [c {::a {:b (ig/ref ::b)}, ::b 1}
          m (ig/init c)]
      (ig/suspend! m)
      (ig/resume {::a []} m)
      (is (= @log [[:init ::b 1]
                   [:init ::a {:b [1]}]
                   [:suspend ::a [{:b [1]}]]
                   [:suspend ::b [1]]
                   [:halt ::b [1]]
                   [:resume ::a [] {:b [1]} [{:b [1]}]]]))))

  (testing "with custom resolve-key"
    (let [c  {::a (ig/ref ::r), ::r 1}
          m  (ig/init c)
          _  (ig/suspend! m)
          m' (ig/resume c m)]
      (is (= m m'))))

  (testing "composite keys"
    (reset! log [])
    (let [c {::a (ig/ref ::x), [::b ::x] 1}
          m (ig/init c)]
      (ig/suspend! m)
      (ig/resume c m)
      (is (= @log [[:init [::b ::x] 1]
                   [:init ::a :x]
                   [:suspend ::a [:x]]
                   [:suspend [::b ::x] :x]
                   [:resume [::b ::x] 1 1 :x]
                   [:resume ::a :rx :x [:x]]]))))

  (testing "resume key with dependencies"
    (reset! log [])
    (let [c {::a {:b (ig/ref ::b)}, ::b 1}
          m (ig/init c [::a])]
      (ig/suspend! m)
      (ig/resume c m [::a])
      (is (= @log
             [[:init ::b 1]
              [:init ::a {:b [1]}]
              [:suspend ::a [{:b [1]}]]
              [:suspend ::b [1]]
              [:resume ::b 1 1 [1]]
              [:resume ::a {:b [1]} {:b [1]} [{:b [1]}]]])))))

(deftest invalid-configs-test
  (testing "ambiguous refs"
    (is (thrown-with-msg?
         #?(:clj clojure.lang.ExceptionInfo :cljs cljs.core.ExceptionInfo)
         (re-pattern (str "Ambiguous key: " ::ppp "\\. "
                          "Found multiple candidates: "
                          "(" ::p ", " ::pp "|" ::pp ", " ::p ")"))
         (ig/init {::a (ig/ref ::ppp), ::p 1, ::pp 2}))))

  (testing "missing refs"
    (is (thrown-with-msg?
         #?(:clj clojure.lang.ExceptionInfo :cljs cljs.core.ExceptionInfo)
         (re-pattern (str "Missing definitions for refs: " ::b))
         (ig/init {::a (ig/ref ::b)}))))

  (testing "missing refs with explicit keys"
    (is (= (ig/init {::a (ig/ref ::ppp), ::p 1, ::pp 2} [::p ::pp])
           {::p [1], ::pp [2]})))

  (testing "missing refs with explicit keys"
    (is (= (ig/init {::a 1, ::b (ig/ref ::c)} [::a])
           {::a [1]}))))

(deftest build-test
  (is (= [{::a [:build ::a [:build ::b 1]]
           ::b [:build ::b 1]}
          [[:build ::b 1]
           [:build ::a [:build ::b 1]]]]
         (build-log {::a (ig/ref ::b)
                     ::b 1}))))

(deftest run-test
  (let [config {::a (ig/ref ::b), ::b 1}
        [system _] (build-log config)]
    (is (= [nil
            [[:test ::b [:build ::b 1]]
             [:test ::a [:build ::a [:build ::b 1]]]]]
           (test-log ig/run! system)))
    (is (= [nil
            [[:test ::a [:build ::a [:build ::b 1]]]
             [:test ::b [:build ::b 1]]]]
           (test-log ig/reverse-run! system)))))

(deftest fold-test
  (let [config {::a (ig/ref ::ppp), ::b (ig/ref ::pp), ::p 1, ::c 2}
        system (ig/init config)]
    (is (= (ig/fold system #(conj %1 [%2 %3]) [])
           [[::p [1]] [::a [[1]]] [::b [[1]]] [::c [2]]]))))

(deftest wrapped-exception-test
  (testing "exception when building"
    (let [ex (try (ig/init {::a 1, ::error-init (ig/ref ::a)}) nil
                  (catch #?(:clj Throwable :cljs :default) t t))]
      (is (some? ex))
      (is (= (#?(:clj .getMessage :cljs ex-message) ex)
             (str "Error on key " ::error-init " when building system")))
      (is (= (ex-data ex)
             {:reason   ::ig/build-threw-exception
              :system   {::a [1]}
              :function ig/init-key
              :key      ::error-init
              :value    [1]}))
      (let [cause (#?(:clj .getCause :cljs ex-cause) ex)]
        (is (some? cause))
        (is (= (#?(:clj .getMessage :cljs ex-message) cause) "Testing"))
        (is (= (ex-data cause) {:reason ::test})))))

  (testing "exception when running"
    (let [system (ig/init {::a 1
                           ::error-halt (ig/ref ::a)
                           ::b (ig/ref ::error-halt)
                           ::c (ig/ref ::b)})
          ex     (try (ig/halt! system)
                      (catch #?(:clj Throwable :cljs :default) t t))]
      (is (some? ex))
      (is (= (#?(:clj .getMessage :cljs ex-message) ex)
             (str "Error on key " ::error-halt " when running system")))
      (is (= (ex-data ex)
             {:reason         ::ig/run-threw-exception
              :system         {::a [1], ::error-halt [[1]]
                               ::b [[[1]]], ::c [[[[1]]]]}
              :completed-keys '(::c ::b)
              :remaining-keys '(::a)
              :function       ig/halt-key!
              :key            ::error-halt
              :value          [[1]]}))
      (let [cause (#?(:clj .getCause :cljs ex-cause) ex)]
        (is (some? cause))
        (is (= (#?(:clj .getMessage :cljs ex-message) cause) "Testing"))
        (is (= (ex-data cause) {:reason ::test}))))))
madstap/igviz
(ns kafka
  (:require [integrant.core :as ig]
            [madstap.igviz.alpha :as igviz]
            [medley.core :as medley]))

(def sconf-old
  {::db     {:url     "..."
             :db-name "foo-db"}
   ::cache  {:url "..."}
   ::server {:port  1234
             :db    (ig/ref ::db)
             :cache (ig/ref ::cache)}})

(def sconf-new
  (-> sconf-old
      (dissoc ::cache)
      (medley/dissoc-in [::server :cache])
      (assoc ::elasticsearch {:url "..."})
      (assoc-in [::server :elastic] (ig/ref ::elasticsearch))))

(def config
  {::db                     {:url "..."}
   ::cache                  {:url "..."}
   ::server                 {:db         (ig/ref ::db)
                             :cache      (ig/ref ::cache)
                             :daemons    (ig/refset :duct/daemon)
                             :all-topics (ig/refset ::topic)
                             ;; :all-consumers (ig/refset ::consumer)
                             }
   [::consumer1 ::consumer] {:topics [(ig/ref ::foo-topic)
                                      (ig/ref ::bar-topic)]}
   [::consumer2 ::consumer] {:topics [(ig/ref ::foo-topic)
                                      (ig/ref ::bar-topic)
                                      (ig/ref ::baz-topic)]}
   ::producer               {:topic (ig/ref ::baz-topic)}
   ::error-component        {:producer (ig/ref ::producer)
                             :consumer (ig/ref ::consumer1)
                             :foo (ig/ref ::bar-topic)}
   [::foo-topic ::topic]    {:topic-name "foo"}
   [::bar-topic ::topic]    {:topic-name "bar"}
   [::baz-topic ::topic]    {:topic-name "baz"}})