The PureConfig library of Scala avoids boilerplate while loading configurations, which has always been an error-prone and monotonous procedure. The common way that we follow is deserializing each field of the configurations, which is again a cumbersome task. The more fields there are, the more code has to be written and tested and maintained. 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.
PureConfig allows us to separate what to load from how it’s being loaded .
Now, let’s look at the typeSafe code and pureConfig code to know the difference between the two-
TypeSafe code:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import com.typesafe.config._ | |
case class Employee(config: Config) { | |
val jobTitle = config.getString("jobTitle") | |
object company { | |
val companyName = config.getString("company.companyName") | |
val place = config.getString("company.place") | |
} | |
} | |
val config = ConfigFactory.parseString( | |
""" | |
|se.typesafe.example { | |
| jobTitle = "Software consultant" | |
| company { | |
| companyName = "Knoldus" | |
| place = "Noida" | |
| } | |
|} | |
""".stripMargin | |
) | |
val employee = Employee(config.getConfig("se.typesafe.example")) | |
employee.jobTitle | |
// res0: String = Software consultant | |
employee.company.companyName | |
// res1: String = Knoldus | |
employee.company.place | |
// res2: String = Noida |
PureConfig code:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import pureconfig.loadConfig | |
case class CompanyEmployee(companyName: String, place: String) | |
case class Employee(jobTitle: String, company: CompanyEmployee) | |
loadConfig[Employee](config, "se.pureconfig.example") | |
// res3: Either[pureconfig.error.ConfigReaderFailures,Employee] = Right(Employee(Software consultant,CompanyEmployee("Knoldus","Noida"))) |
There’s no longer any reference to a Config, no more repetition of names, or tedious work in having to add new fields and defining how they should be loaded. As an added benefit, the configuration has been split into multiple classes, so that different parts of our application can cleanly depend on subsets of our configuration by requiring the appropriate settings type.
We can now simply add a new field with an appropriate type and be sure that PureConfig at compile-time generates what’s necessary to load our configuration. As you can see, loadConfig (res 3) gives us an Either instance back, which implies that it deals with possible errors during loading, which the first version simply ignored.