├── .gitignore ├── LICENSE ├── README.md ├── deps.edn ├── project.clj ├── src └── integrant │ ├── repl.clj │ └── repl │ └── state.clj └── test └── integrant └── repl_test.clj /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | /.cpcache 11 | /.clj-kondo 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016-2023 James Reeves 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Integrant-REPL 2 | 3 | A Clojure library that implements the user functions of Stuart Sierra's 4 | [reloaded workflow][] for [Integrant][]. 5 | 6 | It's very similar to [reloaded.repl][], except that it works for 7 | Integrant, rather than [Component][]. 8 | 9 | [reloaded workflow]: https://cognitect.com/blog/2013/06/04/clojure-workflow-reloaded 10 | [integrant]: https://github.com/weavejester/integrant 11 | [reloaded.repl]: https://github.com/weavejester/reloaded.repl 12 | [component]: https://github.com/stuartsierra/component 13 | 14 | ## Installation 15 | 16 | Add the following dependency to your deps.edn file under a suitable 17 | alias: 18 | 19 | integrant/repl {:mvn/version "0.4.0"} 20 | 21 | Or to your Leiningen project file under the dev profile: 22 | 23 | [integrant/repl "0.4.0"] 24 | 25 | ## Usage 26 | 27 | Require the `integrant.repl` namespace in your user.clj file, and use 28 | the set-prep! function to define a zero-argument function that returns 29 | a prepared Integrant configuration. 30 | 31 | For example: 32 | 33 | ```clojure 34 | (ns user 35 | (:require [integrant.core :as ig] 36 | [integrant.repl :refer [clear go halt prep init reset reset-all]])) 37 | 38 | (def config {::foo {:example? true}}) 39 | 40 | (integrant.repl/set-prep! #(ig/expand config (ig/deprofile [:dev]))) 41 | ``` 42 | 43 | To prepare the configuration, you can now use: 44 | 45 | ```clojure 46 | user=> (prep) 47 | :prepped 48 | ``` 49 | 50 | The configuration is stored in `integrant.repl.state/config`. To 51 | initiate the configuration, use: 52 | 53 | ```clojure 54 | user=> (init) 55 | :initiated 56 | ``` 57 | 58 | This will turn the configuration into a running system, which is 59 | stored in `integrant.repl.state/system`. 60 | 61 | Because these two steps are common, we can instead use: 62 | 63 | ```clojure 64 | user=> (go) 65 | :initiated 66 | ``` 67 | 68 | This performs the `(prep)` and `(init)` steps together. Once the 69 | system is running, we can stop it at any time: 70 | 71 | ```clojure 72 | user=> (halt) 73 | :halted 74 | ``` 75 | 76 | If we want to reload our source files and restart the system, we can 77 | use: 78 | 79 | ```clojure 80 | user=> (reset) 81 | :reloading (...) 82 | :resumed 83 | ``` 84 | 85 | Behind the scenes, Integrant-REPL uses [tools.namespace][]. You can 86 | set the directories that are monitored for changed files by using the 87 | `refresh-dirs` function: 88 | 89 | ```clojure 90 | user=> (require '[clojure.tools.namespace.repl :refer [set-refresh-dirs]]) 91 | nil 92 | user=> (set-refresh-dirs "src/clj") 93 | ("src/clj") 94 | ``` 95 | 96 | [tools.namespace]: https://github.com/clojure/tools.namespace/ 97 | 98 | 99 | ## License 100 | 101 | Copyright © 2024 James Reeves 102 | 103 | Released under the MIT license. 104 | -------------------------------------------------------------------------------- /deps.edn: -------------------------------------------------------------------------------- 1 | {:deps {org.clojure/clojure {:mvn/version "1.11.4"} 2 | org.clojure/tools.namespace {:mvn/version "1.5.0"} 3 | integrant/integrant {:mvn/version "0.13.0"}}} 4 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject integrant/repl "0.4.0" 2 | :description "Reloaded workflow functions for Integrant" 3 | :url "https://github.com/weavejester/integrant-repl" 4 | :license {:name "The MIT License" 5 | :url "http://opensource.org/licenses/MIT"} 6 | :dependencies [[org.clojure/clojure "1.11.4"] 7 | [org.clojure/tools.namespace "1.5.0"] 8 | [integrant "0.13.0"]]) 9 | -------------------------------------------------------------------------------- /src/integrant/repl.clj: -------------------------------------------------------------------------------- 1 | (ns integrant.repl 2 | (:require [integrant.core :as ig] 3 | [integrant.repl.state :as state] 4 | [clojure.tools.namespace.repl :as repl])) 5 | 6 | (repl/disable-reload! (find-ns 'integrant.core)) 7 | 8 | (defn set-prep! [prep] 9 | (alter-var-root #'state/preparer (constantly prep))) 10 | 11 | (defn- prep-error [] 12 | (Error. "No system preparer function found.")) 13 | 14 | (defn prep [] 15 | (if-let [prep state/preparer] 16 | (do (alter-var-root #'state/config (fn [_] (prep))) :prepped) 17 | (throw (prep-error)))) 18 | 19 | (defn- halt-system [system] 20 | (when system (ig/halt! system))) 21 | 22 | (defn- build-system [build wrap-ex] 23 | (try 24 | (build) 25 | (catch clojure.lang.ExceptionInfo ex 26 | (if-let [system (:system (ex-data ex))] 27 | (try 28 | (ig/halt! system) 29 | (catch clojure.lang.ExceptionInfo halt-ex 30 | (throw (wrap-ex ex halt-ex))))) 31 | (throw ex)))) 32 | 33 | (defn- init-system [config keys] 34 | (build-system 35 | (if keys 36 | #(ig/init config keys) 37 | #(ig/init config)) 38 | #(ex-info "Config failed to init; also failed to halt failed system" 39 | {:init-exception %1} 40 | %2))) 41 | 42 | (defn- resume-system [config system] 43 | (build-system 44 | #(ig/resume config system) 45 | #(ex-info "Config failed to resume; also failed to halt failed system" 46 | {:resume-exception %1} 47 | %2))) 48 | 49 | (defn init 50 | ([] (init nil)) 51 | ([keys] 52 | (alter-var-root #'state/system (fn [sys] 53 | (halt-system sys) 54 | (init-system state/config keys))) 55 | :initiated)) 56 | 57 | (defn go 58 | ([] (go nil)) 59 | ([keys] 60 | (prep) 61 | (init keys))) 62 | 63 | (defn clear [] 64 | (alter-var-root #'state/system (fn [sys] (halt-system sys) nil)) 65 | (alter-var-root #'state/config (constantly nil)) 66 | :cleared) 67 | 68 | (defn halt [] 69 | (halt-system state/system) 70 | (alter-var-root #'state/system (constantly nil)) 71 | :halted) 72 | 73 | (defn suspend [] 74 | (when state/system (ig/suspend! state/system)) 75 | :suspended) 76 | 77 | (defn resume [] 78 | (if-let [prep state/preparer] 79 | (let [cfg (prep)] 80 | (alter-var-root #'state/config (constantly cfg)) 81 | (alter-var-root #'state/system (fn [sys] 82 | (if sys 83 | (resume-system cfg sys) 84 | (init-system cfg nil)))) 85 | :resumed) 86 | (throw (prep-error)))) 87 | 88 | (defn reset [] 89 | (suspend) 90 | (repl/refresh :after 'integrant.repl/resume)) 91 | 92 | (defn reset-all [] 93 | (suspend) 94 | (repl/refresh-all :after 'integrant.repl/resume)) 95 | -------------------------------------------------------------------------------- /src/integrant/repl/state.clj: -------------------------------------------------------------------------------- 1 | (ns integrant.repl.state 2 | (:require [clojure.tools.namespace.repl :as repl])) 3 | 4 | (repl/disable-reload!) 5 | 6 | (def config nil) 7 | (def system nil) 8 | (def preparer nil) 9 | -------------------------------------------------------------------------------- /test/integrant/repl_test.clj: -------------------------------------------------------------------------------- 1 | (ns integrant.repl-test 2 | (:require [clojure.test :refer :all] 3 | [integrant.repl :refer :all])) 4 | 5 | (deftest a-test 6 | (testing "FIXME, I fail." 7 | (is (= 0 1)))) 8 | --------------------------------------------------------------------------------