Search code examples
scalarefactoringtypesafe

Providing default value on typesafe config getters


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.


Solution

  • 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