Parse Clojure Command Line Args - clojure

I am trying to separate my cli options into a stand-alone namespace for starting up an HTTP server, and I am getting this error-
clojure.lang.ArraySeq cannot be cast to java.lang.CharSequence
In main.clj, this code works fine-
(ns served.main
(:require [org.httpkit.server :refer [run-server]]
[served.app.core :refer [handler]]
[served.server.cli-options :refer [set-options]]
[clojure.tools.cli :refer [parse-opts]])
(:gen-class))
(def cli-options
[
["-p" "--port PORT" "Port number"
:default 5000
:parse-fn #(Integer/parseInt %)
:validate [#(< 0 % 0x10000) "Must be a number between 0 and 65536"]]
])
(defn -main [& args]
(println "Server starting")
(let [options (get (parse-opts args cli-options) :options)]
;;(let [options (set-options args)]
(println (str options))
(run-server handler options)))
It will work with the default options in (def cli-options) and it compiles correctly if I pass in arguments, such as -p 7000.
When I call the main function with the external namespace served.server.cli-options instead of clojure.tools.cli directly (i.e. switch the comment in main), I get the error only when passing in args.
That is, starting the server without arguments, e.g. lein run compiles fine and will print out the defaults. The error comes with lein run -p 7000.
After deleting (def cli-options) in main to avoid any global conflict, here is served.server.cli-options
(ns served.server.cli-options
(:require [clojure.tools.cli :refer [parse-opts]]))
(def cli-options
[
["-p" "--port PORT" "Port number"
:default 5000
:parse-fn #(Integer/parseInt %)
:validate [#(< 0 % 0x10000) "Must be a number between 0 and 65536"]]
])
(defn set-options [& args]
(let [options (get (parse-opts args cli-options) :options)]
(println (str options))
options))
So far as I can tell, I copied the contents to the new namespace correctly. Here are the docs for parse-opts, here is the example that I am drawing from, and a similar but different SO issue here.
My question - how are the CLI args being transformed to throw casting error, and how do I fix it?
Any help would be greatly appreciated.

Delete the & in:
(defn set-options [& args]
& wraps up any additional arguments in a seq. Since you’ve already wrapped the program arguments once in main, you mustn’t do it again in the call to set-options.

Related

Clojure's load-string works in the repl but not in `lein run`

When I start a repl with lein repl I can run the function greet and it works as expected.
(ns var-test.core
(:gen-class))
(declare ^:dynamic x)
(defn greet []
(binding [x "Hello World."]
(println (load-string "x"))))
(defn -main [& args]
(greet))
But if run the code via lein run it fails with
java.lang.RuntimeException: Unable to resolve symbol: x in this context.
What am I missing?
Is the var x dropped during compilation, despite being declared, since it is never used outside of the string?
Edit:
Solution
#amalloy's comment helped me understand I need to bind *ns* in order load the string within the expected namespace, instead of a new, empty namespace.
This works as expected:
(ns var-test.core
(:gen-class))
(declare ^:dynamic x)
(defn greet []
(binding [x "Hello World."
*ns* (find-ns 'var-test.core)]
(println (load-string "x"))))
(defn -main [& args]
(greet))
Wow, I've never seen that function before!
According to the docs, load-string is meant to read & load forms one-at-a-time from an input string. Observe this code, made from my favorite template project:
(ns tst.demo.core
(:use tupelo.core tupelo.test)
(:require [tupelo.string :as str]))
(dotest
(def y "wilma")
(throws? (eval (quote y)))
(throws? (load-string "y"))
So it appears that load-string starts with a new, empty environment, then reads and evaluates forms one at a time in that new env. Since your x is not in that new environment, it can't be found and you get an error.
Try it another way:
(load-string
(str/quotes->double
"(def ^:dynamic x)
(binding [x 'fred']
(println :bb (load-string 'x'))) " ))
;=> :bb fred
In this case, we give all the code as text to load-string. It reads and eval's first the def, then the binding & nested load-string forms. Everything works as expected since the working environment contains the Var for x.
Some more code illustrates this:
(spy :cc
(load-string
"(def x 5)
x "))
with result
:cc => 5
So the eval produces the var x with value 5, then the reference to x causes the value 5 to be produced.
To my surprise, the partial load-string works in a fresh REPL:
demo.core=> (def x "fred")
#'demo.core/x
demo.core=> (load-string "x")
"fred"
So load-string must be coded to use any pre-existing
REPL environment as the base environment. When using lein run, there is no REPL environment available, so load-string starts with an empty environment.

Light Table read input

I'm having some trouble to get started with Light Table.
Here's my code (Clojure)
(ns prova1-ed.core
(:gen-class))
(use 'clojure.java.io)
(defn -main [& args]
(println "Type the name of the file to read: ")
(let [fileName (read-line)]
(let [rdr (reader fileName)]
(doseq [line (line-seq rdr)]
(println line)
)))
)
I'm sure it works. I've tested with lein run. As you can see, the program should read a file which the name is given by the user.
I've tried CTRL+SPACE in Light Table, but this is what I receive:
ERROR: Unhandled REPL handler exception processing message {:data {:auto? false, :pos {:line 14, :ch 1}, :mime "text/x-clojure", :tags [:editor.clj :editor.clojure], :type-name "Clojure", :line-ending "\r\n", :ns user, :path "C:\\Users\\Tiago\\Documents\\Clojure\\prova1_ed\\src\\prova1_ed\\core.clj", :print-length nil, :name "core.clj", :local true, :code "(ns prova1-ed.core\n (:gen-class))\n\n(use 'clojure.java.io)\n\n(defn -main [& args]\n\n (println \"Type the name of the file to read: \")\n\n (let [fileName (read-line)]\n (let [rdr (reader fileName)]\n (doseq [line (line-seq rdr)]\n (println line)\n )))\n)\n"}, :id 90, :op editor.eval.clj.sonar, :session 65d1da68-a730-4ffe-9365-9527726384e3}
How can i run it in the Light Tables' enviroment, so that I can input the file name?
TLDR
I don't think you can run (read-line) in Light Table as it'd have to add explicit support for allowing input. There's no standard input basically.
An Alternative
I'd suggest you modify your -main function to accept an explicit file-name argument instead of trying to read it from a standard input that isn't available.
I've got a Clojure webapp that I work on in Light Table.
I've got a -main function in a namespace named my-app.web. It looks something like this:
(defn -main [& [port]]
(let [port (Integer. (or port (env :port) 5000))
store (cookie/cookie-store {:key (env :session-secret)})]
(jetty/run-jetty (-> #'secured-app
wrap-with-logging
wrap-current-user
wrap-current-auth
wrap-error-page
(site {:session {:store store}}))
{:port port :join? false})))
In a separate file I've named light-table-start.clj, I've got the following code to run my app inside Light Table:
(require '[my-app.web :as web])
(require '[ring.adapter.jetty :as jetty])
(defonce server (web/-main "5000"))
;; (.start server)
;; (.stop server)
I run the Eval: Eval editor contents command (Ctrl+Shift+Enter on Windows and Linux or ⌘+Shift+Enter on Mac OS) the first time I want to run my app (or later, if the connection is closed for some reason). When I want to start or stop the server I can just highlight the code on the respective commented lines and run the Eval: Eval a form in editor command (Ctrl+Enter on Windows and Linux or ⌘+Enter on Mac OS).

How to start immutant message queue?

From the immutant documentation at http://immutant.org/documentation/current/apidoc/guide-installation.html:
With the dependencies in place, you simply invoke the Immutant
services from your app’s main entry point, identified by the :main
key in your project.clj.
Immutant's web service can be invoked like:
(ns my-app.handler
...
(:require [immutant.web :as web])
... )
(def app ... )
(defn -main [& args]
(web/run app))
What's the equivalent of (web/run app) for immutant.messaging?
here your answer
to start queue
(ns my-project.name
(:require [immutant.messaging :as msg]))
(defn call-fn
[args]
(let [fun (ns-resolve "namespace where fun defined" (symbol (first args)))
params (rest args)]
(if (seq params)
(apply fun )
(fun))))
(msg/start "queue")
(msg/respond "queue" (fn [args] (call-fn args)))
to call the queue just say
#(msg/request "queue" ["fun-name" params])

My logging (with robert-hooke) does not work properly with tools.namespace/refresh, why?

EDIT: Turned out I was using require instead of :require in the namespace declaration. With :require, tools.namespace refreshes the logging namespace, and the problem goes away. I still find it curious, however, that the expression (eval `(var ~(symbol "A/func"))) does not work in the situation described below (that is, if B below is not refreshed).
Summary: I'm using tools.namespace. If I have namespaces A and B, and in B do (eval `(var ~(symbol "A/func"))), and (tools.namespace/refresh) and run the code, that works. But if I make a change to A, do (tools.namespace/refresh), so that only A refreshes, then running that expression gives the error: Cannot resolve var: A/func in this context, even though A/func exists. Why?
Longer version:
In my project, I have a logging module/namespace that uses robert-hooke (see below). I'm using tools.namespace to reload my code when I make changes.
The problem is the following: When I want to log (my logging currently just prints) something, I list the functions that I want to log in my logging namespace and do (t.n/refresh). That works. But if I make changes to the the namespaces that contain the functions that I want to log, and do (t.n/refresh) without making changes to the logging namespace, the logging no longer works (for the functions that have been refreshed). As soon as I make a change to logging, so that it too is refreshed by tools.namespace, it starts working again.
So, it's like the vars in namespaces that have been refreshed don't properly get their logging hooks. But I don't understand why.
Below is my logging namespace. I call add-logging-wrappers each time I run my program.
If I add (eval `(var ~(symbol "sv/register-damage"))) inside add-logging-wrappers, that's fine when logging has just been refreshed and the logging works. But those times the logging does not work, that expression causes the error Cannot resolve var: sv/register-damage in this context.
(ns game.logging
(require [robert.hooke :as rh]
[clojure.pprint :as pp]
[game.server.core :as sv]
[game.client.core :as cl]
[game.math :as math]
(game.common [core-functions :as ccfns]
[graphics :as gfx])
(game.server [pathfinding :as pf]))
(:use [game.utils]))
(defn log-println [name type object]
(println (str (current-thread-name) " // " name " " type ":\n"
(with-out-str
(pp/pprint object)))))
(defn print-output [name f & args]
(let [result (apply f args)]
(log-println name "output" result)
result))
(defn print-input [name f & args]
(log-println name "input" args)
(apply f args))
(defn print-call [name f & args]
(println (str (current-thread-name) "//" name))
(apply f args))
(defmacro make-name-var-list [fn-list]
`[~#(for [fn fn-list]
[(str fn) `(var ~fn)])])
(defmacro defloglist [name & fns]
`(def ~name (make-name-var-list [~#fns])))
(defn add-hooks [name-vars & wrappers]
(when (seq wrappers)
(doseq [[name var] name-vars]
(rh/add-hook var (partial (first wrappers) name)))
(recur name-vars (next wrappers))))
(defn get-ns-name-vars [ns-sym]
(-> (the-ns ns-sym) (#(.name %)) ns-interns))
(defn add-hooks-to-ns [ns-sym & wrappers]
(apply add-hooks (get-ns-name-vars ns-sym) wrappers))
(defloglist log-both
sv/distribute-exp ;; <--- things to log
sv/register-damage
sv/give-exp)
(defloglist log-input)
(defloglist log-output)
(defn add-logging-wrappers []
(dorun (->> (all-ns) (map #(.name %)) (mapcat ns-interns) (map second)
(map rh/clear-hooks)))
(add-hooks log-both print-output print-input)
(add-hooks log-input print-input)
(add-hooks log-output print-output))

Clojure: namespace and -main

I'm trying to understand the -main and its namespace settings. The default namespace in a -main function seems to be "user", but function defined "above" the main function, in the same file, can be called. Are they referred? I was running this to find out:
(ns ack.doodle)
(defn fo [] "bar")
(defn -main [& args]
(println (fo)) ;; bar
(println *ns*) ;; #<Namespace user>
(println (get (ns-refers *ns*) 'sorted-map)) ;; #'clojure.core/sorted-map
(println (get (ns-refers *ns*) 'fo)) ;; nil
(println (get (ns-map *ns*) 'fo)) ;; nil
(println (get (ns-publics *ns*) 'fo)) ;; nil
(println (get (ns-interns *ns*) 'fo)) ;; nil
(println (get (ns-aliases *ns*) 'fo)) ;; nil
(println (ns-resolve *ns* 'fo))) ;; nil
The call (fo) succeeds, yet apparently the symbol 'fo is not known in the current namespace *ns*. What's going on?
This problem hit me when trying to pass the name of some function, along with some arguments, as command line arguments to -main.
steps to reproduce
paste above code in a file ./src/ack/doodle.clj
create ./project.clj with these contents:
(defproject ack "1" :main ack.doodle)
run it:
lein run
It looks like leiningen runs (-main) by calling it from user namespace like that: (ack.doodle/-main). Thus *ns* is bound to the user namespace.
Try running lein repl in your project root. Then run (-main) and see what happens. :)
Didn't I answer this for you last night in #clojure? If there's something unsatisfying about the answer, you'll have to clarify your question.