Hi,
I gave knobs a spin and this are my concerns:
- Typesage Config (TSC) allows you to use lists of maps, e.g. [{a = 42, b = "hello"}, {a = 24, b = "world"}]. When I tried to load a
Config
using the Typesafe
extension I got an error since in the CfgValue
AST there's no case for a Map
-like object. Is there a good reason for that? I mean, apart from the TSC integration I think it would be useful being able to define a list of maps in the config file.
- I think the extension for TSC should be changed. At the moment the
Typesafe
object just expose a config
method type which, internally, loads a TSC object using the default behaviour, that is ConfigFactory.load
. Since you can use other methods to load a TSC wouldn't it make sense exposing a method which accepts a TSC object and returns a knobs Config
? Of course keeping also the config
method type which uses the default behaviour.
Config.require[A:Configured]
throws a KeyError
exception if the key is not found and this is reasonable. The problem is it throws that exception even if the key is present but the CfgValue
could have not been converted into A
. I think it should be changed to give it a more reasonable behaviour.
- There's no instance for
Configured[Long]
. Is there a good reason for that?
Once I was there playing with the toy I tried to solve the four issues aforementioned and, apparently, I made it. I updated the docs too to include the lists of maps value type.
Let me know if you may be interested in a PR of all or some of these new features.
P.S.: This is a REPL session of the new features, where objlist.cfg
is a simple config file as in:
objList = [{a = 42, b = 24}, {a = 1, b = 2}]
long = 10000000000
hi = "hello"
REPL session:
scala> import java.io.File
import java.io.File
scala> import knobs._
import knobs._
scala> import knobs.{Required,FileResource,Config}
import knobs.{Required, FileResource, Config}
scala> import scalaz.concurrent.Task
import scalaz.concurrent.Task
scala> val cfg: Task[Config] = knobs.loadImmutable(Required(FileResource(new File("core/src/test/resources/objlist.cfg"))) :: Nil)
cfg: scalaz.concurrent.Task[knobs.Config] = scalaz.concurrent.Task@26b3524c
scala> val config = cfg.run
config: knobs.Config = Config(Map(objList -> CfgList(List(CfgMap(Map(a -> CfgNumber(42.0), b -> CfgNumber(24.0))), CfgMap(Map(a -> CfgNumber(1.0), b -> CfgNumber(2.0))))), long -> CfgNumber(1.0E10), hi -> CfgText(hello)))
scala> config.require[List[Map[String, Int]]]("objList")
res0: List[Map[String,Int]] = List(Map(a -> 42, b -> 24), Map(a -> 1, b -> 2))
scala> config.require[List[CfgValue]]("objList")
res1: List[knobs.CfgValue] = List(CfgMap(Map(a -> CfgNumber(42.0), b -> CfgNumber(24.0))), CfgMap(Map(a -> CfgNumber(1.0), b -> CfgNumber(2.0))))
scala> config.require[CfgValue]("objList")
res2: knobs.CfgValue = CfgList(List(CfgMap(Map(a -> CfgNumber(42.0), b -> CfgNumber(24.0))), CfgMap(Map(a -> CfgNumber(1.0), b -> CfgNumber(2.0)))))
scala> res2.pretty
res3: String = [{a = 42.0, b = 24.0}, {a = 1.0, b = 2.0}]
scala> config.require[Long]("long")
res4: Long = 10000000000
scala> config.require[Int]("missingKey")
knobs.KeyError: No such key: missingKey
at knobs.Config$$anonfun$1.apply(Config.scala:40)
at knobs.Config$$anonfun$1.apply(Config.scala:40)
...
scala> config.require[Int]("hi")
knobs.ConversionError: Could not convert "hello" to int
at knobs.Config$$anonfun$require$1.apply(Config.scala:41)
at knobs.Config$$anonfun$require$1.apply(Config.scala:41)
...