Lein console (like sbt) - clojure

Is there some built-in functionality or plugin to lein to get a lein console, so for example one could test without waiting every time for JVM to start up.
$ lein console
>>> test
...
>>> test
...
>>> jar
Note: I'd like to trigger test runs myself, not e.g. by watching source files. That's why I'd like to have a lein console.
Clarification: I'm not looking for lein repl. I'd like to have a console where I could run lein task commands.

Older versions of leiningen used to include lein interactive, which behaved much like the feature you are asking for: it opened a shell into which you could type test and have it run lein test from the already-running lein jvm, and so on. This feature was removed in the transition to lein 2.0, I think, and although I don't know why I suspect there was a good reason. Maybe try asking in #leiningen on freenode?

You might want to have a look at grenchman. While it's not a Leiningen console it at least enables reusing of an existing REPL session. From what I gather, usage is as follows:
Move somewhere that is not inside a project and call:
$ lein repl :headless
Within your project directory, use:
$ grench lein <task> <options>
Tasks will be run inside the already spun up Leiningen JVM and the startup overhead should disappear.
Building grenchman seems to be tedious, though, and it is recommended to use one of the precompiled binaries (BUT they are currently not available).
And finally, that page also states:
Grenchman is still very new and may not be fully reliable.
So, good luck, I guess?

One option is to run a repl from leiningen's own jar file.
$ java -cp ~/.lein/self-installs/leiningen-2.5.0-standalone.jar clojure.main
Clojure 1.6.0
user=> (require '[leiningen.core.project :as project] '[leiningen.test :as test])
nil
user=> (def prj (project/read))
#'user/prj
user=> (test/test prj)
lein test org.noisesmith.orsos.load-test
Ran 3 tests containing 3 assertions.
0 failures, 0 errors.
nil
user=> (require '[leiningen.jar :as jar])
nil
user=> (jar/jar prj 'org.noisesmith.orsos)
Compiling org.noisesmith.orsos
Created /media/justin/806084F16084EEEA/clojure/orsos/target/orsos-0.1.0-SNAPSHOT.jar
{[:extension "jar"] "/media/justin/806084F16084EEEA/clojure/orsos/target/orsos-0.1.0-SNAPSHOT.jar"}
user=>
As a baseline, this can run lein tasks without having to restart lein every time. If you also use rlwrap or use nrepl it becomes a bit more usable. As far as I know there is no user friendly tooling around this (though there easily could be).
If you wish to use tasks from lein plugins those can be added to the -cp arg.

Related

How to load this Clojure project in my cider REPL? Why I am receiving the message "The clojure executable isn’t on your ‘exec-path’" (NixOS)?

Recently, I started learning Clojure. I have been using Emacs, and cider (REPL). Also, I have NixOS as OS.
I have been able to successfully program in Clojure using this environment. Usually, I create the files, then I go to the directory where the .clj are, I execute cider-jack-in, a REPL is created, and interactive programming magic happens. Thus, I am able to load the file, load new versions of old functions, etc.
It also works if I just start Emacs and execute `cider-jack-in. The mini-buffer asks:
Are you sure you want to run `cider-jack-in' without a Clojure project? (y or n)
After answering y, everything works fine.
While reading the book Clojure for the Brave and True, I have also successfully used:
$ lein run
For learning purposes, I decided to explore some open source projects. Thus, I did a git clone of this open-source project.
Being on the folder, I decided to reproduce the steps that have worked so far. However, it does not work. After executing cider-jack-in, the mini-buffer echoes the following:
The clojure executable isn’t on your ‘exec-path
Also, if I try with lein I get:
[pedro#system:~/projects/http-cron]$ lein run
No :main namespace specified in project.clj.
Obs.: If I type clojure on the shell, this is what I get:
[pedro#system:~]$ clojure
The program 'clojure' is not in your PATH. You can make it available in an
ephemeral shell by typing:
nix-shell -p clojure
Why is this happening?
How can I solve this (considering that I am using NixOS)?
Going the imperative route, depending on whether you use NixOS with channels (default) or flakes, you can permanently install clojure into your environment with:
channels: nix-env -f '<nixpkgs>' -iA clojure
flakes: nix profile install nixpkgs#clojure
The preferred (declarative) way would however be adding clojure to environment.systemPackages in /etc/nixos/configuration.nix and running sudo nixos-rebuild switch.

Clojure: how to tell if code is running in the REPL or a JAR?

I am writing a CLI framework in Clojure called OneCLI. The main center piece of this framework is a function called go! which parses the command line, environment variables, and config files "for you" and runs one of several different user provided functions based on what was provided in those inputs.
Typically, go! is called from the -main function of the user's calling Clojure program. I use my own library, for example, in another "uberjar" style app called zic. The function go! calls System/exit as part of its run, passing it an exit code that comes from the result of the user provided function. This works great "in production", but it also means that I can't run the zic.cli/-main function from the REPL, as whenever I do it calls System/exit and the REPL exits.
Before you ask, running it from the REPL while developing on a raspberry pi avoids the expensive 45 seconds it takes to run lein uberjar/1 minute 30 seconds to run clj -X:depstar uberjar :jar ....
My question is: Is there some var or value I can check as part of Clojure's standard library that tells my OneCLI code whether it's running from the REPL or if it's running from a JAR?
Such a variable would enable me in OneCLI to detect that we're running from a REPL so that it can avoid calling System/exit.
Instead of trying to have one function that magically detects what environment you're running from, it's quite simple to just have two functions that behave differently.
Extract out the shared behavior to a function that is not part of -main. Call it run or whatever.
Have -main call that function, and then call System/exit
When you wish to use the program from a repl, call run instead of -main. It will finish normally, and not call System/exit.
I don't know how to detect if you're running at a REPL. I took a quick look through Clojure's launching code (clojure.main), but I didn't see any hooks to detect whether you're in a REPL compared to something run via clojure -m.
If you're using AOT (like you are in zic) then you could check whether any of the "REPL" variables (*1, *2, *3, and *e) are bound.
;; returns true in a REPL and `clojure -m`, and
;; returns false in an AOT jar file run with java -jar
(bound? #'*1)
This solves your question as it was asked, but I don't love this "magical" mechanism of guessing the programmer's intent. It might work for your use case (given I think AOT saves on startup time, and CLI tools probably want to start quickly), but none of the projects I work on use AOT at all.
Another option to solve your problem in the clojure -m case as well would be to require developers to explicitly opt out of the "exit on completion" behaviour. One way to do that could be to use a property.
(defn maybe-exit [exit-code]
(cond
(= (System/getProperty "onecli.oncompletion") "remain") (System/exit exit-code)
(= exit-code 0) nil
:else (throw (ex-info "Command completed unsuccessfully" {:exit-code exit-code}))))
Using this code, in a development environment you can add
:jvm-opts ["-Donecli.oncompletion=remain"]
to your deps.edn or project.clj file, but leave it out when running "in production". This has the advantage of being more explicit, but the cost is that developers have to be more explicit.
This is an interesting question because it's usually dreadful to put JVM shutdown into a library, but on the other hand a "real app" involves lots of boilerplate that would be great to share... such as hiding the jar's splash gif at the right time, or (re)opening a Windows terminal if the app wants stdio.
Your uberjar will contain clojure.main, so it is quite possible (and useful) to run the REPL in your uberjar (java -cp my-whole-app.jar clojure.main). Therefore, "detecting" clues on the classpath might not help.
Instead, manage JVM-shutdown work in the -main in the namespace that your jar's manifest declares as its Main-Class. That is: if you run it as java -jar my-whole-app.jar, then it should shut everything down properly.
But I do not always want -main to shut everything down, you say. Then you need two -mains. Make a second -main in a different namespace. Let the jar's Main-Class -main do absolutely nothing but (1) delegate to the second main and (2) shut down the JVM at the end. When you're in the REPL, invoke the second -main, the one that won't clobber the JVM. You can factor out most of each -main into a library. If you went "full framework" you could even make the framework own the uberjarring process and the Main-Class.
Every Java JAR file must have the file META-INF/MANIFEST.MF
added. If it isn't present, you cannot be running in a (normal) JAR file. While you could fool this detector by putting a bogus file on the classpath (i.e. in ./resources, for example), it is a reliable way of detecting a normal JAR file.
Problem:
Dependency JAR files are sometimes sloppy and will pollute the classpath with their own META-INF/MANIFEST.MF files, so the presence of any random META-INF/MANIFEST.MF is not enough to determine the answer in the presence of "noise" files. So, you need to check for the existence of your own specific META-INF/MANIFEST.MF file. This is easy to do if you know the Maven values for ArtifactId and GroupId.
In a Leiningen project, the first line of project.clj looks like
(defproject demo-grp/demo-art "0.1.0-SNAPSHOT"
for a group ID of demo-grp and an artifact ID of demo-art. If your file looks like this:
(defproject demo "0.1.0-SNAPSHOT"
then both the group ID and artifact ID will be demo. Your particular MANIFEST.MF will look like
> cat META-INF/MANIFEST.MF
Manifest-Version: 1.0
Created-By: Leiningen 2.9.1
Built-By: alan
Build-Jdk: 15
Leiningen-Project-ArtifactId: demo-art
Leiningen-Project-GroupId: demo-grp
Leiningen-Project-Version: 0.1.0-SNAPSHOT
Main-Class: demo.core
Set up a function using the to ID strings to detect the presence of your particular project MANIFEST.MF:
(ns demo.core
(:require [clojure.java.io :as io])
(:gen-class))
(def ArtifactId "demo-art")
(def GroupId "demo-grp")
(defn jar-file? []
(let [re-ArtifactId (re-pattern (str ".*ArtifactId.*" ArtifactId))
re-GroupId (re-pattern (str ".*GroupId.*" GroupId))
manifest (slurp (io/resource "META-INF/MANIFEST.MF"))
f1 (re-find re-ArtifactId manifest)
f2 (re-find re-GroupId manifest)
found? (boolean (and f1 f2))]
found?))
(defn -main []
(println "main - enter")
(println "Detected JAR file: " (jar-file?))
)
You can now test the code:
~/expr/demo > lein clean ; lein run
main - enter
Detected JAR file: false
~/expr/demo > lein clean ; lein uberjar
Compiling demo.core
Created /home/alan/expr/demo/target/uberjar/demo-art-0.1.0-SNAPSHOT.jar
Created /home/alan/expr/demo/target/uberjar/demo-art-0.1.0-SNAPSHOT-standalone.jar
~/expr/demo > java -jar /home/alan/expr/demo/target/uberjar/demo-art-0.1.0-SNAPSHOT-standalone.jar
main - enter
Detected JAR file: true
Example of "noise" JAR file: If we do a lein clean; lein run, and add a line to our main program
(println (slurp (io/resource "META-INF/MANIFEST.MF")))
we get out:
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Built-By: jenkins
Created-By: Apache Maven 3.2.5
Build-Jdk: 1.8.0_111
I have no idea where this is coming from to get on the CLASSPATH.
P.S. for Leiningen JAR files
When using lein to build a JAR file, it always places a copy of the project.clj file at the location:
META-INF/leiningen/demo-grp/demo-art/project.clj
so you could also use this file's presence/absence as a detector.
Update
OK, it looks like the the MANIFEST.MF file is highly dependent on your build tool. See
https://docs.oracle.com/javase/tutorial/deployment/jar/defman.html
https://www.baeldung.com/java-jar-manifest
So, your choices appear to be:
For lein, you can use the above technique.
You could use the REPL trick of *1 from the other answer.
You could always have your build tool include a custom key-value pair in the manifest and then detect that.
Update #2
An alternate answer, and perhaps easier, is to use the lein-environ plugin and environ library (you need both) to detect the environment (assuming you are using lein to create your REPL). Your project.clj should look like:
:dependencies [
[clojure.java-time "0.3.2"]
[environ "1.2.0"]
[org.clojure/clojure "1.10.2-alpha1"]
[prismatic/schema "1.1.12"]
[tupelo "21.01.05"]
]
:plugins [[com.jakemccrary/lein-test-refresh "0.24.1"]
[lein-ancient "0.6.15"]
[lein-codox "0.10.7"]
[lein-environ "1.2.0"]
]
and you need a profiles.clj:
{:dev {:env {:env-mode "dev"}}
:test {:env {:env-mode "test"}}
:prod {:env {:env-mode "prod"}}}
and a namespace demo.config like:
(ns demo.config
(:require
[environ.core :as environ]
))
(def ^:dynamic *env-mode* (environ/env :env-mode))
(println " *env-mode* => " *env-mode*)
And then you get results like:
*env-mode* => dev ; for `lein run`
*env-mode* => test ; for `lein test`
*env-mode* => nil ; from `java -jar ...`
You need to type:
lein with-profile :prod run
to produce
*env-mode* => prod

leiningen custom task is not a task

I have the following .lein-classpath
tasks
then under tasks, I have
tasks
> leiningen
>foo-bar.clj
I then have the following contents of foo.clj
(ns leiningen.foo-bar)
(defn foo-bar [project & args]
(println "foo"))
When I then run lein foo-bar, I receive:
'foo-bar' is not a task. See 'lein help'.
Did you mean this?
foo-bar
Running lein help produces (among the general default tasks):
foo Task: 'foo-bar' not found
I am on leiningen 2.7.1 with java 1.8.0_77
.lein-classpath, tasks, and project.clj are all in project root
A few more diagnostic results:
which lein produces: /usr/local/bin/lein
~/.lein contains no .lein-classpath, nor any task definitions
/etc/leinrc does not exist
How would I go about defining this task? And why does it "sort of" see the task (Did you mean this?...)
Recall that for all Clojure files, not just leiningen tasks, the namespace foo-bar must be in a file named foo_bar.clj, not foo-bar.clj.
This error means that leiningen cannot find the task file.
Remember that .lein-classpath must be in your project root, right where your project.clj is. Also, the path is relative to your project root, so if your tasks are in <root>/src/tasks, you have to put src/tasks in your .lein-classpath.

leiningen - clojurescript equivalent of lein run?

In clojure I can run:
lein run -m my.namespace # run the -main function of a namespace
and this will run my code from the command line.
Is there an equivalent for Clojurescipt to run the generated code in node.js? (In leiningen)
(I have read the doco for starting the Clojurescript REPL, for the running on node.js and the reply integrated into my application. I was just looking for a one-line command line solution.)
Depending on what your goal is, you might find it useful to use 'cljsbuild test'. You can specify a test context on your project.clj that uses node.js/v8/phantomjs.
Example:
:cljsbuild {
:test-commands {
"v8" ["v8"
"target/generated-sources/private/js/client.js"
"tests/v8-test-runner.js"]}}
v8-test-runner.js:
path.to.your.entrypoint.start()
You can also leave 'lein cljsbuild auto' running and start the javascript application directly:
v8 target/generated-sources/private/js/client.js tests/v8-test-wrapper.js
That's probably the best option if you are building a node.js application using clojurescript. You don't need to worry about classpaths in javascript (which is the major thing that lein run provides), you can just run your application directly from the assembled javascript file.

How do I add unit tests to a Leiningen project?

I noticed that leiningen has a great unit test plugin - you just enter "lein test" :) . However, it's not clear how it "finds" the test files. Is there a specific folder I need to put them in? Or, if its just scanning namespaces (which is what it says in the lein docs), how do I know what namespace I need to use for leiningen to see my tests?
I was thinking about simply making one test file, called tests.clj. A sample template would really be nice.
At the top level make a test/ directory, and then create some file, say mytests.clj. Here's a sample (caveat: I didn't actually compile this, but simplified an existing test):
(ns mytests
(:use clojure.test))
(defn myfixture [block]
(do
(println "before test")
(block)
(println "after test")))
(use-fixtures :each myfixture)
(deftest mytest
(is (= 2 (+ 1 1))))
for lein version 2.7.1 : {this is from Linux Mint with Leiningen 2.7.1 on Java 1.8.0_121 Java HotSpot(TM) 64-Bit Server VM }
I wanted to do more with testing and now have this new knowledge...
lein new <projname>
cd <projname>
tree -d
cat test/<projname>/core_test.clj
lein test
-> observe failing test template even on blank new project
then you could add: (print "start adding test")
and see your efforts ...