Skip to content

Commit 2110342

Browse files
author
José Valim
committed
Improve docs for OptionParser
Signed-off-by: José Valim <[email protected]>
1 parent 8533df2 commit 2110342

File tree

1 file changed

+61
-30
lines changed

1 file changed

+61
-30
lines changed

lib/elixir/lib/option_parser.ex

Lines changed: 61 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -30,36 +30,33 @@ defmodule OptionParser do
3030
Elixir converts switches to underscored atoms, so `--source-path` becomes
3131
`:source_path`. This is done to better suit Elixir conventions. However, this
3232
means that switches can't contain underscores and switches that do contain
33-
underscores are always returned in the list of invalid options.
33+
underscores are always returned in the list of invalid switches.
3434
35-
Without any options, this function will try to parse all switches in the `argv`.
35+
When parsing, it is common to list switches and their expected types:
3636
37-
iex> OptionParser.parse(["--debug"])
37+
iex> OptionParser.parse(["--debug"], switches: [debug: :boolean])
3838
{[debug: true], [], []}
3939
40-
iex> OptionParser.parse(["--source", "lib"])
40+
iex> OptionParser.parse(["--source", "lib"], switches: [source: :string])
4141
{[source: "lib"], [], []}
4242
43-
iex> OptionParser.parse(["--source-path", "lib", "test/enum_test.exs", "--verbose"])
43+
iex> OptionParser.parse(["--source-path", "lib", "test/enum_test.exs", "--verbose"],
44+
...> switches: [source_path: :string, verbose: :boolean])
4445
{[source_path: "lib", verbose: true], ["test/enum_test.exs"], []}
4546
46-
Switches followed by a value will be assigned the value, as a string.
47-
Switches without an argument, like `--debug` in the examples above, will
48-
automatically be set to `true`.
47+
We will explore the valid switches and operation modes of option parser below.
4948
5049
## Options
5150
5251
The following options are supported:
5352
5453
* `:switches` or `:strict` - see the "Switch definitions" section below
54+
* `:allow_nonexistent_atoms` - see the "Parsing dynamic switches" section below
5555
* `:aliases` - see the "Aliases" section below
56-
* `:allow_nonexistent_atoms` - see the "Parsing undefined switches" section below
5756
5857
## Switch definitions
5958
60-
Often it is better to explicitly list the known
61-
switches and their formats. The switches can be specified via one of two
62-
options:
59+
Switches can be specified via one of two options:
6360
6461
* `:switches` - defines some switches and their types. This function
6562
still attempts to parse switches that are not in this list.
@@ -90,20 +87,20 @@ defmodule OptionParser do
9087
* `:float` - parses the value as a float
9188
* `:string` - parses the value as a string
9289
93-
If a switch can't be parsed according to the given type, it is returned
94-
in the invalid options list.
90+
If a switch can't be parsed according to the given type, it is
91+
returned in the invalid options list.
9592
9693
### Modifiers
9794
9895
Switches can be specified with modifiers, which change how
9996
they behave. The following modifiers are supported:
10097
101-
* `:keep` - keeps duplicated items instead of overriding them; works with
102-
all types except `:count`. Specifying `switch_name: :keep` assumes the
103-
type of `:switch_name` will be `:string`.
98+
* `:keep` - keeps duplicated items instead of overriding them;
99+
works with all types except `:count`. Specifying `switch_name: :keep`
100+
assumes the type of `:switch_name` will be `:string`.
104101
105-
Note that if you want to use `:keep` with a type other than `:string`, use a list
106-
as the type for the switch. For example: `[foo: [:integer, :keep]]`.
102+
To use `:keep` with a type other than `:string`, use a list as the type
103+
for the switch. For example: `[foo: [:integer, :keep]]`.
107104
108105
### Negation switches
109106
@@ -113,14 +110,45 @@ defmodule OptionParser do
113110
iex> OptionParser.parse(["--no-op", "path/to/file"], switches: [op: :boolean])
114111
{[op: false], ["path/to/file"], []}
115112
116-
### Parsing undefined switches
113+
### Parsing dynamic switches
114+
115+
`OptionParser` also includes a dynamic mode where it will attempt to parse
116+
switches dynamically. Such can be done by not specifying the `:switches` or
117+
`:strict` option.
118+
119+
iex> OptionParser.parse(["--debug"])
120+
{[debug: true], [], []}
121+
117122
118-
By default, only arguments that have defined atom representation will be parsed.
119-
This happens because creating atoms at runtime is considered to be unsafe,
120-
but you can still force creation of atoms by passing `allow_nonexistent_atoms: true`
121-
to the list of function options.
123+
Switches followed by a value will be assigned the value, as a string. Switches
124+
without an argument, like `--debug` in the examples above, will automatically be
125+
set to `true`.
122126
123-
This is useful when you are building command-line applications that receive dynamically-named arguments.
127+
Since Elixir converts switches to atoms, the dynamic mode will only parse
128+
switches that translates to atoms used by the runtime. Therefore, the code below
129+
likely won't parse the given option since the `:option_parser_example` atom is
130+
never used anywhere:
131+
132+
OptionParser.parse(["--option-parser-example"])
133+
# Does nothing more...
134+
135+
However, the code below does since the `:option_parser_example` atom is used
136+
at some point later (or earlier) on:
137+
138+
{opts, _, _} = OptionParser.parse(["--option-parser-example"])
139+
opts[:option_parser_example]
140+
141+
In other words, when using dynamic mode, Elixir will do the correct thing and
142+
only parse options that are used by the runtime, ignoring all others. If you
143+
would like to parse all switches, regardless if they exist or not, you can
144+
force creation of atoms by passing `allow_nonexistent_atoms: true` as option.
145+
Such option is useful when you are building command-line applications that
146+
receive dynamically-named arguments but must be used with care on long-running
147+
systems.
148+
149+
Switches followed by a value will be assigned the value, as a string.
150+
Switches without an argument, like `--debug` in the examples above, will
151+
automatically be set to `true`.
124152
125153
## Aliases
126154
@@ -213,10 +241,12 @@ defmodule OptionParser do
213241
214242
## Example
215243
216-
iex> OptionParser.parse_head(["--source", "lib", "test/enum_test.exs", "--verbose"])
244+
iex> OptionParser.parse_head(["--source", "lib", "test/enum_test.exs", "--verbose"],
245+
...> switches: [source: :string, verbose: :boolean])
217246
{[source: "lib"], ["test/enum_test.exs", "--verbose"], []}
218247
219-
iex> OptionParser.parse_head(["--verbose", "--source", "lib", "test/enum_test.exs", "--unlock"])
248+
iex> OptionParser.parse_head(["--verbose", "--source", "lib", "test/enum_test.exs", "--unlock"],
249+
...> switches: [source: :string, verbose: :boolean, unlock: :boolean])
220250
{[verbose: true, source: "lib"], ["test/enum_test.exs", "--unlock"], []}
221251
222252
"""
@@ -236,10 +266,12 @@ defmodule OptionParser do
236266
237267
## Examples
238268
239-
iex> OptionParser.parse_head!(["--source", "lib", "path/to/file", "--verbose"])
269+
iex> OptionParser.parse_head!(["--source", "lib", "path/to/file", "--verbose"],
270+
...> switches: [source: :string, verbose: :boolean])
240271
{[source: "lib"], ["path/to/file", "--verbose"]}
241272
242-
iex> OptionParser.parse_head!(["--number", "lib", "test/enum_test.exs", "--verbose"], strict: [number: :integer])
273+
iex> OptionParser.parse_head!(["--number", "lib", "test/enum_test.exs", "--verbose"],
274+
...> strict: [number: :integer])
243275
** (OptionParser.ParseError) 1 error found!
244276
--number : Expected type integer, got "lib"
245277
@@ -309,7 +341,6 @@ defmodule OptionParser do
309341
* `{:error, rest}` - there are no switches at the head of the given `argv`
310342
311343
"""
312-
313344
@spec next(argv, options) ::
314345
{:ok, key :: atom, value :: term, argv} |
315346
{:invalid, String.t, String.t | nil, argv} |

0 commit comments

Comments
 (0)