diff --git a/template_nest.go b/template_nest.go index 588f457..8f6083d 100644 --- a/template_nest.go +++ b/template_nest.go @@ -14,15 +14,18 @@ type Hash map[string]interface{} // Option holds configuration for TemplateNest type Option struct { - Delimiters [2]string - NameLabel string // Identify the template to be used - TemplateDir string // Directory where templates are located - TemplateExtension string // Appended on the label to idenfity the template - DieOnBadParams bool // Attempt to populate a variable that doesn't exist should result in an error - ShowLabels bool // Prepend & Append a string to every template, helpful in debugging - CommentDelimiters [2]string // Used in conjunction with ShowLabels, if HTML then use '' - FixedIndent bool // Intended to improve readability when inspecting nested templates - TokenEscapeChar string // Escapes a token delimiter, i.e. if set to '\' then variables that have '\' prefix won't be replaced + Delimiters [2]string + NameLabel string // Identify the template to be used + TemplateDir string // Directory where templates are located + TemplateExtension string // Appended on the label to idenfity the template + DieOnBadParams bool // Attempt to populate a variable that doesn't exist should result in an error + ShowLabels bool // Prepend & Append a string to every template, helpful in debugging + CommentDelimiters [2]string // Used in conjunction with ShowLabels, if HTML then use '' + FixedIndent bool // Intended to improve readability when inspecting nested templates + TokenEscapeChar string // Escapes a token delimiter, i.e. if set to '\' then variables that have '\' prefix won't be replaced + DefaultsNamespaceChar string + Defaults Hash // Provide a hash of default values that are substituted if template hash does not provide a value + defaultsFlat Hash } type TemplateNest struct { @@ -73,6 +76,14 @@ func New(opts Option) (*TemplateNest, error) { if opts.TemplateExtension == "" { opts.TemplateExtension = "html" } + if opts.DefaultsNamespaceChar == "" { + opts.DefaultsNamespaceChar = "." + } + if opts.Defaults == nil { + opts.Defaults = make(map[string]interface{}) + } + + opts.defaultsFlat = FlattenMap(opts.Defaults, "", opts.DefaultsNamespaceChar) // Initialize TemplateNest with the final options. nest := &TemplateNest{ @@ -115,6 +126,28 @@ func New(opts Option) (*TemplateNest, error) { return nest, nil } +// FlattenMap flattens a nested map[string]interface{} into a flat map with +// dot-separated keys. +func FlattenMap(input Hash, parentKey string, separator string) Hash { + result := make(map[string]interface{}) + for key, value := range input { + fullKey := key + if parentKey != "" { + fullKey = parentKey + separator + key + } + + // Check if the value is a nested map + if nestedMap, ok := value.(Hash); ok { + for k, v := range FlattenMap(nestedMap, fullKey, separator) { + result[k] = v + } + } else { + result[fullKey] = value + } + } + return result +} + func (nest *TemplateNest) index(filePath string) (TemplateFileIndex, error) { contents, err := ioutil.ReadFile(filePath) if err != nil { @@ -303,7 +336,14 @@ func (nest *TemplateNest) Render(toRender interface{}) (string, error) { // with an empty string. replacement := "" - if value, exists := v[variable.Name]; exists { + value, exists := v[variable.Name] + defaultValue, defaultExists := nest.option.defaultsFlat[variable.Name] + + if exists || defaultExists { + if !exists { + value = defaultValue + } + subRender, err := nest.Render(value) if err != nil { return "", err diff --git a/tests/08_defaults_test.go b/tests/08_defaults_test.go new file mode 100644 index 0000000..94de237 --- /dev/null +++ b/tests/08_defaults_test.go @@ -0,0 +1,51 @@ +package tests + +import ( + "git.virtual.blue/tomgracey/template-nest-go" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestRenderWithDefaults(t *testing.T) { + nest, err := templatenest.New(templatenest.Option{ + TemplateDir: "templates", + Defaults: templatenest.Hash{ + "variable": "Simple Variable", + "space": templatenest.Hash{ + "inside": "A variable inside a space.", + }, + }, + }) + if err != nil { + t.Fatalf("Failed to initialize TemplateNest: %+v", err) + } + + page := templatenest.Hash{ + "TEMPLATE": "00-simple-page", + "simple_component": []templatenest.Hash{ + templatenest.Hash{ + "TEMPLATE": "01-simple-component", + "variable": "Simple Variable in Simple Component", + }, + }, + } + + outputPage := templatenest.Hash{"TEMPLATE": "output/01-simple-page"} + + assert.Equal( + t, + nest.MustRender(outputPage), + nest.MustRender(page), + "Rendered output does not match expected output", + ) + + spacePage := templatenest.Hash{"TEMPLATE": "03-namespace-page"} + spaceOutputPage := templatenest.Hash{"TEMPLATE": "output/11-namespace-page"} + + assert.Equal( + t, + nest.MustRender(spaceOutputPage), + nest.MustRender(spacePage), + "Rendered output does not match expected output", + ) +}