lucastorri / settler   0.2.0

GitHub

boilerplate-free typed settings generation in Scala

Scala versions: 2.12 2.11

settler

settler uses macros to generate implementations for your settings traits in Scala. It allows you to have typed configurations for your applications.

It is inspired by owner.

Usage

Given a configuration file like this:

{
  str: "hello world",
  dbs: [
    { key: "k1", value: 123 },
    { key: "k2", value: 321 }
  ]
}

and a piece of code like the following:

import com.unstablebuild.settler._

trait AppSettings {
  def str: String
  def dbs: Set[DBSettings]
  def opt: Option[String]
  def optOrElse: String = opt.getOrElse("default")
}
trait DBSettings {
  def key: String
  def value: Int
}

val settings = Settler.settings[AppSettings](ConfigFactory.parseFile(myFile))

settler will generate the implementation for your trait and allow you to do this:

  println(settings.str)                  // hello world

  settings.dbs.foreach(db =>             // k1: 123
    println(s"${db.key}: ${db.value}"))  // k2: 321

  println(settings.opt)                  // None

  println(settings.optOrElse)            // default

It can be used out-of-the-box with Typesafe's Config or Java Properties. Additionally, you can implement your own ConfigProvider and use whatever source you prefer (custom file formats, databases, Redis, HTTP calls, etc).

Environment Variables Provider

trait Aws {
  @Key(name = "AWS_SECRET_ACCESS_KEY")
  def key: String
  def awsSecretAccessKey: String
}

val s = Settler.settings[Aws](ConfigProvider.fromEnv())

println(s.key == s.awsSecretAccessKey)

Alternative Name

Case necessary, you can modify the key used to retrieve a setting by using the @Key annotation as so:

trait AlternativeName {
  @Key(name = "rightName")
  def wrongName: Int
}

Lazy vs Eagerly Loading

Depending how your flag is declared, it might be lazy or eagerly loaded. As an example consider the following example:

trait Settings {
  val eagerlyLoaded: String
  def lazyLoaded: String
}

Custom Types

settler supports most of the common Scala types, like Int, String, ConfigProvider, Duration, MemorySize, plus these same types wrapped on Seq, Set, and/or Option.

Whenever your trait define types that are unknown to the library, it will try to find an implicit implementation of ConfigParser. ConfigParsers allow the functionallity of the library to be expanded and define custom types.

For example:

trait CustomSettings {
  def customType: Class[_]
}

implicit val classParser = new ConfigParser[Class[_]] {
  override def apply(value: AnyRef): Class[_] = 
    Class.forName(value.toString)
}

val settings = Settler.settings[CustomSettings](
	ConfigFactory.parseString("customType = java.io.File"))

settings.customType == classOf[java.io.File]

A few custom parsers are available by importing com.unstablebuild.settler.parser._.

Config vs Settings

Along the documentation and the code you might see the usage of the words Config and Settings. Although they can broadly be considered synonyms, on the scope of this library Config refers to external libraries, like Typesafe's Config, who provide the mechanism of fetching configuration files and so on. On the other hand, Settings are those same configurations loaded and exposed in a way that makes sense to the application domain.

Install

To use it with SBT, add the following to your build.sbt file:

resolvers += Resolver.sonatypeRepo("public")

libraryDependencies += "com.unstablebuild" %% "settler" % "1.0.0"

Release

./sbt +test +macros/test
./sbt +publishSigned +macros/publishSigned
./sbt sonatypeReleaseAll

Code Format

This project uses Scalafmt for code formatting. This is done with the help of neo-sbt-scalafmt SBT plugin:

./sbt sbt:scalafmt scalafmt test:scalafmt macros/scalafmt macros/test:scalafmt