Welcome! Please see the About page for a little more info on how this works.

0 votes
in Clojure CLI by
edited by

After installing https://github.com/seancorfield/deps-new I created a project with the command clojure -Tnew app :name clojure-backend and followed pedestal component guide http://pedestal.io/guides/pedestal-with-component. The repo is in: https://github.com/cloped/clojure-backend

I'm trying to achieve the same thing as I used to do on Leiningen project, start my REPL on Cursive on the namespace where the component start function is declared and call it.

Reading the nREPL doc https://nrepl.org/nrepl/usage/server.html there is a :init-ns on Leiningen, that makes this work.

After some several discussions on Clojure slack thread https://clojurians.slack.com/archives/C0744GXCJ/p1681750400843819, and many attempts to load a desired namespace on Cursive REPL and Clojure CLI we understood that this may be some functionally that could go to Clojure CLI

Colin helped me to find where this is implemented on Leiningen: https://github.com/technomancy/leiningen/blob/24fb93936133bd7fc30c393c127e9e69bb5f2392/src/leiningen/repl.clj#L175-L195

I'm wondering if this is something that makes sense to Clojure CLI. And if it is I am excited to help implement this to begin my contributions to Clojure community.

2 Answers

+1 vote
selected by
Best answer

There is no need to introduce code into any tooling to switch namespaces when starting a REPL.

Defining a custom user namespace has been a long standing approach to managing component lifecycle (start, stop, restart) during development that is not editor, tool or component library dependant, i.e. it works with everything.

Create a dev/user.clj file in the project that defines a user namespace. Code in this namespace is loaded during REPL startup, unless it is in comment forms.

The repl starts in the user namespace, so functions defined in dev/user.clj are immediately available without having to switch to a different namespace or use fully qualified names.

Defining functions that manage components in the custom dev/user.clj user namespace makes them immediately available in the default namespace used by the REPL.

The added benefit of this approach is cleaner code and a clear separation between development support code and production code.

Libraries such as component repl and integrant repl can be included via a Clojure CLI alias, so they are not adding weight to CI workflow and the resulting deployed uberjar.

The custom user namespace contains the require statements for component repl (or other develop-time component libraries like Integrant REPL)

Personally I use this approach for launching a range of supporting development tools, regardless of it being Leiningen or Clojure CLI project.

Practicalli Custom REPL Startup details the general approach

Practicalli REPL Reloaded details the specific development tooling I use (portal, hotload, namespace reload, event logging, etc)

Practicalli Project Templates provides production level templates for seancorfiled/deps-new which include a dev/user.clj custom user namespace, the practicalli/service template included Integrant REPL example with aero

+1 vote

You can combine -e with other "main opts" on the command-line to start in a particular namespace:

> cat src/my_proj/api.clj
(ns my-proj.api)

(defn foo [opts]
  (println (get opts :bar "No :bar passed!")))

> clj -M -e "(require 'my-proj.api)(in-ns 'my-proj.api)" -r
#object[clojure.lang.Namespace 0x7957dc72 "my-proj.api"]
it didn't work for me. I got the following:

➜  clojure-backend git:(main) ✗  clj -r -e "(require 'clojure-backend.core) (in-ns 'clojure-backend.core)"
WARNING: Implicit use of clojure.main with options is deprecated, use -M
After in-ns
Clojure 1.11.1
user=> *ns*
#object[clojure.lang.Namespace 0x68dcfd52 "user"]
user=> (my-function)
Syntax error compiling at (REPL:1:1).
Unable to resolve symbol: my-function in this context
I figured it out. I typed -r before -e command so it did not work :D
Still thinking about this, doesn't make sense to be an arg to receive this instead of a code to load and change namespace?
Clojure, in general, follows an approach of small, simple, and composable. Leiningen offers a lot of knobs and dials with an aim to make things "easy". Adding extra arguments makes this easier at the expense of losing small, simple, and composable. Then -e option lets you run any code, including solving the problem you posted.