README ¶
Plush
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.
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 %>
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 %>
<% } %>
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
Builtin Helpers
json
- converts the interface to a JSON objectjsEscape
- escapes the interface to be JavaScript safehtmlEscape
- escapes the interface to be HTML safeupcase
- converts the string to upper casedowncase
- converts the string to lower casecontentFor
- stores a block of HTML to be used latercontentOf
- retrieves a block of HTML previously stored withcontentFor
markdown
- converts the string from Markdown into HTMLlen
- returns the length of the interfacedebug
- returns the%+v
of the interface wrapped in<pre>
tags.inspect
- returns the%+v
of the interfacerange
- interate between, and including two numbersbetween
- iterate between, but not including, two numbersuntil
- iterate until a number is reachedgroupBy
- splits a slice or array inton
groupsenv
- returns the ENV variable for the specified keytruncate
- truncates a string to a specified lengthform
- support for the github.com/gobuffalo/tags/form package (Bootstrap version)form_for
- support for the github.com/gobuffalo/tags/form package (Bootstrap version) to build a form for a model
contentFor and contentOf
Use the contentFor
and contentOf
helpers to dry up your templates with reusable components.
For example, we can define a snippet that generates a fancy title using contentFor
:
<% contentFor("fancy-title") { %>
<h1 class='fancy'><%= title %></h1>
<% } %>
The fancy-title
name is how we will invoke this with contentOf
elsewhere
in our template:
<%= contentOf("fancy-title", {"title":"Welcome to Plush"}) %>
- The second map argument is optional, for static content just use
<%= contentOf("fancy-title") %>
Rendering this would generate this output:
<h1 class='fancy'>Welcome to Plush</h1>
As you can see, the <%= title %>
has been replaced with the Welcome to Plush
string.
truncate
truncate
takes two optional parameters:
size
- the maximum length of the returned stringtrail
- the string to append at the end of a truncated string, defaults to...
<p><%= truncate("a long string", {"size": 10, "trail": "[more]"})%></p>
From github.com/markbates/inflect
asciffy
camelize
camelize_down_first
capitalize
dasherize
humanize
ordinalize
parameterize
pluralize
pluralize_with_size
singularize
tableize
typeify
underscore
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 ¶
- Constants
- Variables
- func BootstrapFormForHelper(model interface{}, opts tags.Options, help HelperContext) (template.HTML, error)
- func BootstrapFormHelper(opts tags.Options, help HelperContext) (template.HTML, error)
- func BuffaloRenderer(input string, data map[string]interface{}, helpers map[string]interface{}) (string, error)
- func FormForHelper(model interface{}, opts tags.Options, help HelperContext) (template.HTML, error)
- func FormHelper(opts tags.Options, help HelperContext) (template.HTML, error)
- func MarkdownHelper(body string, help HelperContext) (template.HTML, error)
- func Render(input string, ctx *Context) (string, error)
- func RunScript(input string, ctx *Context) error
- type Context
- type HTMLer
- type HelperContext
- type HelperMap
- type Iterator
- type Template
Examples ¶
Constants ¶
const Version = "v3.7.18"
Variables ¶
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)
var ErrUnknownIdentifier = errors.New("unknown identifier")
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 BootstrapFormForHelper ¶
func BootstrapFormForHelper(model interface{}, opts tags.Options, help HelperContext) (template.HTML, error)
BootstrapFormForHelper implements a Plush helper around the bootstrap.NewFormFor function in the github.com/gobuffalo/tags/form/bootstrap package
func BootstrapFormHelper ¶
BootstrapFormHelper implements a Plush helper around the bootstrap.New function in the github.com/gobuffalo/tags/form/bootstrap package
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 FormForHelper ¶
FormForHelper implements a Plush helper around the form.NewFormFor function in the github.com/gobuffalo/tags/form package
func FormHelper ¶
FormHelper implements a Plush helper around the form.New function in the github.com/gobuffalo/tags/form package
func MarkdownHelper ¶
func MarkdownHelper(body string, help HelperContext) (template.HTML, error)
Markdown converts the string into HTML using GitHub flavored markdown.
func Render ¶
Render a string using the given the context.
Example ¶
ExampleRender using `if`, `for`, `else`, functions, etc...
Output: <html> <ul> <li>John</li> <li>Paul</li> <li>George</li> <li>Ringo</li> </ul> </html>
Example (CustomHelperFunctions) ¶
Output: <p>1</p> <p>Hi mark</p> <p>i can update</p>
Example (ForIterator) ¶
Output: 45
Example (ScripletTags) ¶
Output: <h1>hi mark</h1>
Types ¶
type Context ¶
Context holds all of the data for the template that is being rendered.
func NewContextWith ¶
NewContextWith returns a fully formed context using the data provided.
func NewContextWithContext ¶
NewContextWithContext returns a new plush.Context given another context
func (*Context) New ¶
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.
type HelperContext ¶
type HelperContext struct { *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(ctx *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
type HelperMap ¶
type HelperMap struct {
// contains filtered or unexported fields
}
HelperMap holds onto helpers and validates they are properly formed.
func NewHelperMap ¶
NewHelperMap containing all of the "default" helpers from "plush.Helpers".
func (*HelperMap) Add ¶
Add a new helper to the map. New Helpers will be validated to ensure they meet the requirements for a helper:
type Iterator ¶
type Iterator interface {
Next() interface{}
}
Iterator type can be implemented and used by the `for` command to build loops in templates
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 ¶
NewTemplate from the input string. Adds all of the global helper functions from "Helpers", this function does not cache the template.
func (*Template) Clone ¶
Clone a template. This is useful for defining helpers on per "instance" of the template.