From eae6e13bdbfafe8655762ac956857c87a435c522 Mon Sep 17 00:00:00 2001 From: Mark Bates Date: Wed, 31 Oct 2018 09:34:28 -0400 Subject: [PATCH] added partial helper (tweaked) (#73) --- compiler.go | 5 ++ helpers.go | 1 + partial_helper.go | 49 +++++++++++ partial_helper_test.go | 190 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 245 insertions(+) create mode 100644 partial_helper.go create mode 100644 partial_helper_test.go diff --git a/compiler.go b/compiler.go index 273e975..5403d0f 100644 --- a/compiler.go +++ b/compiler.go @@ -635,6 +635,11 @@ func (c *compiler) evalForExpression(node *ast.ForExpression) (interface{}, erro c.ctx = octx }() c.ctx = octx.New() + // must copy all data from original (it includes application defined helpers) + for k, v := range octx.data { + c.ctx.data[k] = v + } + iter, err := c.evalExpression(node.Iterable) if err != nil { return nil, errors.WithStack(err) diff --git a/helpers.go b/helpers.go index 41976d7..7860744 100644 --- a/helpers.go +++ b/helpers.go @@ -44,6 +44,7 @@ func init() { Helpers.Add("truncate", truncateHelper) Helpers.Add("env", envy.MustGet) Helpers.Add("envOr", envy.Get) + Helpers.Add("partial", partialHelper) Helpers.Add("raw", func(s string) template.HTML { return template.HTML(s) }) diff --git a/partial_helper.go b/partial_helper.go new file mode 100644 index 0000000..579e33d --- /dev/null +++ b/partial_helper.go @@ -0,0 +1,49 @@ +package plush + +import ( + "html/template" + "strings" + + "github.com/pkg/errors" +) + +// PartialFeeder is callback function should implemented on application side. +type PartialFeeder func(string) (string, error) + +func partialHelper(name string, data map[string]interface{}, help HelperContext) (template.HTML, error) { + if help.Context == nil || help.Context.data == nil { + return "", errors.New("invalid context. abort") + } + for k, v := range data { + help.Set(k, v) + } + + pf, ok := help.Value("partialFeeder").(func(string) (string, error)) + if !ok { + return "", errors.New("could not found partial feeder from helpers") + } + + var part string + var err error + if part, err = pf(name); err != nil { + return "", err + } + + if part, err = Render(part, help.Context); err != nil { + return "", err + } + + if layout, ok := data["layout"].(string); ok { + return partialHelper( + layout, + map[string]interface{}{"yield": template.HTML(part)}, + help) + } + + if ct, ok := help.Value("contentType").(string); ok { + if strings.Contains(ct, "javascript") && strings.HasSuffix(name, ".html") { + part = template.JSEscapeString(string(part)) + } + } + return template.HTML(part), err +} diff --git a/partial_helper_test.go b/partial_helper_test.go new file mode 100644 index 0000000..56fdc6f --- /dev/null +++ b/partial_helper_test.go @@ -0,0 +1,190 @@ +package plush + +import ( + "testing" + + "github.com/pkg/errors" + "github.com/stretchr/testify/require" +) + +func Test_PartialHelper_Nil_Context(t *testing.T) { + r := require.New(t) + + name := "index" + data := map[string]interface{}{} + help := HelperContext{} + + html, err := partialHelper(name, data, help) + r.Error(err) + r.Contains(err.Error(), "invalid context") + r.Equal("", string(html)) +} + +func Test_PartialHelper_Blank_Data(t *testing.T) { + r := require.New(t) + + name := "index" + data := map[string]interface{}{} + help := HelperContext{Context: NewContext()} + help.Context.data = nil + + html, err := partialHelper(name, data, help) + r.Error(err) + r.Contains(err.Error(), "invalid context") + r.Equal("", string(html)) +} + +func Test_PartialHelper_Blank_Context(t *testing.T) { + r := require.New(t) + + name := "index" + data := map[string]interface{}{} + help := HelperContext{Context: NewContext()} + + html, err := partialHelper(name, data, help) + r.Error(err) + r.Contains(err.Error(), "could not found") + r.Equal("", string(html)) +} + +func Test_PartialHelper_Invalid_Feeder(t *testing.T) { + r := require.New(t) + + name := "index" + data := map[string]interface{}{} + help := HelperContext{Context: NewContext()} + help.Set("partialFeeder", "me-rong") + + html, err := partialHelper(name, data, help) + r.Error(err) + r.Contains(err.Error(), "could not found") + r.Equal("", string(html)) +} + +func Test_PartialHelper_Invalid_FeederFunction(t *testing.T) { + r := require.New(t) + + name := "index" + data := map[string]interface{}{} + help := HelperContext{Context: NewContext()} + help.Set("partialFeeder", func(string) string { + return "me-rong" + }) + + html, err := partialHelper(name, data, help) + r.Error(err) + r.Contains(err.Error(), "could not found") + r.Equal("", string(html)) +} + +func Test_PartialHelper_Feeder_Error(t *testing.T) { + r := require.New(t) + + name := "index" + data := map[string]interface{}{} + help := HelperContext{Context: NewContext()} + help.Set("partialFeeder", func(string) (string, error) { + return "", errors.New("me-rong") + }) + + _, err := partialHelper(name, data, help) + r.Error(err) + r.Contains(err.Error(), "me-rong") +} + +func Test_PartialHelper_Good(t *testing.T) { + r := require.New(t) + + name := "index" + data := map[string]interface{}{} + help := HelperContext{Context: NewContext()} + help.Set("partialFeeder", func(string) (string, error) { + return `
Plush!
`, nil + }) + + html, err := partialHelper(name, data, help) + r.NoError(err) + r.Equal(`
Plush!
`, string(html)) +} + +func Test_PartialHelper_With_Data(t *testing.T) { + r := require.New(t) + + name := "index" + data := map[string]interface{}{"name": "Yonghwan"} + help := HelperContext{Context: NewContext()} + help.Set("partialFeeder", func(string) (string, error) { + return `
Hello <%= name %>
`, nil + }) + + html, err := partialHelper(name, data, help) + r.NoError(err) + r.Equal(`
Hello Yonghwan
`, string(html)) +} + +func Test_PartialHelper_Render_Error(t *testing.T) { + r := require.New(t) + + name := "index" + data := map[string]interface{}{} + help := HelperContext{Context: NewContext()} + help.Set("partialFeeder", func(string) (string, error) { + return `
Hello <%= name
`, nil + }) + + _, err := partialHelper(name, data, help) + r.Error(err) +} + +func Test_PartialHelper_With_Layout(t *testing.T) { + r := require.New(t) + + name := "index" + data := map[string]interface{}{ + "name": "Yonghwan", + "layout": "container", + } + help := HelperContext{Context: NewContext()} + help.Set("partialFeeder", func(name string) (string, error) { + if name == "container" { + return `<%= yield %>`, nil + } + return `
Hello <%= name %>
`, nil + }) + + html, err := partialHelper(name, data, help) + r.NoError(err) + r.Equal(`
Hello Yonghwan
`, string(html)) +} + +func Test_PartialHelper_JavaScript(t *testing.T) { + r := require.New(t) + + name := "index.js" + data := map[string]interface{}{} + help := HelperContext{Context: NewContext()} + help.Set("contentType", "application/javascript") + help.Set("partialFeeder", func(string) (string, error) { + return `alert('\'Hello\'');`, nil + }) + + html, err := partialHelper(name, data, help) + r.NoError(err) + r.Equal(`alert('\'Hello\'');`, string(html)) +} + +func Test_PartialHelper_Javascript_With_HTML(t *testing.T) { + r := require.New(t) + + name := "index.html" + data := map[string]interface{}{} + help := HelperContext{Context: NewContext()} + help.Set("contentType", "application/javascript") + help.Set("partialFeeder", func(string) (string, error) { + return `alert('\'Hello\'');`, nil + }) + + html, err := partialHelper(name, data, help) + r.NoError(err) + r.Equal(`alert(\'\\\'Hello\\\'\');`, string(html)) +}