-
Notifications
You must be signed in to change notification settings - Fork 52
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #22 from Fenny/master
Bump dependencies
- Loading branch information
Showing
10 changed files
with
345 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
package slim | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"io" | ||
"net/http" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
"sync" | ||
|
||
"github.com/gofiber/template/utils" | ||
"github.com/mattn/go-slim" | ||
"github.com/valyala/bytebufferpool" | ||
) | ||
|
||
// Engine struct | ||
type Engine struct { | ||
// delimiters | ||
left string | ||
right string | ||
// views folder | ||
directory string | ||
// http.FileSystem supports embedded files | ||
fileSystem http.FileSystem | ||
// views extension | ||
extension string | ||
// layout variable name that incapsulates the template | ||
layout string | ||
// reload on each render | ||
reload bool | ||
// debug prints the parsed templates | ||
debug bool | ||
// lock for funcmap and templates | ||
mutex sync.RWMutex | ||
// template funcmap | ||
funcmap map[string]interface{} | ||
// templates | ||
Templates map[string]*slim.Template | ||
} | ||
|
||
// New returns a Handlebar render engine for Fiber | ||
func New(directory, extension string) *Engine { | ||
engine := &Engine{ | ||
left: "{{", | ||
right: "}}", | ||
directory: directory, | ||
extension: extension, | ||
layout: "embed", | ||
funcmap: make(map[string]interface{}), | ||
} | ||
return engine | ||
} | ||
|
||
func NewFileSystem(fs http.FileSystem, extension string) *Engine { | ||
engine := &Engine{ | ||
left: "{{", | ||
right: "}}", | ||
directory: "/", | ||
fileSystem: fs, | ||
extension: extension, | ||
layout: "embed", | ||
funcmap: make(map[string]interface{}), | ||
} | ||
return engine | ||
} | ||
|
||
// Layout defines the variable name that will incapsulate the template | ||
func (e *Engine) Layout(key string) *Engine { | ||
e.layout = key | ||
return e | ||
} | ||
|
||
// Delims sets the action delimiters to the specified strings, to be used in | ||
// templates. An empty delimiter stands for the | ||
// corresponding default: {{ or }}. | ||
func (e *Engine) Delims(left, right string) *Engine { | ||
fmt.Println("delims: this method is not supported for django") | ||
return e | ||
} | ||
|
||
// AddFunc adds the function to the template's function map. | ||
// It is legal to overwrite elements of the default actions | ||
func (e *Engine) AddFunc(name string, fn interface{}) *Engine { | ||
e.mutex.Lock() | ||
e.funcmap[name] = fn | ||
e.mutex.Unlock() | ||
return e | ||
} | ||
|
||
// Reload if set to true the templates are reloading on each render, | ||
// use it when you're in development and you don't want to restart | ||
// the application when you edit a template file. | ||
func (e *Engine) Reload(enabled bool) *Engine { | ||
e.reload = enabled | ||
return e | ||
} | ||
|
||
// Debug will print the parsed templates when Load is triggered. | ||
func (e *Engine) Debug(enabled bool) *Engine { | ||
e.debug = enabled | ||
return e | ||
} | ||
|
||
// Parse is deprecated, please use Load() instead | ||
func (e *Engine) Parse() error { | ||
fmt.Println("Parse() is deprecated, please use Load() instead.") | ||
return e.Load() | ||
} | ||
|
||
// Load parses the templates to the engine. | ||
func (e *Engine) Load() error { | ||
// race safe | ||
e.mutex.Lock() | ||
defer e.mutex.Unlock() | ||
|
||
e.Templates = make(map[string]*slim.Template) | ||
|
||
// Loop trough each directory and register template files | ||
walkFn := func(path string, info os.FileInfo, err error) error { | ||
// Return error if exist | ||
if err != nil { | ||
return err | ||
} | ||
// Skip file if it's a directory or has no file info | ||
if info == nil || info.IsDir() { | ||
return nil | ||
} | ||
// Get file extension of file | ||
ext := filepath.Ext(path) | ||
// Skip file if it does not equal the given template extension | ||
if ext != e.extension { | ||
return nil | ||
} | ||
// Get the relative file path | ||
// ./views/html/index.tmpl -> index.tmpl | ||
rel, err := filepath.Rel(e.directory, path) | ||
if err != nil { | ||
return err | ||
} | ||
// Reverse slashes '\' -> '/' and | ||
// partials\footer.tmpl -> partials/footer.tmpl | ||
name := filepath.ToSlash(rel) | ||
// Remove ext from name 'index.tmpl' -> 'index' | ||
name = strings.Replace(name, e.extension, "", -1) | ||
// Read the file | ||
// #gosec G304 | ||
buf, err := utils.ReadFile(path, e.fileSystem) | ||
if err != nil { | ||
return err | ||
} | ||
// Create new template associated with the current one | ||
tmpl, err := slim.Parse(bytes.NewReader(buf)) | ||
if err != nil { | ||
return err | ||
} | ||
// tmpl.FuncMap(e.funcmap) | ||
e.Templates[name] = tmpl | ||
// Debugging | ||
if e.debug { | ||
fmt.Printf("views: parsed template: %s\n", name) | ||
} | ||
return err | ||
} | ||
if e.fileSystem != nil { | ||
return utils.Walk(e.fileSystem, e.directory, walkFn) | ||
} | ||
return filepath.Walk(e.directory, walkFn) | ||
} | ||
|
||
// Execute will render the template by name | ||
func (e *Engine) Render(out io.Writer, template string, binding interface{}, layout ...string) error { | ||
if e.reload { | ||
if err := e.Load(); err != nil { | ||
return err | ||
} | ||
} | ||
tmpl := e.Templates[template] | ||
if tmpl == nil { | ||
return fmt.Errorf("render: template %s does not exist", template) | ||
} | ||
if len(layout) > 0 { | ||
buf := bytebufferpool.Get() | ||
defer bytebufferpool.Put(buf) | ||
if err := tmpl.Execute(buf, binding); err != nil { | ||
return err | ||
} | ||
var bind map[string]interface{} | ||
if bind == nil { | ||
bind = make(map[string]interface{}, 1) | ||
} else if context, ok := binding.(map[string]interface{}); ok { | ||
bind = context | ||
} else { | ||
bind = make(map[string]interface{}, 1) | ||
} | ||
bind[e.layout] = buf.String() | ||
lay := e.Templates[layout[0]] | ||
if lay == nil { | ||
return fmt.Errorf("render: layout %s does not exist", layout[0]) | ||
} | ||
return lay.Execute(out, bind) | ||
} | ||
return tmpl.Execute(out, binding) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
package slim | ||
|
||
// import ( | ||
// "bytes" | ||
// "io/ioutil" | ||
// "net/http" | ||
// "regexp" | ||
// "strings" | ||
// "testing" | ||
// ) | ||
|
||
// func trim(str string) string { | ||
// trimmed := strings.TrimSpace(regexp.MustCompile(`\s+`).ReplaceAllString(str, " ")) | ||
// trimmed = strings.Replace(trimmed, " <", "<", -1) | ||
// trimmed = strings.Replace(trimmed, "> ", ">", -1) | ||
// return trimmed | ||
// } | ||
|
||
// func Test_Render(t *testing.T) { | ||
// engine := New("./views", ".slim").Debug(true) | ||
// if err := engine.Load(); err != nil { | ||
// t.Fatalf("load: %v\n", err) | ||
// } | ||
// // Partials | ||
// var buf bytes.Buffer | ||
// if err := engine.Render(&buf, "index", map[string]interface{}{ | ||
// "Title": "Hello, World!", | ||
// }); err != nil { | ||
// t.Fatalf("render: %v\n", err) | ||
// } | ||
// expect := `<h2>Header</h2><h1>Hello, World!</h1><h2>Footer</h2>` | ||
// result := trim(buf.String()) | ||
// if expect != result { | ||
// t.Fatalf("Expected:\n%s\nResult:\n%s\n", expect, result) | ||
// } | ||
// // Single | ||
// buf.Reset() | ||
// engine.Render(&buf, "errors/404", map[string]interface{}{ | ||
// "title": "Hello, World!", | ||
// }) | ||
// expect = `<h1>Hello, World!</h1>` | ||
// result = trim(buf.String()) | ||
// if expect != result { | ||
// t.Fatalf("Expected:\n%s\nResult:\n%s\n", expect, result) | ||
// } | ||
// } | ||
|
||
// func Test_Layout(t *testing.T) { | ||
// engine := New("./views", ".slim") | ||
// engine.Debug(true) | ||
// if err := engine.Load(); err != nil { | ||
// t.Fatalf("load: %v\n", err) | ||
// } | ||
|
||
// var buf bytes.Buffer | ||
// err := engine.Render(&buf, "index", map[string]interface{}{ | ||
// "Title": "Hello, World!", | ||
// }, "layouts/main") | ||
// if err != nil { | ||
// t.Fatalf("render: %v", err) | ||
// } | ||
// expect := `<!DOCTYPE html><html><head><title>Main</title></head><body><h2>Header</h2><h1>Hello, World!</h1><h2>Footer</h2></body></html>` | ||
// result := trim(buf.String()) | ||
// if expect != result { | ||
// t.Fatalf("Expected:\n%s\nResult:\n%s\n", expect, result) | ||
// } | ||
// } | ||
|
||
// func Test_FileSystem(t *testing.T) { | ||
// engine := NewFileSystem(http.Dir("./views"), ".slim") | ||
// engine.Debug(true) | ||
// if err := engine.Load(); err != nil { | ||
// t.Fatalf("load: %v\n", err) | ||
// } | ||
|
||
// var buf bytes.Buffer | ||
// err := engine.Render(&buf, "index", map[string]interface{}{ | ||
// "Title": "Hello, World!", | ||
// }, "layouts/main") | ||
// if err != nil { | ||
// t.Fatalf("render: %v", err) | ||
// } | ||
// expect := `<!DOCTYPE html><html><head><title>Main</title></head><body><h2>Header</h2><h1>Hello, World!</h1><h2>Footer</h2></body></html>` | ||
// result := trim(buf.String()) | ||
// if expect != result { | ||
// t.Fatalf("Expected:\n%s\nResult:\n%s\n", expect, result) | ||
// } | ||
// } | ||
|
||
// func Test_Reload(t *testing.T) { | ||
// engine := NewFileSystem(http.Dir("./views"), ".ace") | ||
// engine.Reload(true) // Optional. Default: false | ||
|
||
// engine.AddFunc("isAdmin", func(user string) bool { | ||
// return user == "admin" | ||
// }) | ||
// if err := engine.Load(); err != nil { | ||
// t.Fatalf("load: %v\n", err) | ||
// } | ||
|
||
// if err := ioutil.WriteFile("./views/reload.ace", []byte("after reload\n"), 0644); err != nil { | ||
// t.Fatalf("write file: %v\n", err) | ||
// } | ||
// defer func() { | ||
// if err := ioutil.WriteFile("./views/reload.ace", []byte("before reload\n"), 0644); err != nil { | ||
// t.Fatalf("write file: %v\n", err) | ||
// } | ||
// }() | ||
|
||
// engine.Load() | ||
|
||
// var buf bytes.Buffer | ||
// engine.Render(&buf, "reload", nil) | ||
// expect := "<after>reload</after>" | ||
// result := trim(buf.String()) | ||
// if expect != result { | ||
// t.Fatalf("Expected:\n%s\nResult:\n%s\n", expect, result) | ||
// } | ||
// } |
Oops, something went wrong.