On Thu 15 May 2014 at 10:45:52AM -0700, Bob Larrick wrote:
A single element in the cli-options can be as brief as
[-p --port A port number]
What is non-obvious is that specifying
--port PORT
has entirely different semantics than specifying
--port
Like I mentioned on the issue page, this is just a shortcut around
passing a `:required` entry with the same information.
The motivation behind this is that the resulting option summary mirrors
the option vectors:
[[-p --port PORT A port number :parse-fn #(Integer/parseInt %)]
[-q --quiet Shhh]]
-p, --port PORT A port number
-q, --quiet Shhh
It is easy to tell at a glance that --port requires an option argument,
while --quiet does not.
If you dislike this syntax (which I borrowed from Ruby), you may replace
it with:
[-p --port A port number
:required PORT
:parse-fn #(Integer/parseInt %)]
In the first case the command lein run -p 3000 will result in an options
map of {:options {:port 3000}}.
Can you guess what the same command would produce given the second case?
I would expect that an option flag that has not been marked as requiring
an argument to be a boolean toggle. This is the convention in C's
getopt(1), bash's getopts builtin, Perl's GetOpt::Long, and Ruby's
OptionParser.
If you're like me, you wouldn't guess {:options {:port true}}. :-/
It is worth noting that in earlier versions of this library --port did
result in {:options {:port 3000}}, so this is a change in behavior from
previous versions.
Yes, this is a change. The old tools.cli/cli still exists for people who
do not want to bother with learning a new API (I can sympathize). It has
also been upgraded to use the new option tokenizer.
The current behavior is neither simple nor easy to understand, and
complects the behavior of the flag with the definition of the long option.
Again, this is optional sugar. The option vectors are actually compiled
to maps:
(#'clojure.tools.cli/compile-option-specs
[[-p --port PORT A port number]
[nil --host :required HOST]])
-
({:id :port
:short-opt -p
:long-opt --port
:required PORT
:desc A port number}
{:id :host
:short-opt nil
:long-opt --host
:required HOST
:desc nil})
You may, if you like, supply the underlying maps instead of the option
vectors to parse-opts. The option specification is documented here:
https://github.com/clojure/tools.cli/blob/master/src/main/clojure/clojure/tools/cli.clj#L265-L309
I suggest that this implicit behavior be removed and that all options be
treated as required unless :required false is explicitly declared.
Or perhaps there should be :boolean flag similar to :parse-fn or :default,
since :required feels a little overloaded.
That way given lein run -p 3000, [-p --port A port number] would
result in {:options {:port 3000}} and
[-p --port A port number :boolean true] would result in {:options
{:port true}}.
That would make when a flag will be treated as a boolean explicit
and obvious, and make this library a little less frustrating and
foot-gunish.
You are suggesting that options require arguments by default is less
implicit than options are boolean toggles by default. This is unclear
to me.
As to which default is preferable and less surprising, I prefer the
latter since it matches the conventions in C, bash, Perl, and Ruby. My
understanding is that the former is the default on Python. Perhaps this
is the source of our disagreement?
I do agree that the documentation is not clear enough on this matter; I
will ameliorate this later today.
guns
pgp60HSRLzVmo.pgp
Description: PGP signature