A boilerplate-free Scala library for loading configuration files.
- Why
- Not Yet Another Configuration Library
- Add PureConfig to your project
- Use PureConfig
- Supported types
- Configurable converters
- Support new types
- Override behaviour for types
- Override behaviour for case classes
- Override behaviour for sealed families
- Error handling
- Handling missing keys
- Example
- Whence the config files
- Support for Duration
- Integrating with other libraries
- Contribute
- FAQ
- License
- Special thanks
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 the 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
, 2.11
or 2.12
:
scalaVersion := "2.12.1" // or "2.11.8", "2.10.5"
Add PureConfig to your library dependencies. For Scala 2.11
and 2.12
:
libraryDependencies ++= Seq(
"com.github.pureconfig" %% "pureconfig" % "0.7.2"
)
For Scala 2.10
you need also the Macro Paradise plugin:
libraryDependencies ++= Seq(
"com.github.pureconfig" %% "pureconfig" % "0.7.2",
compilerPlugin("org.scalamacros" % "paradise" % "2.0.1" cross CrossVersion.patch)
)
For a full example of build.sbt
you can have a look at this build.sbt
used for the example.
Import the library package and use one of the loadConfig
methods:
import pureconfig._
import pureconfig.error.ConfigReaderFailures
case class YourConfClass(name: String, quantity: Int)
val config: Either[pureconfig.error.ConfigReaderFailures,YourConfClass] = loadConfig[YourConfClass]
Currently supported types for fields are:
String
,Boolean
,Double
(standard and percentage format ending with%
),Float
(also supporting percentage),Int
,Long
,Short
,URL
,URI
,Duration
,FiniteDuration
;java.lang.Enum
- all collections implementing the
TraversableOnce
trait where the type of the elements is in this list; Option
for optional values, i.e. values that can or cannot be in the configuration;Map
withString
keys and any value type that is in this list;- everything in
java.time
(must be configured first - see Configurable converters); java.io.File
;java.util.UUID
;java.nio.file.Path
;java.util.regex.Pattern
andscala.util.matching.Regex
;- Typesafe
ConfigValue
,ConfigObject
andConfigList
; - value classes for which readers and writers of the inner type are used;
- case classes;
- sealed families of case classes (ADTs).
First, import the library, define data types, and a case class to hold the configuration:
import com.typesafe.config.ConfigFactory.parseString
import pureconfig.loadConfig
sealed trait MyAdt
case class AdtA(a: String) extends MyAdt
case class AdtB(b: Int) extends MyAdt
final case class Port(value: Int) extends AnyVal
case class MyClass(
boolean: Boolean,
port: Port,
adt: MyAdt,
list: List[Double],
map: Map[String, String],
option: Option[String])
Then, load the configuration (in this case from a hard-coded string):
val conf = parseString("""{
"boolean": true,
"port": 8080,
"adt": {
"type": "adtb",
"b": 1
},
"list": ["1", "20%"],
"map": { "key": "value" }
}""")
// conf: com.typesafe.config.Config = Config(SimpleConfigObject({"adt":{"b":1,"type":"adtb"},"boolean":true,"list":["1","20%"],"map":{"key":"value"},"port":8080}))
loadConfig[MyClass](conf)
// res3: Either[pureconfig.error.ConfigReaderFailures,MyClass] = Right(MyClass(true,Port(8080),AdtB(1),List(1.0, 0.2),Map(key -> value),None))
The core of PureConfig eschews unnecessary dependencies. Separate modules exist to support types which are not part of the standard Scala and Java libraries.
pureconfig-enum
provides converters for enums generated by julienrf's enum library.pureconfig-enumeratum
provides converters for enums generated by Enumeratum.pureconfig-javax
provides converters for value classes in the javax packages.pureconfig-joda
provides configurable converters for Joda Time types.pureconfig-squants
provides converters for Squants's beautiful types representing units of measure.
A non-comprehensive list of other libraries which have integrated with PureConfig to provide a richer experience include:
refined-pureconfig
allows PureConfig to play nicely withrefined
's type refinements. Viktor Lövgren's blog post gleefully explains how PureConfig and refined work together.
Apache Spark (specifically version 2.1.0) has a transitive dependency
on Shapeless 2.0.0. This version is
too old to be used by PureConfig, making your Spark project fail when using
spark-submit
.
If you are using the sbt-assembly plugin to create your JARs you can shade this dependency by adding
assemblyShadeRules in assembly := Seq(ShadeRule.rename("shapeless.**" -> "new_shapeless.@1").inAll)
to your assembly.sbt
file.
PureConfig is a free library developed by several people around the world. Contributions are welcomed and encouraged. If you want to contribute, we suggest to have a look at the available issues and to talk with us on the pureconfig gitter channel.
If you'd like to add support for types which are not part the standard Java or Scala libraries, please consider submitting a pull request to create a module. Pull Request #108 created a very simple module. It should provide a good template for the pieces you'll need to add.
The steps to create a new module, called nexttopmod
, are:
- Define a new project in the root
build.sbt
. There are other examples near the top of the file. - Create a new
modules/nexttopmod/
subdirectory. - Add a
modules/nexttopmod/build.sbt
defining the module's name and special dependencies. - Implement converters. Typically they're in a
package object
inmodules/nexttopmod/src/main/scala/pureconfig/module/nexttopmod/package.scala
. - Test the converters. Usually tests would be in
modules/nexttopmod/src/test/scala/pureconfig/module/nexttopmod/NextTopModSuite.scala
. - Optionally explain a little bit about how it works in
modules/nexttopmod/README.md
.
PureConfig supports the Typelevel code of conduct and wants all of its channels (Gitter, GitHub, etc.) to be welcoming environments for everyone.
Mozilla Public License, version 2.0
To the Shapeless and to the Typesafe Config developers.