So I am having multiple occurrences of similar snippets in my code:
val optionValue = try {
Some(config.getString(key))
} catch {
case _: Missing => None
}
I want to somehow eliminate this duplicates from my code. I know that typesafe provides a way to give a fallback config file to provide default config values. However, in my case, I am not having any default values for some properties. They are optional properties.
What will be the most optimal way to refactor this code.
This is not how the library works, according https://github.com/lightbend/config#how-to-handle-defaults.
You should use withFallback
method to provide a clean configuration.
Since you are using Scala, and assuming you are ok with using implicits, I would go with the recommended approach of using an enrichment class which allows you to keep your Option
syntax.
Example config.
existent.sample.string="I exist!"
existent.sample.boolean=true
Example enrichment class.
package config
import com.typesafe.config.{Config, ConfigException}
object MyConfig {
implicit class RichConfig(val config: Config) extends AnyVal {
def optionalString(path: String): Option[String] = if (config.hasPath(path)) {
Some(config.getString(path))
} else {
None
}
def optionalBoolean(path: String): Option[Boolean] = if (config.hasPath(path)) {
Some(config.getBoolean(path))
} else {
None
}
// An example of using the exception approach - but less efficient than using hasPath
def optionalString2(key: String): Option[String] = try {
Some(config.getString(key))
} catch {
case _: ConfigException => None
}
}
}
Note that it is better to use hasPath
(over using a Try
) to check if a key exists in your scenario rather than having the JVM create an exception which should be of no interest for optional config. which is allowed not to exist.
Demo.
import com.typesafe.config._
object ConfigTest extends App {
import MyConfig._
val conf = ConfigFactory.load
val optionalString = conf.optionalString("existent.sample.string")
val optionalStringNone = conf.optionalString("non-existent.sample.string")
println(s"String config value: $optionalString")
println(s"Optional (non-existent) String config value: $optionalStringNone")
val optionalBoolean = conf.optionalBoolean("existent.sample.boolean")
val optionalBooleanNone = conf.optionalBoolean("non-existent.sample.boolean")
println(s"Boolean config value: $optionalBoolean")
println(s"Optional (non-existent) String config value: $optionalBooleanNone")
}
Prints.
// String config value: Some(I exist!)
// Optional (non-existent) String config value: None
// Boolean config value: Some(true)
// Optional (non-existent) String config value: None
The docs
From Jacko's response, I would prefer to simplify the code and avoid copy&pasting the hasPath
method for each type.
implicit class RichConfig(val config: Config) extends AnyVal {
private def getOptional[T](path: String, get: String => T): Option[T] = {
if (config.hasPath(path)) {
Some(get(path))
} else {
None
}
}
def optionalString(path: String): Option[String] = getOptional(path, config.getString)
def optionalInt(path: String): Option[Int] = getOptional(path, config.getInt)
def optionalDouble(path: String): Option[Double] = getOptional(path, config.getDouble)
def optionalBoolean(path: String): Option[Boolean] = getOptional(path, config.getBoolean)
}
config.hasPath('sample.property') will tell you if the property exists.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With