plush

package module
v4.1.19 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 27, 2023 License: MIT Imports: 20 Imported by: 88

README

Plush

Standard Test Go Reference

Plush is the templating system that Go both needs and deserves. Powerful, flexible, and extendable, Plush is there to make writing your templates that much easier.

Introduction Video

Installation

$ go get -u github.com/gobuffalo/plush

Usage

Plush allows for the embedding of dynamic code inside of your templates. Take the following example:

<!-- input -->
<p><%= "plush is great" %></p>

<!-- output -->
<p>plush is great</p>
Controlling Output

By using the <%= %> tags we tell Plush to dynamically render the inner content, in this case the string plush is great, into the template between the <p></p> tags.

If we were to change the example to use <% %> tags instead the inner content will be evaluated and executed, but not injected into the template:

<!-- input -->
<p><% "plush is great" %></p>

<!-- output -->
<p></p>

By using the <% %> tags we can create variables (and functions!) inside of templates to use later:

<!-- does not print output -->
<%
let h = {name: "mark"}
let greet = fn(n) {
  return "hi " + n
}
%>
<!-- prints output -->
<h1><%= greet(h["name"]) %></h1>
Full Example:
html := `<html>
<%= if (names && len(names) > 0) { %>
	<ul>
		<%= for (n) in names { %>
			<li><%= capitalize(n) %></li>
		<% } %>
	</ul>
<% } else { %>
	<h1>Sorry, no names. :(</h1>
<% } %>
</html>`

ctx := plush.NewContext()
ctx.Set("names", []string{"john", "paul", "george", "ringo"})

s, err := plush.Render(html, ctx)
if err != nil {
  log.Fatal(err)
}

fmt.Print(s)
// output: <html>
// <ul>
// 		<li>John</li>
// 		<li>Paul</li>
// 		<li>George</li>
// 		<li>Ringo</li>
// 		</ul>
// </html>

Comments

You can add comments like this:

<%# This is a comment %>

You can also add line comments within a code section

<%
# this is a comment
not_a_comment()
%>

If/Else Statements

The basic syntax of if/else if/else statements is as follows:

<%
if (true) {
  # do something
} else if (false) {
  # do something
} else {
  # do something else
}
%>

When using if/else statements to control output, remember to use the <%= %> tag to output the result of the statement:

<%= if (true) { %>
  <!-- some html here -->
<% } else { %>
  <!-- some other html here -->
<% } %>
Operators

Complex if statements can be built in Plush using "common" operators:

  • == - checks equality of two expressions
  • != - checks that the two expressions are not equal
  • ~= - checks a string against a regular expression (foo ~= "^fo")
  • < - checks the left expression is less than the right expression
  • <= - checks the left expression is less than or equal to the right expression
  • > - checks the left expression is greater than the right expression
  • >= - checks the left expression is greater than or equal to the right expression
  • && - requires both the left and right expression to be true
  • || - requires either the left or right expression to be true
Grouped Expressions
<%= if ((1 < 2) && (someFunc() == "hi")) { %>
  <!-- some html here -->
<% } else { %>
  <!-- some other html here -->
<% } %>

Maps

Maps in Plush will get translated to the Go type map[string]interface{} when used. Creating, and using maps in Plush is not too different than in JSON:

<% let h = {key: "value", "a number": 1, bool: true} %>

Would become the following in Go:

map[string]interface{}{
  "key": "value",
  "a number": 1,
  "bool": true,
}

Accessing maps is just like access a JSON object:

<%= h["key"] %>

Using maps as options to functions in Plush is incredibly powerful. See the sections on Functions and Helpers to see more examples.

Arrays

Arrays in Plush will get translated to the Go type []interface{} when used.

<% let a = [1, 2, "three", "four", h] %>
[]interface{}{ 1, 2, "three", "four", h }

For Loops

There are three different types that can be looped over: maps, arrays/slices, and iterators. The format for them all looks the same:

<%= for (key, value) in expression { %>
  <%= key %> <%= value %>
<% } %>

You can also continue to the next iteration of the loop:

for (i,v) in [1, 2, 3,4,5,6,7,8,9,10] {
  if (i > 0) {
    continue
  }
  return v   
} 

You can terminate the for loop with break:

for (i,v) in [1, 2, 3,4,5,6,7,8,9,10] {
  if (i > 5) {
    break
  }
  return v   
} 

The values inside the () part of the statement are the names you wish to give to the key (or index) and the value of the expression. The expression can be an array, map, or iterator type.

Arrays
Using Index and Value
<%= for (i, x) in someArray { %>
  <%= i %> <%= x %>
<% } %>
Using Just the Value
<%= for (val) in someArray { %>
  <%= val %>
<% } %>
Maps
Using Index and Value
<%= for (k, v) in someMap { %>
  <%= k %> <%= v %>
<% } %>
Using Just the Value
<%= for (v) in someMap { %>
  <%= v %>
<% } %>
Iterators
type ranger struct {
	pos int
	end int
}

func (r *ranger) Next() interface{} {
	if r.pos < r.end {
		r.pos++
		return r.pos
	}
	return nil
}

func betweenHelper(a, b int) Iterator {
	return &ranger{pos: a, end: b - 1}
}
html := `<%= for (v) in between(3,6) { return v } %>`

ctx := plush.NewContext()
ctx.Set("between", betweenHelper)

s, err := plush.Render(html, ctx)
if err != nil {
  log.Fatal(err)
}
fmt.Print(s)
// output: 45

Helpers

For a full list, and documentation of, all the Helpers included in Plush, see github.com/gobuffalo/helpers.

Custom Helpers
html := `<p><%= one() %></p>
<p><%= greet("mark")%></p>
<%= can("update") { %>
<p>i can update</p>
<% } %>
<%= can("destroy") { %>
<p>i can destroy</p>
<% } %>
`

ctx := NewContext()

// one() #=> 1
ctx.Set("one", func() int {
  return 1
})

// greet("mark") #=> "Hi mark"
ctx.Set("greet", func(s string) string {
  return fmt.Sprintf("Hi %s", s)
})

// can("update") #=> returns the block associated with it
// can("adsf") #=> ""
ctx.Set("can", func(s string, help HelperContext) (template.HTML, error) {
  if s == "update" {
    h, err := help.Block()
    return template.HTML(h), err
  }
  return "", nil
})

s, err := Render(html, ctx)
if err != nil {
  log.Fatal(err)
}
fmt.Print(s)
// output: <p>1</p>
// <p>Hi mark</p>
// <p>i can update</p>
Special Thanks

This package absolutely 100% could not have been written without the help of Thorsten Ball's incredible book, Writing an Interpeter in Go.

Not only did the book make understanding the process of writing lexers, parsers, and asts, but it also provided the basis for the syntax of Plush itself.

If you have yet to read Thorsten's book, I can't recommend it enough. Please go and buy it!

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var BootstrapFormForHelper = bootstrap.FormFor

BootstrapFormForHelper is deprecated use github.com/gobuffalo/helpers/forms/bootstrap#FormFor instead.

View Source
var BootstrapFormHelper = bootstrap.Form

BootstrapFormHelper is deprecated use github.com/gobuffalo/helpers/forms/bootstrap#Form instead.

View Source
var CacheEnabled bool
View Source
var DefaultTimeFormat = "January 02, 2006 15:04:05 -0700"

DefaultTimeFormat is the default way of formatting a time.Time type. This a **GLOBAL** variable, so if you change it, it will change for templates rendered through the `plush` package. If you want to set a specific time format for a particular call to `Render` you can set the `TIME_FORMAT` in the context.

ctx.Set("TIME_FORMAT", "2006-02-Jan")
s, err = Render(input, ctx)
View Source
var FormForHelper = forms.FormFor

FormForHelper is deprecated use github.com/gobuffalo/helpers/forms#FormFor instead.

View Source
var FormHelper = forms.Form

FormHelper is deprecated use github.com/gobuffalo/helpers/forms#Form instead.

View Source
var Helpers = HelperMap{
	// contains filtered or unexported fields
}

Helpers contains all of the default helpers for These will be available to all templates. You should add any custom global helpers to this list.

Functions

func BuffaloRenderer

func BuffaloRenderer(input string, data map[string]interface{}, helpers map[string]interface{}) (string, error)

BuffaloRenderer implements the render.TemplateEngine interface allowing velvet to be used as a template engine for Buffalo

func CacheSet added in v4.1.19

func CacheSet(key string, t *Template)

func GroupByHelper added in v4.1.19

func GroupByHelper(size int, underlying interface{}) (*groupBy, error)

func MarkdownHelper

func MarkdownHelper(body string, help HelperContext) (template.HTML, error)

Markdown converts the string into HTML using GitHub flavored markdown.

func PartialHelper added in v4.1.19

func PartialHelper(name string, data map[string]interface{}, help HelperContext) (template.HTML, error)

func Render

func Render(input string, ctx hctx.Context) (string, error)

Render a string using the given the context.

Example

ExampleRender using `if`, `for`, `else`, functions, etc...

html := `<html>
<%= if (names && len(names) > 0) { %>
<ul>
<%= for (n) in names { %>
	<li><%= capitalize(n) %></li>
<% } %>
</ul>
<% } else { %>
	<h1>Sorry, no names. :(</h1>
<% } %>
</html>`

ctx := plush.NewContext()
ctx.Set("names", []string{"john", "paul", "george", "ringo"})

s, err := plush.Render(html, ctx)
if err != nil {
	log.Fatal(err)
}

fmt.Print(s)
Output:

<html>

<ul>

	<li>John</li>

	<li>Paul</li>

	<li>George</li>

	<li>Ringo</li>

</ul>

</html>
Example (CustomHelperFunctions)
html := `<p><%= one() %></p>
<p><%= greet("mark")%></p>
<%= can("update") { %>
<p>i can update</p>
<% } %>
<%= can("destroy") { %>
<p>i can destroy</p>
<% } %>
`

ctx := plush.NewContext()
ctx.Set("one", func() int {
	return 1
})
ctx.Set("greet", func(s string) string {
	return fmt.Sprintf("Hi %s", s)
})
ctx.Set("can", func(s string, help plush.HelperContext) (template.HTML, error) {
	if s == "update" {
		h, err := help.Block()
		return template.HTML(h), err
	}
	return "", nil
})

s, err := plush.Render(html, ctx)
if err != nil {
	log.Fatal(err)
}
fmt.Print(s)
Output:

<p>1</p>
<p>Hi mark</p>

<p>i can update</p>
Example (ForIterator)
html := `<%= for (v) in between(3,6) { %><%=v%><% } %>`

s, err := plush.Render(html, plush.NewContext())
if err != nil {
	log.Fatal(err)
}
fmt.Print(s)
Output:

45
Example (ScripletTags)
html := `<%
let h = {name: "mark"}
let greet = fn(n) {
  return "hi " + n
}
%>
<h1><%= greet(h["name"]) %></h1>`

s, err := plush.Render(html, plush.NewContext())
if err != nil {
	log.Fatal(err)
}
fmt.Print(s)
Output:

<h1>hi mark</h1>

func RenderR

func RenderR(input io.Reader, ctx hctx.Context) (string, error)

func RunScript

func RunScript(input string, ctx hctx.Context) error

RunScript allows for "pure" plush scripts to be executed.

Types

type Context

type Context struct {
	context.Context
	// contains filtered or unexported fields
}

Context holds all of the data for the template that is being rendered.

func NewContext

func NewContext() *Context

NewContext returns a fully formed context ready to go

func NewContextWith

func NewContextWith(data map[string]interface{}) *Context

NewContextWith returns a fully formed context using the data provided.

func NewContextWithContext

func NewContextWithContext(ctx context.Context) *Context

NewContextWithContext returns a new plush.Context given another context

func NewContextWithOuter added in v4.1.5

func NewContextWithOuter(data map[string]interface{}, out *Context) *Context

NewContextWith returns a fully formed context using the data provided and setting the outer context with the passed seccond argument.

func (*Context) Has

func (c *Context) Has(key string) bool

Has checks the existence of the key in the context.

func (*Context) New

func (c *Context) New() hctx.Context

New context containing the current context. Values set on the new context will not be set onto the original context, however, the original context's values will be available to the new context.

func (*Context) Set

func (c *Context) Set(key string, value interface{})

Set a value onto the context

func (*Context) Value

func (c *Context) Value(key interface{}) interface{}

Value from the context, or it's parent's context if one exists.

type ErrUnknownIdentifier

type ErrUnknownIdentifier struct {
	ID  string
	Err error
}

func (*ErrUnknownIdentifier) Error

func (e *ErrUnknownIdentifier) Error() string

type HTMLer

type HTMLer interface {
	HTML() template.HTML
}

HTMLer generates HTML source

type HelperContext

type HelperContext struct {
	hctx.Context
	// contains filtered or unexported fields
}

HelperContext is an optional last argument to helpers that provides the current context of the call, and access to an optional "block" of code that can be executed from within the helper.

func (HelperContext) Block

func (h HelperContext) Block() (string, error)

Block executes the block of template associated with the helper, think the block inside of an "if" or "each" statement.

func (HelperContext) BlockWith

func (h HelperContext) BlockWith(hc hctx.Context) (string, error)

BlockWith executes the block of template associated with the helper, think the block inside of an "if" or "each" statement, but with it's own context.

func (HelperContext) HasBlock

func (h HelperContext) HasBlock() bool

HasBlock returns true if a block is associated with the helper function

func (HelperContext) Render

func (h HelperContext) Render(s string) (string, error)

Render a string with the current context

type HelperMap

type HelperMap struct {
	// contains filtered or unexported fields
}

HelperMap holds onto helpers and validates they are properly formed.

func NewHelperMap

func NewHelperMap() (HelperMap, error)

NewHelperMap containing all of the "default" helpers from "plush.Helpers".

func (*HelperMap) Add

func (h *HelperMap) Add(key string, helper interface{}) error

Add a new helper to the map. New Helpers will be validated to ensure they meet the requirements for a helper:

func (*HelperMap) AddMany

func (h *HelperMap) AddMany(helpers map[string]interface{}) error

AddMany helpers at the same time.

func (HelperMap) Helpers

func (h HelperMap) Helpers() map[string]interface{}

Helpers returns the underlying list of helpers from the map

type Iterator

type Iterator interface {
	Next() interface{}
}

Iterator type can be implemented and used by the `for` command to build loops in templates

type PartialFeeder

type PartialFeeder func(string) (string, error)

PartialFeeder is callback function should implemented on application side.

type Template

type Template struct {
	Input string
	// contains filtered or unexported fields
}

Template represents an input and helpers to be used to evaluate and render the input.

func NewTemplate

func NewTemplate(input string) (*Template, error)

NewTemplate from the input string. Adds all of the global helper functions from "Helpers", this function does not cache the template.

func Parse

func Parse(input string) (*Template, error)

Parse an input string and return a Template, and caches the parsed template.

func (*Template) Clone

func (t *Template) Clone() *Template

Clone a template. This is useful for defining helpers on per "instance" of the template.

func (*Template) Exec

func (t *Template) Exec(ctx hctx.Context) (string, error)

Exec the template using the content and return the results

func (*Template) Parse

func (t *Template) Parse() error

Parse the template this can be called many times as a successful result is cached and is used on subsequent uses.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL