Skip to content
Powerful configuration management for Scala (JSON, properties, command-line arguments, and environment variables)
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
core
input/shared/src
irpatch/src/main/scala
macros
project
.gitignore
.jvmopts
.travis.yml
LICENSE
README.md
build.sbt
config.hocon
config.json
config.xml
config.yml
publish.sh

README.md

profig

Build Status Gitter Maven Central Latest version

Powerful configuration management for Scala (JSON, properties, command-line arguments, and environment variables)

Justification

In any case where there are existing libraries that accomplish a task it is worthwhile to document the justification for creating yet another library. This is beneficial both for users to understand how it is differentiated as well as for the developers to clarify there is valid purpose in the endeavor.

In the Scala configuration arena the most popular offering is that of Typesafe Config (https://github.com/typesafehub/config). While this is a powerful and useful library it is more complicated to work with and less flexible than we'd like. One of the very specific problems with it is the lack of support for Scala.js.

Features

Our goal is primarily simplicity. A configuration library should do the necessary work and get out of the way of the developer and let them get their job done. To this end we support a unified configuration merging command-line arguments, environment variables, system properties, and configuration files to provide maximum flexibility of defining, defaulting, and overriding configuration in your application.

File Formats

  • JSON (automatically picked up from config.json, configuration.json, app.json, application.json, and defaults.json)
  • Properties (automatically picked up from config.properties, configuration.properties, app.properties, application.properties, and defaults.properties)
  • YAML (automatically picked up from config.yml, config.yaml, configuration.yml, configuration.yaml, app.yml, app.yaml, application.yml, application.yaml, defaults.yml, defaults.yaml)
  • HOCON (automatically picked up from config.hocon, configuration.hocon, app.hocon, application.hocon, and defaults.hocon)
  • XML (automatically picked up from config.xml, configuration.xml, app.xml, application.xml, and defaults.xml)

Setup

SBT Configuration

Profig is published to Sonatype OSS and synchronized to Maven Central supporting JVM and Scala.js on 2.11 and 2.12:

libraryDependencies += "com.outr" %% "profig" % "2.3.0"   // Scala
libraryDependencies += "com.outr" %%% "profig" % "2.3.0"  // Scala.js / Cross-Build

Getting Started

Whether you are using this in JVM or JS you need one import to access everything:

import profig._

This brings some implicits on specific platforms (for example, loading URLs, Files, Sources, etc. in the JVM) but the only class you really need be concerned with is Profig.

Loading Command-Line arguments

When your application starts it is reasonable to want to allow execution of the application to override existing configuration via the command-line. In order to effectively do this we can simply invoke Profig.merge(args) within our main method. This will merge all command-line arguments into Profig.

Loading Files

Profig supports many configuration formats and can look in the classpath as well as the filesystem to find configuration to load. To load a file simply call:

Profig.load(ProfigLookupPath("config.json", ConfigType.Json, LoadType.Merge))

This will look for config.json on the classpath and filesystem, load it as JSON (if found), and merge it into the configuration.

However, if your application doesn't need very explicit files to be loaded you can load defaults instead:

Profig.loadDefaults()

This will look for any standardized configuration file in the classpath and filesystem and load it into the system.

Finally, if you want to look for variations on a name:

Profig.load(ProfigLookupPath.paths(mergePaths = List("config")): _*)

This will look for all variations of config in all known file extensions (json, properties, yml, config, etc.)

Merge or Defaults

When loading configuration into Profig, you'll notice two styles of loading: "merge" and "defaults". The difference between these two has to do with overwriting existing configuration. In the case of "merge", any duplicate values will be overwritten by the new configuration. In the case of "defaults", only new information is integrated, so any existing values are left alone.

Accessing values

As stated above, system properties and environment variables are automatically loaded into the configuration. So if we wanted to access the system property "java.version" we can easily do so:

val javaVersion = Profig("java.version").as[String]

You can also load from a higher level as a case class to get more information. For example:

case class JVMInfo(version: String, specification: Specification)

case class Specification(vendor: String, name: String, version: String)

val info = Profig("java").as[JVMInfo]

Configuration files will automatically be loaded from config.json, config.conf, configuration.json, configuration.conf, application.conf, and application.json if found in the application path or in the classpath.

If default values or Option values are defined in the case class they will be used if the value is not available in the config. However, if any required parameters are missing an exception will be thrown when attempting to read.

Storing values

Adding values at runtime is almost exactly the same as reading values. For example, if we want to store a basic configuration:

case class MyConfig(path: String = "/my/application",
                    timeout: Long = 1000L,
                    username: String = "root",
                    password: String = "password")
                    
Profig.merge(MyConfig(path = "/another/path"))

If you would prefer to merge in an object without overwriting existing values you can use defaults instead of merge:

Profig.defaults(MyConfig(path = "/another/path"))

Next steps

This only scratches the surface of the features and functionality Profig provides. For additional information read the ScalaDocs and the specs: https://github.com/outr/profig/blob/master/core/shared/src/test/scala/spec/ProfigSpec.scala

Roadmap

2.3.0 (Released 05.09.2018)

  • Better naming conventions internally
  • Fix namespace collisions with common names like Configuration
  • Better support for custom encoder / decoder via companion object auto import

2.2.0 (Released 03.12.2018)

2.1.0 (Released 03.08.2018)

  • Override loading support
    • Modify paths to auto-load
    • Allow disabling of loading environment variables
  • Yaml support (use circe-yaml)

2.0.0 (Released 01.26.2018)

  • Auto-init support
  • Support child Profig instances with hierarchical structure
  • Remove field / path support
  • Refactor Config to be named Profig better disambiguation

1.1.0 (Released 08.03.2017)

  • Compile-time integration of configuration into Scala.js projects
  • Usability of Config within Macros at compile-time in Scala and Scala.js

1.0.0 (Released 07.04.2017)

  • Merge support
    • Defaults support (only apply if value doesn't already exist)
  • Load command-line arguments
  • Load environment variables
  • Load properties
  • Loading case classes
  • Storing case classes
  • Trait for application startup (JVM and Scala.js)
  • Loading
    • JSON
      • Directly
      • From Disk (JVM-only)
      • From ClassLoader
    • Properties
      • Directly
      • From Disk (JVM-only)
      • From ClassLoader
    • Automatic lookup of default locations
  • Document classes
  • Document README
You can’t perform that action at this time.