Skip to content

Instantly share code, notes, and snippets.

@aamedina
Last active August 4, 2019 18:12
Show Gist options
  • Select an option

  • Save aamedina/937349a35e724753d00dc47d7c27d7e3 to your computer and use it in GitHub Desktop.

Select an option

Save aamedina/937349a35e724753d00dc47d7c27d7e3 to your computer and use it in GitHub Desktop.
a simple pattern for data-driven reloadable systems with Clojure
;;; deps.edn
{:deps
{org.clojure/clojure {:mvn/version "1.10.0-beta7"}
;; this version is required for clojure.tools.deps.alpha.repl/add-lib
org.clojure/tools.deps.alpha {:git/url "https://github.com/clojure/tools.deps.alpha.git"
:sha "f6c080bd0049211021ea59e516d1785b08302515"}
org.clojure/tools.namespace {:mvn/version "0.2.11"}
com.walmartlabs/schematic {:mvn/version "1.1.0"}
reloaded.repl {:mvn/version "0.2.4"}
;; example libs we might componentize
com.datomic/datomic-free {:mvn/version "0.9.5697"}
org.neo4j/neo4j {:mvn/version "3.4.10"}}
:paths ["src"]}
;;; defsys.edn
{:neo4j {:sc/create-fn defsys-example.system.neo4j/map->Neo4j}
:datomic {:sc/create-fn defsys-example.system.datomic/map->Datomic
:uri "datomic:mem://defsys-example"}
:app {:sc/create-fn defsys-example.system.app/map->App
:sc/refs [:neo4j :datomic]}}
;;; defsys.edn ends here
;;; src/defsys_example/system.clj
(ns defsys-example.system
"A dynamically reloadable data-oriented system definition for the game.
This tiny library ties the following Clojure libraries together for
managing systems of stateful components:
https://github.com/clojure/tools.deps.alpha/tree/add-lib
Downloads and loads libraries from any Java artifact compatible with
tools.deps (Maven, Git repos, local projects, etc.) This is used for
adding new dependencies to the project during development without
restarting the game.
https://github.com/stuartsierra/component
'Component' is a tiny Clojure framework for managing the lifecycle
and dependencies of software components which have runtime state.
https://github.com/walmartlabs/schematic
Schematic is a Clojure library which aids in assembling Component
systems from configuration data.
Only one function is public: `make-system`, which can be given to
repl.reloaded/set-init! to enable REPL-based interactive code
reloading upon reset in your project's user.clj."
(:require
[clojure.tools.deps.alpha.repl :as deps-repl]
[com.walmartlabs.schematic :as scm]))
(defn- load-deps
"Loads the project's deps.edn from disk.
Downloads and installs into the current classloader each library
specified as :deps in the parsed Clojure data."
([]
(load-deps (str (System/getProperty "user.dir") "/deps.edn")))
([deps-path]
(doseq [[lib coord] (:deps (read-string (slurp deps-path)))]
(deps-repl/add-lib lib coord))))
(defn- load-defsys
"Loads the project's defsys.edn from disk.
First it synchronizes the runtime dependencies with the project's
deps.edn, and then reads the system definition from disk."
([]
(load-defsys (str (System/getProperty "user.dir") "/defsys.edn")))
([defsys-path]
(load-deps)
(read-string (slurp defsys-path))))
(defn make-system
"Assembles and returns a system-map from disk and additional
(optional) config.
This system is compatible with Stuart Sierra's Component library and
can be started and stopped at runtime."
([]
(make-system nil))
([config]
(scm/assemble-system (merge (load-defsys) config))))
;;; src/defsys_example/system.clj ends here
;;; src/user.clj
(ns user
(:require
;; Clojure libs
[clojure.repl :refer [doc source pst]]
[clojure.java.io :as io]
[clojure.string :as str]
[clojure.pprint :refer [pp pprint]]
;; System namespaces
[defsys-example.system :as sys]
;; Code reloading support
[clojure.tools.namespace.repl :refer [set-refresh-dirs]]
[reloaded.repl :refer [system init start stop go reset reset-all]]))
(set-refresh-dirs "src")
;; Dev defsys overrides
(def dev-config
{})
(reloaded.repl/set-init! #(sys/make-system dev-config))
;;; src/user.clj ends here
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment