-
Notifications
You must be signed in to change notification settings - Fork 16
Defaults
With FieldArgs
, sumac will only set the fields from the arguments if they are specified on the command line, this means that if nothing is specified, the field will hold the value assigned to it before the call to parse
(usually, the value set at construction time).
class MyArgs extends FieldArgs {
var willDefault: Int = 0
var fillMe: String = _
}
val args = new MyArgs
args.parse(Array[String]("--fillMe", "test"))
assert(args.willDefault == 0)
assert(args.fillMe == "test")
Often, hard coding a default in the source is a bad idea. The first solution is to use the @Required
Validation annotation, sumac will enforce that the argument is specified on the command line, thus always ignoring the value provided before the parse command is provided.
Sumac also provides a mixin trait ConfigArgs
that will automatically load the default values from a config file using the Typesafe Config Library, which is available in the sumac-ext
package.
class MyArgs extends FieldArgs with ConfigArgs {
override val configPrefix = "com.example.config"
var arg1: Option[Duration] = None
}
When using ConfigArgs
as a mixin, you have to specify a configPrefix
that will be appended to the arguments name to search for a default value in the config files. In the previous example, the default will be loaded from the config key: com.example.config.arg1
.
If you use Nesting in your arguments holder, the config key of the nested argument will also be nested in the path:
class Test extends FieldArgs with ConfigArgs {
override val configPrefix = "com.example.config"
var arg1: Option[Duration] = None
}
class Nested extends FieldArgs {
var arg3: Double = 10.5
}
class TestWithNested extends Test {
var arg2: Nested = new Nested
}
In this example, arg3
will be searched in the config file with the key com.example.config.arg2.arg3
.
The default behaviour is to use the library ConfigFactory.load()
to load the config files where to search for the defaults; from config doc (first-listed are higher priority):
- system properties
- application.conf (all resources on classpath with this name)
- application.json (all resources on classpath with this name)
- application.properties (all resources on classpath with this name)
- reference.conf (all resources on classpath with this name)
The idea is that libraries and frameworks should ship with a reference.conf in their jar. Applications should provide an application.conf.
- code defaults are lowest priority;
- when using
ConfigArgs
, the defaults will be loaded from the config file only if no value is provided on the command line; - when using multiple default mixins, the defaults are applied in order of the mixins;
- validation happens after all defaults have been applied
While the typesafe config library provides its own parsing library, the values specified in the config files will be parsed with the sumac parsers in the same way as they would be parsed from the command line.
You can use the addConfig
method to add config files that will take priority over the application.conf
file.
If you do not want at all the default loading behaviour of typesafe config to take place, you can set useDefaultConfig
to false
. Either in the implementation of the arguments holder, or before calling the parse
function. In this case, you should specify the name of the conf file to load from the classpath using ConfigFactory.load(filename)
.
val test = new MyArgs
test.useDefaultConfig = false
test.addConfig("alternate.conf")
test.parse(Array[String]())
It is sometimes not desirable to hardcode the name of the config file to load. A practical use case for this is to have different configuration files for different environments, e.g. dev
, staging
, production
. In this case, it is useful to load the configuration file based on the value of a command line argument. For instance, in our use case, we would have an environment argument: MyApp --env dev
.
Sumac provides a simple modular way to perform such a task with the ConfigFromArg
mixin:
class LoadFromArg extends FieldArgs with ConfigArgs with ConfigFromArg {
var env: String = "dev"
var arg1: Option[Duration] = None
useDefaultConfig = false
override lazy val configFilenameFromArg: Option[String] = {
env match {
case "production" => Some("application.conf")
case "dev" => Some("alternate.conf")
case _ => None
}
}
}
When using this mixin, you need to implement the configFilenameFromArgs: Option[String]
method (be careful, it cannot be overridden with a val
). This method can use any field to decide of the config file to load.
At the time of calling the configFilenameFromArgs
method, the command line arguments will have been parsed and assigned to their field holders and all the previous default mixins will have been applied.