First, we should elaborate a bit how docopt knows what to expect and how to parse/validate your input. To make this work, you are expected to present docopt with a string that follows certain rules. As mentioned above, this is also the string that is being show as the help text. More specific, what it expects is a string that follows the following schema:
Usage: #{program_name} -- #{argument_spec}
#{program_name} -- --help
where program_name equals to the name of the command that is being run, -- (double dash) – this is not due to doctopt but due to rake (more on that in a moment), and argument_spec which can be anything you want to put there.
Let’s look at the aforementioned example:
Usage: stats:multi -- --sites= \
[--aggregates=] \
[ (--start-date= --end-date=) | \
(--days-ago= [--days-ago-end=]) ]
Here, the program_name is stats:multi, which is the actual namespace and task name for our rake task, ‘–‘ and the argument_spec is "--sites= [--aggregates=] [ (--start-date= --end-date=) | (--days-ago= [--days-ago-end=]) ]"
Now, let’s go into details of the argument_spec (split over multiple lines for readability):
--sites= \
[--aggregates=] \
[ (--start-date= --end-date=) | \
(--days-ago= [--days-ago-end=]) ]
Basic rules
docopt considers arguments mandatory, unless they are enclosed in brackets [] – then they are optional. So in this example, our only --sites is required. It also requires a value, given that it is being followed by =. However, here could be anything, and is used to give the user an idea of what is expected as the type of argument. If you would enter --sites on the input without specifying a value, docopt will return an error that the value is missing. No effort needed on your end!
Optional arguments
The next argument [--aggregates=] follows the same pattern, except that this one is fully optional. We will in our code handly the case where this is not specified and come up with some default values.
Grouping and mutual exclusion
The last – optional – argument is used to specify the dates we want to run our computation for, and we want to have three ways of doing so this:
- EITHER by specifically telling the
start-date AND end-date
- OR by specifying the number of
days-ago before the time of the command, taking as an end date the date of the command being run (e.g. 7 days ago until now)
- OR by specifying the number of
days-ago until days-ago-end (e.g. to backfill something between 14 days ago and 7 days ago).
Here is where complicated things can be achieved in a simple manner. The formatting we used for this is in fact:
[ ( a AND b ) | ( c [ d ] ) ]
docopt requires all arguments in a group (...) to be presented on the input. If only a or b are given, it will return and inform us about the missing argument.
Similarly, a logical OR can be added via | (pipe). This will make either of the options a valid input.
Furthermore, you can combine optional arguments within a group, like we did with ( c [ d ] ). This will make the parser aware of the fact that d (in the real example above [--days-ago-end=]) is only valid when c (--days-ago= in the example) has been presented. Trying to use this parameter with --start-days will result in an error.
Note that this whole complex group is optional and we again will come up with some defaults in our code that handles the parsed arguments.
Flags
Lastly, it’s noteworthy that flags (i.e. arguments that don’t take a value), such as --force, will result in a true/false value after parsing.
For more information and examples, consult the docopt documentation here. However, the explanation above should get you already a long way.