go_up

package module
v0.6.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 14, 2020 License: MIT Imports: 5 Imported by: 7

README

go-up! A simple configuration library with placeholders resolution and no magic.

Build Status codecov Go Report Card

go-up provides a simple way to configure an application from multiple sources — built in resources, property files, environment variables, and whatever else you like.

Some features:

  • Recursive placeholders resolution
  • Less than 10Kb
  • No external dependencies
  • Modular and extensible architecture

This library was started after the considerable disappointment I had when I noticed that the configuration library used by my application increased the binary file size from 3MB to 11MB!!! (See the Rationale section below for detailed info)

Go-Up is written to be lightweight and will always remain lean and straightforward.

Getting Started

  1. To get started, import go-up:

In your go file:

import (
	"github.com/ufoscout/go-up"
)

If you use VGO, add in your go.mod file:

require github.com/ufoscout/go-up v0.6.1
  1. Define some properties. You can use placeholders, for example, in config.properties:
# This line is a comment because it stats with '#'

server.port=9090
server.host=127.0.0.1

# Placeholders here, they will be resolved at runtime
server.url=http://${server.host}:${server.port}/

Keys and values in a properties file are automatically trimmed.

  1. Build a go-up object that your properties:
ignoreFileNotFound := false 
up, err := NewGoUp().
		AddFile("./confing.properties", ignoreFileNotFound).
		Build();
  1. Look up properties by key:
port := up.GetIntOrDefault("server.port", 8080); // returns 9090
serverUrl := up.GetString("server.url") // returns http://127.0.0.1:9090/
defaultVal := up.GetStringOrDefault("unknown_Key", "defaultValue") // returns defaultValue

Readers

In go-up a "Reader" is whatever source of properties. By default, go-up offers readers to load properties from:

  • properties files in the file system
  • Environment variables
  • Programmatically typed properties

Custom properties readers can be easily created implementing the github.com/ufoscout/go-up/reader/Reader interface.

Placeholders resolution

go-up resolves placeholders recursively. For example:

fileOne.properties:

server.url=http://${${environment}.server.host}:${server.port}
server.port=8080

fileTwo.properties:

environment=PROD
PROD.server.host=10.10.10.10
DEV.server.host=127.0.0.1

Go code:

up, err := go_up.NewGoUp().
 AddFile("./fileOne.properties", false).
 AddFile("./fileTwo.properties", false).
 Build()

fmt.Println(up.GetString("server.url")) // this prints 'http://10.10.10.10:8080'

By default ${ and } delimiters are used. Custom delimiters can be easily defined:

up, err := go_up.NewGoUp().
 Delimiters("%(", ")"). // using %( and ) as delimiters
 ... etc ...
 Build()

Default Values

Placeholders can have default values which are used if the key is not otherwise provided. Example:

config.properties:

# the default value "8080" is used if 'PORT_NUMBER' is not provided
server.port=${PORT_NUMBER:8080}
    
# default is 127.0.0.1
server.ip=${IP:127.0.0.1}

server.url=${server.ip}/${server.port}

Go code:

up, err := go_up.NewGoUp().
 AddFile("./config.properties", false).
 Build()

// this prints 'http://127.0.0.1:8080'
fmt.Println(up.GetString("server.url")) 

The default separator for the default value is ":". A custom value can be set through the 'DefaultValueSeparator()' method of the GoUpBuilder.

Readers priority -> Last one wins

Properties defined in later readers will override properties defined in earlier readers, in case of overlapping keys. Hence, make sure that the most specific readers are the last ones in the given list of locations.

For example:

fileOne.properties:

server.url=urlFromOne

fileTwo.properties:

server.url=urlFromTwo
up, err := go_up.NewGoUp().
 AddFile("./fileOne.properties", false). 
 AddFile("./fileTwo.properties", false).
 AddReader(go_up.NewEnvReader("", false, false)). // Loading environment variables
 Build()

// this prints 'urlFromTwo'
fmt.Println(up.GetString("server.url"))

BTW, due to the fact that we used go_up.NewEnvReader(...)` as last reader, if at runtime an Environment variable called "server.url" is found, it will override the other values.

Finally, it is possible to specify a custom priority:

up, err := go_up.NewGoUp().

 // load the properties from the file system and specify their priority
 AddFileWithPriority("./fileOne.properties", false, go_up.HighestPriority).

 AddFile("./fileTwo.properties", false). // loads file with default priority
 AddReader(go_up.NewEnvReader("", false, false)). // Loads environment variables
 Build()

// this prints 'urlFromOne'
fmt.Println(up.GetString("server.url"))

The default priority is 100. The highest priority is 0. As usual, when more readers have the same priority, the last one wins in case of overlapping keys.

Working with Environment Variables

Built in support for environment variables is provided through the EnvReader struct:

// These should be defined in your system, not in the code. Here only as example.
os.Setenv("KEY_FROM_ENV", "ValueFromEnv")
os.Setenv("CUSTOM_PREFIX_KEY_FROM_ENV", "ValueFromEnv_WithCustomPrefix")

// Loads only variables with this prefix. The prefix is removed at runtime.
prefix := "CUSTOM_PREFIX_" 
// if true, the Env variable key is converted to lower case
toLowerCase := true 
// if true, the underscores "_" of the env variable key are replaced by dots "."
underscoreToDot := true 

up, err := go_up.NewGoUp().
 AddReader(go_up.NewEnvReader(prefix, toLowerCase, underscoreToDot)). // Loading environment variables
 Build()

// this prints 'ValueFromEnv_WithCustomPrefix'
fmt.Println(up.GetString("key.from.env"))

Real life example

A typical real life configuration would look like:

up, err := go_up.NewGoUp().

 // load a file
 AddFile("./default.properties", false).

 // load another file
 AddFile("./config/config.properties", false).

 // Add a not mandatory file, resource not found errors are ignored.
 // Here I am adding properties requried only during testing.
 AddFile("./test/test.properties", true).

 // Load the Environment variables.
 // The are used as they are defined, e.g. ENV_VARIABLE=XXX
 AddReaderWithPriority(go_up.NewEnvReader("APP_PREFIX_", false, false)).

 // Load the Environment variables and convert their keys
 // from ENV_VARIABLE=XXX to env.variable=XXX
 // This could be desired to override default properties
 AddReaderWithPriority(go_up.NewEnvReader("APP_PREFIX_", true, true)).
 
 // build the go-up object
 .Build()

go-up API

go-up has a straightforward API that hopefully does not need detailed documentation.

Some examples:

up, err := go_up.NewGoUp().
 Add("programmatically.added.key", "12345"). // default values can be added programmatically
 AddFile("./myFile.properties", false).
 Build()

// get a String. The empty string value is returned if the key is not found
aString := up.GetString("key")

// get a String. "defaultValue" is returned if the key is not found
aStringOrDefault := up.GetStringOrDefault("key", "defaultValue")

// get a String. An error is returned if the key is not found
aStringOrError, error1 := up.GetStringOrFail("key")

// get a slice from the comma separated tokens of the property value
aslice := up.GetStringSlice("key", ",")

All available methods are defined in the GoUp interface:

type GoUp interface {

  Exists(key string) bool

  GetBool(key string) bool
  GetBoolOrDefault(key string, defaultValue bool) bool
  GetBoolOrFail(key string) (bool, error)
   
  GetFloat64(key string) float64
  GetFloat64OrDefault(key string, defaultValue float64) float64
  GetFloat64OrFail(key string) (float64, error)
    
  GetInt(key string) int
  GetIntOrDefault(key string, defaultValue int) int
  GetIntOrFail(key string) (int, error)
   
  GetString(key string) string
  GetStringOrDefault(key string, defaultValue string) string
  GetStringOrFail(key string) (string, error)

  GetStringSlice(key string, separator string) []string
  GetStringSliceOrDefault(key string, separator string, defaultValue []string) []string
  GetStringSliceOrFail(key string, separator string) ([]string, error)

}

GoDoc

See https://godoc.org/github.com/ufoscout/go-up

Rationale

Before Go-Up, I used to use Viper. Viper is great and I surely recommend it; nevertheless, all of its features are not for free. In fact, it adds tons of dependencies that make the application much heavier.

So, if like me you aspire to code which is as light as possible and you can live with a smaller set of features (BWT go-up has some unique aces in the hole like recursive placeholders resolution!), then you should probably take into account a lighter alternative like go-up.

To show the impact that a single library can have on your application, I created three small examples (see: go-up_examples ), these are:

  • goup : a Go application that, using go-up, reads "Hello World" from a config file and prints it
  • plain : a Go application that prints "Hello World"
  • viper : a Go application that, using viper, reads "Hello World" from a config file and prints it

When built, they produce surprising results:

Library Produced Binary Size
Go-Up 2.185 KB
Plain Go 2.000 KB
Viper 11.782 KB

The Viper based binary file is nearly 6 times bigger than the other implementations!

(Build performed with go1.13 linux/amd64 on Ubuntu 18.04)

Documentation

Index

Constants

View Source
const DefaultEndDelimiter string = "}"

DefaultEndDelimiter the default placeholder end delimiter

View Source
const DefaultListSeparator string = ","

DefaultListSeparator default list separator

View Source
const DefaultPriority int = 100

DefaultPriority the default priority

View Source
const DefaultStartDelimiter string = "${"

DefaultStartDelimiter the default placeholder start delimiter

View Source
const DefaultValueSeparator string = ":"

DefaultValueSeparator the default separator for a placeholder default value

View Source
const HighestPriority int = 0

HighestPriority the highest priority

Variables

This section is empty.

Functions

func NewEnvReader

func NewEnvReader(prefix string, toLower bool, underscoreToDot bool) reader.Reader

NewEnvReader creates a new Environment variables reader

Types

type GoUp

type GoUp interface {
	Exists(key string) bool
	GetBool(key string) bool
	GetBoolOrDefault(key string, defaultValue bool) bool
	GetBoolOrFail(key string) (bool, error)
	GetFloat64(key string) float64
	GetFloat64OrDefault(key string, defaultValue float64) float64
	GetFloat64OrFail(key string) (float64, error)
	GetInt(key string) int
	GetIntOrDefault(key string, defaultValue int) int
	GetIntOrFail(key string) (int, error)
	GetString(key string) string
	GetStringOrDefault(key string, defaultValue string) string
	GetStringOrFail(key string) (string, error)
	GetStringSlice(key string, separator string) []string
	GetStringSliceOrDefault(key string, separator string, defaultValue []string) []string
	GetStringSliceOrFail(key string, separator string) ([]string, error)
}

GoUp interface

type GoUpBuilder

type GoUpBuilder interface {
	Add(key string, value string) GoUpBuilder
	AddReader(newReader reader.Reader) GoUpBuilder
	AddReaderWithPriority(newReader reader.Reader, priority int) GoUpBuilder
	AddFile(filename string, ignoreNotFound bool) GoUpBuilder
	AddFileWithPriority(filename string, ignoreNotFound bool, priority int) GoUpBuilder
	Delimiters(startDelimiter string, endDelimiter string) GoUpBuilder
	DefaultValueSeparator(defaultValueSeparator string) GoUpBuilder
	IgnoreUnresolvablePlaceholders(ignoreUnresolvablePlaceholders bool) GoUpBuilder
	Build() (GoUp, error)
}

GoUpBuilder the GoUp builder

func NewGoUp

func NewGoUp() GoUpBuilder

NewGoUp creates a new GoUpBuilder

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL