diff --git a/src/fmnoise/coldbrew.clj b/src/fmnoise/coldbrew.clj index 37d5be2..007e79f 100644 --- a/src/fmnoise/coldbrew.clj +++ b/src/fmnoise/coldbrew.clj @@ -86,6 +86,11 @@ (.build cache-builder (cache-loader cache-fn)) (.build cache-builder)))) +(defn cache-keys + "Returns a list of cache keys" + [^Cache cache] + (.keySet (.asMap cache))) + (defn put "Insert/update a cache entry." [^Cache cache key val] @@ -102,6 +107,11 @@ [^Cache cache key] (.invalidate cache key)) +(defn invalidate-keys + "Invalidate a cache entries." + [^Cache cache keys] + (.invalidateAll cache keys)) + (defn lookup "Performs cache lookup. Accepts optional function which uses cache key to calculate missing value" ([^Cache cache key] @@ -139,12 +149,16 @@ (let [options (meta f) condition-fn (-> f meta :when) cache (build-cache options (when-not condition-fn f))] + ^{::cache cache} (fn [& args] (let [key (or args [])] (if condition-fn (cond-lookup cache key condition-fn f args) (lookup cache key)))))) +(defn cached-cache [cached-fn] + (-> (meta cached-fn) ::cache)) + (defmacro defcached "Creates a function which uses Caffeine Loading Cache under the hood. Function declaration is similar to defn: @@ -181,11 +195,14 @@ :else 3)) fdefn (filter some? (list name docstring args prepost))] `(do - (def ^:private ~fname - (cached - (with-meta - (fn [~@cache-key] - ~@body) - ~cache-options))) - (defn ~@fdefn (~fname ~@cache-key)) - (vary-meta ~name merge ~(meta name))))) + (def ^:private ~fname (cached + (with-meta + (fn [~@cache-key] + ~@body) + ~cache-options))) + (defn ~@fdefn (~fname ~@cache-key)) + (alter-meta! (var ~name) merge (meta ~fname) ~(meta name))))) + +(defmacro defcached-cache [defcache-fn] + `(cached-cache (var ~defcache-fn))) + diff --git a/test/fmnoise/coldbrew_test.clj b/test/fmnoise/coldbrew_test.clj index 7086713..9e506c0 100644 --- a/test/fmnoise/coldbrew_test.clj +++ b/test/fmnoise/coldbrew_test.clj @@ -1,6 +1,7 @@ (ns fmnoise.coldbrew-test (:require [clojure.test :refer [deftest is]] - [fmnoise.coldbrew :refer [defcached cached]])) + [fmnoise.coldbrew :refer [defcached cached] :as cb]) + (:import (com.github.benmanes.caffeine.cache Cache))) (deftest cached-fn-test (let [counter (atom 0) @@ -14,6 +15,9 @@ c3 @counter r4 (cf 2 3) c4 @counter] + (is (and (instance? Cache (::cb/cache (meta cf))) + (instance? Cache (cb/cached-cache cf))) + "meta attached correctly") (is (= 3 r1 r2 r3) "function produces correct result") (is (= 1 c1 c2) "counter is affected only on first run") (is (= 2 c3) "counter is affected after expiration") @@ -71,14 +75,14 @@ (is (= 0 @store) "function body isn't called")))) (deftest defcached-declaration-test - (defcached f1 [a b] - ^{:expire 10} [a b] - (- a b) - (+ a b)) + (defcached ^:test-meta f1 [a b] + ^{:expire 10} [a b] + (- a b) + (+ a b)) (defcached f2 "adds a and b" [a b] - ^{:expire 10} [a b] - (- a b) - (+ a b)) + ^{:expire 10} [a b] + (- a b) + (+ a b)) (defcached f3 [a b] {:pre [(pos? a) (pos? b)]} ^{:expire 10} [a b] @@ -89,6 +93,10 @@ ^{:expire 10} [a b] (- a b) (+ a b)) + (is (and (:test-meta (meta #'f1)) + (instance? Cache (::cb/cache (meta #'f1))) + (instance? Cache (cb/defcached-cache f1))) + "meta attached correctly") (is (= 3 (f1 1 2) (f2 1 2) (f3 1 2) (f4 1 2)) "function produces correct result") (is (= "adds a and b" (:doc (meta #'f2)) (:doc (meta #'f4))) "docstring is added to function meta") (is (thrown? AssertionError (f3 0 0)) "pre-conditions are added to function")