Giter Club home page Giter Club logo

Comments (2)

Ramblurr avatar Ramblurr commented on May 28, 2024

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 having database.hikaricp/make-datasource! and sql.honeysql/api functions. then building those two together the way you're doing

  • 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 for find!-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.

Ramblurr avatar Ramblurr commented on May 28, 2024

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.