Merge: proposal: lib/config
authorJean Privat <jean@pryen.org>
Mon, 5 Dec 2016 20:53:32 +0000 (15:53 -0500)
committerJean Privat <jean@pryen.org>
Mon, 5 Dec 2016 20:53:32 +0000 (15:53 -0500)
commit780f101940627978895d7a0de430a6979d28f77e
tree35ff3a00de160f60fd3f69adc10cd27b90dbd940
parent9462dbf168d2d6fabb33aeb13bded3db48aa0261
parente919688319a13a15afc9bc6ca6fdb7c3e940ec41
Merge: proposal: lib/config

We often need basic option handling in our app.

Here a minimalist proposal that covers the basics:
* options holder
* default values
* loading options from ini file

Extract from doc:

# Configuration options for nit tools and apps

This module provides basic services for options handling in your nit programs.

## Basic configuration holder

The `Config` class can be used as a simple option holder and processor:

~~~nit
import config

# Create a new config option
var opt_my = new OptionString("My option", "--my")

# Create the config and add the option
var config = new Config
config.add_option(opt_my)

# Parse the program arguments, usually `args`
config.parse_options(["--my", "myOption", "arg1", "arg2"])

# Access the options and args
assert opt_my.value == "myOption"
assert config.args == ["arg1", "arg2"]
~~~

## Custom configuration class

Instead of using basic `Config` instances, it is better to define new sublcasses
to store options and define custom services.

~~~nit
import config

class MyConfig
super Config

var opt_my = new OptionString("My option", "--my")

init do
super
tool_description = "Usage: MyExample [OPTION]... [ARGS]..."
add_option(opt_my)
end

fun my: String do return opt_my.value or else "Default value"
end

var config = new MyConfig
config.parse_options(["--my", "myOption", "arg1", "arg2"])

assert config.my == "myOption"
assert config.args == ["arg1", "arg2"]
~~~

We define the `my` method to provide an elegant shortcut to `opt_my.value`
and define the default value if the option was not set by the user.

The `tool_description` attribute is used to set the `usage` header printed when
the user request the `help` message.

~~~nit
config.parse_options(["-h"])
if config.help then
config.usage
exit 0
end
~~~

This will display the tool usage like this:

~~~raw
Usage: MyExample [OPTION]... [ARGS]...
 -h, --help   Show this help message
 --my         My option
~~~

## Configuration with `ini` file

The `IniConfig` class provides an easy way to link your configuration to an ini
file.

~~~nit
class MyIniConfig
super IniConfig

var opt_my = new OptionString("My option", "--my")

init do
super
tool_description = "Usage: MyExample [OPTION]... [ARGS]..."
opts.add_option(opt_my)
end

fun my: String do return opt_my.value or else ini["my"] or else "Default"
end
~~~

This time, we define the `my` method to return the option value or the ini
if no option was passed. Finally, if no ini value can be found, we return the
default value.

By default, `IniConfig` looks at a `config.ini` file in the execution directory.
This can be overrided in multiple ways.

First by the app user by setting the `--config` option:

~~~nit
var config = new MyIniConfig
config.parse_options(["--config", "my_config.ini"])

assert config.config_file == "my_config.ini"
~~~

Default config file can also be changed by the library client through the
`default_config_file` attribute:

~~~nit
config = new MyIniConfig
config.default_config_file = "my_config.ini"
config.parse_options(["arg"])

assert config.config_file == "my_config.ini"
~~~

Or by the library developper in the custom config class:

~~~nit
class MyCustomIniConfig
super IniConfig

redef var default_config_file = "my_config.ini"
end

var config = new MyCustomIniConfig
config.parse_options(["arg"])

assert config.config_file == "my_config.ini"
~~~

Pull-Request: #2339
Reviewed-by: Jean Privat <jean@pryen.org>