Configure REPL on Startupλ︎
A Clojure REPL starts in the user
namespace by default. Clojure automatically loads code from a user.clj
file when found on the class path.
The user.clj
file can be used for one or more of the following:
- load code into the REPL by requiring namespaces
- call functions to run an application or service
- start components (i.e for mount, component, integrant)
- hotload libraries into the REPL process without restart
- adding development tools - e.g. portal data inspector
Clojure CLI cannot set the REPL to a different namespace
Clojure CLI has no specific mechanism to start the REPL in a namespace other than user
.
Technically the -e
option could be used to set a different namespace by calling in-ns
with a different namespace, although this approach may affect the running of other tools or add extra complexity to the commands.
Require functions into the user
nameapace rather than use potentially complex commands to set the namespace.
Example projects
practicalli/clojure-app-template contains a dev/user.clj
file for configuring the REPL at start up.
Custom user namespaceλ︎
A custom user.clj
is typically placed in a dev
folder within the root of the project, with the dev
path defined in an alias to keep it separated from production code.
Create a dev/user.clj
file with a namespace called user
.
Create an alias to include the dev
path when running a REPL process
Practicalli Clojure CLI Config includes aliases that add dev
directory to the class path
:dev/env
alias only adds thedev
directory to the classpath:dev/reloaded
adds library hotload, namespace reload, porta data inspector and testing libraries &test
:repl/reloaded
adds Rebel rich terminal UI to the tools provided by:dev/reloaded
Run a Clojure REPL with the :repl/reloaded
alias (or :dev/reloaded
:dev/env
) to add the dev
directory to the class path and load the code in dev/user.clj
file into the REPL.
Keep user.clj
separate
- The
user.clj
code should not be included in live deployments, such as a jar or uberjar. Including thedev/
directory via an alias separates theuser.clj
from deployment actions.
Requiring namespacesλ︎
Namespaces required in the user
ns form will also be loaded. If a required namespace also requires namespaces, they will also be loaded into the REPL during startup.
Functions (defn)
and data (def)
are immediately available.
Require namespace in user ns expression
Add a require expression to the namespace definition in dev/user.clj
Requiring a large number of libraries may slow REPL start up time
Require namespace in require expression
If the library is not always required, place a require
within a (comment ,,,)
expression to be evaluated by the developer any time after REPL startup.
Calling functionsλ︎
Use the fully qualified function name from the required namespace can be called, to start the application for example.
Example
An alias can be used in the require expression, useful if multiple functions from a namespace are to be called
Hotload librariesλ︎
Hotload is a way to add libraries to a running REPL process that were not include as a dependency on REPL startup.
Add-libs is an unreleased feature
add-libs
is not yet an official feature and currently available only in the add-libs3 branch of the now deprecated clojure.tools.deps.alpha
library. add-libs should become official release in 2023, although not in within org.clojure/tools.deps
library.
clojure/tools.deps is the official library for all released functions from the alpha library
:repl/reloaded
and dev/reloaded
aliases in Practicalli Clojure CLI Config provide the add-libs
function.
Edit the project deps.edn
configuration and add an :lib/hotload
alias for the clojure.tools.deps.alpha.repl
library. Or add an alias to the user level configuration for use with any Clojure CLI project.
The add-libs
code is on a separate add-libs3 branch, so requires the SHA from the head of add-libs3 branch
:lib/hotload
{:extra-deps {org.clojure/tools.deps.alpha
{:git/url "https://github.com/clojure/tools.deps.alpha"
:git/sha "e4fb92eef724fa39e29b39cc2b1a850567d490dd"}}}
Alias example from Practicalli Clojure CLI Config
Start a REPL session using Clojure CLI with :repl/reloaded
, dev/reloaded
or :lib/hotload
aliases
Require and refer add-libs function
Require the clojure.tools.deps.alpha
library and refer the add-libs
function. The add-libs
function can then be called without having to use an alias or the fully qualified name.
Hotload one or more libraries into the REPL using the add-lib
function, including the fully qualified name of the library and version string.
Hotload the hiccup library
The hiccup library converts clojure structures into html, where vectors represent the scope of keywords that represent html tags. Load the hiccup library using add-libs
Require the hiccup library so its functions are accessible from the current namespace in the REPL.
Enter an expression using thehiccup/html
function to convert a clojure data structure to html.
System Componentsλ︎
Clojure has several library to manage the life-cycle of components that make up the Clojure system, especially those components with state. The order in which components are started and stopped can be defined to keep the system functioning correctly.
Components can include an http server, routing, persistence, logging publisher, etc.
Example system component management libraries included
- mount - manage system state in an atom
- integrant and Integrant REPL - data definition of system and init & halt defmethod interface
- donut system
- component
Require system namespace in user ns expression
Require the system namespace and use start
, restart
and stop
functions to manage the components in the system
Define code in the dev/system.clj
file which controls the component life-cycle services library for the project.
Create a dev/system.clj
to manage the components, optionally using one of the system component management libraries.
Example life-cycle codeλ︎
Start, stop and restart the components that a system is composed of, e.g. app server, database pool, log publisher, message queue, etc.
Define a dev.clj
file with go
, stop
and restart
functions that manage the life-cycle of mount components. A start
function contains the list of components with optional state.
Require the mount namespace and the main namespace for the project, which should contain all the code to start and stop services.
Define a start function to start all services
(defn start []
(with-logging-status)
(mount/start #'practicalli.app.conf/environment
#'practicalli.app.db/connection
#'practicalli.app.www/business-app
#'practicalli.app.service/nrepl))
The go
function calls start
and marks all components as ready.
The stop
function stops all components, removing all non-persistent state.
The reset function that calls stop
, refreshes the namespaces so that stale definitions are removed and starts all components (loading in any new code).
(defn reset
"Stop all states defined by defstate.
Reload modified source files and restart all states"
[]
(stop)
(namespace/refresh :after 'dev/go))
Example dev.clj file for mount
Use dev
namespace during development
Require practicalli.app.dev
namespace rather than main, to start components in a development environment.
donut.system is a dependency injection library for Clojure and ClojureScript using system and component abstractions to organise and manage startup & shutdown behaviour.
Basic usage guide shows how to define a donut.system
seancorfield/usermanager-example is an example project that uses Component for lifecycle management
Referenceλ︎
- Mount project on GitHub
- Mount - collection of Clojure/Script mount apps
- donut.system
- Component
- A tutorial to Stuart Sierra's Component
- Refactoring to Components - Walmart Labs Lacinia
- Integrant
- Compojure and Integrant
- Build a Clojure web app using Duct - CircleCI
- Reloading Woes - Lambda island