PureConfig
A boilerplate-free Scala library for loading configuration files
Why
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.
Not yet another configuration library
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.
Add PureConfig to your project
In the sbt configuration file:
use scala 2.10
or 2.11
:
scalaVersion := "2.11.7" // or "2.10.5"
Add the library. For scala 2.11
libraryDependencies ++= Seq(
"com.github.melrief" %% "pureconfig" % "0.1.9"
)
For scala 2.10
you need also the scala macro paradise plugin:
libraryDependencies ++= Seq(
"com.github.melrief" %% "pureconfig" % "0.1.9",
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.
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 = new StringConvert[Path] {
override def from(str: String): Try[Path] = Try(Paths.get(str))
override def to(path: Path): String = path.toString
}
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)
License
Mozilla Public License, version 2.0
Special Thanks
To the Shapeless and to the Typesafe config developers.