Explore the intricacies of tools.deps and the Clojure CLI, focusing on dependency management with deps.edn and running code with clj, tailored for Java developers transitioning to Clojure.
As experienced Java developers, you are likely familiar with Maven or Gradle for managing dependencies and building projects. In Clojure, while Leiningen is a popular choice, the tools.deps
library and the Clojure CLI offer a more lightweight and flexible approach to dependency management and project execution. This section will guide you through understanding and utilizing tools.deps
and the Clojure CLI effectively.
tools.deps
is a Clojure library that provides a declarative way to manage dependencies using a file called deps.edn
. The Clojure CLI (clj
and clojure
commands) leverages tools.deps
to run Clojure code, start REPL sessions, and manage project dependencies. This approach is particularly beneficial for Java developers transitioning to Clojure, as it aligns with the simplicity and flexibility of Clojure’s design philosophy.
The deps.edn
file is the cornerstone of dependency management in tools.deps
. It allows you to specify libraries and their versions, similar to pom.xml
in Maven or build.gradle
in Gradle, but with a more concise and readable syntax.
A typical deps.edn
file consists of several key sections:
Here’s an example of a simple deps.edn
file:
{
:deps {org.clojure/clojure {:mvn/version "1.10.3"}
org.clojure/core.async {:mvn/version "1.3.610"}}
:paths ["src" "resources"]
:aliases {:dev {:extra-paths ["dev"]
:extra-deps {org.clojure/tools.namespace {:mvn/version "1.1.0"}}}}
}
To add a dependency, simply include it in the :deps
map with its Maven coordinates. For example, to add the ring
library:
:deps {org.clojure/clojure {:mvn/version "1.10.3"}
ring/ring-core {:mvn/version "1.9.0"}}
Aliases in deps.edn
provide a powerful way to customize your development environment. They can add extra dependencies, paths, or even modify JVM options. For instance, you might define an alias for running tests:
:aliases {:test {:extra-paths ["test"]
:extra-deps {lambdaisland/kaocha {:mvn/version "1.0.732"}}
:main-opts ["-m" "kaocha.runner"]}}
The clj
command is a versatile tool for running Clojure code and managing REPL sessions. It integrates seamlessly with tools.deps
, allowing you to execute code with the dependencies specified in deps.edn
.
To start a REPL session, simply run:
clj
This command will launch a REPL with the dependencies and paths specified in your deps.edn
file. You can also use aliases to customize the REPL environment:
clj -A:dev
This command starts a REPL with the additional paths and dependencies specified under the :dev
alias.
You can execute a Clojure script using the -m
option followed by the namespace containing the -main
function:
clj -m my.namespace
This is analogous to running a Java application with a main
method.
The clojure
command is similar to clj
but is optimized for scripting and automation. It does not start a REPL by default and is often used in scripts or CI/CD pipelines.
While Maven and Gradle are powerful tools for Java projects, tools.deps
offers a more lightweight and flexible approach, aligning with Clojure’s philosophy of simplicity and composability.
deps.edn
is purely declarative, focusing on what dependencies are needed rather than how to build the project.tools.deps
allows for more dynamic and flexible dependency management, with aliases providing a way to customize environments without modifying the core configuration.deps.edn
format is simpler and more concise than XML or Groovy scripts.pom.xml
or Gradle’s build.gradle
, deps.edn
centralizes dependency declarations.tools.deps
can pull dependencies from Maven Central, similar to Java’s build tools.Let’s explore some practical examples to solidify your understanding of tools.deps
and the Clojure CLI.
Suppose you want to add the cheshire
library for JSON parsing. Update your deps.edn
as follows:
:deps {org.clojure/clojure {:mvn/version "1.10.3"}
cheshire/cheshire {:mvn/version "5.10.0"}}
Try running a simple script that uses cheshire
to parse JSON:
(ns my.json
(:require [cheshire.core :as json]))
(defn parse-json [json-str]
(json/parse-string json-str true))
;; Test the function
(println (parse-json "{\"name\":\"Clojure\",\"type\":\"language\"}"))
Run this script using:
clj -m my.json
Create a :dev
alias to include development tools like cider-nrepl
for enhanced REPL capabilities:
:aliases {:dev {:extra-deps {nrepl/nrepl {:mvn/version "0.8.3"}
cider/cider-nrepl {:mvn/version "0.25.9"}}
:main-opts ["-m" "nrepl.cmdline" "--middleware" "[cider.nrepl/cider-middleware]"]}}
Start a REPL with:
clj -A:dev
This command sets up a REPL with additional middleware for a richer development experience.
tools.deps
simplifies or complicates tasks compared to Maven or Gradle.Below is a diagram illustrating the flow of data and dependencies in a Clojure project using tools.deps
.
graph TD; A[deps.edn] --> B[Dependencies]; A --> C[Paths]; A --> D[Aliases]; B --> E[clj Command]; C --> E; D --> E; E --> F[Run Code/Start REPL];
Diagram 1: The flow of data and dependencies in a Clojure project using tools.deps
.
tools.deps
and deps.edn
. Add a few dependencies and write a simple application.tools.deps
handles version conflicts.tools.deps
offers a simple yet flexible way to manage dependencies, aligning with Clojure’s philosophy.clj
command provides a seamless way to run Clojure code and manage dependencies, enhancing productivity.Now that we’ve explored how tools.deps
and the Clojure CLI work, let’s apply these concepts to manage dependencies and run Clojure code effectively in your projects.