Comments (2)
Here is a example code of how I imagine the apis working:
;; db1.clj
(ns myproj.db1 (:require [gungnir.database :as gungnir]))
;; calling make-datasource returns a state-map
;; this state map has all the functions and state you need to operate with this particular connection
(let [{:keys [close-fn changeset-fn datasource find!-fn find-by!-fn all!-fn save!-fn]}
(gungnir/make-datasource! :datasource1
{:adapter "postgresql" :username ....})]
;; the idea for this was taken from the well known and mature websockets library sente
;; see the first code example in the sente readme https://github.com/ptaoussanis/sente
;; the tradeoff is this little bit of boilerplate here
;; but this could easily be wrapped into a gungnir provided namespace for the default case
(def *datasource* datasource)
(def find! find!-fn)
(def find-by! find-by!-fn)
(def all! all!-fn)
(def save! save!-fn)
(def changeset changeset-fn)
(def close close-fn))
;; now an example of how to use it
;; user.clj
(ns myproj.user
(:require [myproj.db1 :refer [find! find-by! all! changeset save!]]
[gungnir.model :as model]))
(model/register!
{:user
[:map
[:user/id {:primary-key true} uuid?]
[:user/email [:re {:error/message "Invalid email"} #".+@.+\..+"]]
[:user/password [:string {:min 6}]]
[:user/created-at {:auto true} inst?]
[:user/updated-at {:auto true} inst?]]})
(defn by-id [id] (find! id))
(defn by-email [email] (find-by! email))
(defn change-password [id new-password]
(save! (changeset (by-id id) {:user/password new-password})))
;; that was the basic use case
;; now, how would one connect to multiple datasources?
;; well, we duplicate db1, but as db2, so you have myproj.db1 and myproj.db2
;; each should connect to different databases, but def the same functions
;; product.clj
;; in this example the user database is separate from the database of products
(ns myproj.products
(:require [myproj.db2 :as db2]
[gungnir.model :as model]))
(model/register!
{:product
[:map
[:product/id {:primary-key true} uuid?]
[:product/sku [:re {:error/message "Invalid SKY"} #"SKU-.*"]]
[:product/name [:string]]
[:product/amount-in-stock [:int {:min 0}]]
[:product/description [:string]]
[:product/image [:string]]
[:product/created-at {:auto true} inst?]
[:product/updated-at {:auto true} inst?]]})
(defn by-id [id] (db2/find! id))
(defn by-sku [sku] (db2/find-by! sku))
(defn decrement-stock [sku]
(let [old-p (by-sku sku)]
(db2/save! (db2/changeset old-p (update old-p :product/amount-in-stock dec)))))
Some points:
- model registration doesn't care about datasources, so you could use the same :user or :product models on the different datasource connections.
- good developer experience: That is, the day-to-day usage of the query and changeset functions stays the same. No extra params to inject when working with multiple datasources, just :require the proper ns for the datasource you need.
- The current gungnir.database namespace could become the default global one, so everything stays the same for devs now. And then if you need to reach out to a 2nd data source you could use a function in a new ns, like gungnir.datasource.factory or something that would provide the make-datasource! function in my snippet above.
- kwrooijen:
I think the only addition we'd need is a new ns / function to do this. Possibly gungnir/make-datasource-map! Or something. There is a problem with this however which needs some thought. Currently the sql and database namespaces are intertwined. I want to separate them, and create a new namespace.
sql.honeysql
database (next-jdbc)
database.hikaricp
The reason for this is that there are users that want to use Gungnir but not use Honeysql. So users can use a different adapter for their needs. In your example we're using a function that holds context of both HoneySQL and HikariCP, breaking that separation. I like your idea though, having a namespace for your datasource / functions. I think that's also what Ecto does but through a macro.
This could probably be fixed by havingdatabase.hikaricp/make-datasource!
andsql.honeysql/api
functions. then building those two together the way you're doing
- kwrooijen:
gungnir/make-datasource-map!
would return functions that are already partial'd with the datasource. example(partial find!-underlyling-impl datasource)
would be what is returned forfind!-fn
- Including the datasource itself in the state map is just for completeness so the dev could hook other stuff into it, if necessary
from gungnir.
I'll add that at first I found the '(let [...] (def..) (def ..))` pattern strange and off-putting when I encountered it in sente, but after using that library a fair bit, it works just fine and gives you great power
from gungnir.
Related Issues (20)
- Support more postgres errors HOT 2
- Multiple relations to the same table
- Transaction support HOT 4
- Spec violation when using local malli registries HOT 4
- Printing honeysql form causes perf issues
- Exception handling is too greedy HOT 1
- Add validations for model structure
- Change atoms to vars in gungnir.model HOT 2
- Casting models with dashes broken HOT 1
- Add support for `:table` schemas
- Support non auto primary keys
- Documentation
- `q/select :model/*` doesn't support relations
- Support non model building queries
- Project is active? HOT 4
- Make Gungnir more modular HOT 1
- help with trivial issues HOT 3
- Support linked relations
- Add gungnir.database/close-datasource! HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from gungnir.