Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 333
vatsal mevada Avatar asked Sep 03 '18 06:09

vatsal mevada


4 Answers

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.

like image 71
Thomas Decaux Avatar answered Oct 18 '22 06:10

Thomas Decaux


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

like image 28
jacks Avatar answered Oct 18 '22 06:10

jacks


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)

  }
like image 26
Franzi Avatar answered Oct 18 '22 05:10

Franzi


config.hasPath('sample.property') will tell you if the property exists.

like image 1
AlexanderAdam Avatar answered Oct 18 '22 07:10

AlexanderAdam