Giter VIP home page Giter VIP logo

pureconfig's Introduction

PureConfig

Build Status Coverage Status Maven Central Scaladoc Join the chat at https://gitter.im/melrief/pureconfig

PureConfig is a Scala library for loading configuration files. It reads Typesafe Config configurations written in HOCON, Java .properties, or JSON to native Scala classes in a boilerplate-free way. Sealed traits, case classes, collections, optional values, and many other types are all supported out-of-the-box. Users also have many ways to add support for custom types or customize existing ones.


Why

Loading configurations has always been a tedious and error-prone procedure. A common way to do it consists in writing code to deserialize each fields of the configuration. The more fields there are, the more code must be written (and tested and maintained...) and this must be replicated for each project.

This kind of code is boilerplate because most of the times the code can be automatically generated by the compiler based on what must be loaded. For instance, if you are going to load an Int for a field named foo, then probably you want some code that gets the values associated with the key foo in the configuration and assigns it to the proper field after converting it to Int.

The goal of this library is to create at compile-time the boilerplate necessary to load a configuration of a certain type. In other words, you define what to load and PureConfig provides how to load it.

Quick Start

To use PureConfig in an existing SBT project with Scala 2.12 or a later version, add the following dependency to your build.sbt:

libraryDependencies += "com.github.pureconfig" %% "pureconfig" % "0.17.6"

For a full example of build.sbt you can have a look at this build.sbt.

Earlier versions of Scala had bugs which can cause subtle compile-time problems in PureConfig. As a result we recommend only using the latest Scala versions within the minor series.

In your code, import pureconfig.generic.auto and define data types and a case class to hold the configuration:

import pureconfig._
import pureconfig.generic.auto._

case class Port(number: Int) extends AnyVal

sealed trait AuthMethod
case class Login(username: String, password: String) extends AuthMethod
case class Token(token: String) extends AuthMethod
case class PrivateKey(pkFile: java.io.File) extends AuthMethod

case class ServiceConf(
  host: String,
  port: Port,
  useHttps: Boolean,
  authMethods: List[AuthMethod]
)

Second, create an application.conf file and add it as a resource of your application (with SBT, they are usually placed in src/main/resources):

// src/main/resources/application.conf
host = "example.com"
port = 8080
use-https = true
auth-methods = [
  { type = "private-key", pk-file = "/home/user/myauthkey" },
  { type = "login", username = "pureconfig", password = "12345678" }
]

Finally, load the configuration:

ConfigSource.default.load[ServiceConf]
// res4: ConfigReader.Result[ServiceConf] = Right(
//   ServiceConf(
//     "example.com",
//     Port(8080),
//     true,
//     List(PrivateKey(/home/user/myauthkey), Login("pureconfig", "12345678"))
//   )
// )

ConfigReader.Result[ServiceConf] is just an alias for Either[ConfigReaderFailures, ServiceConf], so you can handle it just like you would handle an Either value.

The various loadConfig methods defer to Typesafe Config's ConfigFactory to select where to load the config files from. Typesafe Config has well-documented rules for configuration loading which we'll not repeat. Please see Typesafe Config's documentation for a full telling of the subtleties.

Alternatively, PureConfig also provides a loadConfigFromFiles method that builds a configuration from an explicit list of files. Files earlier in the list have greater precedence than later ones. Each file can include a partial configuration as long as the whole list produces a complete configuration. For an example, see the test of loadConfigFromFiles in ApiSuite.scala.

Because PureConfig uses Typesafe Config to load configurations, it supports reading files in HOCON, JSON, and Java .properties formats. HOCON is a superset of both JSON and .properties that is highly recommended. As an added bonus it supports advanced features like variable substitution and file sourcing.

Documentation

Please see the full PureConfig documentation for more information.

Contribute

PureConfig is a free library developed by several people around the world. Contributions are welcomed and encouraged. If you want to contribute, we suggest to have a look at the available issues and to talk with us on the PureConfig Gitter channel.

If you'd like to add support for types which are not part of the standard Java or Scala libraries, please consider submitting a pull request to create a module. Pull Request #108 created a very simple module. It should provide a good template for the pieces you'll need to add.

The steps to create a new module, called nexttopmod, are:

  1. Define a new project in the root build.sbt. There are other examples near the top of the file;
  2. Create a new modules/nexttopmod/ subdirectory;
  3. Add a modules/nexttopmod/build.sbt defining the module's name and special dependencies;
  4. Implement converters. Typically they're in a package object in modules/nexttopmod/src/main/scala/pureconfig/module/nexttopmod/package.scala;
  5. Test the converters. Usually tests would be in modules/nexttopmod/src/test/scala/pureconfig/module/nexttopmod/NextTopModSuite.scala;
  6. Optionally explain a little bit about how it works in modules/nexttopmod/README.md.

PureConfig supports the Typelevel code of conduct and wants all of its channels (Gitter, GitHub, etc.) to be welcoming environments for everyone.

License

Mozilla Public License, version 2.0

Special Thanks

To the Shapeless and to the Typesafe Config developers.

pureconfig's People

Contributors

ajaychandran avatar alonsodomin avatar anshulbajpai avatar bardurdam avatar beatriz avatar bszwej avatar chernikovp avatar cranst0n avatar derekmorr avatar dvirf avatar jcazevedo avatar jd557 avatar keirlawson avatar kubukoz avatar lefou avatar leifwickland avatar melrief avatar miguelpuyol avatar moradology avatar n4to4 avatar neko-kai avatar nigredo-tori avatar queimadus avatar ruippeixotog avatar scala-steward avatar sh0hei avatar stefanobaghino avatar stsatlantis avatar yangzai avatar yuvalitzchakov 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pureconfig's Issues

Allow configuring the word delimiters to be used in implicitly derived ConfigConverts

It would be nice if pureconfig would allow customizing the word delimiters for the ConfigConverts it derives implicitly.

This would allow, for example, to have a class with fields defined in camel case:

case class Conf(standardTimeout: Duration, numberOfNodes: Int)

and the corresponding config using hyphen-separated strings:

{ 
  standard-timeout = 5s
  number-of-nodes = 10
}

I have a working implementation of this in this branch, but it depends on #28.

I approached it by requiring an implicit in scope for deriving products that defines how words are delimited. There's a default implicit available that doesn't do anything to the provided fields, but users can provide a different (with higher priority) one to customize the behavior.

For example, if we have the following class:

case class Conf(standardTimeout: Duration, numberOfNodes: Int)
val conf = Conf(5.seconds, 10)

The default behaviour would be:

println(conf.toConfig)
// SimpleConfigObject({"numberOfNodes":10,"standardTimeout":"5s"})

But declaring a different way to delimit words would yield:

implicit val delim = new WordDelimiterConverter[Conf](CamelCaseWordDelimiter, HyphenWordDelimiter)
println(conf.toConfig)
// SimpleConfigObject({"number-of-nodes":10,"standard-timeout":"5s"})

No valid coproduct type choice found for configuration null for valid configurations since 0.3.3

I still have to investigate this but apparently since 0.3.3 we have a regression in which the coproduct instance receives a null in input. I think the cause is the default field mapping. More info once I can create a small example of this bug.

EDIT: here an example:

scala> sealed abstract class TraceConfig
scala> final case class Trace(tracerHostname: String) extends TraceConfig
scala> final case object DoNotTrace extends TraceConfig
scala> final case class Conf(trace: TraceConfig)
scala> pureconfig.loadConfig[Conf](ConfigFactory.parseString("""{  }""")).get
pureconfig.ConfigConvert$NoValidCoproductChoiceFound: No valid coproduct type choice found for configuration null.
  at pureconfig.ConfigConvert$$anon$5.from(ConfigConvert.scala:160)
  at pureconfig.ConfigConvert$$anon$6.$anonfun$from$2(ConfigConvert.scala:173)
  at scala.util.Failure.orElse(Try.scala:220)
  at pureconfig.ConfigConvert$$anon$6.from(ConfigConvert.scala:173)
  at pureconfig.ConfigConvert$$anon$6.$anonfun$from$2(ConfigConvert.scala:173)
  at scala.util.Failure.orElse(Try.scala:220)
  at pureconfig.ConfigConvert$$anon$6.from(ConfigConvert.scala:173)
  at pureconfig.ConfigConvert$$anon$10.from(ConfigConvert.scala:276)
  at pureconfig.ConfigConvert$$anon$2.fromConfigObject(ConfigConvert.scala:126)
  at pureconfig.ConfigConvert$$anon$2.fromConfigObject(ConfigConvert.scala:120)
  at pureconfig.ConfigConvert$WrappedDefaultValueConfigConvert.fromWithDefault(ConfigConvert.scala:93)
  at pureconfig.ConfigConvert$WrappedDefaultValueConfigConvert.fromWithDefault$(ConfigConvert.scala:91)
  at pureconfig.ConfigConvert$$anon$2.fromWithDefault(ConfigConvert.scala:120)
  at pureconfig.ConfigConvert$$anon$9.from(ConfigConvert.scala:263)
  at pureconfig.package$.loadConfig(package.scala:75)
  ... 37 elided

Note that it's not the coproduct derivation the issue:

scala> pureconfig.loadConfig[TraceConfig](ConfigFactory.parseString("""{  }""")).get
res3: TraceConfig = DoNotTrace

EDIT2: I think the issue is ConfigObject.get and how we use it in line 126: if the key is not found, get returns null and this propagates to the ConfigConvert responsible for the conversion.

EDIT3: the difference between loading directly a coproduct with loadConf[TraceConfig] and use a coproduct as field of a class is that in the first case we pass .root() to the ConfigConvert[TraceConfig], which is a SimpleConfigObject({}) while in the second case we pass null because of .get()

string parameter for start of config path

I don't always want to model the entire config, just some subtree of it (typically my app's bits), can you please add a String parameter like ficus to set the root?

Add the line to the error

As a user pointed out in our gitter, it is possible to write some information together with an error about where the error happens. Typesafe config has ConfigValue::origin which is supposed to be used exactly for this.

Support HOCON arrays

Using a config such as:

myconfig.mylist: [1, 2, 3]

and a case class such as:

case class myconfig(mylist: List[Int])

appears to be unsupported.

Instead, I have to use a syntax such as:

myconfig.mylist: "1,2,3"

could not find implicit value for parameter conv

I'm using pureconfig with Scala 2.10.6, when running sbt compile everything works fine, but my Eclipse keep showing an error on the following line:

loadConfig[Conf.MIH](typesafeConfigToConfig(typesafeConf), "mih").get

The error is

could not find implicit value for parameter conv: pureconfig.ConfigConvert[mih.Conf.MIH]

My definition of MIH is

object Conf {
  case class MIH(s3: S3, redis: Redis)
  case class S3(workspaceBucket: String, workspacePath: String)
  case class Redis(url: String)
}

Add a changelog

It's always good to have a log of what changed between releases. The commit tree sometimes works, but particularly for external users it makes sense to have a compact list enumerating what can users expect after an upgrade - which bugs were fixed, what improvements were made and what are the breaking changes. This last one is particularly important; if there is not a way to make things backwards compatible, it makes sense for users to have a place where they at least know what changed.

Although they are not libraries, I take as good examples the changelogs for terraform and marathon. They are well organized and easily searchable. Based on those, I adopted a much more modest version for one of my libraries.

What do you think of adding such a document? Adding it does not mean we shouldn't try to avoid breaking APIs, but it would give more information to users in case we decide to do it.

(@melrief @jcazevedo @leifwickland I'm opening these issues to get your general opinion, if you think it's a good idea I don't mind writing it :))

Example project doesn't compile

The sample code in example/ doesn't compile:

~/pureconfig/example % sbt clean compile       
// snip
[info] Compiling 2 Scala sources to /home/derek/pureconfig/example/target/scala-2.11/classes...
[error] /home/derek/pureconfig/example/src/main/scala/pureconfig/example/conf/package.scala:40: type mismatch;
[error]  found   : java.nio.file.Path
[error]  required: scala.util.Try[java.nio.file.Path]
[error]  Note: implicit value deriveStringConvertForPath is not applicable here because it comes after the application point and it lacks an explicit result type
[error]   implicit val deriveStringConvertForPath = fromString[Path](Paths.get(_))
[error]                                                                       ^
[error] one error found
[error] (compile:compileIncremental) Compilation failed
[error] Total time: 5 s, completed Feb 12, 2017 5:41:41 PM

The sample converter for java.nio.file.Path doesn't match the version in core/docs/example.md:

implicit val pathConvert = ConfigConvert.fromString[Path](s => Try(Paths.get(s)))

Updating package.scala to use this version fixes the bug.

We're discussing changing the example code in #123 so I'll hold off sending a PR for this issue. Longer term, we should change .travis.yml to check that the example project builds as part of CI.

Add number of bytes class and ConfigConvert

Typesafe config supports getBytes to read the number of bytes from a human friendly string such as 10MB. The spec for this format is here. It would be nice to add this to pureconfig. The way I think we should do this is slightly different from what typesafe config does: I would add a class Bytes and create a ConfigConvert for it. The obvious issue with this is that it would be different from what you have in Typesafe config.

Support loading Path

The library should support loading java.nio.file.Path by parsing Strings from config.

Move from Try[A] to Either[ConfigReadFailure, A]

I'll use Failure instead of Error because Java uses Error to defined a subtype of Throwables. The word Error can be confusing. Failure is good because it would be used when a ConfigConverter fails to convert a configuration. It's not an error.

Add support for java.time data structures

Add ConfigConvert instances for LocalDate, DateTime...

Most of them needs to be somehow configurable because the string format of a date is not fixed. We could let the user import a template of the instance and the configure it:

implicit val dateTimeInstance = makeDateTimeInstance(format)

Cant load or save collection of objects

Hi,
PureConfig is brilliant, but I'm having trouble loading or saving Lists or Maps of objects.
I don't think I'm doing anything wrong.

Here's a failing test case:

  it should "save a list of objects properly" in {
    case class DeepConfig(list:List[Wibble])
    case class Wibble(x:String,y:String)
    withTempFile { (configFile: Path) =>
      val w1=Wibble("X","y")
      val w2=Wibble("hello","goodbye")
      val expectedConfig = DeepConfig(List(w1,w2))
      saveConfigAsPropertyFile(expectedConfig, configFile, overrideOutputPath = true)
      // println(scala.io.Source.fromFile(configFile.toFile).mkString)
      val config = loadConfig[DeepConfig](configFile)

      config should be(Success(expectedConfig))
    }
  }

the output file looks like this :

list.tl$1.tl$1=""
list.tl$1.head.x="hello"
list.head.y="y"
list.tl$1.head.y="goodbye"
list.head.x="X"

"0" is not a valid FiniteDuration anymore

On pureconfig 0.3.x, "0" was accepted as a valid duration. It's the only case where the time unit does not matter, and Akka frequently uses that value in their configs. However, in pureconfig 0.4.0 "0" throws a parsing error.

Can you add again that special case? As a motivating use case, I frequently use values from akka-actor in my refefence.conf as defaults for my own domain configs.

ConfigException when a value is a LIST

Using the getString methog to get the string value from a LIST type throws exception:

Caused by: com.typesafe.config.ConfigException$WrongType: reference.conf: 515: akka.loggers has type LIST rather than STRING
    at com.typesafe.config.impl.SimpleConfig.findKey(SimpleConfig.java:133)
    at com.typesafe.config.impl.SimpleConfig.find(SimpleConfig.java:145)
    at com.typesafe.config.impl.SimpleConfig.find(SimpleConfig.java:151)
    at com.typesafe.config.impl.SimpleConfig.find(SimpleConfig.java:159)
    at com.typesafe.config.impl.SimpleConfig.find(SimpleConfig.java:164)
    at com.typesafe.config.impl.SimpleConfig.getString(SimpleConfig.java:206)
    at pureconfig.conf.package$$anonfun$typesafeConfigToConfig$1.apply(package.scala:32)
    at pureconfig.conf.package$$anonfun$typesafeConfigToConfig$1.apply(package.scala
    at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
    at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
    at scala.collection.Iterator$class.foreach(Iterator.scala:727)
    at scala.collection.AbstractIterator.foreach(Iterator.scala:1157)
    at scala.collection.IterableLike$class.foreach(IterableLike.scala:72)
    at scala.collection.AbstractIterable.foreach(Iterable.scala:54)
    at scala.collection.TraversableLike$class.map(TraversableLike.scala:244)
    at scala.collection.mutable.AbstractSet.scala$collection$SetLike$$super$map(Set.scala:45)
    at scala.collection.SetLike$class.map(SetLike.scala:93)
    at scala.collection.mutable.AbstractSet.map(Set.scala:45)
    at pureconfig.conf.package$.typesafeConfigToConfig(package.scala:30)
    at pureconfig.package$.loadConfig(package.scala:25)

Proposed solution is to use the method to get the raw value but this can break the tests.

Abstract from the typesafe config backend

pureconfig started as layer on top of the typesafe config library. The idea was to keep intact the parsing of files and the resolution rules while adding a generic derivation system that converts a ConfigValue to a value of an arbitrary type. The initial idea was to not tie the library to typesafe config and in future releases to allow to plug in other backends. I would like to start a discussion about this and eventually implement this abstraction in our codebase. My thoughts about this:

  1. abstraction: right now the structure that represents a raw configuration is ConfigValue. ConfigValue represents JSON-like values with support for boolean, list, null, number, object and string. If we decide to abstract from the backend, using typesafe structures makes no sense because we don't want to have typesafe config as dependency when it's not required. What I propose it to create our own representation of a JSON-like value and keep it as small and minimalistic as possible. For each backend, a new adapter from the backend specific structure to our representation should be coded and that's the only required piece of code to make the new backend work with pureconfig. I am open to use an external library to represent a configuration if there is a good reason for it.

  2. loading: there are multiple ways to load a backend implementation at runtime. In Java it is common to set the implementor class somehow, e.g. via configuration files, and then use the class loader to load the specified class. This style allows to create a new backend without recompiling pureconfig but has the side-effect that there are no checks at compile time. I personally don't like this style even if I understand the great flexibility it provides. I like to have my compiler to link all the pieces together before I use the library. Consequently I propose to make the core of pureconfig abstract from the implementation and then have one subproject for each backend. If a user wants to implement a new backend, he/she just have to add a new subproject with minimum effort. Each backend would have its own artifact on maven.

Let me know what you think about these two points and if you want to add other points to the discussion. I would prefer to have a clear idea on which everybody agree before we implement it.

Clarifying nested object conversion

I'm trying to understand how I can achieve the following:

sealed trait TC
case class ITC(count:Int, work:TC) extends TC
case class SSTC(query:String, args:List[AnyRef]) extends TC

case class Global(work:List[TC], runtime:Option[FiniteDuration)

I can't seem to understand the process that would be required to achieve something like this. I get the exception listed in the documentation (i.e could not find implicit value for parameter conv: pureconfig.ConfigConvert[Global]) but I can't for the life of me figure out what's causing it or what steps I can use to fix it.

I've tried to create a custom converter, but I'm not sure how to leverage pureconfig to load the types it already knows about.

Ambiguous implicit values if defining StringConvert

If you define a StringConvert for a class which can automatically have a ConfigConvert generated you get an ambiguous implicit values error. This is valuable if you're defining simple "value classes" that wrap String or Int.

For instance adding this test to PureConfSuite will demonstrate the error:

  case class ConfWithFoo(foo: Foo)

  it should "be able to use a local StringConvert without getting an ImplicitResolutionFailure error" in {
    implicit val custom: StringConvert[Foo] = StringConvert.fromUnsafe(s => Foo(s.toInt), _.i.toString)
    saveAndLoadIsIdentity(ConfWithFoo(Foo(100)))
  }

The reported error is:

[error] /home/cfrederickson/src/github/pureconfig/src/test/scala/pureconfig/PureconfSuite.scala:238: ambiguous implicit values:
[error]  both method primitiveFieldConvert in object FieldConvert of type [T](implicit strConvert: shapeless.Lazy[pureconfig.StringConvert[T]])pureconfig.FieldConvert[T]
[error]  and method configFieldConvert in object FieldConvert of type [T](implicit confConvert: shapeless.Lazy[pureconfig.ConfigConvert[T]])pureconfig.FieldConvert[T]
[error]  match expected type pureconfig.FieldConvert[PureconfSuite.this.Foo]
[error]   it should "be able to use a local StringConvert without getting an ImplicitResolutionFailure error" in {

We can fix this by giving the StringConvert a higher implicit priority.

Default ConfigFieldMapping should be (CamelCase, KebabCase)

The current default mapping between config keys and case class fields is camel case to camel case. However, in Typesafe config the de facto standard is to use dashes between words (see examples in Typesafe config, akka, spray, kamon and many more). As such, it seems more natural to me for the default to be ConfigFieldMapping[T](CamelCase, KebabCase).

What is your opinion on this? Is there a reason for the current default? If you agree with that change I can do a quick pull request.

Generate README and other documentation via `tut` to ensure example code compiles

I propose that we migrate our README and any other applicable documentation to being generated by tut. I recently worked on another project which made the transition and it was overwhelmingly positive. (See rubicon-project/rubiz@d16b499)

The major benefits of tut are:

  • The compiler ensures that examples compile and
  • The runtime ensures that sample outputs are correct, rather than being copy-pasted into a comment.

The downsides of using tut are:

  • Contributors need to remember to edit src/main/tut/README, etc, instead of the normal file they'd expect.
  • Separating the PR for a big change to the code from the PR to update the documentation is no long possible.

If you guys are on board with that idea I'll take a swing at migrating the README, when the docs in a quiescence period between PRs.

Support for case class default values?

Does PureConfig support default arguments to case classes?

In other words, given the example from the README, does

> import pureconfig.loadConfig
> import com.typesafe.config.ConfigFactory.parseString
> sealed trait MyAdt
> case class AdtA(a: String) extends MyAdt
> case class AdtB(b: Int) extends MyAdt
> case class MyClass(int: Int = 13, adt: MyAdt, list: List[Double], map: Map[String, String], option: Option[String])
> val conf = parseString("""{ "adt": { "type": "adtb", "b": 1 }, "list":["1", "20%"], "map": { "key": "value" } }""")

return a MyClass instance with int set to 13?

Support for enumeratum

I'm using Enumeratum with pureconfig, and I wrote a ConfigConvert instance for enumeratum enums. Would there be interest in including it in pureconfig?

I couldn't figure out the correct place to put the implementation if that was the case, but this is the code:

import enumeratum.{Enum, EnumEntry}
import pureconfig.ConfigConvert

import scala.util.Try

trait EnumeratumConfigConvert {
  implicit def enumeratumConfigConvert[A <: EnumEntry](implicit enum: Enum[A]): ConfigConvert[A] =
    ConfigConvert.stringConvert[A](name => Try(enum.withName(name)), _.toString)
}

Support Java 7 (and use typesafe config 1.2)

Is there a specific reason to not support Java 7?

It seems, that building and testing with Oracle JDK 7 against com.typesafe.config-1.2.1 (which does not require Java 8) works.

Support for Map

Add support of Map. Here it is an example:

Config:

params {
  a = 1
  b = 2
  c = 3
}

should maps into

case class Root(params: Map[String, Int])

Prefer Scala's Duration string conversion to Typesafe Config's implementation.

I tried using pureconfig in a Spark 1.x app and discovered that I couldn't parse a FiniteDuration. Spark 1.x includes Typesafe Config 1.2.1 , which doesn't include the Config.getDuration method. (Pureconfig uses 1.3.1.) At runtime I get an error about that method not existing. Of course the Spark version of typesafe supersedes whatever I ship.

The specific error message is:

Exception in thread "main" java.lang.NoSuchMethodError: com.typesafe.config.Config.getDuration(Ljava/lang/String;)Ljava/time/Duration;
        at pureconfig.DurationConvert$$anonfun$from$2.apply(DurationConvert.scala:20)
...

Well, in the process of working around that (which kudos to everybody here that I can easily enough work around that) I realized that Scala's Duration includes a def apply(String): Duration that is (almost) a superset of SimpleConfig.parseDuration.

See
https://github.com/typesafehub/config/blob/master/config/src/main/java/com/typesafe/config/impl/SimpleConfig.java#L622
vs
https://github.com/scala/scala/blob/v2.11.8/src/library/scala/concurrent/duration/Duration.scala#L78

The "almost" is that for abbreviating microseconds Scala used a proper mu while typesafe config used a u, i.e. "µs" vs "us".

When I originally implemented pureconfig's converter for Duration I didn't realize that Scala had its own implementation of something similar. I was shooting for compatibility with Typesafe config.

Pureconfig's Duration converter is one of its most complex. It also includes the "config to string" converter I wrote, which Scala's duration includes.

Proposal:

  • Use Scala's Duration.apply instead of Config.getDuration
  • Maintain compatibility with typesafe config by translating "us" to "µs".
  • Discard most of pureconfig's duration converter. It could be replaced by something like ConfigConvert.fromNonEmptyString(Duration.apply(_)). (With the addition of the unit converter mentioned above.)

@melrief @jcazevedo, if you guys are marginally supportive, I'll send a PR for you to consider further.

An interesting side effect of this change is that it'd help #5, support for Java 7. Pureconfig could offer converters for Duration and FiniteDuration without typesafe config 1.3.x.

Fields with type Option[ComplexType] are always converted to None

Fields with type Option[ComplexType] always seem to be converted to None.

Consider the below example. My intention is to configure the authentication needed to access a certain service. The service may or may not be configured to require authentication, which is a pair of username and password. Thus, my model is as follows.

Consider the following .conf file:

credentials {
  username = "admin"
  password = "secret"
}

And consider the following code:

import pureconfig._

case class Credentials(username: String, password: String)
case class Config(credentials: Option[Credentials])

object Main extends App {
    val config = loadConfig[Config].get
    println("Config: " + config)
}

When run, it wrongly prints:

Config: Config(None)

However, if I make the credentials mandatory, and thus I change the declaration of the credentials fields of Config class and remove the Option wrapper:

case class Config(credentials: Credentials)

Then, the code correctly prints:

Config: Config(Credentials(admin,secret))

If the credentials section is missing altogether from the .conf file, then, the conversion correctly uses None. Also, if the type parameter of Option is a primitive type (number or String), then conversion seems to properly work. Only the conversion of Options with complex types seems to be wrong, and the behavior seems to be always converting to None.

Support for Scala 2.10 is not correctly documented

By looking at pureconfig's releases for Scala 2.10 on maven central, it seems that 0.1.9 was the last release for Scala 2.10. In contrast, for Scala 2.11, the versions 0.2.0 and 0.2.1 were also released.

However, pureconfig's main documentation gives the impression that the latest 0.2.1 release is also supported for 2.11, and, even has an non-working example:

libraryDependencies ++= Seq(
  "com.github.melrief" %% "pureconfig" % "0.2.2",
compilerPlugin("org.scalamacros" % "paradise" % "2.0.1" cross CrossVersion.full)
)

This made me wonder for about a minute on why can't my dependencies be resolved (I am bound to Scala 2.11 because of a dependency to the Spark 1.6 line).

Either the documentation needs to be fixed, or the 0.2.x line needs to be released for Scala 2.10.

parse UUID

Pureconfig should parse UUIDs from config as a java.util.UUID.

Ensuring invertible mapping in ConfigConvert[]

There are cases such as #13 where configuration values do not does not map back and there's no compile-time error.

In order to test the mappability of fields in tests it is possible to do the following:

private def testMappability[T](from: T, namespace: String)(implicit convert: ConfigConvert[T]): Unit = {
    val rawPure: RawConfig = convert.to(from, namespace)
    val typesafeConfig: Config = {
      // there's no nice way to convert to a Typesafe .conf using the .render() method as the rawPure is a Map
      val propsConfig = rawPure.map { case (k, v) => s"$k=$v" }.mkString("\n")
      ConfigFactory.parseString(propsConfig, ConfigParseOptions.defaults().setSyntax(ConfigSyntax.PROPERTIES))
    }
    val returnedRawPure: RawConfig = pureconfig.conf.typesafeConfigToConfig(typesafeConfig)
    returnedRawPure shouldEqual rawPure
  }

However I would think there should be a compile-time error where possible.
Best example for this actually is play-json with its macro based derivations.

Pureconfig errors are misleading in the face of ConfigConverts missing.

Today if you have nested configs like the following

case class Foo(bar: Bar)
case class Bar(nel: NonEmptyList[String])

When you try and load a config for Foo you get an error like this:
could not find implicit value for parameter conv: pureconfig.ConfigConvert[Foo] when in reality, it's Bar that's the problem.

Do you have any ideas about how we could improve the error? One thought I had was to add a "semi automatic" derivation mode where we could create a ConfigConvert on the companion object of each of the nested objects.

Like this:

case class Bar(nel: NonEmptyList[String])

object Bar {
  implicit val configConvert = pureconfig.derive[Bar]
}

case class Foo(bar:Bar)

object Foo{
  implicit val configConvert = pureconfig.derive[Foo]
}

Obviously this changes the way that pureconfig works and we'd have to have 2 different imports, one for "full auto" and one for "semi auto" derivation.

@melrief, Would you be interested in a change like this? Do you see another way around the terrible error messages?

Thanks!

Add modules section to README

Now that we are starting to have some modules for pureconfig, it would be nice to add to the main page all the supported libraries together with the name of the dependency to add to sbt.

`ConfigFieldMapping` and `ProductHint` concepts

The current naming conventions currently only apply (and are only relevant) to products. In my view, a ConfigFieldMapping is a just a special case of a ProductHint - a way for developers to "advise" pureconfig on how to derive converters for certain kinds of products.

In the derivation code a ConfigFieldMapping is already handled just like a CoproductHint and a first version of ProductHint could start to have an interface just equal to the current ConfigFieldMapping. However, the more general concept would allow us to add in the future new ways to customize how products are converted. One example that comes to my mind is customizing how Option fields should be handled (currently they are a special case).

By separating the concept of ConfigFieldMapping from a ProductHint, we could start to use ConfigFieldMapping for both products and coproducts. The current FieldCoproductHint could take an instance of ConfigFieldMapping instead of having a fieldValue function, for example (that is actually something that we can start doing now, but the semantics would be a bit muddier IMO).

What do you think of this change?

Support Javascript

We are using all scala libraries that support ScalaJS and pureconfig code should be compatible with ScalaJS so it's natural to create and publish the library also for ScalaJS.

2.10

can you please release for scala 2.10?

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.