{"id":24083368,"url":"https://github.com/mitranim/clojure-forge","last_synced_at":"2025-04-30T18:23:26.995Z","repository":{"id":62431894,"uuid":"108562099","full_name":"mitranim/clojure-forge","owner":"mitranim","description":"Support library for Clojure servers built on the System/Component pattern","archived":false,"fork":false,"pushed_at":"2018-07-19T06:35:40.000Z","size":20,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-26T06:56:55.935Z","etag":null,"topics":["clojure","devtools"],"latest_commit_sha":null,"homepage":null,"language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mitranim.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-10-27T15:20:47.000Z","updated_at":"2022-06-06T02:44:09.000Z","dependencies_parsed_at":"2022-11-01T21:00:38.360Z","dependency_job_id":null,"html_url":"https://github.com/mitranim/clojure-forge","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mitranim%2Fclojure-forge","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mitranim%2Fclojure-forge/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mitranim%2Fclojure-forge/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mitranim%2Fclojure-forge/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mitranim","download_url":"https://codeload.github.com/mitranim/clojure-forge/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251758797,"owners_count":21639111,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clojure","devtools"],"created_at":"2025-01-09T23:56:29.962Z","updated_at":"2025-04-30T18:23:26.950Z","avatar_url":"https://github.com/mitranim.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Overview\n\nSupport library for Clojure servers built on the [system/component\npattern](https://github.com/stuartsierra/component) and Ring.\n\nSee example project templates:\n\n  * [Clojure with Datomic](https://github.com/Mitranim/clojure-datomic-starter)\n  * [Clojure with Auth0](https://github.com/Mitranim/clojure-auth0-starter)\n\nProvides plumbing:\n\n  * a place to store the system singleton, safe from namespace reloads (see below)\n  * shortcuts for system lifecycle management: recreate, restart\n\nProvides a smooth development experience:\n\n  * automatic code reload on change\n  * automatic system reset on code change\n  * automatic webpage refresh on system reset\n  * rendering of compile errors and runtime exceptions\n\nInspired by:\n\n  * [`component.repl`](https://github.com/stuartsierra/component.repl): system\n    storage, lifecycle shortcuts\n  * [`lein-ring`](https://github.com/weavejester/lein-ring): automatic code\n    reload, webpage refresh, error rendering\n\nUses [`clojure.tools.namespace`](https://github.com/clojure/tools.namespace) for\ncode reload. Adapts it to work in background threads such as filesystem\nwatchers. Properly refreshes namespaces in nREPL sessions (assumes\n`clojure.tools.nrepl`).\n\nComparison with [`component.repl`](https://github.com/stuartsierra/component.repl):\n\n  * automatic code reload\n\n  * background code reload correctly refreshes the REPL\n\n  * integrated webserver goodies: auto refresh on system reset, error rendering\n\nComparison with [`lein-ring`](https://github.com/weavejester/lein-ring):\n\n  * not Ring-specific\n\n  * doesn't mess with your build, AOT compilation works properly\n\n  * code reload uses\n    [`clojure.tools.namespace`](https://github.com/clojure/tools.namespace),\n    avoiding limbo states\n\n  * code reload involves a system reset, making it easy to redefine background\n    activities such as job queues\n\n  * background code reload correctly refreshes the REPL\n\n  * code reload works on its own, you don't need a webpage open\n\n  * code reload is vastly more reliable\n\n  * page refresh is vastly more reliable\n\n\n## Installation\n\nAdd to `project.clj`:\n\n\u003c!-- [![Clojars Project](https://img.shields.io/clojars/v/com.mitranim/forge.svg)](https://clojars.org/com.mitranim/forge) --\u003e\n\n```clj\n[com.mitranim/forge \"0.1.0\"]\n```\n\nRequire in code:\n\n```clj\n(:require [com.mitranim.forge :as forge])\n```\n\n\n## Usage\n\n```clj\n(ns core\n  (:gen-class)\n  (:require\n    [com.mitranim.forge :as forge]\n    [com.stuartsierra.component :as component]))\n\n(defn create-system [prev-system]\n  (reify\n    component/Lifecycle\n    (start [this] (println \"starting\") this)\n    (stop [this] (println \"stopping\") this)))\n\n(defn -main []\n  (forge/reset-system! create-system))\n\n(defn -main-dev []\n  (forge/start-development! {:system-symbol `create-system})\n  (forge/reset-system! create-system))\n```\n\nWhen using Ring, add the middleware that automatically refreshes webpages and\nrenders errors:\n\n```clj\n(let [my-ring-handler (forge/wrap-development-features my-ring-handler)])\n```\n\nLaunch your REPL and run an equivalent of this:\n\n```clj\n(forge/start-development! {:system-symbol `create-system})\n(forge/reset-system! create-system)\nforge/sys\n```\n\nNow, modifying source files or running `(forge/reset)` will trigger a code\nreload and system reset. The current system is always stored in `forge/sys`.\n\nEnjoy your workflow!\n\nThe `template` folder in this repo provides the absolute smallest starting core.\nCopy it to start playing around.\n\n## API\n\nThe most important stuff is listed here. To dig deeper, check the source. It's\nsimple and hackable.\n\nAll functions here are thread-safe and idempotent.\n\n#### `set-system-symbol!`\n\nTells Forge where to find your `create-system` function after a namespace\nrefresh. Needs to be called once before using `reset`. `start-development!` also\nsets this.\n\n```clj\n(forge/set-system-symbol! `create-system)\nforge/system-symbol\n```\n\n#### `sys`\n\nStores the current system. Gets modified by `reset` and `reset-system!`. Can be\nused to avoid passing the system everywhere. Also convenient in the REPL.\n\n```clj\n(forge/reset)\nforge/sys\n```\n\n#### `start-development!`\n\nStarts auto-reload and other goodies. Run it once after launching the REPL. See\n[Usage](#usage) for example code.\n\n#### `reset`\n\nReloads modified namespaces, recreates and restarts the system. Must be called\nafter `set-system-symbol!` or `start-development!`.\n\nAfter one `start-development!` call, `reset` runs automatically on every source\nchange.\n\n```clj\n; once\n(forge/start-development! {:system-symbol `create-system})\n(forge/reset)\n```\n\n#### `reset-system!`\n\nRecreates and restarts the system, storing the result in `#'forge/sys`.\nIn development, use `reset` instead of this.\n\nDefine your \"create-system\" function. It must take one argument, the previous\nversion of the system, and return the next version _without starting it_.\n\nHandles exceptions carefully:\n\n* exception when stopping → store the partially stopped system so you can fix it\n  manually\n\n* exception when starting → stop the partially started system, store the\n  remainder\n\nThe latter can be convenient when debugging production failures. If any\ncomponent fails to start, the rest won't keep the JVM from shutting down.\n\n```clj\n(defn create-system [prev-system]\n  (component/system-map))\n\n(defn -main []\n  (try (forge/reset-system! create-system)\n    (catch Throwable err\n      (shutdown-agents)\n      (binding [*out* *err*] (prn err))\n      (System/exit 1))))\n```\n\n#### `wrap-development-features`\n\nOptional Ring middleware for auto-refresh and error rendering. Add to your\nmiddleware stack as an outer layer, typically just before the 500 handler:\n\n```clj\n(def handler\n  (-\u003e routes\n      ... other middleware ...\n      forge/wrap-development-features\n      my-500-handler))\n```\n\nRunning `restart-system!` or `reset` will refresh any open webpages.\n\n#### `refresh-namespaces`\n\nRefreshes any modified namespaces. This is a version of\n`clojure.tools.namespace.repl/refresh` that works in background threads, so it's\nusable in filesystem watchers, HTTP handlers, etc. Used internally by `reset`.\n\n```clj\n(forge/refresh-namespaces)\n\n; works\n(future (forge/refresh-namespaces))\n```\n\n**Note:** unlike `(require 'my-ns :reload)`, this completely replaces namespace\nobjects, breaking `defonce`. To preserve state, keep it in your System and\nmigrate between system resets. If you have sufficiently good reasons, you can\nopt a namespace out of \"hard\" reload into \"soft\" reload:\n\n```clj\n(ns my-ns\n  (:require\n    [clojure.tools.namespace.repl :refer [disable-unload!]]))\n\n(disable-unload!)\n\n(defonce blah blah)\n```\n\n#### ...\n\nFor lower-level stuff, please run `(dir com.mitranim.forge)` and check the\nsource; it's annotated and self-explanatory.\n\n## Changelog\n\n### 0.1.2\n\nAvoid double status notification on `reset`.\n\n### 0.1.1\n\nMore reliable webpage reloading. Now uses websockets to avoid a few edge cases\nin long polling.\n\n### 0.1.0\n\ninit\n\n## Misc\n\nFeedback, criticism, suggestions, and pull requests are welcome!\n\nOpen an issue or reach me on skype:mitranim.web or me@mitranim.com.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmitranim%2Fclojure-forge","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmitranim%2Fclojure-forge","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmitranim%2Fclojure-forge/lists"}