Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 39 additions & 10 deletions src/integrant/core.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@
(set (for [[k v] (find-derived config key)]
(resolvef k v)))))

(defrecord RefMap [key]
RefLike
(ref-key [_] key)
(ref-resolve [_ config resolvef]
(into {} (for [[k v] (find-derived config key)]
[k (resolvef k v)]))))

(defn- composite-key? [keys]
(and (vector? keys) (every? qualified-keyword? keys)))

Expand All @@ -77,6 +84,12 @@
{:pre [(valid-config-key? key)]}
(->RefSet key))

(defn refmap
"Create a map of references to all matching top-level keys in a config map."
[key]
{:pre [(valid-config-key? key)]}
(->RefMap key))

(defn ref?
"Return true if its argument is a ref."
[x]
Expand All @@ -87,8 +100,13 @@
[x]
(instance? RefSet x))

(defn refmap?
"Return true if its argument is a refmap."
[x]
(instance? RefMap x))

(defn reflike?
"Return true if its argument is a ref or a refset."
"Return true if its argument is a ref, refset or a refmap."
[x]
(satisfies? RefLike x))

Expand All @@ -113,22 +131,30 @@
(throw (ambiguous-key-exception m k (map key kvs))))
(first kvs)))

(defn- find-derived-refs [config v include-refsets?]
(->> (depth-search (if include-refsets? reflike? ref?) v)
(map ref-key)
(mapcat #(map key (find-derived config %)))))
(defn- find-derived-refs [config v include-refsets? include-refmaps?]
(let [preds (cond-> #{ref?}
include-refsets? (conj refset?)
include-refmaps? (conj refmap?))
pred (fn [r] (some #(% r) preds))]
(->> (depth-search pred v)
(map ref-key)
(mapcat #(map key (find-derived config %))))))

(defn dependency-graph
"Return a dependency graph of all the refs and refsets in a config. Resolves
derived dependencies. Takes the following options:

`:include-refsets?`
: whether to include refsets in the dependency graph (defaults to true)"
: whether to include refsets in the dependency graph (defaults to true)

`:include-refmaps?`
: whether to include refmaps in the dependency graph (defaults to true)"
([config]
(dependency-graph config {}))
([config {:keys [include-refsets?] :or {include-refsets? true}}]
([config {:keys [include-refsets? include-refmaps?]
:or {include-refsets? true include-refmaps? true}}]
(letfn [(find-refs [v]
(find-derived-refs config v include-refsets?))]
(find-derived-refs config v include-refsets? include-refmaps?))]
(reduce-kv (fn [g k v] (reduce #(dep/depend %1 k %2) g (find-refs v)))
(dep/graph)
config))))
Expand All @@ -141,7 +167,8 @@
(dep/topo-comparator #(compare (str %1) (str %2)) graph))

(defn- find-keys [config keys f]
(let [graph (dependency-graph config {:include-refsets? false})
(let [graph (dependency-graph config {:include-refsets? false
:include-refmaps? false})
keyset (set (mapcat #(map key (find-derived config %)) keys))]
(->> (f graph keyset)
(set/union keyset)
Expand All @@ -154,7 +181,9 @@
(reverse (find-keys config keys dep/transitive-dependents-set)))

#?(:clj
(def ^:private default-readers {'ig/ref ref, 'ig/refset refset}))
(def ^:private default-readers {'ig/ref ref
'ig/refset refset
'ig/refmap refmap}))

#?(:clj
(defn read-string
Expand Down
43 changes: 42 additions & 1 deletion test/integrant/core_test.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@
(is (ig/reflike? (ig/refset ::foo)))
(is (ig/reflike? (ig/refset [::foo ::bar]))))

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

(deftest composite-keyword-test
(let [k (ig/composite-keyword [::a ::b])]
(is (isa? k ::a))
Expand All @@ -86,14 +92,20 @@
(is (= (ig/expand {::a (ig/refset ::ppp), ::p 1, ::pp 2})
{::a #{1 2}, ::p 1, ::pp 2}))
(is (= (ig/expand {::a (ig/refset ::ppp)})
{::a #{}})))
{::a #{}}))
(is (= (ig/expand {::a (ig/refmap ::ppp), ::p 1, ::pp 2})
{::a {::p 1 ::pp 2}, ::p 1, ::pp 2}))
(is (= (ig/expand {::a (ig/refmap ::ppp)})
{::a {}})))

#?(: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 "{:foo/a #ig/refmap :foo/b, :foo/b 1}")
{:foo/a (ig/refmap :foo/b), :foo/b 1}))
(is (= (ig/read-string {:readers {'test/var find-var}}
"{:foo/a #test/var clojure.core/+}")
{:foo/a #'+}))))
Expand Down Expand Up @@ -163,6 +175,19 @@

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

(let [m {::a (ig/ref ::p), ::b (ig/refmap ::ppp) ::p 1, ::pp 2}]
(testing "graph with refmaps"
(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 refmaps"
(let [g (ig/dependency-graph m {:include-refmaps? false})]
(is (dep/depends? g ::a ::p))
(is (not (dep/depends? g ::b ::p)))
(is (not (dep/depends? g ::b ::pp)))))))
Expand Down Expand Up @@ -296,6 +321,22 @@
(is (= (ig/init m [::a ::p]) {::a [#{[1]}] ::p [1]}))
(is (= (ig/init m [::a ::pp]) {::a [#{[1] [2]}] ::p [1] ::pp [2]}))))

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

(testing "with refmaps and keys"
(reset! log [])
(let [m {::a (ig/refmap ::ppp), ::p 1, ::pp 2}]
(is (= (ig/init m [::a]) {::a [{}]}))
(is (= (ig/init m [::a ::p]) {::a [{::p [1]}] ::p [1]}))
(is (= (ig/init m [::a ::pp]) {::a [{::p [1] ::pp [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 {}
Expand Down