Skip to content

Instantly share code, notes, and snippets.

@vvvvalvalval
Last active February 28, 2016 10:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vvvvalvalval/985b1e759f3e0ff8a21c to your computer and use it in GitHub Desktop.
Save vvvvalvalval/985b1e759f3e0ff8a21c to your computer and use it in GitHub Desktop.
Forkable stores

This Gist shows how you may want to implement a forkable Ring Session Store for development and testing.

(ns forkable.protocols)
(defprotocol PotentiallyForkable
"A protocol to ask an object if it supports forking."
(forkable? [this] "whether or not this instance supports forking.")
(fork [this] "If this instance supports forking, returns a newly created forked version,
otherwise throws an Exception. You should call forkable? before calling this function."))
;; we can ask any Object if it supports forking; by default the answer is no.
(extend-protocol PotentiallyForkable
Object
(forkable? [_] false)
(fork [this] (throw (ex-info "This instance is not forkable." {:this this}))))
(ns forkable.stores.ring-store
(:require [ring.middleware.session.store :as rstore]
[forkable.protocols :as frk])
(:use clojure.repl clojure.pprint)
(:import (java.util UUID)))
(defrecord MockRingStore
[a_map serialize deserialize generate-key]
rstore/SessionStore
(rstore/read-session [_ key]
(when-let [v (@a_map key)]
(deserialize v)))
(rstore/write-session [_ key data]
(let [key (or key (generate-key))]
(swap! a_map assoc key (serialize data))
key))
(rstore/delete-session [_ key]
(swap! a_map dissoc key))
frk/PotentiallyForkable
(frk/forkable? [_] true)
(frk/fork [this]
(->MockRingStore (atom @a_map)
serialize deserialize generate-key)))
(defn ^rstore/SessionStore mock-ring-store
"Creates an in-memory, forkable Ring Session store.
* `starting-point-state` must be a map from string keys to (potentially serialized) values.
* `generate-key` must be a 0-arity function which generates a unique key.
* the `serialize` and `deserialize` functions can be provided to better emulate the behaviour of your production store,
but will default to identity for efficiency."
([starting-point-state {:keys [serialize deserialize generate-key]
:or {serialize identity
deserialize identity
generate-key #(str "frk:ring:store:" (UUID/randomUUID))}}]
(->MockRingStore (atom starting-point-state)
serialize deserialize generate-key))
([starting-point-state]
(mock-ring-store starting-point-state {}))
([]
(mock-ring-store starting-point-state {}))
)
(defn fork
[^rstore/SessionStore store]
(if (ftr/forkable? store)
(frk/fork store)
(mock-ring-store) ;; if not forkable (e.g if its a production instance backed by Redis), we start with a blank state
))
(comment
;; example usage
(require '[taoensso.nippy :as nippy])
(def my-store (mock-ring-store {}
{serialize nippy/freeze
deserialize nippy/thaw}))
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment