Skip to content

Instantly share code, notes, and snippets.

@ndrew
Created September 22, 2013 09:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ndrew/6658542 to your computer and use it in GitHub Desktop.
Save ndrew/6658542 to your computer and use it in GitHub Desktop.
stagosaurus: iteration_1 — configuration
package stagosaurus
import (
"errors"
)
// Constructor for Config. If defaults are not needed just do new(Config)
//
func NewConfig(defaults *Config) *Config {
cfg := new(Config)
cfg.Defaults = defaults
return cfg
}
// Generic configuration class
//
type Config struct {
Defaults *Config
cfg map[interface{}]interface{}
}
// Get value by key from configuration dictionary
//
// Note: as go doesn't support polymorphic funcs, I do trick with variable arguments to have
// fluent interface for providing optional overriding default value
func (this *Config) Get(key ...interface{}) interface{} {
if this.cfg == nil {
this.cfg = make(map[interface{}]interface{})
}
switch {
case 0 == len(key):
return this
case 1 == len(key):
if nil != this.Defaults && this != this.Defaults && nil == this.cfg[key[0]] {
return this.Defaults.Get(key[0])
}
return this.cfg[key[0]]
case 2 == len(key):
v := this.cfg[key[0]]
if nil == v {
return key[1]
}
return v
default:
panic("Incorrect number of params passed to Config.Get")
}
}
// Sets value for key in configuration dictionary
//
func (this *Config) Set(key interface{}, value interface{}) interface{} {
if this.cfg == nil {
this.cfg = make(map[interface{}]interface{})
}
this.cfg[key] = value
return this.Get(key)
}
// Configuration validation via key => validity-predicate map. Returns result of validation and list of failed keys, if any
//
func (this *Config) Validate(predicateMap map[interface{}]func(interface{}) bool) (bool, []interface{}) {
res := true
noErrors := [0]interface{}{}
errors := noErrors[:]
for k, predicate := range predicateMap {
if valid := predicate(this.Get(k)); false == valid {
res = false
errors = append(errors, k)
}
}
return res, errors
}
// convenience getters
// string
//
func (this *Config) String(key ...interface{}) (string, error) {
v := this.Get(key...)
if nil != v {
if ret, ok := v.(string); ok {
return ret, nil
}
}
iv := "" // identity value
return iv, errors.New("Value in config is not String")
}
// func (v Value) Bytes() []byte
func (this *Config) Bool(key ...interface{}) (bool, error) {
v := this.Get(key...)
if nil != v {
if ret, ok := v.(bool); ok {
return ret, nil
}
}
iv := false // identity value
return iv, errors.New("Value in config is not Bool")
}
// example.go
package main
import (
site "./.." // substite this to "github.com/ndrew/stagosaurus" for production use
"errors"
"strings"
)
func main() {
// provide defaults for configuration
defaults := new(site.Config)
defaults.Set("greeting", "Hello")
config := site.NewConfig(defaults)
config.Set("blogName", "Stagosaurus")
// create 'blog' example
blog, err := New(config)
if err != nil {
// config validation: ["greeting"=>"Hello"] won't pass, so we fix config and recreate blog
config.Set("greeting", "Rhoaarrrr")
blog, err = New(config)
}
// we don't have any posts yet, so will be rendering data from config
if err = blog.HelloWorld(); err == nil {
println("\nCongratulation, you did it!")
} else {
println(err.Error())
}
}
func New(config *site.Config) (*Blog, error) { // blog constructor
// validate the config key 'greeting'
validator := map[interface{}](func(interface{}) bool){
"greeting": func(v interface{}) bool {
return v != nil && v != "Hello"
},
}
if original, _ := config.Validate(validator); !original {
return nil, errors.New("You've provided too trivial value! Try again, be original!")
}
blog := new(Blog)
blog.Config = config
return blog, nil
}
// your future blog generator
type Blog struct {
Config *site.Config
}
// test validation and 'publish' hello-world
//
func (this *Blog) HelloWorld() error {
output := ""
// cast to type manually
hello := this.Config.Get("greeting")
if hello != nil {
if helloStr, ok := hello.(string); ok {
output = helloStr
} else {
return errors.New("hello is not a string!")
}
}
// or use shorthand for common types: string/bool/int
world, err := this.Config.String("blogName")
if err != nil {
return err
}
output = output + " " + world + "!"
return this.Render(output)
}
// rendering text in a fancy border
//
func (this *Blog) Render(output string) error {
filler := strings.Repeat("═", len(output))
println("╔═" + filler + "═╗")
println("╟ " + output + " ╢")
println("╚═" + filler + "═╝")
return nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment