Back

init (clj)

(source)

function

(init config) (init config keys)
Turn a config map into an system map. Keys are traversed in dependency order, initiated via the init-key multimethod, then the refs associated with the key are resolved.

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/init-key :default [k v]
  (swap! log conj [:init k v])
  [v])

(defmethod ig/init-key ::x [k v]
  (swap! log conj [:init k v])
  :x)

(defmethod ig/init-key ::error-init [_ _]
  (throw (ex-info "Testing" {:reason ::test})))

(defmethod ig/init-key ::k [_ v] v)

(defmethod ig/init-key ::n [_ v] (inc v))
(defmethod ig/assert-key ::n [_ v]
  (assert (nat-int? v) "should be a natural number"))

(defmethod ig/init-key ::r [_ v] {:v v})
(defmethod ig/resolve-key ::r [_ {:keys [v]}] v)
(defmethod ig/resume-key ::r [k v _ _] (ig/init-key k v))

  (testing "prep then init"
    (is (= (ig/init (ig/prep {::p {:b 2}, ::a 1}))
           {::p [{:a [1], :b 2}], ::a [1]}))))

(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}))))

  (testing "with failing composite specs"
    (is (thrown-with-msg?
         #?(:clj clojure.lang.ExceptionInfo :cljs cljs.core.ExceptionInfo)
         (re-pattern (str "Assertion failed on key \\[" ::n " " ::nnn "\\] "
                          "when building system"))
         (ig/init {[::n ::nnn] 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 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}))))))
penpot/penpot
(ns app.storage.fs
  (:require
   [app.common.data.macros :as dm]
   [app.common.exceptions :as ex]
   [app.common.spec :as us]
   [app.common.uri :as u]
   [app.storage :as-alias sto]
   [app.storage.impl :as impl]
   [clojure.spec.alpha :as s]
   [cuerdas.core :as str]
   [datoteka.fs :as fs]
   [datoteka.io :as io]
   [integrant.core :as ig])
  (:import
   java.nio.file.Files
   java.nio.file.Path))

(defmethod ig/pre-init-spec ::backend [_]
  (s/keys :opt [::directory]))

(defmethod ig/init-key ::backend
  [_ cfg]
  ;; Return a valid backend data structure only if all optional
  ;; parameters are provided.
  (when (string? (::directory cfg))
    (let [dir (fs/normalize (::directory cfg))]
      (assoc cfg
             ::sto/type :fs
             ::directory (str dir)
             ::uri (u/uri (str "file://" dir))))))
TechEmpower/FrameworkBenchmarks
(ns hello.handler.single-query
  (:require [integrant.core :as ig]
            [ataraxy.response :as response]
            [hello.boundary.world-db :as world-db]))

(defmethod ig/init-key :hello.handler/single-query [_ {:keys [db]}]
  (fn [{[_] :ataraxy/result}]
    [::response/ok (world-db/make-single-query db)]))
clj-kondo/clj-kondo
(ns defmulti
  (:require [integrant.core :as ig]))

(defmethod ig/pre-init-spec :my/key [_] ::args)
clj-kondo/clj-kondo
(ns schema.defmethod
  (:require
   [integrant.core :as ig]
   [schema.core :as sc]))

(sc/defmethod ig/init-key :config :- {:config/env sc/Keyword}
  [_
   {:keys [:config/env]} :- {:config/env sc/Keyword}]
  {:config/env env})

;; When dispatch-val is vector
(sc/defmethod ig/init-key [:config1 :config2] :- {:config/env sc/Keyword}
  [_
   {:keys [:config/env]} :- {:config/env sc/Keyword}]
  (let [a 1]
    {:config/env env
     :a          a}))

;; Missing return schema
(sc/defmethod ig/init-key [:config1 :config2]
  [_
   {:keys [:config/env]} :- {:config/env sc/Keyword}]
  (let [a 1]
    {:config/env env
     :a          a}))

;; With multiple arities
(sc/defmethod ig/init-key [:config1 :config2] :- {:config/env sc/Keyword}
  ([_
    {:keys [:config/env]} :- {:config/env sc/Keyword}]
   {:config/env env})
  ([_ :- sc/Str
    _ :- sc/Int
    {:keys [:config/env]} :- {:config/env sc/Keyword}]
   {:config/env env}))
duct-framework/duct
{{=<< >>=}}
(ns <<namespace>>.service.example
  (:require [duct.logger :as log]
            [integrant.core :as ig]))

(defmethod ig/init-key :<<namespace>>.service/example [_ {:keys [logger]}]
  (log/log logger :report ::example-initiated))