Configure REPL on Startup
A Clojure REPL starts in the user
namespace by default. Clojure will automatically load code from a user.clj
file into the REPL to carry out common startup tasks.
- load project code into the REPL by requiring namespaces
- call functions to run an application or service
- start components (i.e for mount, component, integrant)
Example project
practicalli/clojure-configure-repl project contains example code for configuring the REPL start up
juxt/edge has example projects using the same technique.
Create a dev/user.clj
file and :env/dev
alias
Create a dev/user.clj
file with a namespace called user
.
user.clj
should include a namespace definition
(ns user)
practicalli/clojure-deps-edn includes a :env/dev
alias which adds the dev
directory to the project classpath.
Alternatively, edit the deps.edn
file and add the following code:
:env/dev
{:extra-paths ["dev"]}
Running a Clojure REPL with the -A:env/dev
alias will make the dev/user.clj
file available to be loaded by the REPL.
In this example the dev/
path is added to the project and then the REPL is run using rebel readline.
clojure -M:env/dev:repl/rebel
Using the dev/ directory
Requiring namespaces
By requiring a namespace in the dev/user.clj
file, the code defined in that namespace will be loaded into the REPL once started.
Add a require expression to the namespace definition in dev/user.clj
(ns user
(:require [practicalli.project-namespace]))
Require loads all the expressions into the REPL, so functions are immediately available.
Calling functions
Functions from the required namespace can be called, to start the application for example.
(ns user
(:require [practicalli.project-namespace]))
(practicalli.project-namespace/-main)
Fuzzy searching for library dependencies - deps.edn
The find-deps project fuzzy searches Maven Central and Clojars for dependencies when given a name.
Add the find-deps project to the env/dev
alias as an :extra-deps
(:env/dev
is available in practicalli/clojure-deps-edn)
:env/dev
{:extra-paths ["dev"]
:extra-deps {find-deps/find-deps
{:git/url "https://github.com/hagmonk/find-deps"
:sha "6fc73813aafdd2288260abb2160ce0d4cdbac8be"}}}
Require the find-deps.core
namespace in the dev/user.clj
file to use its deps
and print-deps
functions
(ns user
(:require [find-deps.core :as find-deps]))
Start a REPL using the :env/dev
alias.
To start a Rebel REPL with :env/dev
use the following command in a terminal
clojure -A:env/dev:repl/rebel
In the REPL, call the (find-deps/deps "library-name")
to return a map of the matching dependency, or (find-deps/print-deps "library name")
to print dependencies in a table.
Starting Component Life-cycle Services
Clojure has several library to manage the life-cycle of components that make up the application, especially those components with state. Components can be started and stopped in a specific order.
Example component life-cycle libraries included
In Clojure it is idiomatic to define the component life-cycle services in a namespace called dev
. In the dev/user.clj
file, add the following ns
declaration to require the dev
namespace and change to that namespace with in-ns
(ns user
(:require [dev]))
(dev/go)
Now define code in the dev/dev.clj
file that controls the component life-cycle services library for the project.
Example project with component life-cycle
Using mount, its common to 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.
(ns user
:require [mount.core :refer [defstate]]
[practicalli.app.main])
(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.
(defn go
"Start all states defined by defstate"
[]
(start)
:ready)
The stop
function stops all components, removing all non-persistent state.
(defn stop [] (mount/stop))
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))
TODO: pull requests accepted
TODO: pull requests accepted