Skip to content

Commit

Permalink
Allowed infinite nesting of constants to create tree structures
Browse files Browse the repository at this point in the history
  • Loading branch information
James Stewart committed Apr 1, 2016
1 parent d9bccfc commit f8db7c1
Show file tree
Hide file tree
Showing 4 changed files with 435 additions and 209 deletions.
116 changes: 69 additions & 47 deletions constant.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,40 +7,46 @@ import (
"text/template"
)

// Returns the value of the constant in the pool named 'name' as a string.
func (pool *Pool) Str(name string) string {
pool.mutex.RLock()
defer pool.mutex.RUnlock()

if pool.defaults[name] == nil {
// Returns the value of the node as defined by path.
// If the node's default value is nil an empty string is returned.
// If the envionment variable associated with the node is not equal to an empty string that value is used instead of the node's default value.
// Templates in the node's value are parsed (see sections Template, Template Context and Example for details).
func (n *Node) Str(path ...string) string {
node := n.Node(path...)
if node == nil {
return ""
}

var tmpl string
if env := os.Getenv(pool.env_name(name)); env == "" {
tmpl = *pool.defaults[name]
} else {
node_fullname := node.FullName()
tmpl := node.Default()

node.mutex.RLock()
defer node.mutex.RUnlock()

parent := node.parent

if env := os.Getenv(node_fullname); env != "" {
tmpl = env
}

t, err := template.New("constant").Funcs(template.FuncMap{
"const": func(in_name string) string {
if in_name == name {
"const": func(path ...string) string {
if len(path) == 1 && path[0] == node.name {
return ""
}
return pool.Str(in_name)
return parent.Str(path...)
},
"list": func() []string {
consts := pool.List()
consts := parent.List()
for i, cnst := range consts {
if cnst == name {
if cnst == node.name {
consts = append(consts[:i], consts[i+1:]...)
}
}
return consts
},
"isset": func(in_name string) bool {
return pool.IsSet(in_name)
"isset": func(path ...string) bool {
return parent.IsSet(path...)
},
}).Parse(tmpl)

Expand All @@ -56,67 +62,83 @@ func (pool *Pool) Str(name string) string {
return byte_string.String()
}

// Returns the value of the constant in the pool named 'name' as an integer.
// Alias of n.Str()
func (n *Node) String() string {
return n.Str()
}

// Returns the value of n.Str(path...) as an integer.
//
// Follows convention of strconv.Atoi (https://golang.org/pkg/strconv/#Atoi).
func (pool *Pool) Int(name string) (val int, err error) {
val, err = strconv.Atoi(pool.Str(name))
func (n *Node) Int(path ...string) (val int, err error) {
val, err = strconv.Atoi(n.Str(path...))
return
}

// Run Int but ignore errors
func (pool *Pool) IntI(name string) (val int) {
val, _ = pool.Int(name)
// Run n.Int(path...) but ignore errors
func (n *Node) IntI(path ...string) (val int) {
val, _ = n.Int(path...)
return
}

// Returns the value of the constant in the pool named 'name' as a float64.
// Returns the value of n.Str(path...) as a float64.
//
// Follows convention of strconv.ParseFloat (https://golang.org/pkg/strconv/#ParseFloat).
func (pool *Pool) Float(name string, bitSize int) (val float64, err error) {
val, err = strconv.ParseFloat(pool.Str(name), bitSize)
func (n *Node) Float(bitSize int, path ...string) (val float64, err error) {
val, err = strconv.ParseFloat(n.Str(path...), bitSize)
return
}

// Run Float but ignore errors
func (pool *Pool) FloatI(name string, bitSize int) (val float64) {
val, _ = pool.Float(name, bitSize)
// Run n.Float(bitSize, path...) but ignore errors
func (n *Node) FloatI(bitSize int, path ...string) (val float64) {
val, _ = n.Float(bitSize, path...)
return
}

// Returns the value of the constant in the pool named 'name' as a boolean.
// Returns the value of n.Str(path...) as a boolean.
//
// Follows convention of strconv.ParseBool (https://golang.org/pkg/strconv/#ParseBool).
func (pool *Pool) Bool(name string) (val bool, err error) {
val, err = strconv.ParseBool(pool.Str(name))
func (n *Node) Bool(path ...string) (val bool, err error) {
val, err = strconv.ParseBool(n.Str(path...))
return
}

// Run Bool but ignore errors
func (pool *Pool) BoolI(name string) (val bool) {
val, _ = pool.Bool(name)
// Run n.Bool(path...) but ignore errors
func (n *Node) BoolI(path ...string) (val bool) {
val, _ = n.Bool(path...)
return
}

// Returns if the constant named 'name' is set in the pool.
func (pool *Pool) IsSet(name string) bool {
pool.mutex.RLock()
defer pool.mutex.RUnlock()
// Returns false if: the node as defined by path doesn't exist; the node's default value is nil; the node's default value it an empty string.
// Otherwise returns true.
func (n *Node) IsSet(path ...string) bool {
node := n.Node(path...)
if node == nil {
return false
}

node.mutex.RLock()
defer node.mutex.RUnlock()

return pool.defaults[name] != nil
return node.def_val != nil && *node.def_val != ""
}

// Returns the default value of the constant in the pool named 'name'.
// Returns the default value of node as defined by path.
// If the default value contains templates the templates will not be parsed.
// If the default value is not a string it will be converted to a string as per the strconv package (https://golang.org/pkg/strconv/).
func (pool *Pool) Default(name string) string {
if pool.defaults[name] == nil {
// If the default value is nil an empty string is returned.
func (n *Node) Default(path ...string) string {
node := n.Node(path...)
if node == nil {
return ""
}

return *pool.defaults[name]
}
if !node.IsSet() {
return ""
}

node.mutex.RLock()
defer node.mutex.RUnlock()

func (pool *Pool) env_name(name string) string {
return pool.prefix + name
return *node.def_val
}
71 changes: 51 additions & 20 deletions constant_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import (
"testing"
)

var pool *constant.Pool
var pool_prefix = "test_"
var node *constant.Node
var node_prefix = "test"
var node_delimiter = "_"

type constPair struct {
name string
Expand Down Expand Up @@ -43,7 +44,7 @@ type invalid string
var tests = []test_type{
{constPair{"string1", "string value"}, "string value", false, "string value", val_err{0, true}, val_err{0.0, true}, val_err{false, true}, true},
{constPair{"string1", "already exists"}, "string value", true, "string value", val_err{0, true}, val_err{0.0, true}, val_err{false, true}, true},
{constPair{"string2", ""}, "", false, "", val_err{0, true}, val_err{0.0, true}, val_err{false, true}, true},
{constPair{"string2", ""}, "", false, "", val_err{0, true}, val_err{0.0, true}, val_err{false, true}, false},
{constPair{"byte1", []byte("byte string value")}, "byte string value", false, "byte string value", val_err{0, true}, val_err{0.0, true}, val_err{false, true}, true},
{constPair{"fmtStringer1", stringer("fmt.Stringer value")}, "fmt.Stringer value", false, "fmt.Stringer value", val_err{0, true}, val_err{0.0, true}, val_err{false, true}, true},
{constPair{"int1", 12}, "12", false, "12", val_err{12, false}, val_err{12.0, false}, val_err{false, true}, true},
Expand All @@ -59,7 +60,7 @@ var tests = []test_type{
{constPair{"bool7", "T"}, "T", false, "T", val_err{0, true}, val_err{0.0, true}, val_err{true, false}, true},
{constPair{"bool8", "F"}, "F", false, "F", val_err{0, true}, val_err{0.0, true}, val_err{false, false}, true},
{constPair{"invalid1", invalid("not a valid type")}, "", true, "", val_err{0, true}, val_err{0.0, true}, val_err{false, true}, false},
{constPair{"invalid2", nil}, "", true, "", val_err{0, true}, val_err{0.0, true}, val_err{false, true}, false},
{constPair{"nil_value", nil}, "", false, "", val_err{0, true}, val_err{0.0, true}, val_err{false, true}, false},
{constPair{"", "empty name"}, "", true, "", val_err{0, true}, val_err{0.0, true}, val_err{false, true}, false},
{constPair{"2name", "name starting with number"}, "", true, "", val_err{0, true}, val_err{0.0, true}, val_err{false, true}, false},
{constPair{"namew1thnum", "name containing number"}, "name containing number", false, "name containing number", val_err{0, true}, val_err{0.0, true}, val_err{false, true}, true},
Expand All @@ -76,13 +77,13 @@ var tests = []test_type{
}

func TestMain(m *testing.M) {
pool = constant.NewPool(pool_prefix)
node = constant.NewTree(node_prefix, node_delimiter)
os.Exit(m.Run())
}

func TestNew(t *testing.T) {
for _, test := range tests {
err := pool.New(test.pair.name, test.pair.value)
_, err := node.New(test.pair.name, test.pair.value)
if (err != nil) != test.new_error {
no_str := ""
if !test.new_error {
Expand All @@ -97,9 +98,39 @@ func TestNew(t *testing.T) {
}
}

func TestDelimiter(t *testing.T) {
res := node.Delimiter()
if res != node_delimiter {
t.Error(
"Expected", node_delimiter,
"got", res,
)
}
}

func TestName(t *testing.T) {
name := node.Name()
if name != node_prefix {
t.Error(
"Expected", node_prefix,
"got", name,
)
}
}

func TestFullName(t *testing.T) {
full_name := node.Name()
if full_name != node_prefix {
t.Error(
"Expected", node_prefix,
"got", full_name,
)
}
}

func TestStr(t *testing.T) {
for _, test := range tests {
str := pool.Str(test.pair.name)
str := node.Str(test.pair.name)
if str != test.str {
t.Error(
"For", test.pair,
Expand All @@ -112,7 +143,7 @@ func TestStr(t *testing.T) {

func TestInt(t *testing.T) {
for _, test := range tests {
val, err := pool.Int(test.pair.name)
val, err := node.Int(test.pair.name)
if val != test.int_res.val || (err != nil) != test.int_res.err {
t.Error(
"For", test.pair,
Expand All @@ -125,7 +156,7 @@ func TestInt(t *testing.T) {

func TestFloat(t *testing.T) {
for _, test := range tests {
val, err := pool.Float(test.pair.name, 64)
val, err := node.Float(64, test.pair.name)
if val != test.float_res.val || (err != nil) != test.float_res.err {
t.Error(
"For", test.pair,
Expand All @@ -138,7 +169,7 @@ func TestFloat(t *testing.T) {

func TestBool(t *testing.T) {
for _, test := range tests {
val, err := pool.Bool(test.pair.name)
val, err := node.Bool(test.pair.name)
if val != test.bool_res.val || (err != nil) != test.bool_res.err {
t.Error(
"For", test.pair,
Expand All @@ -151,11 +182,11 @@ func TestBool(t *testing.T) {

func TestIsSet(t *testing.T) {
for _, test := range tests {
val := pool.IsSet(test.pair.name)
val := node.IsSet(test.pair.name)
if val != test.isset {
t.Error(
"For", test.pair,
"expected", test.new_error,
"expected", test.isset,
"got", val,
)
}
Expand All @@ -164,7 +195,7 @@ func TestIsSet(t *testing.T) {

func TestDefault(t *testing.T) {
for _, test := range tests {
val := pool.Default(test.pair.name)
val := node.Default(test.pair.name)
if val != test.default_s {
t.Error(
"For", test.pair,
Expand All @@ -178,12 +209,12 @@ func TestDefault(t *testing.T) {
func TestList(t *testing.T) {
exp_list := make([]string, 0)
for _, test := range tests {
if test.new_error == false {
if test.new_error == false && test.pair.value != nil {
exp_list = append(exp_list, test.pair.name)
}
}

res_list := pool.List()
res_list := node.List()

sort.StringSlice(exp_list).Sort()
sort.StringSlice(res_list).Sort()
Expand All @@ -199,12 +230,12 @@ func TestList(t *testing.T) {
func TestEnvironment(t *testing.T) {
exp_list := make([]string, 0)
for _, test := range tests {
if test.new_error == false {
exp_list = append(exp_list, pool_prefix+test.pair.name)
if test.new_error == false && test.pair.value != nil {
exp_list = append(exp_list, node_prefix+node_delimiter+test.pair.name)
}
}

res_list := pool.Environment()
res_list := node.Environment()

sort.StringSlice(exp_list).Sort()
sort.StringSlice(res_list).Sort()
Expand All @@ -219,7 +250,7 @@ func TestEnvironment(t *testing.T) {

func TestDelete(t *testing.T) {
for _, test := range tests {
err := pool.Delete(test.pair.name)
err := node.Delete(test.pair.name)
if (err != nil) != test.new_error {
no_str := ""
if !test.new_error {
Expand All @@ -233,7 +264,7 @@ func TestDelete(t *testing.T) {
}
}

list_len := len(pool.List())
list_len := len(node.List())
if list_len != 0 {
t.Error(
"expected 0 items left",
Expand Down
Loading

0 comments on commit f8db7c1

Please sign in to comment.