Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No way to pass command line arguments to lein ring server not the generated jar #172

Open
not-raspberry opened this issue May 3, 2016 · 10 comments

Comments

@not-raspberry
Copy link

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?

@weavejester
Copy link
Owner

Sorry, but I'm not clear what you expect this to do. What does --param 121 mean?

@not-raspberry
Copy link
Author

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?

@weavejester
Copy link
Owner

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?

@not-raspberry
Copy link
Author

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.

@weavejester
Copy link
Owner

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

@rpazyaquian
Copy link

rpazyaquian commented Aug 11, 2017

@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.

@not-raspberry
Copy link
Author

not-raspberry commented Aug 11, 2017

@rpazyaquian

Add

   :main your-app.core

to defproject in project.clj. The your-app.core namespace is where you place your -main.

@rpazyaquian
Copy link

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.

@simon-brooke
Copy link

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.

@simon-brooke
Copy link

simon-brooke commented Sep 16, 2017

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants