Add extension, delimiter tests & various fixes
This commit is contained in:
parent
e09a3298c3
commit
aec5482578
@ -17,15 +17,15 @@ type Hash map[string]interface{}
|
||||
// Option holds configuration for TemplateNest
|
||||
type Option struct {
|
||||
Delimiters [2]string
|
||||
NameLabel string // Identify the template to be used
|
||||
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
|
||||
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
|
||||
DefaultsNamespaceChar *string
|
||||
Defaults Hash // Provide a hash of default values that are substituted if template hash does not provide a value
|
||||
NoEscapeInput bool // By default all template values are html escaped
|
||||
}
|
||||
@ -73,14 +73,17 @@ func New(opts Option) (*TemplateNest, error) {
|
||||
if opts.CommentDelimiters == [2]string{} {
|
||||
opts.CommentDelimiters = [2]string{"<!--", "-->"}
|
||||
}
|
||||
if opts.NameLabel == "" {
|
||||
opts.NameLabel = "TEMPLATE"
|
||||
if opts.NameLabel == nil {
|
||||
nameLabel := "TEMPLATE"
|
||||
opts.NameLabel = &nameLabel
|
||||
}
|
||||
if opts.TemplateExtension == "" {
|
||||
opts.TemplateExtension = "html"
|
||||
if opts.TemplateExtension == nil {
|
||||
ext := "html"
|
||||
opts.TemplateExtension = &ext
|
||||
}
|
||||
if opts.DefaultsNamespaceChar == "" {
|
||||
opts.DefaultsNamespaceChar = "."
|
||||
if opts.DefaultsNamespaceChar == nil {
|
||||
namespaceChar := "."
|
||||
opts.DefaultsNamespaceChar = &namespaceChar
|
||||
}
|
||||
if opts.Defaults == nil {
|
||||
opts.Defaults = make(map[string]interface{})
|
||||
@ -90,7 +93,7 @@ func New(opts Option) (*TemplateNest, error) {
|
||||
nest := &TemplateNest{
|
||||
option: opts,
|
||||
cache: make(map[string]TemplateFileIndex),
|
||||
defaultsFlat: FlattenMap(opts.Defaults, "", opts.DefaultsNamespaceChar),
|
||||
defaultsFlat: flattenMap(opts.Defaults, "", opts.DefaultsNamespaceChar),
|
||||
}
|
||||
|
||||
// Walk through the template directory and index the templates.
|
||||
@ -99,8 +102,14 @@ func New(opts Option) (*TemplateNest, error) {
|
||||
return err
|
||||
}
|
||||
|
||||
// Skip directories
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check if the file has the correct extension.
|
||||
if !strings.HasSuffix(info.Name(), "."+opts.TemplateExtension) {
|
||||
if *opts.TemplateExtension != "" &&
|
||||
!strings.HasSuffix(info.Name(), "."+*opts.TemplateExtension) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -117,7 +126,11 @@ func New(opts Option) (*TemplateNest, error) {
|
||||
}
|
||||
|
||||
// Remove the extension from the relative path.
|
||||
templateName := strings.TrimSuffix(relPath, "."+opts.TemplateExtension)
|
||||
templateName := relPath
|
||||
if *opts.TemplateExtension != "" {
|
||||
templateName = strings.TrimSuffix(relPath, "."+*opts.TemplateExtension)
|
||||
}
|
||||
|
||||
nest.cache[templateName] = templateIndex
|
||||
return nil
|
||||
})
|
||||
@ -128,20 +141,24 @@ func New(opts Option) (*TemplateNest, error) {
|
||||
return nest, nil
|
||||
}
|
||||
|
||||
// FlattenMap flattens a nested map[string]interface{} into a flat map with
|
||||
// flattenMap flattens a nested map[string]interface{} into a flat map with
|
||||
// dot-separated keys.
|
||||
func FlattenMap(input Hash, parentKey string, separator string) Hash {
|
||||
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
|
||||
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
|
||||
// If separator is nil then there is no point in the user passing us
|
||||
// a nested map.
|
||||
if separator != nil {
|
||||
for k, v := range flattenMap(nestedMap, fullKey, separator) {
|
||||
result[k] = v
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result[fullKey] = value
|
||||
@ -242,9 +259,13 @@ func (nest *TemplateNest) index(filePath string) (TemplateFileIndex, error) {
|
||||
// getTemplateFilePath takes a template name and returns the file path for the
|
||||
// template.
|
||||
func (nest *TemplateNest) getTemplateFilePath(templateName string) string {
|
||||
if *nest.option.TemplateExtension == "" {
|
||||
return filepath.Join(nest.option.TemplateDir, templateName)
|
||||
}
|
||||
|
||||
return filepath.Join(
|
||||
nest.option.TemplateDir,
|
||||
fmt.Sprintf("%s.%s", templateName, nest.option.TemplateExtension),
|
||||
fmt.Sprintf("%s.%s", templateName, *nest.option.TemplateExtension),
|
||||
)
|
||||
}
|
||||
|
||||
@ -307,7 +328,7 @@ func (nest *TemplateNest) renderSlice(toRender interface{}) (string, error) {
|
||||
}
|
||||
|
||||
func (nest *TemplateNest) renderHash(hash map[string]interface{}) (string, error) {
|
||||
tLabel, ok := hash[nest.option.NameLabel]
|
||||
tLabel, ok := hash[*nest.option.NameLabel]
|
||||
if !ok {
|
||||
return "", fmt.Errorf(
|
||||
"encountered hash with no name label (name label: `%s`)", nest.option.NameLabel,
|
||||
@ -343,7 +364,7 @@ func (nest *TemplateNest) renderHash(hash map[string]interface{}) (string, error
|
||||
// If a variable in template hash is not present in template
|
||||
// file and it's not the template label then it's a bad param.
|
||||
_, exists := tIndex.VariableNames[k]
|
||||
if !exists && k != nest.option.NameLabel {
|
||||
if !exists && k != *nest.option.NameLabel {
|
||||
return "", fmt.Errorf(
|
||||
"bad params in template hash, variable not present in template file: `%s`", k,
|
||||
)
|
||||
|
77
tests/04_template_extension_test.go
Normal file
77
tests/04_template_extension_test.go
Normal file
@ -0,0 +1,77 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"git.virtual.blue/tomgracey/template-nest-go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRenderWithJsExtension(t *testing.T) {
|
||||
ext := "js"
|
||||
nest, err := templatenest.New(templatenest.Option{
|
||||
TemplateDir: "templates",
|
||||
TemplateExtension: &ext,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to initialize TemplateNest: %+v", err)
|
||||
}
|
||||
|
||||
page := map[string]interface{}{
|
||||
"TEMPLATE": "30-main",
|
||||
"var": "Simple Variable",
|
||||
}
|
||||
|
||||
render := nest.MustRender(page)
|
||||
|
||||
outputPath := "templates/output/06-main-template-extension.js"
|
||||
outputContents, err := ioutil.ReadFile(outputPath)
|
||||
if err != nil {
|
||||
t.Fatalf("error reading file (`%s`): %+v", outputPath, err)
|
||||
}
|
||||
|
||||
assert.Equal(
|
||||
t,
|
||||
strings.TrimSpace(string(outputContents)),
|
||||
render,
|
||||
"Rendered output does not match expected output",
|
||||
)
|
||||
}
|
||||
|
||||
func TestRenderWithNoExtension(t *testing.T) {
|
||||
ext := ""
|
||||
nest, err := templatenest.New(templatenest.Option{
|
||||
TemplateDir: "templates",
|
||||
TemplateExtension: &ext,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to initialize TemplateNest: %+v", err)
|
||||
}
|
||||
|
||||
page := map[string]interface{}{
|
||||
"TEMPLATE": "00-simple-page.html",
|
||||
"variable": "Simple Variable",
|
||||
"simple_component": []interface{}{
|
||||
map[string]interface{}{
|
||||
"TEMPLATE": "01-simple-component.html",
|
||||
"variable": "Simple Variable in Simple Component",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
render := nest.MustRender(page)
|
||||
|
||||
outputPath := "templates/output/01-simple-page.html"
|
||||
outputContents, err := ioutil.ReadFile(outputPath)
|
||||
if err != nil {
|
||||
t.Fatalf("error reading file (`%s`): %+v", outputPath, err)
|
||||
}
|
||||
|
||||
assert.Equal(
|
||||
t,
|
||||
strings.TrimSpace(string(outputContents)),
|
||||
render,
|
||||
"Rendered output does not match expected output",
|
||||
)
|
||||
}
|
77
tests/06_token_delims_test.go
Normal file
77
tests/06_token_delims_test.go
Normal file
@ -0,0 +1,77 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"git.virtual.blue/tomgracey/template-nest-go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRenderWithAltDelim(t *testing.T) {
|
||||
nest, err := templatenest.New(templatenest.Option{
|
||||
TemplateDir: "templates",
|
||||
Delimiters: [2]string{"<%", "%>"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to initialize TemplateNest: %+v", err)
|
||||
}
|
||||
|
||||
page := map[string]interface{}{
|
||||
"TEMPLATE": "00-simple-page-alt-delim",
|
||||
"variable": "Simple Variable",
|
||||
"simple_component": map[string]interface{}{
|
||||
"TEMPLATE": "01-simple-component-alt-delim",
|
||||
"variable": "Simple Variable in Simple Component",
|
||||
},
|
||||
}
|
||||
|
||||
render := nest.MustRender(page)
|
||||
|
||||
outputPath := "templates/output/01-simple-page.html"
|
||||
outputContents, err := ioutil.ReadFile(outputPath)
|
||||
if err != nil {
|
||||
t.Fatalf("error reading file (`%s`): %+v", outputPath, err)
|
||||
}
|
||||
|
||||
assert.Equal(
|
||||
t,
|
||||
strings.TrimSpace(string(outputContents)),
|
||||
render,
|
||||
"Rendered output does not match expected output",
|
||||
)
|
||||
}
|
||||
|
||||
func TestRenderWithAltDelimAndFixedIndent(t *testing.T) {
|
||||
nest, err := templatenest.New(templatenest.Option{
|
||||
TemplateDir: "templates",
|
||||
Delimiters: [2]string{"<%", "%>"},
|
||||
FixedIndent: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to initialize TemplateNest: %+v", err)
|
||||
}
|
||||
|
||||
page := map[string]interface{}{
|
||||
"TEMPLATE": "00-simple-page-alt-delim",
|
||||
"variable": "Simple Variable",
|
||||
"simple_component": map[string]interface{}{
|
||||
"TEMPLATE": "02-simple-component-multi-line-alt-delim",
|
||||
},
|
||||
}
|
||||
|
||||
render := nest.MustRender(page)
|
||||
|
||||
outputPath := "templates/output/07-simple-page-fixed-indent.html"
|
||||
outputContents, err := ioutil.ReadFile(outputPath)
|
||||
if err != nil {
|
||||
t.Fatalf("error reading file (`%s`): %+v", outputPath, err)
|
||||
}
|
||||
|
||||
assert.Equal(
|
||||
t,
|
||||
strings.TrimSpace(string(outputContents)),
|
||||
render,
|
||||
"Rendered output does not match expected output",
|
||||
)
|
||||
}
|
Loading…
Reference in New Issue
Block a user