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
|
// Option holds configuration for TemplateNest
|
||||||
type Option struct {
|
type Option struct {
|
||||||
Delimiters [2]string
|
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
|
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
|
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
|
ShowLabels bool // Prepend & Append a string to every template, helpful in debugging
|
||||||
CommentDelimiters [2]string // Used in conjunction with ShowLabels, if HTML then use '<!--', '-->'
|
CommentDelimiters [2]string // Used in conjunction with ShowLabels, if HTML then use '<!--', '-->'
|
||||||
FixedIndent bool // Intended to improve readability when inspecting nested templates
|
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
|
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
|
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
|
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{} {
|
if opts.CommentDelimiters == [2]string{} {
|
||||||
opts.CommentDelimiters = [2]string{"<!--", "-->"}
|
opts.CommentDelimiters = [2]string{"<!--", "-->"}
|
||||||
}
|
}
|
||||||
if opts.NameLabel == "" {
|
if opts.NameLabel == nil {
|
||||||
opts.NameLabel = "TEMPLATE"
|
nameLabel := "TEMPLATE"
|
||||||
|
opts.NameLabel = &nameLabel
|
||||||
}
|
}
|
||||||
if opts.TemplateExtension == "" {
|
if opts.TemplateExtension == nil {
|
||||||
opts.TemplateExtension = "html"
|
ext := "html"
|
||||||
|
opts.TemplateExtension = &ext
|
||||||
}
|
}
|
||||||
if opts.DefaultsNamespaceChar == "" {
|
if opts.DefaultsNamespaceChar == nil {
|
||||||
opts.DefaultsNamespaceChar = "."
|
namespaceChar := "."
|
||||||
|
opts.DefaultsNamespaceChar = &namespaceChar
|
||||||
}
|
}
|
||||||
if opts.Defaults == nil {
|
if opts.Defaults == nil {
|
||||||
opts.Defaults = make(map[string]interface{})
|
opts.Defaults = make(map[string]interface{})
|
||||||
@ -90,7 +93,7 @@ func New(opts Option) (*TemplateNest, error) {
|
|||||||
nest := &TemplateNest{
|
nest := &TemplateNest{
|
||||||
option: opts,
|
option: opts,
|
||||||
cache: make(map[string]TemplateFileIndex),
|
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.
|
// Walk through the template directory and index the templates.
|
||||||
@ -99,8 +102,14 @@ func New(opts Option) (*TemplateNest, error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Skip directories
|
||||||
|
if info.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the file has the correct extension.
|
// 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,7 +126,11 @@ func New(opts Option) (*TemplateNest, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove the extension from the relative path.
|
// 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
|
nest.cache[templateName] = templateIndex
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@ -128,21 +141,25 @@ func New(opts Option) (*TemplateNest, error) {
|
|||||||
return nest, nil
|
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.
|
// 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{})
|
result := make(map[string]interface{})
|
||||||
for key, value := range input {
|
for key, value := range input {
|
||||||
fullKey := key
|
fullKey := key
|
||||||
if parentKey != "" {
|
if parentKey != "" {
|
||||||
fullKey = parentKey + separator + key
|
fullKey = parentKey + *separator + key
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the value is a nested map
|
// Check if the value is a nested map
|
||||||
if nestedMap, ok := value.(Hash); ok {
|
if nestedMap, ok := value.(Hash); ok {
|
||||||
for k, v := range FlattenMap(nestedMap, fullKey, separator) {
|
// 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
|
result[k] = v
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
result[fullKey] = value
|
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
|
// getTemplateFilePath takes a template name and returns the file path for the
|
||||||
// template.
|
// template.
|
||||||
func (nest *TemplateNest) getTemplateFilePath(templateName string) string {
|
func (nest *TemplateNest) getTemplateFilePath(templateName string) string {
|
||||||
|
if *nest.option.TemplateExtension == "" {
|
||||||
|
return filepath.Join(nest.option.TemplateDir, templateName)
|
||||||
|
}
|
||||||
|
|
||||||
return filepath.Join(
|
return filepath.Join(
|
||||||
nest.option.TemplateDir,
|
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) {
|
func (nest *TemplateNest) renderHash(hash map[string]interface{}) (string, error) {
|
||||||
tLabel, ok := hash[nest.option.NameLabel]
|
tLabel, ok := hash[*nest.option.NameLabel]
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", fmt.Errorf(
|
return "", fmt.Errorf(
|
||||||
"encountered hash with no name label (name label: `%s`)", nest.option.NameLabel,
|
"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
|
// 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.
|
// file and it's not the template label then it's a bad param.
|
||||||
_, exists := tIndex.VariableNames[k]
|
_, exists := tIndex.VariableNames[k]
|
||||||
if !exists && k != nest.option.NameLabel {
|
if !exists && k != *nest.option.NameLabel {
|
||||||
return "", fmt.Errorf(
|
return "", fmt.Errorf(
|
||||||
"bad params in template hash, variable not present in template file: `%s`", k,
|
"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