Where to put `-main` and how to call it with Integrant

I’ve started creating my first web project in Clojure literally yesterday and so far the experience was surprisingly nice and straight forward. I just can’t find any information on how to best handle -main with integrant-repl. Let me explain: I have a user.clj file which is added through a :dev alias. So for development I just run clj -A:rebel-nrepl -A:dev. This let’s me run (go) and (halt) from a REPL for very easy reloading. But where should I put -main and what’s the preferred way of then loading my system? Before I added integrant-repl I had this in my “main” clojure entrypoint

; (def system
;   (ig/init config))

; (defn -main
;   [& args]
;   (system))

but this means whenever I reload all namespaces the server is started and it then just keeps running regardless of what I do in the REPL with (halt). Should I just call (go) from -main? Keep in mind I started with all these technologies yesterday so it’s hard for me to tell if (go) is strictly dev only and would have negative consequences (I’m not aware of) when used in production. I tried googling and looking for some examples but so far I didn’t find anything.

Bottom line: Where does -main go if I use Integrant and integrant-repl. What do you then use as CMD for your Dockerfile when running everything with just clj -m ...

1 Like

I plan to describe the exact process on my blog soon. But here are some code snippets:

I’ve a core namespace with:

(ns com.example.core
  (:require [com.example.system :as system]))

(defn -main [& _args]
  (system/start :dev))

It just starts the system:

(ns com.example.system
  (:require [integrant.core :as ig]))

;; read it using juxt/aero for example
(def config {})

(defn start [profile]
  (ig/init config))

And that’s all.

I’ve a separate namespace user to run integrant-repl:

(ns user
  (:require [clojure.tools.namespace.repl :refer [set-refresh-dirs]]
            [clojure.java.classpath :refer [classpath-directories]]
            [integrant.repl :as ig-repl :refer [clear go halt prep init reset reset-all]]
            [com.example.system :as system]))

(let [target-dir? (fn [f]
                    ;; match target, target/dev target/prod, etc.
                    (re-matches #".*target(/\w+)?" (str f)))]
  (apply set-refresh-dirs
    (remove target-dir? (classpath-directories))))

(ig-repl/set-prep! (constantly (system/config))

I don’t use rebel. I run the app with clojure -m com.example.core and the REPL with some aliases like clojure -A:dev:repl.

I hope it helps.

Also. If you need something with batteries included you can try out juxt/edge :slight_smile:

Hm I see but then I’d still have the issue that when I reload all namespaces it’s gonna call that -main function, right? One idea I had was to include that core namespace you mentioned through an alias. Meaning when I’m in dev mode I don’t include that file at all, just like I handle user.clj.

Also thanks for the tip about edge, I wasn’t aware of that

No, running REPL in the development doesn’t run -main at all. Notice:

in the user namespace.

I really hope to have a complete example available soon :slight_smile: I will let you know here when I’m done.