A boilerplate-free Scala library for loading configuration files
Loading configurations has always been a tedious and error-prone procedure. A common way to do it consists in writing code to deserialize each fields of the configuration. The more fields there are, the more code must be written (and tested and maintained...) and this must be replicated for each project.
This kind of code is boilerplate because most of the times the code can be automatically generated by
the compiler based on what must be loaded. For instance, if you are going to load an Int
for a field
named foo
, then probably you want some code that gets the values associated with the key foo
in
the configuration and assigns it to the proper field after converting it to Int
.
The goal of this library is to create at compile-time the boilerplate necessary to load a configuration of a certain type. In other words, you define what to load and PureConfig provides how to load it.
PureConfig is not a configuration library in the sense that it doesn't search for files or parse them. It can be seen as a better front-end for the existing libraries. It uses typesafe config library for loading raw configurations and then uses the raw configurations to do its magic.
In the sbt configuration file:
use scala 2.10
or 2.11
:
scalaVersion := "2.11.8" // or "2.10.5"
Add the library. For scala 2.11
libraryDependencies ++= Seq(
"com.github.melrief" %% "pureconfig" % "0.3.1"
)
For scala 2.10
you need also the scala macro paradise plugin:
libraryDependencies ++= Seq(
"com.github.melrief" %% "pureconfig" % "0.3.1",
compilerPlugin("org.scalamacros" % "paradise" % "2.0.1" cross CrossVersion.full)
)
For a full example of build.sbt
you can have a look at this build.sbt
used for the example.
In the example directory there is an example of usage of pureconfig. In the example, the idea is to load a configuration for a directory watcher service. The configuration file (a real one is available here) for this program will look like
dirwatch.path="/path/to/observe"
dirwatch.filter="*"
dirwatch.email.host=host_of_email_service
dirwatch.email.port=port_of_email_service
dirwatch.email.message="Dirwatch new path found report"
dirwatch.email.recipients=["recipient1,recipient2"]
dirwatch.email.sender="sender"
To load it, we define some classes that have proper fields and names
import java.nio.file.Path
case class Config(dirwatch: DirWatchConfig)
case class DirWatchConfig(path: Path, filter: String, email: EmailConfig)
case class EmailConfig(host: String, port: Int, message: String, recipients: Set[String], sender: String)
The use of Path
gives us a chance to use a custom converter
import pureconfig._
import java.nio.file.Paths
import scala.util.Try
implicit val deriveStringConvertForPath = fromString[Path](Paths.get)
And then we load the configuration
val config = loadConfig[Config].get // loadConfig returns a Try
And that's it.
You can then use the configuration as you want:
println("dirwatch.path: " + config.dirwatch.path)
println("dirwatch.filter: " + config.dirwatch.filter)
println("dirwatch.email.host: " + config.dirwatch.email.host)
println("dirwatch.email.port: " + config.dirwatch.email.port)
println("dirwatch.email.message: " + config.dirwatch.email.message)
println("dirwatch.email.recipients: " + config.dirwatch.email.recipients)
println("dirwatch.email.sender: " + config.dirwatch.email.sender)
It's also possible to operate directly on Config
and ConfigValue
types
of typesafe config with the implicit helpers provided in the
pureconfig.syntax
package:
import com.typesafe.config.ConfigFactory
import pureconfig.syntax._
val config = ConfigFactory.load().to[Config].get
println("The loaded configuration is: " + config.toString)
By default, PureConfig's loadConfig()
methods load all resources in the classpath named:
application.conf
,application.json
,application.properties
, andreference.conf
.
The various pureconfig.loadConfig()
methods defer to typesafe config's
ConfigFactory
to
select where to load the config files from. Typesafe Config has well-documented rules for configuration
loading which we'll not repeat. Please see Typesafe
Config's documentation for a full telling of the subtleties. If you need greater control over how config
files are loaded, refer to ConfigFactory
's options.
Alternatively, PureConfig also provides pureconfig.loadConfigFromFiles()
which builds a configuration from
an explicit list of files. Files earlier in the list have greater precedence than later ones. Each file can
include a partial configuration as long as the whole list produces a complete configuration. For an example,
see the test of loadConfigFromFiles()
in
PureconfSuite.scala
.
Because PureConfig uses Typesafe Config to load configuration, it supports reading files in HOCON, JSON, and Java .properties
formats. HOCON is a delightful superset of both JSON and .properties
that is highly recommended. As an added bonus it supports advanced features like variable substitution and file sourcing.
Mozilla Public License, version 2.0
To the Shapeless and to the Typesafe config developers.