-
-
Notifications
You must be signed in to change notification settings - Fork 179
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
defclifn subcommands #346
Comments
Boot implements subcommands via a pipeline, which is I think the way you want to do it. The reason why there are commands and subcommands is so the preceeding things can prepare context for the things that follow. With boot the command line and REPL correspond naturally---subcommand just means function composition. |
Oh, interesting. So maybe what we want is to make functions defined using |
They're functions, so you can compose them, no problem there :) Check out the |
Why not just use tasks? Those are "subcommands", no? |
Yeah, tasks could definitely work for what I'm trying to do. I've never thought about tasks as things that could be exposed to the user, though -- can you define tasks using What got me thinking about this is Alda's CLI interface. I have a bunch of tasks defined using |
I think 2.5.2-SNAPSHOT fixes some issues. Consider this boot script (call it foop): #!/usr/bin/env boot
;; vim: set ft=clojure:
(use 'boot.cli 'boot.util)
(defn dosub
[[sub & args]]
(if-let [subfn (guard (resolve (symbol sub)))]
(apply subfn args)
(throw (Exception. (format "no such subcommand: %s" sub)))))
(defclifn -main
[a aarp bool "The aarp option."]
(prn :main :opts *opts* :args *args*)
(some-> *args* seq dosub))
(defclifn sub1
[b barp bool "The barp option."]
(prn :sub1 :opts *opts* :args *args*)
(some-> *args* seq dosub))
(defclifn sub2
[c carp bool "The carp option."]
(prn :sub2 :opts *opts* :args *args*)
(some-> *args* seq dosub)) I can now do:
:main :opts {:aarp true} :args nil
:main :opts {:aarp true} :args ("sub1" "-b")
:sub1 :opts {:barp true} :args nil
:main :opts {:aarp true} :args ("sub1" "-b" "sub2" "-c")
:sub1 :opts {:barp true} :args ("sub2" "-c")
:sub2 :opts {:carp true} :args nil Can you please set |
This is awesome! Exactly what I had in mind. Thanks! 👍 |
I've been thinking a lot about how to build robust Clojure command-line apps that have a hierarchy of commands and subcommands.
clojure.tools.cli has a way to do this, but it's not ideal, IMHO, because it stops parsing once it gets to an argument it doesn't recognize, so it's up to you to call
parse-opts
again to handle parsing the subcommands.I found another thing called cliopatra that supports both option-binding and standalone argument-binding, which I think could be used to do what I'm describing, but I think this could still be made more intuitive, and it still relies on clojure.tools.cli, which is inferior to Boot's
defclifn
IMHO.defclifn
is the most elegant and concise way that I've seen to represent a CLI command as a function. I think it would be extra elegant if we added support for delegating unrecognized arguments to subcommands, which would also just be functions (probably defined usingdefclifn
, in most cases). I'd be happy to try my hand at a PR for this, but I'd like to get some feedback on the idea first.I'm imagining being able to do something like this:
The idea would be that I could then call my main function from the command line (let's say this is a boot script called
git
) and give the main function both task options and subcommands to perform:I'm not sure yet how this might translate into REPL usage... maybe something like this?
Re:
do-subcommand
above: I'm thinking this could be a built-in symbol that is available only within adefclifn
form, so it has the subcommand's parsed options as a context for executing the subcommand. There could also be ado-subcommands
, which can take 0 or more subcommands as arguments and execute each of them (using their parsed options) in the order provided at the command-line. The 0 arity could just execute all of the subcommands the user included. I feel iffy about makingdefclifn
an anaphoric macro though, and I'm open to other approaches.Anyway, thoughts?
The text was updated successfully, but these errors were encountered: