DEV Community

Volodymyr Kozieiev
Volodymyr Kozieiev

Posted on • Originally published at kozieiev.com

4

Full Clojure project in 5 seconds!

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.

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.

Installing deps-new tool

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

clojure -Ttools install io.github.seancorfield/deps-new '{:git/tag "v0.4.13"}' :as new
Enter fullscreen mode Exit fullscreen mode

: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:

$ 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
Enter fullscreen mode Exit fullscreen mode

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

$ clojure -Tnew lib :name vkjr/mycoollib
  Creating project from org.corfield.new/lib in mycoollib
Enter fullscreen mode Exit fullscreen mode

And here is a content of new library:

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
Enter fullscreen mode Exit fullscreen mode

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

{: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}}}
Enter fullscreen mode Exit fullscreen mode

src/vkjr/mycoollib.clj contains a dummy function

(ns vkjr.mycoollib)

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

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

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

(deftest a-test
  (testing "FIXME, I fail."
    (is (= 0 1))))
Enter fullscreen mode Exit fullscreen mode

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

(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)))
Enter fullscreen mode Exit fullscreen mode

Note, that build.clj implements actions using the 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:

$ clojure -Tnew app :name vkjr/mycoolapp
Creating project from org.corfield.new/app in mycoolapp
Enter fullscreen mode Exit fullscreen mode

And here is a folder structure:

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
Enter fullscreen mode Exit fullscreen mode

Main differences from the library project:

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

(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)}))
Enter fullscreen mode Exit fullscreen mode

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

...
{:run-m {:main-opts ["-m" "vkjr.mycoolapp"]}
 :run-x {:ns-default vkjr.mycoolapp
         :exec-fn greet
         :exec-args {:name "Clojure"}}
...
}
Enter fullscreen mode Exit fullscreen mode

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

...
(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)))
Enter fullscreen mode Exit fullscreen mode

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:

$ clojure -Tnew scratch :name playground                                            
Creating project from org.corfield.new/scratch in playground 
Enter fullscreen mode Exit fullscreen mode

Folder structure for such "scratch":

playground
├── deps.edn
└── src
    └── scratch.clj
Enter fullscreen mode Exit fullscreen mode

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

Speedy emails, satisfied customers

Postmark Image

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay