Clojure beginners sometimes struggle with setting up a new Clojure deps.edn
project compared to setting up a lein project. This is one of the reasons I've built neil. But not only for beginners, I've been using neil
myself a ton too, to add common features to existing deps.edn
projects. You may think that neil
is a pun on lein
. Of course it is. But the name is also an hommage to Neil
Peart, one of the greatest progressive rock drummers to have ever lived.
The intent of this post is to give you a starting point from where you can figure out things further. This post isn't going to explain any details of how deps.edn
and related tooling works. For that I'm going to refer you to here.
Let's install neil
which is available for brew, scoop (Windows), nix, Clojure
JVM or can easily be installed manually. Unless you use clojure JVM, neil runs with babashka for fast startup time.
If you've already installed babashka
(perhaps indirectly by installing neil
) but didn't yet install the clojure
CLI or have problems doing so, then you can run bb clojure
instead of clojure
for launching Clojure. Instead of clj
, on linux/macOS you'll want to use rlwrap bb clojure
. If you are on Windows and struggle with the official clojure
Powershell-based installation, bb clojure
may come in handy too.
To start a new Clojure project, run neil new --name myproject
. This produces a myproject
directory with a very basic project layout based on the deps-new scratch
template. Now we are are going to incrementally add some functionality to this project. If you like to skip most of these steps, you can start from the more fully featured app
template with neil new app --name myproject
.
Let's decide that we're going to need a library to deal with files. Let's search for one:
$ neil dep search "file system"
:lib fs/fs :version 1.3.3 :description "File system utilities for clojure"
:lib me.raynes/fs :version 1.4.6 :description "File system utilities for clojure"
:lib babashka/fs :version 0.1.6 :description "Babashka file system utilities."
Let's go with the babashka/fs library:
$ neil dep add :lib babashka/fs :version 0.1.6
Now the library is added to deps.edn
and we can use it in our project:
$ clj
Clojure 1.11.0
user=> (require '[babashka.fs :as fs])
nil
Let's start by adding a test runner. Type: neil add test
. After doing this, you will be able to run:
clojure -M:test
We don't have any tests in this project, so let's add one by adding a file:
test/myproject/core_test.clj
(ns myproject.core-test
(:require [clojure.test :as t :refer [deftest is testing]]))
(deftest failing-test
(testing "TODO: fix test"
(is (= 3 4))))
Now run clojure -M:test
again:
$ clojure -M:test
Running tests in #{"test"}
Testing myproject.core-test
FAIL in (failing-test) (core_test.clj:6)
TODO: fix test
expected: (= 3 4)
actual: (not (= 3 4))
Ran 1 tests containing 1 assertions.
1 failures, 0 errors.
The test runner we added is the Cognitect Labs test-runner so check out the README of that project if you need to know more.
Update 2022-08-06: neil
now comes with a new subcommand: test
, so you can replace clojure -M:test
with neil test
. Also when you've created a project with neil new
, neil add test
will generate one default test for you.
Run:
neil add nrepl
to add a :nrepl
alias to your project. Now you can run clj -M:nrepl
to get a console REPL, but also a running nREPL server that you can connect to from your editor. Note that many editors also support jack-in
and if you prefer to use that, you won't need this.
What's the equivalent of lein uberjar
in the deps.edn
world? You're going to need tools.build. To create a build.clj
file (the program that is going to build your uberjar), run:
neil add build
Since the default build.clj
file is going to assume your project is under git
version control, let's initialize a git repo first:
git init
git add deps.edn src test
git commit -m "initial commit"
Before creating the uberjar, we have to add :gen-class
to src/scratch.clj
:
(ns scratch
(:gen-class))
and we add :main 'scratch
in the call to b/uber
:
(b/uber {:class-dir class-dir
:uber-file uber-file
:basis basis
:main 'scratch})
Now let's compile the uberjar:
clojure -T:build uber
And then let's run it:
$ java -jar target/lib1-1.2.1-standalone.jar 1 2 3
-main with (1 2 3)
If you have difficulty remembering the above invocations, you can write a bb.edn
with some tasks:
bb.edn
:
{:tasks
{:requires ([babashka.fs :as fs])
test {:doc "Run tests"
:task (apply clojure "-M:test" *command-line-args*)}
nrepl {:doc "Start REPL"
:task (if (fs/windows?)
(clojure "-M:nrepl")
(shell "rlwrap bb clojure -M:nrepl"))}
uber {:doc "Build uberjar"
:task (clojure "-T:build uber")}}}
The clojure
function is built into babashka and is a drop-in replacement for the clojure CLI which does not require any installation. With
(apply ... *command-line-args*)
we send any args you pass to a task invocation to clojure. So to run a specific test, you can write:
bb test -v myproject.core-test/failing-test
If you prefer to use the installed clojure CLI, you can do this by using:
(apply shell "clojure" ... *command-line-args*)
Now whenever you forget what to do in the current project, run bb tasks
:
$ bb tasks
The following tasks are available:
test Run tests
nrepl Start REPL
uber Build uberjar
Hope this helps!
Published: 2022-08-02