Git Product home page Git Product logo

configs's Introduction

configs

Build Status Maven Central Scaladoc

configs is Scala wrapper for Typesafe config.

Usage

Add the following line to your build file:

libraryDependencies += "com.github.kxbmap" %% "configs" % "0.6.1"

Quick Start

import com.typesafe.config.ConfigFactory
import configs.ConfigReader

Result type of get a value from config is configs.Result. If get successfully, returns configs.Result.Success, if not configs.Result.Failure:

val config = ConfigFactory.parseString("foo = 42")
val foo = ConfigReader[Int].read(config, "foo")
// foo: configs.Result[Int] = Success(value = 42)

foo.valueOrElse(0)
// res0: Int = 42

val missing = ConfigReader[Int].read(config, "missing")
// missing: configs.Result[Int] = Failure(
//   error = ConfigError(
//     head = Exceptional(
//       throwable = com.typesafe.config.ConfigException$Missing: String: 1: No configuration setting found for key 'missing',
//       paths = List("missing")
//     ),
//     tail = Vector()
//   )
// )

missing.valueOrElse(0)
// res1: Int = 0

Import configs.syntax._ provides extension methods for Config:

import configs.syntax._
config.get[Int]("foo")
// res2: configs.Result[Int] = Success(value = 42)

get[Option[A]] will return success with value None if path is not exists:

config.get[Option[Int]]("missing")
// res3: configs.Result[Option[Int]] = Success(value = None)

config.getOrElse("missing", 0) // Alias for config.get[Option[Int]]("missing").map(_.getOrElse(0))
// res4: configs.Result[Int] = Success(value = 0)

You can get a case class value out of the box:

import scala.concurrent.duration.FiniteDuration

case class MyConfig(foo: String, bar: Int, baz: List[FiniteDuration])
val config = ConfigFactory.parseString("""
  my-config {
    foo = My config value
    bar = 123456
    baz = [1h, 2m, 3s]
  }
  """)
config.get[MyConfig]("my-config")
// res5: configs.Result[MyConfig] = Success(
//   value = MyConfig(
//     foo = "My config value",
//     bar = 123456,
//     baz = List(1 hour, 2 minutes, 3 seconds)
//   )
// )

If failed, Result accumulates error messages:

val config = ConfigFactory.parseString("""
  my-config {
    bar = 2147483648
    baz = [aaa, bbb, ccc]
  }
  """)
val result = config.get[MyConfig]("my-config")
// result: configs.Result[MyConfig] = Failure(
//   error = ConfigError(
//     head = Exceptional(
//       throwable = com.typesafe.config.ConfigException$Missing: String: 2: No configuration setting found for key 'foo',
//       paths = List("my-config", "foo")
//     ),
//     tail = Vector(
//       Exceptional(
//         throwable = com.typesafe.config.ConfigException$WrongType: String: 2: bar has type out-of-range value 2147483648 rather than int (32-bit integer),
//         paths = List("my-config", "bar")
//       ),
//       Exceptional(
//         throwable = com.typesafe.config.ConfigException$BadValue: String: 4: Invalid value at '0': No number in duration value 'aaa',
//         paths = List("my-config", "baz", "0")
//       ),
//       Exceptional(
//         throwable = com.typesafe.config.ConfigException$BadValue: String: 4: Invalid value at '1': No number in duration value 'bbb',
//         paths = List("my-config", "baz", "1")
//       ),
//       Exceptional(
//         throwable = com.typesafe.config.ConfigException$BadValue: String: 4: Invalid value at '2': No number in duration value 'ccc',
//         paths = List("my-config", "baz", "2")
//       )
//     )
//   )
// )

result.failed.foreach { error =>
  error.messages.foreach(println)
}
// [my-config.foo] String: 2: No configuration setting found for key 'foo'
// [my-config.bar] String: 2: bar has type out-of-range value 2147483648 rather than int (32-bit integer)
// [my-config.baz.0] String: 4: Invalid value at '0': No number in duration value 'aaa'
// [my-config.baz.1] String: 4: Invalid value at '1': No number in duration value 'bbb'
// [my-config.baz.2] String: 4: Invalid value at '2': No number in duration value 'ccc'

You can get a value without key using extract:

val config = ConfigFactory.parseString("""
  foo = My config value
  bar = 123456
  baz = [1h, 2m, 3s]
  """)
config.extract[MyConfig]
// res7: configs.Result[MyConfig] = Success(
//   value = MyConfig(
//     foo = "My config value",
//     bar = 123456,
//     baz = List(1 hour, 2 minutes, 3 seconds)
//   )
// )

You may use the ~ operator to combine multiple results and apply a function with the results passed as arguments, this is useful when you want to construct a complex case class from several config extractors.

case class ServiceConfig(name: String, port: Int, hosts: List[String])

val config = ConfigFactory.parseString(
  """
    |name = "foo"
    |port = 9876
    |hosts = ["localhost", "foo.com"]
  """.stripMargin)
(
  config.get[String]("name") ~
  config.get[Int]("port") ~
  config.get[List[String]]("hosts")
)(ServiceConfig) // Alternatively (name, port, hosts) => ServerConfig(name, port, posts)
// res8: configs.Result[ServiceConfig] = Success(
//   value = ServiceConfig(
//     name = "foo",
//     port = 9876,
//     hosts = List("localhost", "foo.com")
//   )
// )

Supported types

configs can get many type values from config. It is provided by type class ConfigReader.

There are a number of built-in ConfigReader instances:

  • Primitive/Wrapper types
    • Long, Int, Short, Byte, Double, Float, Char, Boolean
    • java.lang.{Long, Integer, Short, Byte, Double, Float, Character, Boolean}
  • Big number types
    • BigInt, BigDecimal
    • java.math.{BigInteger, BigDecimal}
  • String representation types
    • String
    • Symbol, java.util.{UUID, Locale}
    • java.io.File, java.nio.file.Path
    • java.net.{URI, InetAddress}
  • Duration types
    • java.time.Duration
    • scala.concurrent.duration.{Duration, FiniteDuration}
  • Config types
    • com.typesafe.config.{Config, ConfigValue, ConfigList, ConfigObject, ConfigMemorySize}
    • configs.Bytes
  • Enum types
    • Java enum types
    • Scala Enumeration types
  • Collection types
    • F[A] (using CanBuildFrom[Nothing, A, F[A]], e.g. List[String], Seq[Int])
    • M[S, A] (using CanBuildFrom[Nothing, (S, A), M[S, A]], e.g. Map[String, Int], TreeMap[UUID, Config])
    • java.util.{List[A], Map[S, A], Set[A], Collection[A]}, java.lang.Iterable[A]
    • java.util.Properties
  • Optional types
    • Option[A]
    • java.util.{Optional[A], OptionalLong, OptionalInt, OptionalDouble}
  • case classes
  • ADTs (sealed trait + classes/objects). See ADTs support
  • Java Beans. See Java Beans support

In this list, A means any type that is ConfigReader instance. And S means any type that is StringConverter instance.

ADTs support

If there is such an ADT:

sealed trait Tree
case class Branch(value: Int, left: Tree, right: Tree) extends Tree
case object Leaf extends Tree

You can get an ADT value from config:

val config = ConfigFactory.parseString("""
  tree = {
    value = 42
    left = Leaf
    right {
      value = 123
      left = Leaf
      right = Leaf
    }
  }
  """)
config.get[Tree]("tree")
// res9: configs.Result[Tree] = Success(
//   value = Branch(
//     value = 42,
//     left = Leaf,
//     right = Branch(value = 123, left = Leaf, right = Leaf)
//   )
// )

Java Beans support

If there is Java Beans class like the follows:

package com.example;

@lombok.Data
public class MyBean {
    private int intValue;
    private java.util.List<String> stringList;
    private java.util.Map<java.util.Locale, java.time.Duration> localeToDuration;
}

Then you define ConfigReader instance using deriveBean macro:

import com.example.MyBean

implicit val myBeanConfigReader: ConfigReader[MyBean] =
  ConfigReader.deriveBean[MyBean]

And then you can get Java Beans value:

val config = ConfigFactory.parseString("""
  int-value = 42
  string-list = [foo, bar, baz]
  locale-to-duration {
    ja_JP = 42ms
    en_US = 123s
  }
  """)
config.extract[MyBean]
// res11: configs.Result[MyBean] = Success(
//   value = MyBean(intValue=42, stringList=[foo, bar, baz], localeToDuration={en_US=PT2M3S, ja_JP=PT0.042S})
// )

License

Copyright 2013-2016 Tsukasa Kitachi

Apache License, Version 2.0

configs's People

Contributors

kxbmap avatar plm avatar sethtisue avatar xuwei-k avatar zkull avatar zzeekk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

configs's Issues

Using deps of java 8 under java 7

Error:scalac: missing or invalid dependency detected while loading class file 'ConfigsInstances.class'.
Could not access type Duration in value java.time,
because it (or its dependencies) are missing. Check your build definition for
missing or conflicting dependencies. (Re-run with -Ylog-classpath to see the problematic classpath.)
A full rebuild may help if 'ConfigsInstances.class' was compiled against an incompatible version of java.time.

Some[A] has no available apply methods

Compilation fails with error:

Some[A] has no available apply methods

for configuration case classes:

case class TestConfiguration(test2: Option[Test2Configuration])

case class Test2Configuration(testOption: Option[Int])

Compilation succeeds after

  • renaming testOption to testoption
  • or changing testOption type to Int
  • or moving testOption to TestConfiguration.

So this issue happens when you have camel case Option property in second or deeper level case class.

Derived Configs for a sealed trait does not search for other implicits

Implicits are not resolved when automatically deriving Configs for a sealed trait:

case class A1(b: B1)

object A1 {

  implicit val cConfigs: Configs[C1] = {
    (c, _) => c.get[String]("d").map(C1.make)
  }
  implicit val bConfigs: Configs[B1] = Configs.autoDeriveConfigs[B1]
  implicit val aConfigs: Configs[A1] = Configs.autoDeriveConfigs[A1]
}

sealed trait B1

class C1 private(val d: String) extends B1

object C1 {

  def make(d: String): C1 = new C1(d)
}

This fails with:

[error] C1 has no public constructor
[error]   implicit val bConfigs: Configs[B1] = Configs.autoDeriveConfigs[B1]
[error]                                                                 ^

The simple case works:

case class A2(b: B2)

object A2 {

  implicit val aConfigs: Configs[A2] = Configs.autoDeriveConfigs[A2]
}

sealed trait B2

case class C2(d: String) extends B2

We can show that implicits are resolved when automatically deriving Configs for a case class:

case class A3(b: C3)

object A3 {

  implicit val cConfigs: Configs[C3] = {
    (c, _) => c.get[String]("d").map(C3.make)
  }
  implicit val aConfigs: Configs[A3] = Configs.autoDeriveConfigs[A3]
}

class C3 private(val d: String)

object C3 {

  def make(d: String): C3 = new C3(d)
}

Scala 3 support

I have the following line in my code:

private val deployments = Deployments(config.get[List[Deployment]]("deployments").valueOrElse(List.empty))

where Deployment is a case class with some String fields and Deployments is a case class with one field List[Deployment].
Compilation of the code pasted above fails with

[error] 21 |  private val deployments = Deployments(config.get[List[Deployment]]("deployments").valueOrElse(List.empty))
[error]    |                                                                                   ^
[error]    |Reference to method errorJavaListConfigReader in class ConfigReaderInstances0 should not have survived,
[error]    |it should have been processed and eliminated during expansion of an enclosing macro or term erasure.

How to get a Map[String, List[String]]] where Keys have reserved tokens like '.' or '$'

I have this simple config

dtool {
    config {
        partners {
            "nic.at" =  ["\\.at$"]
            
        }
    }
}

When i try to run this code in my app

appConfig.get[Map[String, List[String]]]("dtool.config.partners")

I get an exception

Exception in thread "main" com.typesafe.config.ConfigException$Missing: No configuration setting found for key 'nic'

When i try to double quote ""nic.at"" i get this error

ConfigException$WrongType: : nic has type OBJECT rather than LIST

All i want is to get a Map[String, List[String]]]

I also looked up the typesafe documentation but could not find any answer

Publish Scala 2.10 artifacts?

Maybe it is possible to publish Scala 2.10 artifacts of configs 0.4.4 or any later version? That would be helpful.

Default naming strategy should be Identity

The behavior of kxbmap:configs has changed after 0.4 in that the default naming strategy expects camel-case names to be translated into dash-separated names. For example, case class Data(baseUrl: String = "/base") will silently fail to parse the config such as

baseUrl = "/service/user"

because it expects the name in the config to be base-url. Instead it will assign the default value /base to the config element. This leads to bugs that are not easy to detect.

This breaks code upgraded from 0.3.x . A workaround is to insert code such as

  implicit def configKeyNaming[C]: ConfigKeyNaming[C] = ConfigKeyNaming.identity[C]

However, it is not always easy to guess where to insert this workaround code. It should be inserted just before the config is parsed into a specific case class. But sometimes the code parses configs of different classes at the same time, and it leads to confusion. The reason is that the naming strategy is used for auto-generating the ConfigReader for the given case class. But it is not always clear where this auto-generation takes place in the user code.

I think it would be better if the default naming strategy were identity, and/or if the API were changed so that it is easier to figure out where the implicit naming strategy should be inserted into the code.

Alternatively, a new API should be added to parse the config while giving a specific naming strategy as an implicit argument.

Add getWithOrigin methods

E.g. ConfigOps.getWithOrigin[A](key: String): Result[(A, ConfigOrigin)]. Similarly for getOpt and getOrElse.

Instances for single field value classes

Oftentimes I find myself creating a lot of value classes to wrap primitives for better type safety, e.g.

case class BitOffset(n: Int) extends AnyVal
case class Temperature(celsius: Int) extends AnyVal
// so on and so forth

This, however turns out to be slightly annoying when loading those as config values - the automatic derivation expects the values to be passes as fields in the config and leads to runtime errors like: bit-offset has type NUMBER rather than OBJECT

So far I work this around by making my own instances for those types but I can imagine this could get troublesome and boilerplate-y for applications that use a lot of them:

object ConfigsInstances {

  def valueClassWrapperInstance[C, T](constr: T => C): Configs[C] = Configs.from { (conf, path) =>
    Configs[T].get(conf, path).map(constr)
  }
  implicit val BitOffsetInstance: Configs[BitOffset] = valueClassWrapperInstance(BitOffset)
// imagine more implicits here ;-)

I believe that some sort of optional import for those cases would be a convenient feature.

Probably just confused

Looking to use something like this config file (currently in application.conf):

default {
depth = 7
prune = true
}

red-player {
pType = Human
depth = ${default.depth}
prune = ${default.prune}
}

black-player {
pType = Human
depth = ${default.depth}
prune = ${default.prune}
}

I'm trying to create two instances of this case class (defined in a PlayerSettings.scala file):

sealed trait ScalaPlayerType
case object Human extends ScalaPlayerType
case object Computer extends ScalaPlayerType

case class PlayerSetting(
pType: ScalaPlayerType,
depth: Int,
prune: Boolean
)

To retrieve the settings for red-player and black-player, but I'm running into a fairly obnoxious error..

(this is the code in my main method)

val config = ConfigFactory.load()

val redPlayer = config.get[PlayerSetting]("red-player")
val blackPlayer = config.get[PlayerSetting]("black-player")

println("Red Player: \n" + redPlayer)
println("Black Player \n" + blackPlayer)

I'm not sure if I'm just doing something stupid, or if I really stumbled across a bug. Very new to scala in general, so it could really be something simple.
Error Below:

Exception in thread "main" com.typesafe.config.ConfigException$BadValue: merge of system properties,application.conf @ file:/G:/Software%20Dev%20Stuff/workspace/ScalaConnectFour/target/scala-2.11/classes/application.conf: 1: Invalid value at 'black-player': null
at com.github.kxbmap.configs.Configs$.com$github$kxbmap$configs$Configs$$get$body$2(Configs.scala:77)
at com.github.kxbmap.configs.Configs$$anonfun$2.get(Configs.scala:72)
at com.github.kxbmap.configs.syntax.package$ConfigOps$.get$extension(package.scala:29)
at ConnectFourScala$.main(ConnectFour.scala:19)
at ConnectFourScala.main(ConnectFour.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
Caused by: java.lang.NullPointerException
at ConnectFourScala$$anonfun$self$lzycompute$6$1.a$macro$14$lzycompute$1(ConnectFour.scala:19)
at ConnectFourScala$$anonfun$self$lzycompute$6$1.a$macro$14$1(ConnectFour.scala:19)
at ConnectFourScala$$anonfun$self$lzycompute$6$1.apply(ConnectFour.scala:19)
at ConnectFourScala$$anonfun$self$lzycompute$6$1.apply(ConnectFour.scala:19)
at com.github.kxbmap.configs.package$PipeOps$.$bar$greater$extension(package.scala:25)
at com.github.kxbmap.configs.Configs$$anonfun$onPath$1.apply(Configs.scala:80)
at com.github.kxbmap.configs.Configs$$anonfun$onPath$1.apply(Configs.scala:80)
at com.github.kxbmap.configs.Configs$.com$github$kxbmap$configs$Configs$$get$body$2(Configs.scala:74)
... 9 more

Problem reading lists of non-trivial types

Hi

I have come across an interesting problem when using configs version 0.4.4 and 0.5.0-SNAPSHOT (22ad412) with scala 2.11.

Example:

object MyEnum extends Enumeration {
  type MyEnum = Value

  val Foo = Value("foo")
  val Bar = Value("bar")
}

case class Foo2(enum: Option[MyEnum])

Reading this class from a config works:

val config2_0 = ConfigFactory.parseString("foo = {enum = foo}")
val result2_0 = ConfigReader[Foo2].read(config2_0, "foo")
println(result2_0)

However, reading a List (or Seq) of this class does not compile!

val config2_1 = ConfigFactory.parseString("foo = [{enum = foo}, {enum = bar}]")
val result2_1 = ConfigReader[List[Foo2]].read(config2_1, "foo")
println(result2_1)
Error:(36, 33) diverging implicit expansion for type configs.ConfigReader[java.util.List[Foo2]]
starting with method fromStringConfigReader in class ConfigReaderInstances
    val result2_1 = ConfigReader[List[Foo2]].read(config2_1, "foo")

Error:(36, 33) diverging implicit expansion for type configs.ConfigReader[Option[MyEnum.MyEnum]]
starting with macro method autoDeriveConfigReader in class ConfigReaderInstances3
    val result2_1 = ConfigReader[List[Foo2]].read(config2_1, "foo")

Error:(36, 33) not enough arguments for method apply: (implicit A: configs.ConfigReader[List[Foo2]])configs.ConfigReader[List[Foo2]] in object ConfigReader.
Unspecified value parameter A.
    val result2_1 = ConfigReader[List[Foo2]].read(config2_1, "foo")

The same problem appears in other similar cases. For example:

case class EitherExample(num: Either[Int, String])

with the following reader:

implicit def eitherReader[A, B](implicit aReader: ConfigReader[A], bReader: ConfigReader[B]): ConfigReader[Either[A, B]] = {
  ConfigReader.fromTry { (c, p) =>
    aReader.read(c, p).map(Left(_)).orElse(bReader.read(c, p).map(Right(_))).valueOrThrow(_.configException)
  }
}

Reading this class from a config works:

val config3_0 = ConfigFactory.parseString("{num = two}")
val result3_0 = ConfigReader[EitherExample].extract(config3_0)
println(result3_0)

However, reading a List (or Seq) of this class does not compile!

val config3_1 = ConfigFactory.parseString("foo = [{num = 2}, {num = two}]")
val result3_1 = ConfigReader[List[EitherExample]].read(config3_1, "foo")
print(result3_1)
Error:(44, 33) diverging implicit expansion for type configs.ConfigReader[Either[Int,String]]
starting with method eitherReader in object Bug
    val result3_1 = ConfigReader[List[EitherExample]].read(config3_1, "foo")

Error:(44, 33) diverging implicit expansion for type configs.ConfigReader[java.util.List[EitherExample]]
starting with method fromStringConfigReader in class ConfigReaderInstances
    val result3_1 = ConfigReader[List[EitherExample]].read(config3_1, "foo")

Error:(44, 33) diverging implicit expansion for type configs.ConfigReader[Either[Int,String]]
starting with macro method autoDeriveConfigReader in class ConfigReaderInstances3
    val result3_1 = ConfigReader[List[EitherExample]].read(config3_1, "foo")

Error:(44, 33) not enough arguments for method apply: (implicit A: configs.ConfigReader[List[EitherExample]])configs.ConfigReader[List[EitherExample]] in object ConfigReader.
Unspecified value parameter A.
    val result3_1 = ConfigReader[List[EitherExample]].read(config3_1, "foo")

Note: All examples compile and run with Scala 2.13. However, I do not have the option to upgrade beyond 2.11 in the project I am working on.

Any clues as to what is happening and whether it can be fixed or avoided?

Thank you!

Add support for Map that preserves insertion order

Currently there is just support for Map that doesn't preserve the insertion order of the config items.

config {
    items{
        "key1" =  "Hello"
        "key2" =  "World"
    }
}

When u call this code its not garuanteed that the keys are in the order they have been defined

 println(config.get[Map[String, String]]("config.items"))

Maybe add support for ListMap or TreeMap or any kind of Map which preserves insertion order ?

New release

Hello @kxbmap,

Would it be possible to create a new release including the new feature of detecting superfluous keys?
We would like to create a release for another project where we want to use this feature, but cannot release using snapshot dependencies.

Thanks, Zach

Version 6.0.0 / Scala 2.11 sometimes fails to find default values for case class attributes

Configs version 6.0.0 with scala 2.11 has sometimes problems finding default values of case classes. It works with scala 2.12. I had the following cases:

  • a case class at 2nd level has an attribute "xyz: Option[Boolean] = Some(true)". A config without this attribute is parsed as "xyz = None", not respecting its default value.
  • a case class at 2nd level has an attribute "xyz: Boolean = true". A config without this attribute throws an error that configuration for attribute "xyz" is missing. It seems that Configs doesn't know about its default value.

The strange thing about these errors is that they are not reproducible in unit tests of this project. I guess that it has to do with the numbers of macros that are generated in the project, and that scala optimizes loading of corresponding type metadata.

Compilation failure when creating derived `Configs` for `HikariDataSource` bean

Using version Configs 0.4.4 and Scala 2.11.8, I'm trying to create a derived Configs bean for the com.zaxxer.hikari.HikariDataSource class as follows:

val hikariDataSourceConfigs = Configs.deriveBean[HikariDataSource]

This code produces the following error at compile time:

Some[A] has no available apply methods

I tried the alternate form for deriving a bean where I provide the instance myself:

val hikariDataSourceConfigs = Configs.deriveBeanWith { new HikariDataSource }

SBT-based compilation of this code fails with a different error:

A is not a class

IntelliJ-driven compilation reports this error as well, but additionally reports:

Some cannot be instantiated because it does not conform to its self-type Some[A]

Running the SBT build with remote debugging enabled, I can see that in ConfigsMacro on line 201, the ctx.target is A, so this may explain the reported errors.

The HikariDataSource bean has various setters which take non-primitive types as their argument, such as setThreadFactory(ThreadFactory). I don't have a Configs[ThreadFactory] in scope. If this is related to this failure, is it possible for the BeanConfigsMacro only generate code to invoke setters for which the requisite Configs type is in scope?

Create Java 7 version

The actual library is intended for JDK 1.8, it would be nice to make it compile to Java 7, too.

case class -> Config support

pretty self explanatory, this does everything i could ask for going from Config -> case class, would be awesome if that arrow were bidirectional :)
potential usecase

val baseConfig = ConfigFactory.load("baseConfig").extract[BaseConfig]
val finalConf = baseConfig.copy(baseProp = baseConfig.baseProp + someDymanicStuffMethod(dynamicVar))
val finalConfLocation = "s3://final-confs/me.conf"
uploadFinalConf(finalConfLocation, finalConf.toConfig.root.render)
startupSomeRemoteProcessThatLoadsBaseConf(finalConfLocation)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.