diff --git a/src/main/scala/net/ceedubs/ficus/FicusConfig.scala b/src/main/scala/net/ceedubs/ficus/FicusConfig.scala index 61f9617..bc30ee3 100644 --- a/src/main/scala/net/ceedubs/ficus/FicusConfig.scala +++ b/src/main/scala/net/ceedubs/ficus/FicusConfig.scala @@ -9,6 +9,8 @@ trait FicusConfig { def as[A](path: String)(implicit reader: ValueReader[A]): A = reader.read(config, path) + def as[A](implicit reader: ValueReader[A]): A = as(".") + def getAs[A](path: String)(implicit reader: ValueReader[Option[A]]): Option[A] = reader.read(config, path) def getOrElse[A](path: String, default: => A)(implicit reader: ValueReader[Option[A]]): A = getAs[A](path).getOrElse(default) diff --git a/src/main/scala/net/ceedubs/ficus/readers/ArbitraryTypeReader.scala b/src/main/scala/net/ceedubs/ficus/readers/ArbitraryTypeReader.scala index 2cd5e77..71c7e07 100644 --- a/src/main/scala/net/ceedubs/ficus/readers/ArbitraryTypeReader.scala +++ b/src/main/scala/net/ceedubs/ficus/readers/ArbitraryTypeReader.scala @@ -61,7 +61,7 @@ class ArbitraryTypeReaderMacros(val c: blackbox.Context) extends ReflectionUtils method.paramLists.head.zipWithIndex map { case (param, index) => val name = param.name.decodedName.toString - val key = q"""$path + "." + $mapper.map($name)""" + val key = q"""if ($path == ".") $mapper.map($name) else $path + "." + $mapper.map($name)""" val returnType: Type = param.typeSignatureIn(c.weakTypeOf[T]) companionObjectMaybe.filter(_ => param.asTerm.isParamWithDefault) map { companionObject => diff --git a/src/main/scala/net/ceedubs/ficus/readers/ConfigReader.scala b/src/main/scala/net/ceedubs/ficus/readers/ConfigReader.scala index 0917ae0..8284915 100644 --- a/src/main/scala/net/ceedubs/ficus/readers/ConfigReader.scala +++ b/src/main/scala/net/ceedubs/ficus/readers/ConfigReader.scala @@ -5,7 +5,7 @@ import net.ceedubs.ficus.{SimpleFicusConfig, FicusConfig} trait ConfigReader { implicit val configValueReader: ValueReader[Config] = new ValueReader[Config] { - def read(config: Config, path: String): Config = config.getConfig(path) + def read(config: Config, path: String): Config = if (path == ".") config else config.getConfig(path) } implicit val ficusConfigValueReader: ValueReader[FicusConfig] = configValueReader.map(SimpleFicusConfig) diff --git a/src/test/scala/net/ceedubs/ficus/readers/ArbitraryTypeReaderSpec.scala b/src/test/scala/net/ceedubs/ficus/readers/ArbitraryTypeReaderSpec.scala index 0c2ee3d..b4a12cf 100644 --- a/src/test/scala/net/ceedubs/ficus/readers/ArbitraryTypeReaderSpec.scala +++ b/src/test/scala/net/ceedubs/ficus/readers/ArbitraryTypeReaderSpec.scala @@ -8,6 +8,7 @@ import shapeless.test.illTyped class ArbitraryTypeReaderSpec extends Spec { def is = s2""" An arbitrary type reader should instantiate with a single-param apply method $instantiateSingleParamApply + instantiate with a single-param apply method from config itself $instantiateSingleParamApplyFromSelf instantiate with no apply method but a single constructor with a single param $instantiateSingleParamConstructor instantiate with a multi-param apply method $instantiateMultiParamApply instantiate with no apply method but a single constructor with multiple params $instantiateMultiParamConstructor @@ -36,6 +37,14 @@ class ArbitraryTypeReaderSpec extends Spec { def is = s2""" instance.foo must_== foo2 } + def instantiateSingleParamApplyFromSelf = prop { foo2: String => + import Ficus.stringValueReader + import ArbitraryTypeReader._ + val cfg = ConfigFactory.parseString(s"simple { foo2 = ${foo2.asConfigValue} }") + val instance: WithSimpleCompanionApply = arbitraryTypeValueReader[WithSimpleCompanionApply].read(cfg.getConfig("simple"), ".") + instance.foo must_== foo2 + } + def instantiateSingleParamConstructor = prop { foo: String => import Ficus.stringValueReader import ArbitraryTypeReader._ diff --git a/src/test/scala/net/ceedubs/ficus/readers/CaseClassReadersSpec.scala b/src/test/scala/net/ceedubs/ficus/readers/CaseClassReadersSpec.scala index 17c01c8..f0420a2 100644 --- a/src/test/scala/net/ceedubs/ficus/readers/CaseClassReadersSpec.scala +++ b/src/test/scala/net/ceedubs/ficus/readers/CaseClassReadersSpec.scala @@ -22,6 +22,7 @@ class CaseClassReadersSpec extends Spec { def is = s2""" A case class reader should be able to be used implicitly $useImplicitly hydrate a simple case class $hydrateSimpleCaseClass + hydrate a simple case class from config itself $hydrateSimpleCaseClassFromSelf hydrate a case class with multiple fields $multipleFields use another implicit value reader for a field $withOptionField read a nested case class $withNestedCaseClass @@ -43,6 +44,11 @@ class CaseClassReadersSpec extends Spec { def is = s2""" cfg.as[SimpleCaseClass]("simple") must_== SimpleCaseClass(bool = bool) } + def hydrateSimpleCaseClassFromSelf = prop { bool: Boolean => + val cfg = ConfigFactory.parseString(s"simple { bool = $bool }") + cfg.getConfig("simple").as[SimpleCaseClass] must_== SimpleCaseClass(bool = bool) + } + def multipleFields = prop { (foo: String, long: Long) => val cfg = ConfigFactory.parseString( s""" diff --git a/src/test/scala/net/ceedubs/ficus/readers/ConfigReaderSpec.scala b/src/test/scala/net/ceedubs/ficus/readers/ConfigReaderSpec.scala index 4ea1f04..8eed179 100644 --- a/src/test/scala/net/ceedubs/ficus/readers/ConfigReaderSpec.scala +++ b/src/test/scala/net/ceedubs/ficus/readers/ConfigReaderSpec.scala @@ -10,6 +10,7 @@ class ConfigReaderSpec extends Spec { def is = s2""" implicitly read a config $implicitlyReadConfig read a ficus config $readFicusConfig implicitly read a ficus config $implicitlyReadFicusConfig + implicitly read a ficus config itself $implicitlyReadFicusConfigFromSelf """ def readConfig = prop { i: Int => @@ -51,4 +52,14 @@ class ConfigReaderSpec extends Spec { def is = s2""" """.stripMargin) cfg.as[FicusConfig]("myConfig").as[Int]("myValue") must beEqualTo(i) } + + def implicitlyReadFicusConfigFromSelf = prop { i: Int => + val cfg = ConfigFactory.parseString( + s""" + |myConfig { + | myValue = $i + |} + """.stripMargin) + cfg.getConfig("myConfig").as[FicusConfig].as[Int]("myValue") must beEqualTo(i) + } }