Search code examples
clojuremonger

Clojure - avoid duplicated code with monger requests


I am using Clojure and Monger

It works fine, and I group functions by the collection they relate to. Therefore, every file begins like this :

(ns img-cli.model.mycollectionname
  (:require [monger.core            :as mg]
            [monger.collection      :as mc]
            [edn-config.core        :refer [env]])
  (:import  [com.mongodb MongoOptions ServerAddress DB WriteConcern]
            [org.bson.types ObjectId]))


(def config (get-in env [:mongo]))

;; using MongoOptions allows fine-tuning connection parameters,
;; like automatic reconnection (highly recommended for production
;; environment)
(def ^MongoOptions  opts (mg/mongo-options { :threads-allowed-to-block-for-connection-multiplier 300}))
(def ^ServerAddress sa   (mg/server-address (:url config) (:port config)))
(def conn                (mg/connect sa opts))
(def db                  (mg/get-db conn (:db config)))

(def collection-name "asset")

;; I have to write one like this every time
(defn find-one-as-map
  "fetch asset by Id"
  [^String id]
  (mc/find-one-as-map db collection-name {:_id (ObjectId. id)}))

Code duplication has of course several disadvantages in itself. Also I'm not sure if connections are properly pooled afterwards ?

How can I avoid doing this ? I sense I could pass an additional "db" parameter to each function, but then where would it come from ?

If I create the db connection in the "entry" file of my program, then how could it be passed to every function from there ?

For instance let's says I have Compojure routes in different files :

;; in the main handler file

(def db ...) ;; if I move the previous db configuration
             ;; in here, it could be the only place where this is set

;; importing Compojure routes from different files
(defroutes routes-from-file1
                  routes-from-file2...)

Let's say that some functions called from some of the routes in "file2" need access to the db, how can I pass this variable to them ?

I also have a lot of repetitive code afterwards, for instance to get data by Id for every collection... I feel this could be simplified, but I'm not sure how.


Solution

  • Just refer to it by its namespace

    (ns foo
      (:require [handler :as h]))
    (println h/db)