Skip to content

Instantly share code, notes, and snippets.

@bguthrie
Last active August 29, 2015 13:57
Show Gist options
  • Save bguthrie/9497397 to your computer and use it in GitHub Desktop.
Save bguthrie/9497397 to your computer and use it in GitHub Desktop.
(ns persistable.async
(:import [persistable.core Persistable])
(:require [persistable.core :as core]))
(defn async-persistable [wrapped]
(reify Persistable
(update [this id values] (future (core/update wrapped id values)))
(insert [this values] (future (core/insert wrapped values)))
(delete [this query] (future (core/delete wrapped query)))
(find-one [this query] (future (core/find-one wrapped query)))
(find-all [this query] (future (core/find-all wrapped query)))
(count-all [this query] (future (core/count-all wrapped query)))))
(ns persistable.core
(:require [clojure.java.jdbc :as sql]
[honeysql.core :as honey]))
;; Interface
;; Presumes nothing about the underlying datastore or the kinds of queries it's expected to perform.
(defprotocol Persistable
(update [this id values] "Given an id and a map of values, updates the record and returns the updated values.")
(insert [this values] "Given a map of values, inserts the record and returns the updated values.")
(delete [this query] "Given a query, deletes all records that match it and returns the number affected.")
(find-one [this query] "Given a query, returns the first record matching it.")
(find-all [this query] "Given a query, returns all records matching it.")
(count-all [this query] "Given a query, returns the count of records matching it."))
;; Stuff you can do now that you have an interface.
(defn find-by-id [persistable id]
(find-one persistable [:= :id id]))
(defn reload [persistable values]
(find-by-id persistable (:id values)))
(defn delete-by-id [persistable id]
(delete persistable ["id = ?" id]))
(defn exists? [persistable query]
(= 0 (count-all persistable query)))
(defn new-record? [persistable values]
(or (nil? (:id values)) (not (exists? [:= :id (:d values)]))))
(defn save [persistable values]
(if (new-record? persistable values)
(insert persistable values)
(update persistable (:id values) values)))
(ns persistable.honey
(:import [persistable.core Persistable])
(:require [honeysql.core :as honey]))
; Helper to merge Honey queries.
(defn- merge-query [base-query where-query]
(merge base-query (if (vector? where-query) {:where where-query} where-query)))
(defn db-persistable
([conn table-name] (db-persistable conn table-name identity identity))
([conn table-name ->db <-db]
(reify Persistable
(update [this id values]
(sql/with-connection conn
(sql/update-values table-name [:id id] (->db values))))
(insert [this values]
(sql/with-connection conn
(sql/insert-record table-name (->db values))))
(delete [this query]
(sql/with-connection conn
(sql/delete-rows table-name query)))
(find-one [this query]
(sql/with-connection conn
(sql/with-query-results res
(honey/format (merge-query {:select [:*] :from [table-name] :limit 1} query))
(when-let [record (first res)] (<-db record)))))
(find-all [this query]
(sql/with-connection conn
(sql/with-query-results res
(honey/format (merge-query {:select [:*] :from [table-name]} query))
(map <-db (doall res))))))
(count-all [this query]
(sql/with-connection conn
(sql/with-query-results res
(honey/format (merge-query {:select [:%count.*] :from [table-name]} query))
(:count (first res))))))))
(ns persistable.stub
(:import [persistable.core Persistable]))
(defn stub-persistable [& stubs]
(let [stubs (or (first stubs) {})]
(reify Persistable
(update [this id values] (or (:update stubs) values))
(insert [this values] (or (:insert stubs) values))
(delete [this query] (or (:delete stubs) 0))
(find-one [this query] (or (:find-one stubs) {}))
(find-all [this query] (or (:find-all stubs) []))
(count-all [this query] (or (:count stubs) 0)))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment