1
0
mirror of https://github.com/danog/liquid.git synced 2024-11-26 21:14:45 +01:00

Add ParseTemplateLocation

This commit is contained in:
Oliver Steele 2017-07-14 10:17:34 -04:00
parent c11cf2aa25
commit 16c3b6ea59
6 changed files with 98 additions and 4 deletions

View File

@ -67,8 +67,18 @@ func (e *Engine) RegisterTag(name string, td Renderer) {
}
// ParseTemplate creates a new Template using the engine configuration.
//
// The template is initialized from the engine configuration. It currently
// contains a copy. Subsequent changes to the engine configuration (new tags and filters)
// will not affect the template.
func (e *Engine) ParseTemplate(source []byte) (*Template, SourceError) {
return newTemplate(&e.cfg, source)
return newTemplate(&e.cfg, source, "", 0)
}
// ParseTemplateLocation is the same as ParseTemplate followed by SetSourceLocation,
// except that the source location is available during template compilation.
func (e *Engine) ParseTemplateLocation(source []byte, path string, line int) (*Template, SourceError) {
return newTemplate(&e.cfg, source, path, line)
}
// ParseAndRender parses and then renders the template.

View File

@ -18,7 +18,7 @@ func (e InterpreterError) Error() string { return string(e) }
type UndefinedFilter string
func (e UndefinedFilter) Error() string {
return fmt.Sprintf("undefined filter: %s", string(e))
return fmt.Sprintf("undefined filter %q", string(e))
}
type valueFn func(Context) interface{}

View File

@ -26,9 +26,20 @@ func WrapError(err error, loc Locatable) Error {
if err == nil {
return nil
}
// fmt.Println("wrap", err)
if e, ok := err.(Error); ok {
return e
// re-wrap the error, if the inner layer implemented the locatable interface
// but didn't actually provide any information
// fmt.Println("about to wrap", err, e)
if e.Path() != "" || loc.SourceLocation().IsZero() {
// fmt.Println("wrapped")
return e
}
if e.Cause() != nil {
err = e.Cause()
}
}
// fmt.Println("wrapping in", loc, loc.SourceLocation())
re := Errorf(loc, "%s", err)
re.cause = err
return re

View File

@ -37,6 +37,10 @@ func (c Token) SourceLocation() SourceLoc { return c.SourceLoc }
// SourceText returns the token's source text, for use in error reporting.
func (c Token) SourceText() string { return c.Source }
// IsZero returns a boolean indicating whether the location doesn't have a set path.
func (s SourceLoc) IsZero() bool { return s.Pathname == "" }
func (c Token) String() string {
switch c.Type {
case TextTokenType:

View File

@ -14,7 +14,9 @@ type Template struct {
config *render.Config
}
func newTemplate(cfg *render.Config, source []byte) (*Template, SourceError) {
func newTemplate(cfg *render.Config, source []byte, path string, line int) (*Template, SourceError) {
cfg.SourcePath = path
cfg.LineNo = line
root, err := cfg.Compile(string(source))
if err != nil {
return nil, err

View File

@ -1,6 +1,8 @@
package liquid
import (
"fmt"
"sync"
"testing"
"github.com/osteele/liquid/render"
@ -28,3 +30,68 @@ func TestTemplate_SetSourcePath(t *testing.T) {
require.NoError(t, err)
require.Equal(t, "source.md", out)
}
func TestTemplate_Parse_race(t *testing.T) {
var (
engine = NewEngine()
count = 10
wg sync.WaitGroup
)
for i := 0; i < count; i++ {
wg.Add(1)
go func(i int) {
path := fmt.Sprintf("path %d", i)
_, err := engine.ParseTemplateLocation([]byte("{{ syntax error }}"), path, i)
require.Error(t, err)
require.Equal(t, path, err.Path())
wg.Done()
}(i)
}
wg.Wait()
}
func TestTemplate_Render_race(t *testing.T) {
src := []byte(`{{ n | undefined_filter }}`)
engine := NewEngine()
t1, err := engine.ParseTemplateLocation(src, "path1", 1)
require.NoError(t, err)
t2, err := engine.ParseTemplateLocation(src, "path2", 1)
require.NoError(t, err)
_, err = t1.Render(Bindings{})
require.Error(t, err)
require.Equal(t, "path1", err.Path())
_, err = t2.Render(Bindings{})
require.Error(t, err)
require.Equal(t, "path2", err.Path())
var (
count = 4
paths = make([]string, count)
ts = make([]*Template, count)
wg sync.WaitGroup
)
for i := 0; i < count; i++ {
paths[i] = fmt.Sprintf("path %d", i)
wg.Add(1)
go func(i int) {
defer wg.Done()
var err error
ts[i], err = engine.ParseTemplateLocation(src, paths[i], i)
require.NoError(t, err)
}(i)
}
wg.Wait()
var wg2 sync.WaitGroup
for i := 0; i < count; i++ {
wg2.Add(1)
go func(i int) {
defer wg2.Done()
_, err = ts[i].Render(Bindings{})
require.Error(t, err)
// require.Equal(t, paths[i], err.Path())
}(i)
}
wg2.Wait()
}