Add ace templates to dependencies

This commit is contained in:
Christine Dodrill 2015-07-31 01:35:33 -07:00
parent d80e5a05ca
commit 01eeb5e874
90 changed files with 3390 additions and 0 deletions

6
vendor/manifest vendored
View File

@ -98,6 +98,12 @@
"revision": "5ec5e003b21ac1f06e175898413ada23a6797fc0",
"branch": "master",
"path": "/tiff"
},
{
"importpath": "github.com/yosssi/ace",
"repository": "https://github.com/yosssi/ace",
"revision": "78e48a2f0ac5fb5a642585f96b03a5f47f7775f5",
"branch": "master"
}
]
}

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Keiji Yoshida
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,109 @@
# Ace - HTML template engine for Go
[![wercker status](https://app.wercker.com/status/8d3c657bcae7f31d10c8f88bbfa966d8/m "wercker status")](https://app.wercker.com/project/bykey/8d3c657bcae7f31d10c8f88bbfa966d8)
[![GoDoc](http://godoc.org/github.com/yosssi/ace?status.svg)](http://godoc.org/github.com/yosssi/ace)
## Overview
Ace is an HTML template engine for Go. This is inspired by [Slim](http://slim-lang.com/) and [Jade](http://jade-lang.com/). This is a refinement of [Gold](http://gold.yoss.si/).
## Example
```ace
= doctype html
html lang=en
head
title Hello Ace
= css
h1 { color: blue; }
body
h1 {{.Msg}}
#container.wrapper
p..
Ace is an HTML template engine for Go.
This engine simplifies HTML coding in Go web application development.
= javascript
console.log('Welcome to Ace');
```
becomes
```html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Hello Ace</title>
<style type="text/css">
h1 { color: blue; }
</style>
</head>
<body>
<h1>Hello Ace</h1>
<div id="container" class="wrapper">
<p>
Ace is an HTML template engine for Go.<br>
This engine simplifies HTML coding in Go web application development.
</p>
</div>
<script type="text/javascript">
console.log('Welcome to Ace');
</script>
</body>
</html>
```
## Features
### Making Use of the Go Standard Template Package
**Ace fully utilizes the strength of the [html/template](http://golang.org/pkg/html/template/) package.** You can embed [actions](http://golang.org/pkg/text/template/#hdr-Actions) of the template package in Ace templates. Ace also uses [nested template definitions](http://golang.org/pkg/text/template/#hdr-Nested_template_definitions) of the template package and Ace templates can pass [pipelines](http://golang.org/pkg/text/template/#hdr-Pipelines) (parameters) to other templates which they include.
### Simple Syntax
Ace has a simple syntax and **this makes template files simple and light**.
### Caching Function
Ace has a caching function which caches the result data of the templates parsing process. **You can omit the templates parsing process and save template parsing time** by using this function.
### Binary Template Load Function
Ace has a binary template load function which loads Ace templates from binary data in memory instead of template files on disk. **You can compile your web application into one binary file** by using this function. [go-bindata](https://github.com/jteeuwen/go-bindata) is the best for generating binary data from template files.
## Getting Started
Please check the following documentation.
* [Getting Started](documentation/getting-started.md) - shows the getting started guide.
* [Examples](examples) - shows the examples of the web applications which use the Ace template engine.
## Documentation
You can get the documentation about Ace via the following channels:
* [Documentation](documentation) - includes the getting started guide and the syntax documentation.
* [GoDoc](https://godoc.org/github.com/yosssi/ace) - includes the API documentation.
## Discussion & Contact
You can discuss Ace and contact the Ace development team via the following channels:
* [GitHub Issues](https://github.com/yosssi/ace/issues)
* [Gitter (Chat)](https://gitter.im/yosssi/ace)
## Contributions
**Any contributions are welcome.** Please feel free to [create an issue](https://github.com/yosssi/ace/issues/new) or [send a pull request](https://github.com/yosssi/ace/compare/).
## Renderers for web frameworks
* [Martini Acerender](https://github.com/yosssi/martini-acerender) - For [Martini](http://martini.codegangsta.io/)
## Tools
* [vim-ace](https://github.com/yosssi/vim-ace) - Vim syntax highlighting for Ace templates
* [ace-tmbundle](https://github.com/yosssi/ace-tmbundle) - TextMate/Sublime syntax highlighting for Ace templates
## Projects using Ace
[Here](documentation/projects-using-ace.md) is the list of the projects using Ace. Please feel free to add your awesome project to the list!

70
vendor/src/github.com/yosssi/ace/ace.go vendored Normal file
View File

@ -0,0 +1,70 @@
package ace
import (
"html/template"
"sync"
)
var cache = make(map[string]template.Template)
var cacheMutex = new(sync.RWMutex)
// Load loads and returns an HTML template. Each Ace templates are parsed only once
// and cached if the "DynamicReload" option are not set.
func Load(basePath, innerPath string, opts *Options) (*template.Template, error) {
// Initialize the options.
opts = InitializeOptions(opts)
name := basePath + colon + innerPath
if !opts.DynamicReload {
if tpl, ok := getCache(name); ok {
return &tpl, nil
}
}
// Read files.
src, err := readFiles(basePath, innerPath, opts)
if err != nil {
return nil, err
}
// Parse the source.
rslt, err := ParseSource(src, opts)
if err != nil {
return nil, err
}
// Compile the parsed result.
tpl, err := CompileResult(name, rslt, opts)
if err != nil {
return nil, err
}
if !opts.DynamicReload {
setCache(name, *tpl)
}
return tpl, nil
}
// getCache returns the cached template.
func getCache(name string) (template.Template, bool) {
cacheMutex.RLock()
tpl, ok := cache[name]
cacheMutex.RUnlock()
return tpl, ok
}
// setCache sets the template to the cache.
func setCache(name string, tpl template.Template) {
cacheMutex.Lock()
cache[name] = tpl
cacheMutex.Unlock()
}
// FlushCache clears all cached templates.
func FlushCache() {
cacheMutex.Lock()
cache = make(map[string]template.Template)
cacheMutex.Unlock()
}

View File

@ -0,0 +1,38 @@
package ace
import (
"bytes"
"io"
"strings"
)
// action represents an action.
type action struct {
elementBase
}
// WriteTo writes data to w.
func (e *action) WriteTo(w io.Writer) (int64, error) {
var bf bytes.Buffer
// Write the action
bf.WriteString(strings.TrimSpace(e.ln.str))
// Write the children's HTML.
if i, err := e.writeChildren(&bf); err != nil {
return i, err
}
// Write the buffer.
i, err := w.Write(bf.Bytes())
return int64(i), err
}
// newAction creates and returns an action.
func newAction(ln *line, rslt *result, src *source, parent element, opts *Options) *action {
return &action{
elementBase: newElementBase(ln, rslt, src, parent, opts),
}
}

View File

@ -0,0 +1,90 @@
package main
import (
"flag"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"github.com/yosssi/ace"
"github.com/yosssi/gohtml"
)
var (
noFormat bool
lineNo bool
)
func compileResultFromStdin() (string, error) {
b, err := ioutil.ReadAll(os.Stdin)
if err != nil {
return "", err
}
name, baseFile := "stdin", "stdin.ace"
base := ace.NewFile(baseFile, b)
inner := ace.NewFile("", []byte{})
src := ace.NewSource(base, inner, []*ace.File{})
rslt, err := ace.ParseSource(src, nil)
if err != nil {
return "", err
}
tpl, err := ace.CompileResult(name, rslt, nil)
if err != nil {
return "", err
}
return tpl.Lookup(name).Tree.Root.String(), nil
}
func compileResultFromFile(baseFile, innerFile string) (string, error) {
base := baseFile[:len(baseFile)-len(filepath.Ext(baseFile))]
var inner string
if len(innerFile) > 0 {
inner = innerFile[:len(innerFile)-len(filepath.Ext(innerFile))]
}
name := base + ":" + inner
tpl, err := ace.Load(base, inner, nil)
if err != nil {
return "", err
}
return tpl.Lookup(name).Tree.Root.String(), nil
}
func main() {
flag.BoolVar(&noFormat, "no-format", false, "output HTML without format")
flag.BoolVar(&lineNo, "lineno", false, "output formatted HTML with line numbers")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage:\n %s [options] [base.ace] [inner.ace]\n\nOptions:\n", os.Args[0])
flag.PrintDefaults()
}
flag.Parse()
var (
compiled string
err error
)
baseFile := flag.Arg(0)
if len(baseFile) == 0 {
compiled, err = compileResultFromStdin()
} else {
compiled, err = compileResultFromFile(baseFile, flag.Arg(1))
}
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
if noFormat {
fmt.Println(compiled)
} else {
if lineNo {
fmt.Println(gohtml.FormatWithLineNo(compiled))
} else {
fmt.Println(gohtml.Format(compiled))
}
}
}

View File

@ -0,0 +1,25 @@
package ace
import "io"
// comment represents a comment.
type comment struct {
elementBase
}
// Do nothing.
func (e *comment) WriteTo(w io.Writer) (int64, error) {
return 0, nil
}
// ContainPlainText returns true.
func (e *comment) ContainPlainText() bool {
return true
}
// newComment creates and returns a comment.
func newComment(ln *line, rslt *result, src *source, parent element, opts *Options) *comment {
return &comment{
elementBase: newElementBase(ln, rslt, src, parent, opts),
}
}

View File

@ -0,0 +1,107 @@
package ace
import (
"bytes"
"fmt"
"html/template"
)
// Actions
const (
actionDefine = `%sdefine "%s"%s`
actionEnd = "%send%s"
actionTemplate = `%stemplate "%s"%s`
actionTemplateWithPipeline = `%stemplate "%s" %s%s`
)
// PreDefinedFuncs
const (
preDefinedFuncNameHTML = "HTML"
)
// CompileResult compiles the parsed result to the template.Template.
func CompileResult(name string, rslt *result, opts *Options) (*template.Template, error) {
// Initialize the options.
opts = InitializeOptions(opts)
// Create a template.
t := template.New(name)
return CompileResultWithTemplate(t, rslt, opts)
}
// CompileResultWithTemplate compiles the parsed result and associates it with t.
func CompileResultWithTemplate(t *template.Template, rslt *result, opts *Options) (*template.Template, error) {
// Initialize the options.
opts = InitializeOptions(opts)
var err error
// Create a buffer.
baseBf := bytes.NewBuffer(nil)
innerBf := bytes.NewBuffer(nil)
includeBfs := make(map[string]*bytes.Buffer)
// Write data to the buffer.
for _, e := range rslt.base {
if _, err := e.WriteTo(baseBf); err != nil {
return nil, err
}
}
for _, e := range rslt.inner {
if _, err = e.WriteTo(innerBf); err != nil {
return nil, err
}
}
for path, elements := range rslt.includes {
bf := bytes.NewBuffer(nil)
// Write a define action.
bf.WriteString(fmt.Sprintf(actionDefine, opts.DelimLeft, path, opts.DelimRight))
for _, e := range elements {
if _, err = e.WriteTo(bf); err != nil {
return nil, err
}
}
// Write an end action.
bf.WriteString(fmt.Sprintf(actionEnd, opts.DelimLeft, opts.DelimRight))
includeBfs[path] = bf
}
// Set Delimiters.
t.Delims(opts.DelimLeft, opts.DelimRight)
// Set FuncMaps.
t.Funcs(template.FuncMap{
preDefinedFuncNameHTML: func(s string) template.HTML {
return template.HTML(s)
},
})
t.Funcs(opts.FuncMap)
// Parse a string to the template.
t, err = t.Parse(baseBf.String())
if err != nil {
return nil, err
}
t, err = t.Parse(innerBf.String())
if err != nil {
return nil, err
}
for _, bf := range includeBfs {
t, err = t.Parse(bf.String())
if err != nil {
return nil, err
}
}
return t, nil
}

View File

@ -0,0 +1,83 @@
package ace
import (
"testing"
)
func TestLineCompile(t *testing.T) {
for i, this := range []struct {
template string
expect string
}{
{
template: `a href=./foo foo`,
expect: `<a href="./foo">foo</a>`,
},
{
template: `span.color-red red`,
expect: `<span class="color-red">red</span>`,
},
{
template: `span#ref1 text`,
expect: `<span id="ref1">text</span>`,
},
{
template: `span#ref1.color-red.text-big text`,
expect: `<span id="ref1" class="color-red text-big">text</span>`,
},
{
template: `span.color-red#ref1.text-big text`,
expect: `<span id="ref1" class="color-red text-big">text</span>`,
},
{
template: `#ref1 text`,
expect: `<div id="ref1">text</div>`,
},
{
template: `#ref1.color-red.text-big text`,
expect: `<div id="ref1" class="color-red text-big">text</div>`,
},
{
template: `.color-red#ref1.text-big text`,
expect: `<div id="ref1" class="color-red text-big">text</div>`,
},
{
template: "div class=\"dialog {{ if eq .Attr `important` }}color-red{{end}}\" text",
expect: "<div class=\"dialog {{if eq .Attr `important`}}color-red{{end}}\">text</div>",
},
{
template: "div class=\"dialog {{ if eq .Attr `important` }}color-red text-big{{end}}\" text",
expect: "<div class=\"dialog {{if eq .Attr `important`}}color-red text-big{{end}}\">text</div>",
},
{
template: "div class=\"dialog {{ if eq .Attr \"important\" }}color-red{{end}}\" text",
expect: "<div class=\"dialog {{if eq .Attr \"important\"}}color-red{{end}}\">text</div>",
},
{
template: "div class=\"dialog {{ if eq .Attr \"important\" }}color-red text-big{{end}}\" text",
expect: "<div class=\"dialog {{if eq .Attr \"important\"}}color-red text-big{{end}}\">text</div>",
},
} {
name, filepath := "dummy", "dummy.ace"
base := NewFile(filepath, []byte(this.template))
inner := NewFile("", []byte{})
src := NewSource(base, inner, []*File{})
rslt, err := ParseSource(src, nil)
if err != nil {
t.Errorf("[%d] failed: %s", i, err)
continue
}
tpl, err := CompileResult(name, rslt, nil)
if err != nil {
t.Errorf("[%d] failed: %s", i, err)
continue
}
compiled := tpl.Lookup(name).Tree.Root.String()
if compiled != this.expect {
t.Errorf("[%d] Compiler didn't return an expected value, got %v but expected %v", i, compiled, this.expect)
}
}
}

View File

@ -0,0 +1,2 @@
// Package ace provides an HTML template engine.
package ace

View File

@ -0,0 +1,6 @@
# Documentation
* [Getting Started](getting-started.md)
* [Syntax](syntax.md)
* [Options](options.md)
* [Projects using Ace](projects-using-ace.md)

View File

@ -0,0 +1,104 @@
# Getting Started
This document explains a basic usage of Ace by showing a simple example.
## 1. Create Ace templates
Create the following Ace templates.
base.ace
```ace
= doctype html
html lang=en
head
meta charset=utf-8
title Ace example
= css
h1 { color: blue; }
body
h1 Base Template : {{.Msg}}
#container.wrapper
= yield main
= yield sub
= include inc .Msg
= javascript
alert('{{.Msg}}');
```
inner.ace
```ace
= content main
h2 Inner Template - Main : {{.Msg}}
= content sub
h3 Inner Template - Sub : {{.Msg}}
```
inc.ace
```ace
h4 Included Template : {{.}}
```
## 2. Create a web application
Create the following web application.
main.go
```go
package main
import (
"net/http"
"github.com/yosssi/ace"
)
func handler(w http.ResponseWriter, r *http.Request) {
tpl, err := ace.Load("base", "inner", nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := tpl.Execute(w, map[string]string{"Msg": "Hello Ace"}); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
```
## 3. Check the result.
Run the above web application and access [localhost:8080](http://localhost:8080). The following HTML will be rendered.
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Ace example</title>
<style type="text/css">
h1 { color: blue; }
</style>
</head>
<body>
<h1>Base Template : Hello Ace</h1>
<div id="container" class="wrapper">
<h2>Inner Template - Main : Hello Ace</h2>
<h3>Inner Template - Sub : Hello Ace</h3>
<h4>Included Template : Hello Ace</h4>
</div>
<script type="text/javascript">
alert('Hello Ace');
</script>
</body>
</html>
```

View File

@ -0,0 +1,36 @@
# Options
Here is a sample Go code which calls an Ace template engine.
```go
package main
import (
"net/http"
"github.com/yosssi/ace"
)
func handler(w http.ResponseWriter, r *http.Request) {
tpl, err := ace.Load("base", "inner", nil)
if err != nil {
panic(err)
}
if err := tpl.Execute(w, map[string]string{"Msg": "Hello Ace"}); err != nil {
panic(err)
}
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
```
You can pass parsing options to the Ace template engine via `ace.Load`'s third argument.
```go
tpl, err := ace.Load("base", "inner", &ace.Options{DynamicReload: true})
```
Please check [GoDoc](https://godoc.org/github.com/yosssi/ace#Options) for more detail about options.

View File

@ -0,0 +1,6 @@
# Projects using Ace
* [Ace Proxy](https://github.com/yosssi/ace-proxy) - Proxy for the Ace template engine
* [ace.yoss.si](https://github.com/yosssi/ace.yoss.si) - Website of Ace
* [Martini Acerender](https://github.com/yosssi/martini-acerender) - Martini middleware/handler for parsing Ace templates and rendering HTML
* [Hugo](https://github.com/spf13/hugo) - Static Site Generator written in Go

View File

@ -0,0 +1,330 @@
# Syntax
## Indent
A unit of an indent must be 2 spaces.
```ace
html
body
div
p
```
becomes
```html
<html>
<body>
<div></div>
<p></p>
</body>
</html>
```
## HTML Tags
A head word of a line is interpreted as an HTML tag. The rest words of the same line are interpreted as attributes or a text. An attribute value which contains spaces must be surrounded by double quotes. An attribute without value (like "check" and "required") can be defined by specifying no value and ending with an equal (=).
```ace
div id=container style="font-size: 12px; color: blue;"
p class=row This is interpreted as a text.
a href=https://github.com/ Go to GitHub
input type=checkbox checked=
```
becomes
```html
<div id="container" style="font-size: 12px; color: blue;">
<p class="row">This is interpreted as a text.</p>
<a href="https://github.com/">Go to GitHub</a>
<input type="checkbox" checked>
</div>
```
ID and classes can be defined with a head word of a line.
```ace
p#foo.bar
#container
.wrapper
```
becomes
```html
<p id="foo" class="bar"></p>
<div id="container"></div>
<div class="wrapper"></div>
```
Block texts can be defined as a child element of an HTML tag by appending a dot (.) or double dot (..) to the head word of a line. BR tags are inserted to each line except for the last line by appending a double dot (..) to the head word of a line.
```ace
script.
var msg = 'Hello Ace';
alert(msg);
p..
This is a block text.
BR tags are inserted
automatically.
```
becomes
```html
<script>
var msg = 'Hello Ace';
alert(msg);
</script>
<p>
This is a block text.<br>
BR tags are inserted<br>
automatically.
</p>
```
## Plain Texts
A line which starts with a pipe (|) or double pipe (||) is interpreted as a block of plain texts. BR tags are inserted to each line except for the last line by having a line start with a double pipe (||).
```ace
div
| This is a single line.
div
|
This is a
block line.
div
||
This is a
block line
with BR tags.
```
becomes
```html
<div>
This is a single line.
</div>
<div>
This is a
block line.
</div>
<div>
This is a<br>
block line<br>
with BR tags.
</div>
```
## Helper Methods
A line which starts withs an equal (=) is interpreted as a helper method.
```ace
= helperMethodName
```
The following helper methods are available.
### Conditional Comment Helper Method
A conditional comment helper method generates a [conditional comment](http://en.wikipedia.org/wiki/Conditional_comment).
```ace
= conditionalComment commentType condition
```
The following comment types are acceptable:
| Comment Type | Generated HTML |
| ------------ |----------------------------------------|
| hidden | <!--[if expression]> HTML <![endif]--> |
| revealed | <![if expression]> HTML <![endif]> |
```ace
= conditionalComment hidden IE 6
<p>You are using Internet Explorer 6.</p>
= conditionalComment revealed !IE
<link href="non-ie.css" rel="stylesheet">
```
becomes
```html
<!--[if IE 6]>
<p>You are using Internet Explorer 6.</p>
<![endif]-->
<![if !IE]>
<link href="non-ie.css" rel="stylesheet">
<![endif]>
```
### Content Helper Method
A content helper method defines a block content which is embedded in the base template. This helper method must be used only in the inner template.
```
= content main
h2 Inner Template - Main : {{.Msg}}
= content sub
h3 Inner Template - Sub : {{.Msg}}
```
### CSS Helper Method
A css helper method generates a style tag which has "text/css" type.
```ace
= css
body {
margin: 0;
}
h1 {
font-size: 200%;
color: blue;
}
```
becomes
```html
<style type="text/css">
body {
margin: 0;
}
h1 {
font-size: 200%;
color: blue;
}
</style>
```
### Doctype Helper Method
A doctype helper method generates a doctype tag.
```ace
= doctype doctypeName
```
The following doctype names are acceptable:
| Doctype Name | Generated HTML |
| ------------ |--------------------------------------|
| html | <!DOCTYPE html> |
| xml | <?xml version="1.0" encoding="utf-8" ?> |
| transitional | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
| strict | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> |
| frameset | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"> |
| 1.1 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> |
| basic | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd"> |
| mobile | <!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd"> |
```ace
= doctype html
```
becomes
```html
<!DOCTYPE html>
```
### Include Helper Method
An include helper method includes another template. You can pass a pipeline (parameter) from the including template to the included template.
```ace
= include templatePathWithoutExtension pipeline
```
### Javascript Helper Method
A javascript helper method generates a script tag which has "text/javascript" type.
```ace
= javascript
var msg = 'Hello Ace';
alert(msg);
```
becomes
```html
<script type="text/javascript">
var msg = 'Hello Ace';
alert(msg);
</script>
```
### Yield Helper Method
A yield helper method generates the HTML tags which are defined in the inner template. This helper method must be used only in the base template.
```
= yield main
| This message is rendered if the "main" content is not defined in the inner template.
= yield sub
| This message is rendered if the "sub" content is not defined in the inner template.
```
## Comments
A line which starts with a slash (/) or double slash (//) is interpreted as a comment. A line which starts with a slash (/) is not rendered. A line which starts with a double slash (//) is renderd as an HTML comment.
```ace
/ This is a single line comment which is not rendered.
/
This is a multiple lines comment
which is not rendered.
// This is a single line comment which is rendered as an HTML comment.
//
This is a multiple lines comment
which is rendered as an HTML comment.
```
becomes
```html
<!-- This is a single line comment which is rendered as an HTML comment. -->
<!--
This is a multiple lines comment
which is rendered as an HTML comment.
-->
```
## Actions
[Actions](http://golang.org/pkg/text/template/#hdr-Actions) of the template package can be embedded in Ace templates.
```ace
body
h1 Base Template : {{.Msg}}
{{if true}}
p Conditional block
{{end}}
```
The following functions are predefined.
### HTML function
HTML function returns a non-escaped stirng.
```ace
{{"<div>"}}
{{HTML "<div>"}}
```
becomes
```html
&lt;br&gt;
<br>
```

View File

@ -0,0 +1,72 @@
package ace
import (
"fmt"
"io"
)
// Helper method names
const (
helperMethodNameConditionalComment = "conditionalComment"
helperMethodNameContent = "content"
helperMethodNameCSS = "css"
helperMethodNameDoctype = "doctype"
helperMethodNameYield = "yield"
helperMethodNameInclude = "include"
helperMethodNameJavascript = "javascript"
)
// element is an interface for storing an element.
type element interface {
io.WriterTo
AppendChild(child element)
ContainPlainText() bool
Base() *elementBase
CanHaveChildren() bool
InsertBr() bool
SetLastChild(lastChild bool)
}
// newElement creates and returns an element.
func newElement(ln *line, rslt *result, src *source, parent element, opts *Options) (element, error) {
var e element
var err error
switch {
case parent != nil && parent.ContainPlainText():
e = newPlainTextInner(ln, rslt, src, parent, parent.InsertBr(), opts)
case ln.isEmpty():
e = newEmptyElement(ln, rslt, src, parent, opts)
case ln.isComment():
e = newComment(ln, rslt, src, parent, opts)
case ln.isHTMLComment():
e = newHTMLComment(ln, rslt, src, parent, opts)
case ln.isHelperMethod():
switch {
case ln.isHelperMethodOf(helperMethodNameConditionalComment):
e, err = newHelperMethodConditionalComment(ln, rslt, src, parent, opts)
case ln.isHelperMethodOf(helperMethodNameContent):
e, err = newHelperMethodContent(ln, rslt, src, parent, opts)
case ln.isHelperMethodOf(helperMethodNameCSS):
e = newHelperMethodCSS(ln, rslt, src, parent, opts)
case ln.isHelperMethodOf(helperMethodNameDoctype):
e, err = newHelperMethodDoctype(ln, rslt, src, parent, opts)
case ln.isHelperMethodOf(helperMethodNameInclude):
e, err = newHelperMethodInclude(ln, rslt, src, parent, opts)
case ln.isHelperMethodOf(helperMethodNameJavascript):
e = newHelperMethodJavascript(ln, rslt, src, parent, opts)
case ln.isHelperMethodOf(helperMethodNameYield):
e, err = newHelperMethodYield(ln, rslt, src, parent, opts)
default:
err = fmt.Errorf("the helper method name is invalid [file: %s][line: %d]", ln.fileName(), ln.no)
}
case ln.isPlainText():
e = newPlainText(ln, rslt, src, parent, opts)
case ln.isAction():
e = newAction(ln, rslt, src, parent, opts)
default:
e, err = newHTMLTag(ln, rslt, src, parent, opts)
}
return e, err
}

View File

@ -0,0 +1,76 @@
package ace
import "bytes"
// elementBase holds common fields for the elements.
type elementBase struct {
ln *line
rslt *result
src *source
parent element
children []element
opts *Options
lastChild bool
}
// AppendChild appends the child element to the element.
func (e *elementBase) AppendChild(child element) {
e.children = append(e.children, child)
}
// ContainPlainText returns false.
// This method should be overrided by a struct which contains
// the element base struct.
func (e *elementBase) ContainPlainText() bool {
return false
}
// Base returns the element base.
func (e *elementBase) Base() *elementBase {
return e
}
// CanHaveChildren returns true.
// This method should be overrided by a struct which contains
// the element base struct.
func (e *elementBase) CanHaveChildren() bool {
return true
}
// InsertBr returns false.
// This method should be overrided by a struct which contains
// the element base struct.
func (e *elementBase) InsertBr() bool {
return false
}
// SetLastChild set the value to the last child field.
func (e *elementBase) SetLastChild(lastChild bool) {
e.lastChild = lastChild
}
// writeChildren writes the children's HTML.
func (e *elementBase) writeChildren(bf *bytes.Buffer) (int64, error) {
l := len(e.children)
for index, child := range e.children {
if index == l-1 {
child.SetLastChild(true)
}
if i, err := child.WriteTo(bf); err != nil {
return int64(i), err
}
}
return 0, nil
}
// newElementBase creates and returns an element base.
func newElementBase(ln *line, rslt *result, src *source, parent element, opts *Options) elementBase {
return elementBase{
ln: ln,
rslt: rslt,
src: src,
parent: parent,
opts: opts,
}
}

View File

@ -0,0 +1,7 @@
package ace
import "testing"
func Test_newElement(t *testing.T) {
}

View File

@ -0,0 +1,25 @@
package ace
import "io"
// emptyElement represents an empty element.
type emptyElement struct {
elementBase
}
// Do nothing.
func (e *emptyElement) WriteTo(w io.Writer) (int64, error) {
return 0, nil
}
// CanHaveChildren returns false.
func (e *emptyElement) CanHaveChildren() bool {
return false
}
// newEmpty creates and returns an empty element.
func newEmptyElement(ln *line, rslt *result, src *source, parent element, opts *Options) *emptyElement {
return &emptyElement{
elementBase: newElementBase(ln, rslt, src, parent, opts),
}
}

View File

@ -0,0 +1,24 @@
# Examples
This documentation shows the examples of the web applications which use the Ace template engine.
## Basic
* [Single Template](single_template)
* [Base and Inner Template](base_inner_template)
* [HTML Tags](html_tags)
* [Plain Texts](plain_texts)
* [CSS and Javascript Helper Method](css_javascript_helper_method)
* [Comments](comments)
* [Include Helper Method](include_helper_method)
* [Actions](actions)
## Advanced
* [Dynamic Reload](dynamic_reload)
* [Load Templates from Binary Data](load_templates_from_binary_data)
* [Pass a Pipeline (Parameter) to the Included Template](pass_pipeline_to_included_template)
* [Set a Default Value to the Yield Helper Method](set_default_value_to_the_yield_helper_method)
* [Change the Action Delimiter](change_action_delimiter)
* [Set Custom Functions](set_custom_functions)
* [Cache Options](cache_options)

View File

@ -0,0 +1,3 @@
# Actions
This example shows how to embed actions in Ace templates.

View File

@ -0,0 +1,15 @@
= doctype html
html lang=en
head
meta charset=utf-8
title {{.Title}}
body
h1 {{.Title}}
ul
{{range .Msgs}}
li {{.}}
{{end}}
div
{{"<div>Escaped String</div>"}}
div
{{HTML "<div>Non-Escaped String</div>"}}

View File

@ -0,0 +1,32 @@
package main
import (
"net/http"
"github.com/yosssi/ace"
)
func handler(w http.ResponseWriter, r *http.Request) {
tpl, err := ace.Load("example", "", nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
data := map[string]interface{}{
"Title": "Actions",
"Msgs": []string{
"Message1",
"Message2",
"Message3",
},
}
if err := tpl.Execute(w, data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}

View File

@ -0,0 +1,3 @@
# Base and Inner Template
This example shows how to use a base and inner Ace template file.

View File

@ -0,0 +1,9 @@
= doctype html
html lang=en
head
meta charset=utf-8
title Base and Inner Template
body
h1 This is a base template
= yield main
= yield sub

View File

@ -0,0 +1,5 @@
= content main
h2 This is a content named "main" of an inner template.
= content sub
h2 This is a content named "sub" of an inner template.

View File

@ -0,0 +1,24 @@
package main
import (
"net/http"
"github.com/yosssi/ace"
)
func handler(w http.ResponseWriter, r *http.Request) {
tpl, err := ace.Load("base", "inner", nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := tpl.Execute(w, nil); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}

View File

@ -0,0 +1,3 @@
# Cache Options
This example shows how to cache the options for Ace template engine so that you don't have to specify them every time calling the Ace APIs.

View File

@ -0,0 +1,28 @@
package main
import (
"net/http"
"github.com/yosssi/ace"
"github.com/yosssi/ace-proxy"
)
var p = proxy.New(&ace.Options{BaseDir: "views", DynamicReload: true})
func handler(w http.ResponseWriter, r *http.Request) {
tpl, err := p.Load("example", "", nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := tpl.Execute(w, nil); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}

View File

@ -0,0 +1,7 @@
= doctype html
html lang=en
head
meta charset=utf-8
title Cache Options
body
h1 Cache Options

View File

@ -0,0 +1,3 @@
# Change the Action Delimiter
This example shows how to change the action delimiter.

View File

@ -0,0 +1,7 @@
= doctype html
html lang=en
head
meta charset=utf-8
title Change the Action Delimiter
body
h1 <%.Msg%>

View File

@ -0,0 +1,30 @@
package main
import (
"net/http"
"github.com/yosssi/ace"
)
func handler(w http.ResponseWriter, r *http.Request) {
tpl, err := ace.Load("example", "", &ace.Options{
DelimLeft: "<%",
DelimRight: "%>",
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
data := map[string]interface{}{
"Msg": "Hello Ace",
}
if err := tpl.Execute(w, data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}

View File

@ -0,0 +1,3 @@
# Comments
This example shows how to define comments.

View File

@ -0,0 +1,18 @@
= doctype html
html lang=en
head
meta charset=utf-8
title Comments
body
/ This is a single line comment which is not rendered.
/
This is a multiple lines comment
which is not rendered.
// This is a single line comment which is rendered as an HTML comment.
//
This is a multiple lines comment
which is rendered as an HTML comment.
= conditionalComment hidden IE 6
<p>You are using Internet Explorer 6.</p>
= conditionalComment revealed !IE
<link href="non-ie.css" rel="stylesheet">

View File

@ -0,0 +1,24 @@
package main
import (
"net/http"
"github.com/yosssi/ace"
)
func handler(w http.ResponseWriter, r *http.Request) {
tpl, err := ace.Load("example", "", nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := tpl.Execute(w, nil); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}

View File

@ -0,0 +1,3 @@
# CSS and Javascript Helper Method
This example shows how to use the css and javascript helper method.

View File

@ -0,0 +1,18 @@
= doctype html
html lang=en
head
meta charset=utf-8
title CSS and Javascript Helper Method
= css
h1 {
color: blue;
}
h2 {
color: green;
}
body
h1 CSS and Javascript Helper Method
h2 This example shows how to use the css and javascript helper method.
= javascript
var msg = 'CSS and Javascript Helper Method';
alert(msg);

View File

@ -0,0 +1,24 @@
package main
import (
"net/http"
"github.com/yosssi/ace"
)
func handler(w http.ResponseWriter, r *http.Request) {
tpl, err := ace.Load("example", "", nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := tpl.Execute(w, nil); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}

View File

@ -0,0 +1,3 @@
# Dynamic Reload
This example shows how to reload templates dynamically. Ace caches the parsed templates by default but you can have Ace reload templates dynamically by setting the "DynamicReload" option to the Ace template enginge. This option should be used in development.

View File

@ -0,0 +1,7 @@
= doctype html
html lang=en
head
meta charset=utf-8
title Dynamic Reload
body
h1 Dynamic Reload

View File

@ -0,0 +1,24 @@
package main
import (
"net/http"
"github.com/yosssi/ace"
)
func handler(w http.ResponseWriter, r *http.Request) {
tpl, err := ace.Load("example", "", &ace.Options{DynamicReload: true})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := tpl.Execute(w, nil); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}

View File

@ -0,0 +1,3 @@
# HTML Tags
This example shows how to generate various HTML tags.

View File

@ -0,0 +1,21 @@
= doctype html
html lang=en
head
meta charset=utf-8
title HTML Tags
body
header
h1 HTML Tags
section#main-section.class1.class2 class=class3
#container
.wrapper
div Single text line can follow the tag name.
p..
This is a block text.
BR tags are inserted automatically.
a href=https://github.com Go to GitHub
input type=checkbox checked=
footer
script.
var msg = 'Hello Ace';
alert(msg);

View File

@ -0,0 +1,24 @@
package main
import (
"net/http"
"github.com/yosssi/ace"
)
func handler(w http.ResponseWriter, r *http.Request) {
tpl, err := ace.Load("example", "", nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := tpl.Execute(w, nil); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}

View File

@ -0,0 +1,3 @@
# Include Helper Method
This example shows how to use an include helper method.

View File

@ -0,0 +1,8 @@
= doctype html
html lang=en
head
meta charset=utf-8
title Include Helper Method
body
h1 This is a base template
= include inc

View File

@ -0,0 +1 @@
h2 This is an included template.

View File

@ -0,0 +1,24 @@
package main
import (
"net/http"
"github.com/yosssi/ace"
)
func handler(w http.ResponseWriter, r *http.Request) {
tpl, err := ace.Load("example", "", nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := tpl.Execute(w, nil); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}

View File

@ -0,0 +1,13 @@
# Load Templates from Binary Data
This example shows how to load templates from binary data instead of template files.
You can run this example web application by executing the following command.
```sh
$ go run main.go asset.go
```
`asset.go` is created by executing `make_asset.sh`. This shell script executes [go-bindata](https://github.com/jteeuwen/go-bindata) and generates binary data from the Ace template.
**You can compile your web application into one binary file by using this function.**

View File

@ -0,0 +1,68 @@
package main
import (
"bytes"
"compress/gzip"
"fmt"
"io"
)
func bindata_read(data []byte, name string) ([]byte, error) {
gz, err := gzip.NewReader(bytes.NewBuffer(data))
if err != nil {
return nil, fmt.Errorf("Read %q: %v", name, err)
}
var buf bytes.Buffer
_, err = io.Copy(&buf, gz)
gz.Close()
if err != nil {
return nil, fmt.Errorf("Read %q: %v", name, err)
}
return buf.Bytes(), nil
}
func views_example_ace() ([]byte, error) {
return bindata_read([]byte{
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x00, 0xff, 0x84, 0xcb,
0xb1, 0x11, 0x83, 0x30, 0x14, 0x03, 0xd0, 0x9e, 0x29, 0xb4, 0x40, 0x8a,
0x74, 0x69, 0xbc, 0x41, 0x96, 0x10, 0xfc, 0x4f, 0xcc, 0x9d, 0xed, 0xcf,
0x19, 0xa5, 0xf0, 0xf6, 0x21, 0x2c, 0x40, 0x23, 0xdd, 0xe9, 0x9e, 0x12,
0x2c, 0x16, 0x8d, 0xdd, 0x91, 0x55, 0xcb, 0xf4, 0x0f, 0x14, 0xb6, 0x4f,
0xf2, 0x36, 0x01, 0xd9, 0x69, 0x67, 0x01, 0xd5, 0x45, 0x2c, 0x99, 0xfd,
0x70, 0xa5, 0xaf, 0xd6, 0xc7, 0xeb, 0x9a, 0xb5, 0xa9, 0x38, 0xde, 0x41,
0x83, 0xbc, 0xee, 0x85, 0xf2, 0x03, 0x6b, 0x8f, 0x8a, 0x79, 0x6b, 0xec,
0x03, 0x46, 0xf1, 0x94, 0x73, 0xd8, 0xb8, 0x0e, 0xf9, 0x79, 0xab, 0x7f,
0x01, 0x00, 0x00, 0xff, 0xff, 0x6b, 0xc6, 0x6a, 0x49, 0x92, 0x00, 0x00,
0x00,
},
"views/example.ace",
)
}
// Asset loads and returns the asset for the given name.
// It returns an error if the asset could not be found or
// could not be loaded.
func Asset(name string) ([]byte, error) {
if f, ok := _bindata[name]; ok {
return f()
}
return nil, fmt.Errorf("Asset %s not found", name)
}
// AssetNames returns the names of the assets.
func AssetNames() []string {
names := make([]string, 0, len(_bindata))
for name := range _bindata {
names = append(names, name)
}
return names
}
// _bindata is a table, holding each asset generator, mapped to its name.
var _bindata = map[string] func() ([]byte, error) {
"views/example.ace": views_example_ace,
}

View File

@ -0,0 +1,26 @@
package main
import (
"net/http"
"github.com/yosssi/ace"
)
func handler(w http.ResponseWriter, r *http.Request) {
tpl, err := ace.Load("views/example", "", &ace.Options{
Asset: Asset,
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := tpl.Execute(w, nil); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}

View File

@ -0,0 +1,2 @@
#!/bin/sh
go-bindata -o asset.go views

View File

@ -0,0 +1,7 @@
= doctype html
html lang=en
head
meta charset=utf-8
title Load templates from binary data
body
h1 Load templates from binary data

View File

@ -0,0 +1,3 @@
# Pass a Pipeline (Parameter) to the Included Template
This example shows how to pass a pipeline (parameter) to the included template.

View File

@ -0,0 +1,11 @@
= doctype html
html lang=en
head
meta charset=utf-8
title Pass a Pipeline (Parameter) to the Included Template
body
h1 Pass a Pipeline (Parameter) to the Included Template
ul
{{range .Pets}}
= include pet .
{{end}}

View File

@ -0,0 +1,38 @@
package main
import (
"net/http"
"github.com/yosssi/ace"
)
// Pet represents a pet.
type Pet struct {
Species string
Name string
Age int
}
func handler(w http.ResponseWriter, r *http.Request) {
tpl, err := ace.Load("example", "", nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
data := map[string]interface{}{
"Pets": []Pet{
Pet{Species: "Dog", Name: "Taro", Age: 5},
Pet{Species: "Cat", Name: "Hanako", Age: 10},
Pet{Species: "Rabbit", Name: "Jiro", Age: 1},
},
}
if err := tpl.Execute(w, data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}

View File

@ -0,0 +1 @@
li {{.Species}}, {{.Name}}, {{.Age}}

View File

@ -0,0 +1,3 @@
# Plain Texts
This example shows how to generate plain texts.

View File

@ -0,0 +1,17 @@
= doctype html
html lang=en
head
meta charset=utf-8
title Plain Texts
body
div
| This is a single line.
div
|
This is a
block line.
div
||
This is a
block line
with BR tags.

View File

@ -0,0 +1,24 @@
package main
import (
"net/http"
"github.com/yosssi/ace"
)
func handler(w http.ResponseWriter, r *http.Request) {
tpl, err := ace.Load("example", "", nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := tpl.Execute(w, nil); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}

View File

@ -0,0 +1,3 @@
# Set Custom Functions
This example shows how to set custom functions.

View File

@ -0,0 +1,7 @@
= doctype html
html lang=en
head
meta charset=utf-8
title Set Custom Functions
body
h1 {{Greeting "Ace"}}

View File

@ -0,0 +1,32 @@
package main
import (
"html/template"
"net/http"
"github.com/yosssi/ace"
)
func handler(w http.ResponseWriter, r *http.Request) {
funcMap := template.FuncMap{
"Greeting": func(s string) string {
return "Hello " + s
},
}
tpl, err := ace.Load("example", "", &ace.Options{
FuncMap: funcMap,
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := tpl.Execute(w, nil); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}

View File

@ -0,0 +1,3 @@
# Set a Default Value to the Yield Helper Method
This example shows how to set a default value to the yield helper method.

View File

@ -0,0 +1,11 @@
= doctype html
html lang=en
head
meta charset=utf-8
title Set a Default Value to the Yield Helper Method
body
h1 This is a base template
= yield main
| This message is rendered if the "main" content is not defined in the inner template.
= yield sub
| This message is rendered if the "sub" content is not defined in the inner template.

View File

@ -0,0 +1,2 @@
= content main
h2 This is a content named "main" of an inner template.

View File

@ -0,0 +1,24 @@
package main
import (
"net/http"
"github.com/yosssi/ace"
)
func handler(w http.ResponseWriter, r *http.Request) {
tpl, err := ace.Load("base", "inner", nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := tpl.Execute(w, nil); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}

View File

@ -0,0 +1,3 @@
# Single Template
This example shows how to build a web application which uses a single Ace template file.

View File

@ -0,0 +1,7 @@
= doctype html
html lang=en
head
meta charset=utf-8
title Single Template
body
h1 Single Template

View File

@ -0,0 +1,24 @@
package main
import (
"net/http"
"github.com/yosssi/ace"
)
func handler(w http.ResponseWriter, r *http.Request) {
tpl, err := ace.Load("example", "", nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := tpl.Execute(w, nil); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}

View File

@ -0,0 +1,15 @@
package ace
// File represents a file.
type File struct {
path string
data []byte
}
// NewFile creates and returns a file.
func NewFile(path string, data []byte) *File {
return &File{
path: path,
data: data,
}
}

View File

@ -0,0 +1,105 @@
package ace
import (
"bytes"
"fmt"
"io"
"strings"
)
// Comment types
const (
commentTypeHidden = "hidden"
commentTypeRevealed = "revealed"
)
// helperMethodConditionalComment represents a helper method
// conditional comment.
type helperMethodConditionalComment struct {
elementBase
commentType string
condition string
}
// WriteTo writes data to w.
func (e *helperMethodConditionalComment) WriteTo(w io.Writer) (int64, error) {
var bf bytes.Buffer
// Write an open tag.
bf.WriteString(e.opts.DelimLeft)
bf.WriteString(preDefinedFuncNameHTML)
bf.WriteString(space)
bf.WriteString(doubleQuote)
bf.WriteString(lt)
bf.WriteString(exclamation)
bf.WriteString(doubleQuote)
bf.WriteString(e.opts.DelimRight)
if e.commentType == commentTypeHidden {
bf.WriteString(hyphen)
bf.WriteString(hyphen)
}
bf.WriteString(bracketOpen)
bf.WriteString("if ")
bf.WriteString(e.condition)
bf.WriteString(bracketClose)
bf.WriteString(gt)
bf.WriteString(lf)
// Write the children's HTML.
if i, err := e.writeChildren(&bf); err != nil {
return i, err
}
// Write a close tag.
bf.WriteString(e.opts.DelimLeft)
bf.WriteString(preDefinedFuncNameHTML)
bf.WriteString(space)
bf.WriteString(doubleQuote)
bf.WriteString(lt)
bf.WriteString(exclamation)
bf.WriteString(doubleQuote)
bf.WriteString(e.opts.DelimRight)
bf.WriteString(bracketOpen)
bf.WriteString("endif")
bf.WriteString(bracketClose)
if e.commentType == commentTypeHidden {
bf.WriteString(hyphen)
bf.WriteString(hyphen)
}
bf.WriteString(gt)
// Write the buffer.
i, err := w.Write(bf.Bytes())
return int64(i), err
}
// ContainPlainText returns true.
func (e *helperMethodConditionalComment) ContainPlainText() bool {
return true
}
// newHelperMethodConditionalComment creates and returns an HTML comment.
func newHelperMethodConditionalComment(ln *line, rslt *result, src *source, parent element, opts *Options) (*helperMethodConditionalComment, error) {
switch len(ln.tokens) {
case 2:
return nil, fmt.Errorf("no comment type is specified [file: %s][line: %d]", ln.fileName(), ln.no)
case 3:
return nil, fmt.Errorf("no condition is specified [file: %s][line: %d]", ln.fileName(), ln.no)
}
commentType := ln.tokens[2]
if commentType != commentTypeHidden && commentType != commentTypeRevealed {
return nil, fmt.Errorf("the comment type is invalid [file: %s][line: %d]", ln.fileName(), ln.no)
}
e := &helperMethodConditionalComment{
elementBase: newElementBase(ln, rslt, src, parent, opts),
commentType: commentType,
condition: strings.Join(ln.tokens[3:], space),
}
return e, nil
}

View File

@ -0,0 +1,53 @@
package ace
import (
"bytes"
"fmt"
"io"
)
// helperMethodContent represents a helper method content.
type helperMethodContent struct {
elementBase
name string
}
// WriteTo writes data to w.
func (e *helperMethodContent) WriteTo(w io.Writer) (int64, error) {
var bf bytes.Buffer
inner := e.src.inner
if inner == nil {
return 0, fmt.Errorf("inner is not specified [file: %s][line: %d]", e.ln.fileName(), e.ln.no)
}
// Write a define action.
bf.WriteString(fmt.Sprintf(actionDefine, e.opts.DelimLeft, inner.path+doubleColon+e.name, e.opts.DelimRight))
// Write the children's HTML.
if i, err := e.writeChildren(&bf); err != nil {
return i, err
}
// Write an end action.
bf.WriteString(fmt.Sprintf(actionEnd, e.opts.DelimLeft, e.opts.DelimRight))
// Write the buffer.
i, err := w.Write(bf.Bytes())
return int64(i), err
}
// newHelperMethodContent creates and returns a helper method content.
func newHelperMethodContent(ln *line, rslt *result, src *source, parent element, opts *Options) (*helperMethodContent, error) {
if len(ln.tokens) < 3 || ln.tokens[2] == "" {
return nil, fmt.Errorf("no name is specified [file: %s][line: %d]", ln.fileName(), ln.no)
}
e := &helperMethodContent{
elementBase: newElementBase(ln, rslt, src, parent, opts),
name: ln.tokens[2],
}
return e, nil
}

View File

@ -0,0 +1,51 @@
package ace
import (
"bytes"
"io"
)
// helperMethodCSS represents a helper method css.
type helperMethodCSS struct {
elementBase
}
// WriteTo writes data to w.
func (e *helperMethodCSS) WriteTo(w io.Writer) (int64, error) {
var bf bytes.Buffer
// Write an open tag.
bf.WriteString(lt)
bf.WriteString(`style type="text/css"`)
bf.WriteString(gt)
bf.WriteString(lf)
// Write the children's HTML.
if i, err := e.writeChildren(&bf); err != nil {
return i, err
}
// Write an open tag.
bf.WriteString(lt)
bf.WriteString(slash)
bf.WriteString("style")
bf.WriteString(gt)
// Write the buffer.
i, err := w.Write(bf.Bytes())
return int64(i), err
}
// ContainPlainText returns true.
func (e *helperMethodCSS) ContainPlainText() bool {
return true
}
// helperMethodCSS creates and returns a helper method css.
func newHelperMethodCSS(ln *line, rslt *result, src *source, parent element, opts *Options) *helperMethodCSS {
return &helperMethodCSS{
elementBase: newElementBase(ln, rslt, src, parent, opts),
}
}

View File

@ -0,0 +1,50 @@
package ace
import (
"fmt"
"io"
)
// Doctypes
var doctypes = map[string]string{
"html": `<!DOCTYPE html>`,
"xml": `<?xml version="1.0" encoding="utf-8" ?>`,
"transitional": `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">`,
"strict": `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">`,
"frameset": `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">`,
"1.1": `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">`,
"basic": `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">`,
"mobile": `<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">`,
}
// helperMethodDoctype represents a helper method doctype.
type helperMethodDoctype struct {
elementBase
doctype string
}
// WriteTo writes data to w.
func (e *helperMethodDoctype) WriteTo(w io.Writer) (int64, error) {
i, err := w.Write([]byte(doctypes[e.doctype]))
return int64(i), err
}
// newHelperMethodDoctype creates and returns a helper method doctype.
func newHelperMethodDoctype(ln *line, rslt *result, src *source, parent element, opts *Options) (*helperMethodDoctype, error) {
if len(ln.tokens) < 3 {
return nil, fmt.Errorf("doctype is not specified [file: %s][line: %d]", ln.fileName(), ln.no)
}
doctype := ln.tokens[2]
if _, ok := doctypes[doctype]; !ok {
return nil, fmt.Errorf("doctype is invalid [file: %s][line: %d][doctype: %s]", ln.fileName(), ln.no, doctype)
}
e := &helperMethodDoctype{
elementBase: newElementBase(ln, rslt, src, parent, opts),
doctype: doctype,
}
return e, nil
}

View File

@ -0,0 +1,50 @@
package ace
import (
"fmt"
"io"
"strings"
)
// helperMethodInclude represents a helper method include.
type helperMethodInclude struct {
elementBase
templateName string
pipeline string
}
// WriteTo writes data to w.
func (e *helperMethodInclude) WriteTo(w io.Writer) (int64, error) {
var s string
if e.pipeline == "" {
s = fmt.Sprintf(actionTemplate, e.opts.DelimLeft, e.templateName, e.opts.DelimRight)
} else {
s = fmt.Sprintf(actionTemplateWithPipeline, e.opts.DelimLeft, e.templateName, e.pipeline, e.opts.DelimRight)
}
i, err := w.Write([]byte(s))
return int64(i), err
}
// newHelperMethodInclude creates and returns a helper method include.
func newHelperMethodInclude(ln *line, rslt *result, src *source, parent element, opts *Options) (*helperMethodInclude, error) {
if len(ln.tokens) < 3 {
return nil, fmt.Errorf("no template name is specified [file: %s][line: %d]", ln.fileName(), ln.no)
}
var pipeline string
if len(ln.tokens) > 3 {
pipeline = strings.Join(ln.tokens[3:], space)
}
e := &helperMethodInclude{
elementBase: newElementBase(ln, rslt, src, parent, opts),
templateName: ln.tokens[2],
pipeline: pipeline,
}
return e, nil
}

View File

@ -0,0 +1,51 @@
package ace
import (
"bytes"
"io"
)
// helperMethodJavascript represents a helper method javascript.
type helperMethodJavascript struct {
elementBase
}
// WriteTo writes data to w.
func (e *helperMethodJavascript) WriteTo(w io.Writer) (int64, error) {
var bf bytes.Buffer
// Write an open tag.
bf.WriteString(lt)
bf.WriteString(`script type="text/javascript"`)
bf.WriteString(gt)
bf.WriteString(lf)
// Write the children's HTML.
if i, err := e.writeChildren(&bf); err != nil {
return i, err
}
// Write an open tag.
bf.WriteString(lt)
bf.WriteString(slash)
bf.WriteString("script")
bf.WriteString(gt)
// Write the buffer.
i, err := w.Write(bf.Bytes())
return int64(i), err
}
// ContainPlainText returns true.
func (e *helperMethodJavascript) ContainPlainText() bool {
return true
}
// helperMethodJavascript creates and returns a helper method javascript.
func newHelperMethodJavascript(ln *line, rslt *result, src *source, parent element, opts *Options) *helperMethodJavascript {
return &helperMethodJavascript{
elementBase: newElementBase(ln, rslt, src, parent, opts),
}
}

View File

@ -0,0 +1,60 @@
package ace
import (
"bytes"
"fmt"
"io"
)
// helperMethodYield represents a helper method yield.
type helperMethodYield struct {
elementBase
templateName string
}
// WriteTo writes data to w.
func (e *helperMethodYield) WriteTo(w io.Writer) (int64, error) {
var bf bytes.Buffer
inner := e.src.inner
if inner == nil {
return 0, fmt.Errorf("inner is not specified [file: %s][line: %d]", e.ln.fileName(), e.ln.no)
}
var templateExists bool
for _, innerE := range e.rslt.inner {
ln := innerE.Base().ln
if ln.isHelperMethodOf(helperMethodNameContent) && len(ln.tokens) > 2 && ln.tokens[2] == e.templateName {
templateExists = true
break
}
}
if templateExists {
bf.WriteString(fmt.Sprintf(actionTemplateWithPipeline, e.opts.DelimLeft, inner.path+doubleColon+e.templateName, dot, e.opts.DelimRight))
} else {
// Write the children's HTML.
if i, err := e.writeChildren(&bf); err != nil {
return i, err
}
}
i, err := w.Write(bf.Bytes())
return int64(i), err
}
// newHelperMethodYield creates and returns a helper method yield.
func newHelperMethodYield(ln *line, rslt *result, src *source, parent element, opts *Options) (*helperMethodYield, error) {
if len(ln.tokens) < 3 {
return nil, fmt.Errorf("no template name is specified [file: %s][line: %d]", ln.fileName(), ln.no)
}
e := &helperMethodYield{
elementBase: newElementBase(ln, rslt, src, parent, opts),
templateName: ln.tokens[2],
}
return e, nil
}

View File

@ -0,0 +1,69 @@
package ace
import (
"bytes"
"io"
"strings"
)
// htmlComment represents an HTML comment.
type htmlComment struct {
elementBase
}
// WriteTo writes data to w.
func (e *htmlComment) WriteTo(w io.Writer) (int64, error) {
var bf bytes.Buffer
// Write an open tag.
bf.WriteString(e.opts.DelimLeft)
bf.WriteString(preDefinedFuncNameHTML)
bf.WriteString(space)
bf.WriteString(doubleQuote)
bf.WriteString(lt)
bf.WriteString(exclamation)
bf.WriteString(doubleQuote)
bf.WriteString(e.opts.DelimRight)
bf.WriteString(hyphen)
bf.WriteString(hyphen)
// Write the HTML comment
if len(e.ln.tokens) > 1 {
bf.WriteString(space)
bf.WriteString(strings.Join(e.ln.tokens[1:], space))
}
// Write the children's HTML.
if len(e.children) > 0 {
bf.WriteString(lf)
if i, err := e.writeChildren(&bf); err != nil {
return i, err
}
} else {
bf.WriteString(space)
}
// Write a close tag.
bf.WriteString(hyphen)
bf.WriteString(hyphen)
bf.WriteString(gt)
// Write the buffer.
i, err := w.Write(bf.Bytes())
return int64(i), err
}
// ContainPlainText returns true.
func (e *htmlComment) ContainPlainText() bool {
return true
}
// newHTMLComment creates and returns an HTML comment.
func newHTMLComment(ln *line, rslt *result, src *source, parent element, opts *Options) *htmlComment {
return &htmlComment{
elementBase: newElementBase(ln, rslt, src, parent, opts),
}
}

View File

@ -0,0 +1,305 @@
package ace
import (
"bytes"
"fmt"
"io"
"strings"
)
// Tag names
const (
tagNameDiv = "div"
)
// Attribute names
const (
attributeNameID = "id"
)
// htmlAttribute represents an HTML attribute.
type htmlAttribute struct {
key string
value string
}
// htmlTag represents an HTML tag.
type htmlTag struct {
elementBase
tagName string
id string
classes []string
containPlainText bool
insertBr bool
attributes []htmlAttribute
textValue string
}
// WriteTo writes data to w.
func (e *htmlTag) WriteTo(w io.Writer) (int64, error) {
var bf bytes.Buffer
// Write an open tag.
bf.WriteString(lt)
bf.WriteString(e.tagName)
// Write an id.
if e.id != "" {
bf.WriteString(space)
bf.WriteString(attributeNameID)
bf.WriteString(equal)
bf.WriteString(doubleQuote)
bf.WriteString(e.id)
bf.WriteString(doubleQuote)
}
// Write classes.
if len(e.classes) > 0 {
bf.WriteString(space)
bf.WriteString(e.opts.AttributeNameClass)
bf.WriteString(equal)
bf.WriteString(doubleQuote)
for i, class := range e.classes {
if i > 0 {
bf.WriteString(space)
}
bf.WriteString(class)
}
bf.WriteString(doubleQuote)
}
// Write attributes.
if len(e.attributes) > 0 {
for _, a := range e.attributes {
bf.WriteString(space)
bf.WriteString(a.key)
if a.value != "" {
bf.WriteString(equal)
bf.WriteString(doubleQuote)
bf.WriteString(a.value)
bf.WriteString(doubleQuote)
}
}
}
bf.WriteString(gt)
// Write a text value
if e.textValue != "" {
bf.WriteString(e.textValue)
}
if e.containPlainText {
bf.WriteString(lf)
}
// Write children's HTML.
if i, err := e.writeChildren(&bf); err != nil {
return i, err
}
// Write a close tag.
if !e.noCloseTag() {
bf.WriteString(lt)
bf.WriteString(slash)
bf.WriteString(e.tagName)
bf.WriteString(gt)
}
// Write the buffer.
i, err := w.Write(bf.Bytes())
return int64(i), err
}
// ContainPlainText returns the HTML tag's containPlainText field.
func (e *htmlTag) ContainPlainText() bool {
return e.containPlainText
}
// InsertBr returns true if the br tag is inserted to the line.
func (e *htmlTag) InsertBr() bool {
return e.insertBr
}
// setAttributes parses the tokens and set attributes to the element.
func (e *htmlTag) setAttributes() error {
parsedTokens := e.parseTokens()
var i int
var token string
var setTextValue bool
// Set attributes to the element.
for i, token = range parsedTokens {
kv := strings.Split(token, equal)
if len(kv) < 2 {
setTextValue = true
break
}
k := kv[0]
v := strings.Join(kv[1:], equal)
// Remove the prefix and suffix of the double quotes.
if len(v) > 1 && strings.HasPrefix(v, doubleQuote) && strings.HasSuffix(v, doubleQuote) {
v = v[1 : len(v)-1]
}
switch k {
case attributeNameID:
if e.id != "" {
return fmt.Errorf("multiple IDs are specified [file: %s][line: %d]", e.ln.fileName(), e.ln.no)
}
e.id = v
case e.opts.AttributeNameClass:
e.classes = append(e.classes, strings.Split(v, space)...)
default:
e.attributes = append(e.attributes, htmlAttribute{k, v})
}
}
// Set a text value to the element.
if setTextValue {
e.textValue = strings.Join(parsedTokens[i:], space)
}
return nil
}
// noCloseTag returns true is the HTML tag has no close tag.
func (e *htmlTag) noCloseTag() bool {
for _, name := range e.opts.NoCloseTagNames {
if e.tagName == name {
return true
}
}
return false
}
// newHTMLTag creates and returns an HTML tag.
func newHTMLTag(ln *line, rslt *result, src *source, parent element, opts *Options) (*htmlTag, error) {
if len(ln.tokens) < 1 {
return nil, fmt.Errorf("an HTML tag is not specified [file: %s][line: %d]", ln.fileName(), ln.no)
}
s := ln.tokens[0]
tagName := extractTagName(s)
id, err := extractID(s, ln)
if err != nil {
return nil, err
}
classes := extractClasses(s)
e := &htmlTag{
elementBase: newElementBase(ln, rslt, src, parent, opts),
tagName: tagName,
id: id,
classes: classes,
containPlainText: strings.HasSuffix(s, dot),
insertBr: strings.HasSuffix(s, doubleDot),
attributes: make([]htmlAttribute, 0, 2),
}
if err := e.setAttributes(); err != nil {
return nil, err
}
return e, nil
}
// extractTag extracts and returns a tag.
func extractTagName(s string) string {
tagName := strings.Split(strings.Split(s, sharp)[0], dot)[0]
if tagName == "" {
tagName = tagNameDiv
}
return tagName
}
// extractID extracts and returns an ID.
func extractID(s string, ln *line) (string, error) {
tokens := strings.Split(s, sharp)
l := len(tokens)
if l < 2 {
return "", nil
}
if l > 2 {
return "", fmt.Errorf("multiple IDs are specified [file: %s][line: %d]", ln.fileName(), ln.no)
}
return strings.Split(tokens[1], dot)[0], nil
}
// extractClasses extracts and returns classes.
func extractClasses(s string) []string {
var classes []string
for i, token := range strings.Split(s, dot) {
if i == 0 {
continue
}
class := strings.Split(token, sharp)[0]
if class == "" {
continue
}
classes = append(classes, class)
}
return classes
}
// parseTokens parses the tokens and return them
func (e *htmlTag) parseTokens() []string {
var inQuote bool
var inDelim bool
var tokens []string
var token string
str := strings.Join(e.ln.tokens[1:], space)
for _, chr := range str {
switch c := string(chr); c {
case space:
if inQuote || inDelim {
token += c
} else {
tokens = append(tokens, token)
token = ""
}
case doubleQuote:
if !inDelim {
if inQuote {
inQuote = false
} else {
inQuote = true
}
}
token += c
default:
token += c
if inDelim {
if strings.HasSuffix(token, e.opts.DelimRight) {
inDelim = false
}
} else {
if strings.HasSuffix(token, e.opts.DelimLeft) {
inDelim = true
}
}
}
}
if len(token) > 0 {
tokens = append(tokens, token)
}
return tokens
}

117
vendor/src/github.com/yosssi/ace/line.go vendored Normal file
View File

@ -0,0 +1,117 @@
package ace
import (
"fmt"
"strings"
)
const unicodeSpace = 32
const indentTop = 0
// line represents a line of codes.
type line struct {
no int
str string
indent int
tokens []string
opts *Options
file *File
}
// isEmpty returns true if the line is empty.
func (l *line) isEmpty() bool {
return strings.TrimSpace(l.str) == ""
}
// isTopIndent returns true if the line's indent is the top level.
func (l *line) isTopIndent() bool {
return l.indent == indentTop
}
// isHelperMethod returns true if the line is a helper method.
func (l *line) isHelperMethod() bool {
return len(l.tokens) > 1 && l.tokens[0] == equal
}
// isHelperMethodOf returns true if the line is a specified helper method.
func (l *line) isHelperMethodOf(name string) bool {
return l.isHelperMethod() && l.tokens[1] == name
}
// isPlainText returns true if the line is a plain text.
func (l *line) isPlainText() bool {
return len(l.tokens) > 0 && (l.tokens[0] == pipe || l.tokens[0] == doublePipe)
}
// isComment returns true if the line is a comment.
func (l *line) isComment() bool {
return len(l.tokens) > 0 && l.tokens[0] == slash
}
// isHTMLComment returns true if the line is an HTML comment.
func (l *line) isHTMLComment() bool {
return len(l.tokens) > 0 && l.tokens[0] == slash+slash
}
// isAction returns true if the line is an action.
func (l *line) isAction() bool {
str := strings.TrimSpace(l.str)
return strings.HasPrefix(str, l.opts.DelimLeft) && strings.HasSuffix(str, l.opts.DelimRight)
}
// fileName returns the file name.
func (l *line) fileName() string {
return l.file.path + dot + l.opts.Extension
}
// childOf returns true is the line is a child of the element.
func (l *line) childOf(parent element) (bool, error) {
var ok bool
var err error
switch {
case l.isEmpty():
ok = true
case parent.ContainPlainText():
switch {
case parent.Base().ln.indent < l.indent:
ok = true
}
default:
switch {
case l.indent == parent.Base().ln.indent+1:
ok = true
case l.indent > parent.Base().ln.indent+1:
err = fmt.Errorf("the indent is invalid [file: %s][line: %d]", l.fileName(), l.no)
}
}
return ok, err
}
// newLine creates and returns a line.
func newLine(no int, str string, opts *Options, f *File) *line {
return &line{
no: no,
str: str,
indent: indent(str),
tokens: strings.Split(strings.TrimLeft(str, space), space),
opts: opts,
file: f,
}
}
// indent returns the line's indent.
func indent(str string) int {
var i int
for _, b := range str {
if b != unicodeSpace {
break
}
i++
}
return i / 2
}

View File

@ -0,0 +1,93 @@
package ace
import "html/template"
// Defaults
const (
defaultExtension = "ace"
defaultDelimLeft = "{{"
defaultDelimRight = "}}"
defaultAttributeNameClass = "class"
)
// Default NoCloseTagNames
var defaultNoCloseTagNames = []string{
"br",
"hr",
"img",
"input",
"link",
"meta",
}
// Options represents options for the template engine.
type Options struct {
// Extension represents an extension of files.
Extension string
// DelimLeft represents a left delimiter for the html template.
DelimLeft string
// DelimRight represents a right delimiter for the html template.
DelimRight string
// AttributeNameClass is the attribute name for classes.
AttributeNameClass string
// NoCloseTagNames defines a set of tags which should not be closed.
NoCloseTagNames []string
// DynamicReload represents a flag which means whether Ace reloads
// templates dynamically.
// This option should only be true in development.
DynamicReload bool
// BaseDir represents a base directory of the Ace templates.
BaseDir string
// Asset loads and returns the asset for the given name.
// If this function is set, Ace load the template data from
// this function instead of the template files.
Asset func(name string) ([]byte, error)
// FuncMap represents a template.FuncMap which is set to
// the result template.
FuncMap template.FuncMap
}
// InitializeOptions initializes the options.
func InitializeOptions(opts *Options) *Options {
if opts == nil {
opts = &Options{}
}
if opts.Extension == "" {
opts.Extension = defaultExtension
}
if opts.DelimLeft == "" {
opts.DelimLeft = defaultDelimLeft
}
if opts.DelimRight == "" {
opts.DelimRight = defaultDelimRight
}
if opts.AttributeNameClass == "" {
opts.AttributeNameClass = defaultAttributeNameClass
}
if opts.NoCloseTagNames == nil {
opts.NoCloseTagNames = make([]string, len(defaultNoCloseTagNames))
copy(opts.NoCloseTagNames, defaultNoCloseTagNames)
}
return opts
}
// AddNoCloseTagName appends name to .NoCloseTagNames set.
func (opts *Options) AddNoCloseTagName(name string) {
opts.NoCloseTagNames = append(opts.NoCloseTagNames, name)
}
// DeleteNoCloseTagName deletes name from .NoCloseTagNames set.
func (opts *Options) DeleteNoCloseTagName(name string) {
var newset []string
for _, n := range opts.NoCloseTagNames {
if n != name {
newset = append(newset, n)
}
}
opts.NoCloseTagNames = newset
}

View File

@ -0,0 +1,114 @@
package ace
import "strings"
// ParseSource parses the source and returns the result.
func ParseSource(src *source, opts *Options) (*result, error) {
// Initialize the options.
opts = InitializeOptions(opts)
rslt := newResult(nil, nil, nil)
base, err := parseBytes(src.base.data, rslt, src, opts, src.base)
if err != nil {
return nil, err
}
inner, err := parseBytes(src.inner.data, rslt, src, opts, src.inner)
if err != nil {
return nil, err
}
includes := make(map[string][]element)
for _, f := range src.includes {
includes[f.path], err = parseBytes(f.data, rslt, src, opts, f)
if err != nil {
return nil, err
}
}
rslt.base = base
rslt.inner = inner
rslt.includes = includes
return rslt, nil
}
// parseBytes parses the byte data and returns the elements.
func parseBytes(data []byte, rslt *result, src *source, opts *Options, f *File) ([]element, error) {
var elements []element
lines := strings.Split(formatLF(string(data)), lf)
i := 0
l := len(lines)
// Ignore the last empty line.
if l > 0 && lines[l-1] == "" {
l--
}
for i < l {
// Fetch a line.
ln := newLine(i+1, lines[i], opts, f)
i++
// Ignore the empty line.
if ln.isEmpty() {
continue
}
if ln.isTopIndent() {
e, err := newElement(ln, rslt, src, nil, opts)
if err != nil {
return nil, err
}
// Append child elements to the element.
if err := appendChildren(e, rslt, lines, &i, l, src, opts, f); err != nil {
return nil, err
}
elements = append(elements, e)
}
}
return elements, nil
}
// appendChildren parses the lines and appends the children to the element.
func appendChildren(parent element, rslt *result, lines []string, i *int, l int, src *source, opts *Options, f *File) error {
for *i < l {
// Fetch a line.
ln := newLine(*i+1, lines[*i], opts, f)
// Check if the line is a child of the parent.
ok, err := ln.childOf(parent)
if err != nil {
return err
}
if !ok {
return nil
}
child, err := newElement(ln, rslt, src, parent, opts)
if err != nil {
return err
}
parent.AppendChild(child)
*i++
if child.CanHaveChildren() {
if err := appendChildren(child, rslt, lines, i, l, src, opts, f); err != nil {
return err
}
}
}
return nil
}

View File

@ -0,0 +1,57 @@
package ace
import (
"bytes"
"io"
"strings"
)
// plainText represents a plain text.
type plainText struct {
elementBase
insertBr bool
}
// WriteTo writes data to w.
func (e *plainText) WriteTo(w io.Writer) (int64, error) {
var bf bytes.Buffer
// Write the plain text.
bf.WriteString(strings.Join(e.ln.tokens[1:], space))
if len(e.ln.tokens) > 1 && e.insertBr {
bf.WriteString(htmlBr)
}
// Write the children's HTML.
if len(e.children) > 0 {
bf.WriteString(lf)
if i, err := e.writeChildren(&bf); err != nil {
return i, err
}
}
// Write the buffer.
i, err := w.Write(bf.Bytes())
return int64(i), err
}
// ContainPlainText returns true.
func (e *plainText) ContainPlainText() bool {
return true
}
// InsertBr returns true if the br tag is inserted to the line.
func (e *plainText) InsertBr() bool {
return e.insertBr
}
// newPlainText creates and returns a plain text.
func newPlainText(ln *line, rslt *result, src *source, parent element, opts *Options) *plainText {
return &plainText{
elementBase: newElementBase(ln, rslt, src, parent, opts),
insertBr: ln.tokens[0] == doublePipe,
}
}

View File

@ -0,0 +1,44 @@
package ace
import "io"
// HTML
const (
htmlBr = "<br>"
)
// plainTextInner represents a plain text inner.
type plainTextInner struct {
elementBase
insertBr bool
}
// WriteTo writes data to w.
func (e *plainTextInner) WriteTo(w io.Writer) (int64, error) {
s := ""
if (e.parent.Base().ln.indent+1)*2 <= len(e.ln.str) {
s = e.ln.str[(e.parent.Base().ln.indent+1)*2:]
}
if e.insertBr && !e.lastChild {
s += htmlBr
}
i, err := w.Write([]byte(s + lf))
return int64(i), err
}
// CanHaveChildren returns false.
func (e *plainTextInner) CanHaveChildren() bool {
return false
}
// newPlainTextInner creates and returns a plain text.
func newPlainTextInner(ln *line, rslt *result, src *source, parent element, insertBr bool, opts *Options) *plainTextInner {
return &plainTextInner{
elementBase: newElementBase(ln, rslt, src, parent, opts),
insertBr: insertBr,
}
}

144
vendor/src/github.com/yosssi/ace/read.go vendored Normal file
View File

@ -0,0 +1,144 @@
package ace
import (
"fmt"
"io/ioutil"
"path/filepath"
"strings"
)
// Special characters
const (
cr = "\r"
lf = "\n"
crlf = "\r\n"
space = " "
equal = "="
pipe = "|"
doublePipe = pipe + pipe
slash = "/"
sharp = "#"
dot = "."
doubleDot = dot + dot
colon = ":"
doubleColon = colon + colon
doubleQuote = `"`
lt = "<"
gt = ">"
exclamation = "!"
hyphen = "-"
bracketOpen = "["
bracketClose = "]"
)
// readFiles reads files and returns source for the parsing process.
func readFiles(basePath, innerPath string, opts *Options) (*source, error) {
// Read the base file.
base, err := readFile(basePath, opts)
if err != nil {
return nil, err
}
// Read the inner file.
inner, err := readFile(innerPath, opts)
if err != nil {
return nil, err
}
var includes []*File
// Find include files from the base file.
if err := findIncludes(base.data, opts, &includes, base); err != nil {
return nil, err
}
// Find include files from the inner file.
if err := findIncludes(inner.data, opts, &includes, inner); err != nil {
return nil, err
}
return NewSource(base, inner, includes), nil
}
// readFile reads a file and returns a file struct.
func readFile(path string, opts *Options) (*File, error) {
var data []byte
var err error
if path != "" {
name := filepath.Join(opts.BaseDir, path+dot+opts.Extension)
if opts.Asset != nil {
data, err = opts.Asset(name)
} else {
data, err = ioutil.ReadFile(name)
}
if err != nil {
return nil, err
}
}
return NewFile(path, data), nil
}
// findIncludes finds and adds include files.
func findIncludes(data []byte, opts *Options, includes *[]*File, targetFile *File) error {
includePaths, err := findIncludePaths(data, opts, targetFile)
if err != nil {
return err
}
for _, includePath := range includePaths {
if !hasFile(*includes, includePath) {
f, err := readFile(includePath, opts)
if err != nil {
return err
}
*includes = append(*includes, f)
if err := findIncludes(f.data, opts, includes, f); err != nil {
return err
}
}
}
return nil
}
// findIncludePaths finds and returns include paths.
func findIncludePaths(data []byte, opts *Options, f *File) ([]string, error) {
var includePaths []string
for i, str := range strings.Split(formatLF(string(data)), lf) {
ln := newLine(i+1, str, opts, f)
if ln.isHelperMethodOf(helperMethodNameInclude) {
if len(ln.tokens) < 3 {
return nil, fmt.Errorf("no template name is specified [file: %s][line: %d]", ln.fileName(), ln.no)
}
includePaths = append(includePaths, ln.tokens[2])
}
}
return includePaths, nil
}
// formatLF replaces the line feed codes with LF and returns the result.
func formatLF(s string) string {
return strings.Replace(strings.Replace(s, crlf, lf, -1), cr, lf, -1)
}
// hasFile return if files has a file which has the path specified by the parameter.
func hasFile(files []*File, path string) bool {
for _, f := range files {
if f.path == path {
return true
}
}
return false
}

View File

@ -0,0 +1,17 @@
package ace
// result represents a result of the parsing process.
type result struct {
base []element
inner []element
includes map[string][]element
}
// newResult creates and returns a result.
func newResult(base []element, inner []element, includes map[string][]element) *result {
return &result{
base: base,
inner: inner,
includes: includes,
}
}

View File

@ -0,0 +1,17 @@
package ace
// source represents source for the parsing process.
type source struct {
base *File
inner *File
includes []*File
}
// NewSource creates and returns source.
func NewSource(base, inner *File, includes []*File) *source {
return &source{
base: base,
inner: inner,
includes: includes,
}
}

View File

@ -0,0 +1,35 @@
box: yosssi/golang-latest@1.0.7
# Build definition
build:
# The steps that will be executed on build
steps:
# Sets the go workspace and places you package
# at the right place in the workspace tree
- setup-go-workspace
# Gets the dependencies
- script:
name: go get
code: |
cd $WERCKER_SOURCE_DIR
go version
go get -t ./...
# Build the project
- script:
name: go build
code: |
go build ./...
# Test the project
- script:
name: go test
code: |
go test -cover ./...
# Invoke goveralls
- script:
name: goveralls
code: |
go get github.com/mattn/goveralls
go test -v -covermode=count -coverprofile=coverage.out
goveralls -coverprofile=coverage.out -service=wercker.com -repotoken $COVERALLS_REPO_TOKEN