Full Clojure project in 5 seconds!
Tags: clojure, application, template
Published: 2022-10-18 14:44:12


{% embed https://youtu.be/2K7cQ8UYRzo %}

Every time you start a new Clojure project, there is a lot of boilerplate that needs to be added: 

- `deps.edn` with aliases to run tests and build actions
- `build.clj` with build actions
- `.gitignore`
- `README.md`
- you name it…

Creating all these can be easily automated with [deps-new](https://github.com/seancorfield/deps-new).

## Install `deps-new`

`deps-new` intended to be installed as a "tool" for Clojure CLI.

### "Tools" in Clojure CLI

Tools are 3rd party libraries that can be installed and than invoked from Clojure CLI using `clj -T` option. They use their own classpath and do not interfere with the project's `deps.edn` if you run tool commands from the project directory. 

Few commands you may need:

`clj -Ttools list` - to view already installed tools

`clj -Ttools remove :tool tool-name` - to remove the tool

`clj -Ttools install tool-path tool-version :as tool-name` - to install a new tool

More info about tools you can find in [this video](https://www.youtube.com/watch?v=8aCO_wNuScQ&t=506s).

### Installing `deps-new` tool

Here is a command to add `deps-new` to your system 

```bash
clojure -Ttools install io.github.seancorfield/deps-new '{:git/tag "v0.4.13"}' :as new
```

`:as new` part of the command means that "new" is now a name for `deps-new` tool on your system. You can use any name you want.

Let's check our list of tools now:

```bash
$ clj -Ttools list
TOOL   LIB                              TYPE  VERSION
new    io.github.seancorfield/deps-new  :git  v0.4.13
tools  io.github.clojure/tools.tools    :git  v0.2.8
```

 To check what functions `new` tool exposes you can run the command:

`clojure -A:deps -Tnew help/doc`

## Create a library

Now, when `deps-new` is installed, we can use it to create a new library.

The following command will create a library with root namespace `vkjr` and one source file `mycoollib.clj` in it

```bash
$ clojure -Tnew lib :name vkjr/mycoollib
  Creating project from org.corfield.new/lib in mycoollib
```

And here is a content of new library:

```bash
mycoollib
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── build.clj
├── deps.edn
├── doc
│   └── intro.md
├── pom.xml
├── resources
│   └── .keep
├── src
│   └── vkjr
│       └── mycoollib.clj
└── test
    └── vkjr
        └── mycoollib_test.clj
```

Top level directory of the library was also named `mycoollib` but you can override this behavior by providing additional key/value pair to the library creation command `:target-dir some-folder-name`. 

### Content of created library

As you can see, there are plenty of automatically generated files in our new lib.

`deps.edn` from the start contains aliases for testing and build actions

```clojure
{:paths ["src" "resources"]
 :deps {org.clojure/clojure {:mvn/version "1.11.1"}}
 :aliases
 {:test
  {:extra-paths ["test"]
   :extra-deps {org.clojure/test.check {:mvn/version "1.1.1"}
                io.github.cognitect-labs/test-runner
                {:git/tag "v0.5.1" :git/sha "dfb30dd"}}}
  :build {:deps {io.github.seancorfield/build-clj
                 {:git/tag "v0.8.2" :git/sha "0ffdb4c"}}
          :ns-default build}}}
```

`src/vkjr/mycoollib.clj` contains a dummy function

```clojure
(ns vkjr.mycoollib)

(defn foo
  "I don't do a whole lot."
  [x]
  (prn x "Hello, World!"))
```

And in `test/vkjr/mycoollib_test.clj` already exists one failing test

```clojure
(ns vkjr.mycoollib-test
  (:require [clojure.test :refer :all]
            [vkjr.mycoollib :refer :all]))

(deftest a-test
  (testing "FIXME, I fail."
    (is (= 0 1))))
```

`build.clj` contains build actions `test`, `ci`, `install` and `deploy`

```clojure
(ns build
  (:refer-clojure :exclude [test])
  (:require [clojure.tools.build.api :as b] ; for b/git-count-revs
            [org.corfield.build :as bb]))

(def lib 'net.clojars.vkjr/mycoollib)
(def version "0.1.0-SNAPSHOT")
#_ ; alternatively, use MAJOR.MINOR.COMMITS:
(def version (format "1.0.%s" (b/git-count-revs nil)))

(defn test "Run the tests." [opts]
  (bb/run-tests opts))

(defn ci "Run the CI pipeline of tests (and build the JAR)." [opts]
  (-> opts
      (assoc :lib lib :version version)
      (bb/run-tests)
      (bb/clean)
      (bb/jar)))

(defn install "Install the JAR locally." [opts]
  (-> opts
      (assoc :lib lib :version version)
      (bb/install)))

(defn deploy "Deploy the JAR to Clojars." [opts]
  (-> opts
      (assoc :lib lib :version version)
      (bb/deploy)))
```

Note, that `build.clj` implements actions using the [build-clj](https://github.com/seancorfield/build-clj) library instead of standard Clojure's `tools.build`. That library hides some unnecessary details and provides the functionality of deploying to Clojars, which is missed in `tools.build`

There are a bunch of other useful files also: `.gitignore`, `README.md`, `pom.xml`, `LICENSE`.. real time-saver, ha?

## Create an application

Creating an application with `deps-new` as simple as a library:

```bash
$ clojure -Tnew app :name vkjr/mycoolapp
Creating project from org.corfield.new/app in mycoolapp
```

And here is a folder structure:

```bash
mycoolapp
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── build.clj
├── deps.edn
├── doc
│   └── intro.md
├── pom.xml
├── resources
│   └── .keep
├── src
│   └── vkjr
│       └── mycoolapp.clj
└── test
    └── vkjr
        └── mycoolapp_test.clj
```

Main differences from the library project:

`src/vkjr/mycoolapp.clj` contains the `(-main)` and `(greet)` functions

```clojure
(ns vkjr.mycoolapp
  (:gen-class))

(defn greet
  "Callable entry point to the application."
  [data]
  (println (str "Hello, " (or (:name data) "World") "!")))

(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (greet {:name (first args)}))
```

`deps.edn` has additional aliases `run-m` and `run-x` to run (main) and (greet) functions respectively

```clojure
...
{:run-m {:main-opts ["-m" "vkjr.mycoolapp"]}
 :run-x {:ns-default vkjr.mycoolapp
         :exec-fn greet
         :exec-args {:name "Clojure"}}
...
}
```

`build.clj` creates an uberjar instead of a jar and doesn't have (install) and (deploy) functions

```clojure
...
(defn ci "Run the CI pipeline of tests (and build the uberjar)." [opts]
  (-> opts
      (assoc :lib lib :version version :main main)
      (bb/run-tests)
      (bb/clean)
      (bb/uber)))
```

## Create a minimal "scratch" project

For the cases when you don't need a full-blown application, but merely a playground, `deps-new` supports creating a "scratch" projects:

```bash
$ clojure -Tnew scratch :name playground                                            
Creating project from org.corfield.new/scratch in playground 
```

Folder structure for such "scratch":

```bash
playground
├── deps.edn
└── src
    └── scratch.clj
```

With almost empty `deps.edn` and `scratch.clj` with (-main) and (greet) functions, like in the application example

Using `deps-new` you can create your own project template, but this is another story… ;)

originally posted at [kozieiev.com](https://kozieiev.com/blog/full-clojure-project-in-5-seconds/)