System
BigConfig System is a library for implementing system lifecycles as programmable workflows rather than static dependency graphs. You should be familiar with libraries like Integrant .
- Add BigConfig as a dependency to your project
Terminal window neil add dep io.github.amiorin/big-config - Require the system library, it is just 6 functions so far
(ns systems(:require[babashka.process :as p][big-config.core :refer [->workflow]][big-config.system :as system :refer [stop stop! add-stop-fn re-process env destroy!]]))
- Trivial background process in place of a real database for example
(let [kill-timeout 150shutdown-timeout 100clj-timeout 3000];; Define a managed background process with a specific lifecycle;; Start the process in the background and block the main thread;; until the regex is found or the timeout triggers.(defn background-process [opts](let [re-opts (into {} [[:cmd (format "clj -X big-config.system/main :shutdown-timeout %s" shutdown-timeout)][:regex #"token"][:timeout clj-timeout][:key ::proc]])opts (re-process re-opts opts)](add-stop-fn opts (fn [{:keys [::proc] :as opts}](when proc(destroy! proc kill-timeout)))))))(defn main [& {:keys [shutdown-timeout]}](assert shutdown-timeout)(.addShutdownHook (Runtime/getRuntime)(Thread. (fn [](println "\n[Shutdown Hook] Cleaning up resources before exit...")(Thread/sleep shutdown-timeout))))(println "Script is running... (Press Ctrl+C to test)")(println "token")@(promise))
- The system function is just a BigConfig workflow
;; Define the system as a stateful, wired workflow(def ->system(->workflow {:first-step ::start:wire-fn (fn [step _](case step::start [background-process ::end]::end [stop]))}))
- Start and stop the system differently based on the use case
;; sys1 starts and stops the system. It useful during the development of the system itself.;; sys2 starts only. This is useful in all the other cases.(into {} [[:sys1 (->system [log-step-fn] {::bc/env :repl})][:sys2 (let [system (atom (->system [log-step-fn]{::bc/env :repl ::async true}))](stop! @system)@system)]])
With use-fixtures
Section titled “With use-fixtures”In Clojure’s clojure.test framework, use-fixtures is the standard mechanism for managing setup and teardown logic across your test suite. It allows you to wrap your tests with specific functions to prepare a clean environment—such as starting a database connection or binding dynamic variables—and then clean up afterward. You can apply fixtures at two levels: each (running once for every individual test) or once (running a single time for the entire namespace). A fixture is simply a higher-order function that accepts a test function as an argument, executes your setup code, calls the test function, and finally runs your teardown logic.
(defonce system (atom nil))
(defn with-system [f] (when @system (system/stop! @system)) (reset! system (components/->system {::bc/env :repl ::components/profile :test ::system/async true})) (f))
(use-fixtures :each with-system)