How to display Clojure version in REPL? - clojure

Such as:
(println clojure-version)
?

Even shorter :
user> (clojure-version)
"1.2.0-beta1"
user>
Oops, I have to upgrade...

Very close.
user> (println *clojure-version*)
{:major 1, :minor 2, :incremental 0, :qualifier }
nil
Most builtin "global" variables like this have Common Lisp-style asterisk "earmuffs".

Just typing *clojure-version* will do the trick.
*clojure-version*
=> {:major 1, :minor 3, :incremental 0, :qualifier nil}

Extra repl examples
user=> (clojure-version)
"1.10.1"
user=> (println (clojure-version))
1.10.1
nil
user=> (print (clojure-version))
1.10.1nil
Use from clj
$ clj -M -e "(clojure-version)"
"1.10.1"
$ clj -M -e "(print (clojure-version))"
1.10.1
$ clj -M -e "*clojure-version*"
{:major 1, :minor 10, :incremental 1, :qualifier nil}

clojure -M -e '(println "Java" (System/getProperty "java.version") "Clojure" (clojure-version))'

Related

Exporting environmental variables in Clojure / Babashka

I am trying to write an install script in babaska for my other babaska script (for some fun homogeneity).
Export doesn't seem to work using:
(shell "export NAME=" value)
Is there a canonical way to set environmental variables in Clojure / Babashka?
export is shell built-in; you can not call it outside a shell. So
what you want to do instead is run your command with another
environment. This can be done in sh with the :env
option. E.g.
(-> (shell/sh "sh" "-c" "echo $X" :env {"X" "Hello"}) :out)
edit: global state for environment
At least on the JVM, there is no easy way to change the
environment.
So you are better off, writing your own function to do the calls and
merge with your own global environment.
This example uses an atom to keep the environment around:
(def sh-env (atom {}))
(defn export!
[m]
(swap! sh-env merge m))
(defn sh
([cmd]
(sh cmd {}))
([cmd env]
(apply shell/sh (concat cmd [:env (merge #sh-env env)]))))
;;;
(def echo ["sh" "-c" "echo $X"])
(prn (sh echo))
(export! {"X" "Hello"})
(prn (sh echo))
(prn (sh echo {"X" "Goodbye"}))
(export! {"X" "Goodbye"})
(prn (sh echo))
; {:exit 0, :out "\n", :err ""}
; {:exit 0, :out "Hello\n", :err ""}
; {:exit 0, :out "Goodbye\n", :err ""}
; {:exit 0, :out "Goodbye\n", :err ""}
They way to set environment variables with shell is like this:
(shell {:extra-env {"NAME" "FOOBAR"}} command)
See docs here.

How to split a vector of CLI arguments into pairs in Clojure?

I have e.g. -m 4 -y 2016 and want to end up with [["-m" 4] ["-y" 2016]] or maybe a map. How do I split them into pairs?
To split them into pairs, use partition:
dev=> (partition 2 ["-m" 4 "-y" 2016])
(("-m" 4) ("-y" 2016))
To make a map, apply hash-map to it:
dev=> (apply hash-map ["-m" 4 "-y" 2016])
{"-y" 2016, "-m" 4}
There is a special library for that.
(let [cli-options [["-m" "--max MAX" "M description"
:parse-fn #(Integer/parseInt %)]
["-y" "--year YEAR" "Year"
:parse-fn #(Integer/parseInt %)]]]
(-> "-m 4 -y 2016"
(str/split #"\s")
(parse-opts cli-options)))
=>
{:options {:max 4, :year 2016},
:arguments [],
:summary " -m, --max MAX M description\n-y, --year YEAR Year",
:errors nil}

How to successfully install ubergraph

First I have to say, I'm completely new to clojure, so forgive me if I'm missing something obvious.
I recently installed the clojure package on the atom text editor in order to create some graphs and tried to add ubergraph, an extension that makes weighted graphs possible, since these are not supported in the standard clojure package.
I followed the quickstart guide on ubergraphs github https://github.com/Engelberg/ubergraph and managed to complete the first step (adding ubergraph to leiningen dependencies). I downloaded the git repository and don't know how to carry on from here. Running the example code
(ns example.core
(:require [ubergraph.core :as uber]))
(def graph1
(uber/graph [:a :b] [:a :c] [:b :d]))
on the repl as described on github ends up with the following error:
CompilerException java.lang.NullPointerException, compiling:(ubergraph/core.clj:11:1)
The line that seems to cause the error in core.clj is:
(import-vars
[...])
I skipped over the vars since I don't think they're causing the problem.
Clojure runs on the correct version (1.9.0) and java 8 is installed. Help is appreciated, thanks in advance.
Based on your comment "Also, do I have to put the lib somewhere specific?", this seems to caused by a misunderstanding of how to install a library. You shouldn't be manually dealing with stuff like that; leiningen handles library installation for you.
Here's a quick guide that assumes you haven't created a project yet. If you have, skip to step 2.
Run lein new app you-project-name-here. This will create a barebones project with a project.clj and basic file structure. If you use an IDE like IntelliJ+Cursive, creating a new project will do this step automatically.
Go into your project.clj, and add [ubergraph "0.5.2"] to the :dependencies entry. As a minimal, reduced example, it should look something like:
(defproject example "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.10.0"]
[ubergraph "0.5.2"]]
:main example.core) ; The path to your core
Have your core as something like:
(ns example.core
(:require [ubergraph.core :as uber])
(:gen-class))
(def graph1
(uber/graph [:a :b] [:a :c] [:b :d]))
(defn -main
"I don't do a whole lot ... yet."
[& args]
(println "The graph:" graph1))
Now run lein run. You should see it download the dependencies, then print something like this mess:
The graph: {:node-map {:a #ubergraph.core.NodeInfo{:out-edges {:b #{#ubergraph.core.UndirectedEdge{:id #uuid "0768ef5b-1507-4bb0-b3da-fc14a84d013d", :src :a, :dest :b, :mirror? false}}, :c #{#ubergraph.core.UndirectedEdge{:id #uuid "acddd770-52cc-4b1f-aec1-762861e70ee2", :src :a, :dest :c, :mirror? false}}}, :in-edges {:b #{#ubergraph.core.UndirectedEdge{:id #uuid "0768ef5b-1507-4bb0-b3da-fc14a84d013d", :src :b, :dest :a, :mirror? true}}, :c #{#ubergraph.core.UndirectedEdge{:id #uuid "acddd770-52cc-4b1f-aec1-762861e70ee2", :src :c, :dest :a, :mirror? true}}}, :out-degree 2, :in-degree 2}, :b #ubergraph.core.NodeInfo{:out-edges {:a #{#ubergraph.core.UndirectedEdge{:id #uuid "0768ef5b-1507-4bb0-b3da-fc14a84d013d", :src :b, :dest :a, :mirror? true}}, :d #{#ubergraph.core.UndirectedEdge{:id #uuid "ef931d4e-8143-4cd1-8a10-c3692c47072f", :src :b, :dest :d, :mirror? false}}}, :in-edges {:a #{#ubergraph.core.UndirectedEdge{:id #uuid "0768ef5b-1507-4bb0-b3da-fc14a84d013d", :src :a, :dest :b, :mirror? false}}, :d #{#ubergraph.core.UndirectedEdge{:id #uuid "ef931d4e-8143-4cd1-8a10-c3692c47072f", :src :d, :dest :b, :mirror? true}}}, :out-degree 2, :in-degree 2}, :c #ubergraph.core.NodeInfo{:out-edges {:a #{#ubergraph.core.UndirectedEdge{:id #uuid "acddd770-52cc-4b1f-aec1-762861e70ee2", :src :c, :dest :a, :mirror? true}}}, :in-edges {:a #{#ubergraph.core.UndirectedEdge{:id #uuid "acddd770-52cc-4b1f-aec1-762861e70ee2", :src :a, :dest :c, :mirror? false}}}, :out-degree 1, :in-degree 1}, :d #ubergraph.core.NodeInfo{:out-edges {:b #{#ubergraph.core.UndirectedEdge{:id #uuid "ef931d4e-8143-4cd1-8a10-c3692c47072f", :src :d, :dest :b, :mirror? true}}}, :in-edges {:b #{#ubergraph.core.UndirectedEdge{:id #uuid "ef931d4e-8143-4cd1-8a10-c3692c47072f", :src :b, :dest :d, :mirror? false}}}, :out-degree 1, :in-degree 1}}, :allow-parallel? false, :undirected? true, :attrs {}, :cached-hash #object[clojure.lang.Atom 0x16da1abc {:status :ready, :val -1}]}
I suspect the NPE was because you had installed ubergraph somehow, but didn't allow it to automatically resolve its dependencies. When it tried to run import-vals, one of the libraries it depends on wasn't found, and it threw a fit.

clojure core.tools.cli: How to override boolean option?

I want a command that takes arguments which look like this:
--enable-boolean-flag --disable-boolean-flag --enable-boolean-flag
In the :options key returned by clojure.tools.cli/parse-opts, I want to have the :boolean-flag option set to true if the --enable-boolean-flag option came last on the command line and false if --disable-boolean-flag came last on the command line, if that makes any sense.
Any ideas?
EDIT: I'm using 0.3.6 of the core.tools.cli library.
You can achieve this by taking advantage of :id, :default, and :assoc-fn properties that tools-cli lets you specify for each command line option.
Use :id to set the same id for "--enable" and "--disable" options
Use :default on one of the options to specify what you want to happen if neither "--enable" or "--disable" are specified
Use :assoc-fn to specify what effect the option has on the options map. You want the value set to false every time "--disable" appears and to true every time --enable appears.
Putting it all together:
(ns clis.core
(:require [clojure.tools.cli :refer [parse-opts]])
(:gen-class))
(def cli-options
[["-e" "--enable" "Enable"
:default true
:id :boolean-flag
:assoc-fn (fn [m k _] (assoc m k true))]
["-d" "--disable" "Disable"
:id :boolean-flag
:assoc-fn (fn [m k _] (assoc m k false))]])
(defn -main [& args]
(parse-opts args cli-options))
Testing at the REPL:
(-main)
;; {:options {:boolean-flag true}, :arguments [], :summary " -e, --enable Enable\n -d, --disable Disable", :errors nil}
(-main "-e" "-d" "-e")
;; {:options {:boolean-flag true}, :arguments [], :summary " -e, --enable Enable\n -d, --disable Disable", :errors nil}
(-main "-e" "-d" "-e" "-d")
;; {:options {:boolean-flag false}, :arguments [], :summary " -e, --enable Enable\n -d, --disable Disable", :errors nil}

Why can't clojure.repl/print-doc binding be changed in Clojure REPL?

This works as expected:
java -jar clojure-1.4.0.jar -e "(do (require 'clojure.repl) (.setDynamic #'clojure.repl/print-doc) (with-bindings {#'clojure.repl/print-doc str} (eval '(clojure.repl/doc println))))"
Output:
"{:ns #<Namespace clojure.core>, :name println, :arglists ([& more]), :added \"1.0\", :static true, :doc \"Same as print followed by (newline)\", :line 3325, :file \"clojure/core.clj\"}"
But the same does not work in the REPL:
java -jar clojure-1.4.0.jar -e "(do (require 'clojure.repl) (.setDynamic #'clojure.repl/print-doc) (clojure.main/repl :init (fn [] {#'clojure.repl/print-doc str}))))"
Output of (doc println):
user=> (doc println)
-------------------------
clojure.core/println
([& more])
Same as print followed by (newline)
nil
user=>
I don't know what I'm doing wrong.
Found the answer after diving into the counterclockwise and nrepl code:
java -jar clojure-1.4.0.jar -e "(do (require 'clojure.repl) (.setDynamic #'clojure.repl/print-doc) (with-bindings {#'clojure.repl/print-doc str} (clojure.main/repl)))))"
The output is the same as above:
"{:ns #<Namespace clojure.core>, :name println, :arglists ([& more]), :added \"1.0\", :static true, :doc \"Same as print followed by (newline)\", :line 3325, :file \"clojure/core.clj\"}"
The trick is to use with-bindings before calling repl:
(with-bindings {#'clojure.repl/print-doc str}
(repl))