diff --git a/src/main/scala/eri/commons/config/SSConfig.scala b/src/main/scala/eri/commons/config/SSConfig.scala index df160d3..72c34cb 100644 --- a/src/main/scala/eri/commons/config/SSConfig.scala +++ b/src/main/scala/eri/commons/config/SSConfig.scala @@ -15,8 +15,7 @@ package eri.commons.config -import com.typesafe.config.{Config ⇒ TConfig, ConfigException, ConfigFactory} - +import com.typesafe.config.{ConfigException, ConfigFactory, Config ⇒ TConfig} import scala.language.dynamics /** @@ -30,6 +29,10 @@ import scala.language.dynamics * other { * fred = "fun" * } + * dashed-name = 23 + * underscored_name = "happy" + * lowercased = false + * UPPERCASED = true * } * }}} * @@ -49,12 +52,22 @@ import scala.language.dynamics * val n2 = Config2.other.fred.as[String] * }}} * + * If created with lenient set to true dashed (i.e. foo-bar), underscored (i.e. foo_bar), all uppercased (i.e. FOO) and all lowercased config keys can be looked up via camelCased (i.e. fooBar) identifiers: + * {{{ + * object Config3 extends SSConfig(lenient = true) + * val i = Config3.farmer.fred.dashedName.as[Int] + * val s = Config3.farmer.fred.underscoredName.as[String] + * val b1 = Config3.farmer.fred.lowerCased.as[Boolean] + * val b2 = Config3.farmer.fred.upperCased.as[Boolean] + * }}} + * * @param relPath Scoping path specifier * @param relConfig Configuration data + * @param lenient Use lenient lookup (default is false) * @author Simeon H.K. Fitch * @since 3/24/16 */ -class SSConfig(relPath: String = "", relConfig: TConfig = ConfigFactory.load()) extends Dynamic { +class SSConfig(relPath: String = "", relConfig: TConfig = ConfigFactory.load(), lenient: Boolean = false) extends Dynamic { def this(config: TConfig) = this("", config) /** @@ -85,6 +98,27 @@ class SSConfig(relPath: String = "", relConfig: TConfig = ConfigFactory.load()) val next = if (relPath.nonEmpty && relConfig.hasPath(relPath)) relConfig.getConfig(relPath) else relConfig - new SSConfig(name, next) + new SSConfig(aliases(name).find(next.hasPath).getOrElse(name), next) + } + + private[this] def aliases(name: String): Seq[String] = { + if (!lenient) Seq(name, name.toLowerCase(), name.toUpperCase) + else { + val dashed = StringBuilder.newBuilder + val underscored = StringBuilder.newBuilder + var current: Char = 0 + var i: Int = 0 + while (i < name.length) { + current = name.charAt(i) + if (i > 0 && current.isUpper) { + dashed.append('-') + underscored.append('_') + } + dashed.append(current.toLower) + underscored.append(current.toLower) + i += 1 + } + Seq(name, dashed.mkString, underscored.mkString, name.toLowerCase(), name.toUpperCase).distinct + } } } diff --git a/src/test/resources/application.conf b/src/test/resources/application.conf index d736734..1da6e5c 100644 --- a/src/test/resources/application.conf +++ b/src/test/resources/application.conf @@ -106,4 +106,10 @@ akka.actor.creation-timeout = 3s myapp.tempdir = /tmp/foo my.phone = "1-881-555-1212" + + // leniency + nameinlowercase = valueinlowercase + NAMEINUPPERCASE = VALUEINUPPERCASE + name_with_underscore = value_with_underscore + name-with-dash = value-with-dash } \ No newline at end of file diff --git a/src/test/scala/eri/commons/config/SSConfigTest.scala b/src/test/scala/eri/commons/config/SSConfigTest.scala index 2ccafe2..05cd172 100644 --- a/src/test/scala/eri/commons/config/SSConfigTest.scala +++ b/src/test/scala/eri/commons/config/SSConfigTest.scala @@ -138,4 +138,27 @@ class SSConfigTest extends FunSpec { assert(phone.extension === 1212) } } + describe("leniency") { + val conf = new SSConfig(lenient = true) + it("should support lookups converted to all lower or upper case") { + assert(conf.nameinlowercase.as[String] === "valueinlowercase") + assert(conf.NAMEINLOWERCASE.as[String] === "valueinlowercase") + assert(conf.nameInLowerCase.as[String] === "valueinlowercase") + assert(conf.nameinuppercase.as[String] === "VALUEINUPPERCASE") + assert(conf.NAMEINUPPERCASE.as[String] === "VALUEINUPPERCASE") + assert(conf.nameInUpperCase.as[String] === "VALUEINUPPERCASE") + } + it("should support names with underscores") { + assert(conf.name_with_underscore.as[String] === "value_with_underscore") + } + it("should support names with underscores if written as camelCase") { + assert(conf.nameWithUnderscore.as[String] === "value_with_underscore") + } + it("should support names with dashes if written as symbol") { + assert(conf.`name-with-dash`.as[String] === "value-with-dash") + } + it("should support names with dashes if written as camelCase") { + assert(conf.nameWithDash.as[String] === "value-with-dash") + } + } }