Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge remote branch 'raynes/masai'

  • Loading branch information...
commit 5ffff26c6f22a2932ed63192d3a46ff6ab19702f 2 parents 37229ea + c618759
@bmabey bmabey authored
View
1  .gitignore
@@ -13,3 +13,4 @@ pom.xml
## OSx
.DS_Store
+.cake
View
21 masai/README.markdown
@@ -0,0 +1,21 @@
+# Masai support for cache-dot-clj
+
+Masai is a common interface to several key-value stores. Right now, Masai supports both Redis and Tokyo Cabinet, and will support more in the future. This adds Masai support to cache-dot-clj, giving you the ability to cache to any of the databases that Masai supports.
+
+## Example using Masai's redis backend
+
+ (ns an-example
+ (:use cache-dot-clj.cache)
+ (:require [cache-dot-clj.masai :as masai]))
+
+ (defn-cached get-user-from-db
+ (masai/strategy)
+ [username]
+ ;; Slow database read goes here
+ )
+
+## Dependencies
+
+To use Masai and cache-dot-clj pull in the following dependency using Leiningen, Cake or Maven:
+
+ [uk.org.alienscience/masai-dot-clj "0.0.4-SNAPSHOT"]
View
13 masai/project.clj
@@ -0,0 +1,13 @@
+(defproject uk.org.alienscience/masai-dot-clj "0.0.4-SNAPSHOT"
+ :description "Masai support for cache-dot-clj."
+ :dependencies [[org.clojars.raynes/masai "0.5.1-SNAPSHOT"]
+ [org.clojars.raynes/jedis "2.0.0-SNAPSHOT"]
+ [tokyocabinet "1.24.1-SNAPSHOT"]
+ [uk.org.alienscience/cache-dot-clj "0.0.4-SNAPSHOT"]
+ [cereal "0.1.0-SNAPSHOT"]]
+ :dev-dependencies [[org.clojure/clojure "1.2.0"]
+ [org.clojure/clojure-contrib "1.2.0"]]
+ :license {:name "Eclipse Public License - v 1.0"
+ :url "http://www.eclipse.org/legal/epl-v10.html"
+ :distribution :repo
+ :comments "same as Clojure"})
View
65 masai/src/cache_dot_clj/masai.clj
@@ -0,0 +1,65 @@
+(ns cache-dot-clj.masai
+ (:use [cereal format java])
+ (require (masai [redis :as redis]
+ [tokyo :as tokyo]
+ [db :as db])))
+
+(def form (make))
+
+(defn add
+ "Add an item to the given cache and return the value added."
+ [^DB cache k v]
+ (db/put! cache (str k) (encode form v))
+ v)
+
+(defn lookup
+ "Looks up an item in the given cache. Returns a vector:
+ [element-exists? value]"
+ [^DB cache k]
+ (let [record (db/get cache (str k))]
+ [(-> record nil? not) (and record (decode form record))]))
+
+(defn invalidate
+ "Removes an item from the cache."
+ [^DB cache k]
+ (db/delete! cache (str k)))
+
+(defn- make-strategy
+ "Create a strategy map for use with cache-dot-clj.cache"
+ [init-fn]
+ {:init init-fn
+ :lookup lookup
+ :miss! add
+ :invalidate! invalidate
+ :description "Masai backend"
+ :plugs-into :external-memoize})
+
+(defn- prefix [f-name s] (str f-name "/" s))
+
+(defn- key-format [f-name]
+ (fn [^String s] (bytes (.getBytes (prefix f-name)))))
+
+(defn- open [db]
+ (db/open db)
+ db)
+
+(defn strategy
+ "Returns a strategy for use with cache-dot-clj.cache. Given
+ no arguments, uses Redis as the backend with default configuration.
+ If passed an argument, that argument is expected to be a keyword
+ naming the Masai backend to use. Possible keywords are :tokyo and
+ :redis. If given two arguments, the second argument is expected to
+ be a map of options to pass to whatever backend your using as options."
+ ([] (make-strategy
+ #(open (redis/make {:key-format (key-format %)}))))
+ ([back opts]
+ (case
+ back
+ :redis (make-strategy
+ #(open
+ (redis/make
+ (assoc opts :key-format (key-format %)))))
+ :tokyo (make-strategy
+ #(open
+ (tokyo/make
+ (assoc opts :key-format (key-format %))))))))
View
52 masai/test/cache_dot_clj/masai_test.clj
@@ -0,0 +1,52 @@
+(ns cache-dot-clj.masai-test
+ (:use clojure.test
+ cache-dot-clj.cache)
+ (:require [cache-dot-clj.masai :as masai]))
+
+(defn slow [a] (Thread/sleep a) a)
+
+(def fast-default (cached slow (masai/strategy)))
+
+(defmacro how-long [expr]
+ `(let [start# (. System (nanoTime))
+ ret# ~expr
+ msecs# (/ (double (- (. System (nanoTime)) start#))
+ 1000000.0)]
+ {:msecs msecs# :ret ret#}))
+
+(defn expect [situation f check t should]
+ (let [{:keys [msecs ret]} (how-long (f t))]
+ (is (check msecs t) (str situation " (expected time:" t ", actual time: " msecs ") " should))
+ (is (= ret t) (str situation " returns correct value"))))
+
+(defn is-caching [f t]
+ (invalidate-cache f t)
+ (expect "First call" f > t "hits function" )
+ (expect "Second call" f < t "is cached")
+ (expect "Third call" f < t "is cached"))
+
+(deftest is-caching-ehcache (is-caching fast-default 100))
+
+(defn invalidating [f t1 t2 t3]
+ (invalidate-cache f t1)
+ (invalidate-cache f t2)
+ (invalidate-cache f t3)
+ (expect "First call" f > t1 "hits function")
+ (expect "First call" f > t2 "hits function")
+ (expect "First call" f > t3 "hits function")
+ (invalidate-cache f t1)
+ (expect "Invalidated entry" f > t1 "hits function")
+ (expect "Second call" f < t2 "is cached")
+ (expect "Second call" f < t3 "is cached")
+ (expect "Third call" f < t1 "is cached"))
+
+(deftest invalidating-ehcache (invalidating fast-default 50 51 52))
+
+(defn-cached cached-fn
+ (masai/strategy)
+ "A cached function definition"
+ [t]
+ (Thread/sleep t)
+ t)
+
+(deftest is-caching-def (is-caching cached-fn 100))
Please sign in to comment.
Something went wrong with that request. Please try again.