lein-ring
lein-ring copied to clipboard
No way to pass command line arguments to `lein ring server` not the generated jar
Extra command-line arguments trigger errors:
$ lein ring server-headless 3000 --param 121
clojure.lang.ArityException: Wrong number of args (4) passed to: server-headless/server-headless
Same happens with generated uberjars:
$ java -jar target/<PROJECT_NAME>-<VERSION>-standalone.jar --param 1
Exception in thread "main" clojure.lang.ArityException: Wrong number of args (2) passed to: main/-main
Is it possible to allow the use of traditional command-line flags?
Sorry, but I'm not clear what you expect this to do. What does --param 121
mean?
I expect extra command-line parameters to be ignored by lein-ring
and handled by the application (by parsing *command-line-args*
in some function hooked into :init
in project.clj).
For example:
-
$ lein ring server-headless --config /home/sth/app-config.edn
. -
$ java -jar /path/to/the/jar/file.jar --config /etc/sth-app/app-config.edn
Or:
-
$ java -jar /path/to/the/jar/file.jar --redis-port 6121
Maybe there's a better clojury way to do this but what can replace command-line arguments?
I think the problem is that if we go this route, we lose the ability to have arguments specific to the plugin. You could write your own -main
function, but usually environment variables are used for configuration. Is there a reason you want to use command line arguments specifically?
Environment variables have their own issues. Mostly, they are implicit and slightly clumsy. That's a philosophical dispute.
Writing my own -main
is the way I chose.
Regarding losing ability to use command-line options with lein-ring, I think this is a solved problem and typically --
is used to separate "our" arguments from "someone else's arguments". --
normally means "force following args to be positional", which is still OK for us. E.g vagrant ssh -- -A
will pass -A
to the invoked ssh.
You can close it if you find argument passing unnecessary. Use your judgement.
A --
is an interesting idea. It might also be sufficient just to add a & _
to the end of the generated -main
function.
Though I don't see a big difference between:
java -jar /path/to/the/jar/file.jar --redis-port 6121
And:
REDIS_PORT=6121 java -jar /path/to/the/jar/file.jar
@not-raspberry , can you share more details on how you wrote/specified your own -main
function? I've tried replacing it myself, but it always ends up ignored - adding this to the end of my handler.clj
file from a lein new compojure
generated project doesn't cause the string to be printed:
(defn -main
[& args]
(println "test"))
To explain further, I want to do something like:
> java -jar target/<PROJECT_NAME>-<VERSION>-standalone.jar --port 3333
So that whoever has the uberjar can specify their own port when running it.
@rpazyaquian
Add
:main your-app.core
to defproject
in project.clj. The your-app.core
namespace is where you place your -main
.
Alright, I think I got it! Thanks a bunch. Important to note that lein ring uberjar
won't use whatever you have in core
, so it's best to skip straight to lein uberjar
after including all your ring
dependencies.
Coming in on this late. My Smeagol app reads a configuration file all of whose entries can be overridden by environment variables, but you have to set an environment variable to indicate where the configuration file is.
Feedback from users is, Windows users typically don't know how to set an environment variable, so I'd like the jar to accept one argument, a path to a configuration file. This would allow the user to double-click on the configuration file and then select the jar from the Open With
dialog.
Of course I could create a batch file which takes one argument, sets SMEAGOL_CONFIG
, and then invokes the jar file; so this isn't a reason to change lein-ring, but it is a use case.
OK, looking at the source, there is a means of overriding main:
(defn main-namespace [project]
(or (get-in project [:ring :main])
(default-main-namespace project)))
So we can set the :main
key in the :ring
submap of project.clj
to the name of a namespace. If we don't set :main
, but do set :handler
, it will look for a main in the namespace of the handler:
(defn default-main-namespace [project]
(let [handler-sym (get-in project [:ring :handler])]
(str (namespace handler-sym) ".main")))
This means we can in fact define our own -main
. What's important is to know what that custom -main
needs to do to to invoke the same things the default would invoke.
It turns out that the default -main
is dynamically generated:
(defn compile-main [project]
(let [main-ns (symbol (main-namespace project))
options (-> (select-keys project [:ring])
(assoc-in [:ring :open-browser?] false)
(assoc-in [:ring :stacktraces?] false)
(assoc-in [:ring :auto-reload?] false)
(assoc-in [:ring :auto-refresh?] false))]
(compile-form project main-ns
`(do (ns ~main-ns
(:gen-class))
(defn ~'-main []
(~(generate-resolve 'ring.server.leiningen/serve) '~options))))))
So the default is to invoke ring.server.leiningen/serve
, and pass to it a lightly massaged version of the project map. The use of tilde-notation in a defn
somewhat worries me. In a defmacro
tilde-notation would force the evaluation of the tilde-notated symbol, and the Reader documentation agrees that that's what it does generally. I don't understand what (unquote (quote -main))
does that -main
wouldn't do (it may be about resolving at run time vs resolving at compile time but I am only an egg). However, the general idea is clear.
I'm working out how to do this and will document it in this thread.