Extruder

Typelevel Incubator CircleCI codecov

This library uses Shapeless, Cats and Cats Effect to provide a neat syntax to instantiate Scala case classes from a data source.

Quickstart Guide

Follow the instructions below or try it out in Scastie!

Add the following to your build.sbt:

libraryDependencies += "io.extruder" %% "extruder" % "0.11.0"

// only if you require support for cats-effect instances
libraryDependencies += "io.extruder" %% "extruder-cats-effect" % "0.11.0"

// only if you require support for Typesafe config
libraryDependencies += "io.extruder" %% "extruder-typesafe" % "0.11.0"

// only if you require support for Circe types
libraryDependencies += "io.extruder" %% "extruder-circe" % "0.11.0"

// only if you require support for Circe YAML
libraryDependencies += "io.extruder" %% "extruder-circe-yaml" % "0.11.0"

// only if you require support for refined types
libraryDependencies += "io.extruder" %% "extruder-refined" % "0.11.0"

// only if you require support for AWS config
libraryDependencies += "io.extruder" %% "extruder-aws" % "0.11.0"

// only if you require support for prometheus encoders
libraryDependencies += "io.extruder" %% "extruder-metrics-prometheus" % "0.11.0"

// only if you require support for dropwizard encoders
libraryDependencies += "io.extruder" %% "extruder-metrics-dropwizard" % "0.11.0"

// only if you require support for spectator encoders
libraryDependencies += "io.extruder" %% "extruder-metrics-spectator" % "0.11.0"

Rules for resolution are specified in the declaration of the case class itself:

In ApplicationConfig below default will be set to 100, noDefault will cause a validation failure to be logged and optional will be set to None, should the data source not contain a value for each parameter.

See the page on decoding and encoding for more information on the different types of decode and encode methods.

import cats.data.EitherT
import cats.effect.IO
import extruder.core._
import extruder.data._
import extruder.map._

case class ApplicationConfig(default: Int = 100, noDefault: String, optional: Option[Double])

val config: Map[String, String] = Map("applicationconfig.nodefault" -> "extruder")
val applicationConfig: ApplicationConfig = ApplicationConfig(noDefault = "extruder", optional = None)

// Create a type alias for EitherT monad transformer
type EitherTIO[A] = EitherT[IO, ValidationErrors, A]

// Decode from configuration into different target monads
val decoded: Either[ValidationErrors, ApplicationConfig] = decode[ApplicationConfig](config)
val decodedIO: EitherTIO[ApplicationConfig] = decodeF[EitherTIO, ApplicationConfig](config)

// Encode to configuration into different target monads
val encoded: Map[String, String] = encode(applicationConfig)
val encodedIO: EitherTIO[Map[String, String]] = encodeF[EitherTIO](applicationConfig)

It is also possible to print parameters as a table, with keys formatted as they would be in the source data:

val params: String = parameters[ApplicationConfig]
println(params)

Which outputs the following:

+-----------------------------+----------+--------+---------+------------------+
| Key                         | Required | Type   | Default | Permitted Values |
+-----------------------------+----------+--------+---------+------------------+
| applicationconfig.default   | N        | Int    | 100     |                  |
| applicationconfig.nodefault | Y        | String |         |                  |
| applicationconfig.optional  | N        | Double |         |                  |
+-----------------------------+----------+--------+---------+------------------+

Motivation

To learn more about shapeless having read Dave Gurnell’s excellent introduction: “The Type Astronaut’s Guide to Shapeless”.

Try out Grafter. This project complements applications which use Grafter or other dependency injection frameworks and techniques by providing a way of resolving values in case classes from a data source.

Specifically Grafter requires that all configuration be part single case class to be passed to the entry point of the application. Structuring the config in classes like this works well, but leaves the question of how are these classes populated with config?

This is where Extruder comes in, you can load your configuration from a data source and use it with Grafter for dependency injection.

Modules

Module Description Download
Extruder Main module, includes core functionality and basic resolvers. Download
Cats Effect Provides Cats Effect typeclass implementations. Download
Typesafe Config Support for resolution from Typesafe Config. Download
Circe Bridge to Circe encoding/decoding. Download
Circe YAML Bridge to Circe YAML encoding/decoding. Download
Refined Support for Refined types. Download
AWS Support for AWS types. Download
Prometheus Support for encoding data as Prometheus metrics. Download
Dropwizard Support for encoding data as Dropwizard metrics. Download
Spectator Support for encoding data as Spectator metrics. Download

Supported Functionality

Similar Projects

PureConfig

PureConfig uses a similar technique to create case classes from Typesafe Config.

  • Extruder supports pluggable backends - does not rely on Typesafe Config by default
  • Pureconfig supports time parsing using java.time which Extruder does not out of the box, a date parsing module may be added in the future, until then custom data sources may be added
  • Resolution of Typesafe ConfigValue, ConfigObject and ConfigList is only supported by the Typesafe Config configuration backend, as it is closely tied to Typesafe Config, Pureconfig supports this by default as it is directly tied to Typesafe config
  • Pureconfig supports returning Map[String, String], for the time being Extruder does not support this
  • Extruder supports control of class and parameter name formatting by overriding a method, however Pureconfig supports this via predefined configuration schemes

Participation

This project supports the Scala Code of Conduct and aims that its channels (mailing list, Gitter, github, etc.) to be welcoming environments for everyone.

Licence

MIT License

Copyright (c) 2017-2019 Chris Jansen

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.