Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Memoize2 #36

Draft
wants to merge 23 commits into
base: master
Choose a base branch
from
Draft
33 changes: 33 additions & 0 deletions src/darkleaf/di/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -864,3 +864,36 @@
middlewares
(inspect-middleware))]
@components))


;; Мы используем locking, чтобы не было проблем с параллельным выполнением тестов.
;; Нужен ли тут тест?

;; Кто создал объект, тот его и уничтожает. Для этого мы храним в ключе ссылку на фабрику.


(defn ->memoize []
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returns middleware that memoizes all registry build accesses. Must be the last in a registry

(let [cache (volatile! {})]
(fn [registry]
(fn [key]
(let [factory (registry key)]
(reify p/Factory
(dependencies [_]
(p/dependencies factory))
(build [owner deps]
(locking cache
(?? (get @cache [key deps])
(let [obj (p/build factory deps)]
(vswap! cache assoc
[key deps] obj
;; NOTE: track which factory creates `obj`
;; so that only that factory can demolish it later
[key owner obj] deps)
obj))))
(demolish [owner obj]
(locking cache
(when-some [deps (get @cache [key owner obj])]
(vswap! cache dissoc
[key deps]
[key owner obj])
(p/demolish factory obj))))))))))
123 changes: 123 additions & 0 deletions test/darkleaf/di/memoize_test.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
(ns darkleaf.di.memoize-test
(:require
[clojure.test :as t]
[darkleaf.di.core :as di]))

(set! *warn-on-reflection* true)

(t/deftest memoize-test
(let [a 'a
identity* (memoize identity)]
(identity* a)
(t/is (not (identical? a (identity 'a))))
(t/is (identical? a (identity* 'a)))))

(defn- some+identical? [a b]
(and (some? a)
(some? b)
(identical? a b)))

(defn- some+not-identical? [a b]
(and (some? a)
(some? b)
(not (identical? a b))))

(defn a
{::di/kind :component}
[{_ ::param}]
(Object.))

(t/deftest ok-test
(let [mem (di/->memoize)
registry (fn []
[{::param (Object.)}
mem])]
(with-open [main (di/start `a (registry))
secondary (di/start `a (registry))]
(t/is (some+identical? @main @secondary)))))

(t/deftest changed-not-identical-test
(let [mem (di/->memoize)
registry (fn []
[{::param (Object.)}
mem])]
(with-open [main (di/start `a (registry))
secondary (di/start `a (registry)
{::param (Object.)})]
(t/is (some+not-identical? @main @secondary)))))

(t/deftest changed-equal-and-identical-test
(let [mem (di/->memoize)
registry (fn []
[{::param :equal-and-identical}
mem])]
(with-open [main (di/start `a (registry))
secondary (di/start `a (registry)
{::param :equal-and-identical})]
(t/is (some+identical? @main @secondary)))))


(t/deftest changed-equal-but-not-identical-test
(let [mem (di/->memoize)
registry (fn []
[{::param 'equal-but-not-identical}
mem])]
(with-open [main (di/start `a (registry))
secondary (di/start `a (registry)
{::param 'equal-but-not-identical})]
(t/is (some+identical? @main @secondary)))))

(t/deftest changed-equal-but-different-test
(let [mem (di/->memoize)
registry (fn []
[{::param []}
mem])]
(with-open [main (di/start `a (registry))
secondary (di/start `a (registry)
{::param '()})]
(t/is (some+identical? @main @secondary)))))


(t/deftest start-stop-order-test
(let [mem (di/->memoize)
registry (fn []
[{::param :param}
mem])
log (atom [])
callbacks (fn [system]
{:after-build! (fn [{:keys [key]}]
(swap! log conj [:start system key]))
:after-demolish! (fn [{:keys [key]}]
(swap! log conj [:stop system key]))})]
(with-open [_ (di/start `a
(registry)
(di/log (callbacks :main)))
_ (di/start [::x `a]
{::x :x}
(di/log (callbacks :second))
(registry))
_ (di/start [::y `a]
{::y :y}
(di/log (callbacks :third))
(registry))])

(t/is (= [[:start :main ::param]
[:start :main `a]
[:start :main ::di/implicit-root]

[:start :second ::x]
[:start :second ::di/implicit-root]

[:start :third ::y]
[:start :third ::di/implicit-root]

[:stop :third ::di/implicit-root]
[:stop :third ::y]

[:stop :second ::di/implicit-root]
[:stop :second ::x]

[:stop :main ::di/implicit-root]
[:stop :main `a]
[:stop :main ::param]]
@log))))
54 changes: 54 additions & 0 deletions test/darkleaf/di/tutorial/z_memoize_test.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
(ns darkleaf.di.tutorial.z-memoize-test ;;todo: name
(:require
[clojure.test :as t]
[darkleaf.di.core :as di]))

(defn connection
{::di/stop #(reset! % :stopped)}
[{url "CONNECTION_URL"}]
(atom :started))

(defn migrations
"A long running side effect"
{::di/kind :component}
[{connection `connection}]
#_
(when (= :stopped @connection)
(throw (IllegalStateException. "Connection is not started")))
(random-uuid))

(defn root
{::di/kind :component}
[{migrations `migrations}]
{:migrations migrations})

;; todo: check registry placement

#_
(t/deftest ok
(let [[cache global-system :as root]
(di/start [::di/cache `root]
{"CONNECTION_URL" "1"}
(di/collect-cache))]

(with-open [local (di/start `root
(di/use-cache cache))]
(t/is (identical? global-system @local)))

(with-open [local (di/start `root
(di/use-cache cache)
{"CONNECTION_URL" "1"})]
(t/is (identical? global-system @local)))

(with-open [local (di/start `root
;; `use-cache` should be the first registry
(di/use-cache cache)
{"CONNECTION_URL" "2"})]
(t/is (not (identical? global-system @local))))


(di/stop root)

(t/is (thrown? IllegalStateException
(di/start `root
(di/use-cache cache))))))
Loading