This Gist shows how you may want to implement a forkable Ring Session Store for development and testing.
Last active
February 28, 2016 10:19
-
-
Save vvvvalvalval/985b1e759f3e0ff8a21c to your computer and use it in GitHub Desktop.
Forkable stores
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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})))) | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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