pax_global_header00006660000000000000000000000064127463340300014514gustar00rootroot0000000000000052 comment=ea038f4770b6746c3f8f84f14fa60d9fe1205b56 ace-0.0.5/000077500000000000000000000000001274633403000122465ustar00rootroot00000000000000ace-0.0.5/.gitignore000066400000000000000000000004031274633403000142330ustar00rootroot00000000000000# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test ace-0.0.5/LICENSE000066400000000000000000000020671274633403000132600ustar00rootroot00000000000000The 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.ace-0.0.5/README.md000066400000000000000000000101301274633403000135200ustar00rootroot00000000000000# 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 Hello Ace

Hello Ace

Ace is an HTML template engine for Go.
This engine simplifies HTML coding in Go web application development.

``` ## 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 * [atom-ace](https://github.com/pariz/atom-ace) - Atom Editor 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! ace-0.0.5/ace.go000066400000000000000000000026451274633403000133340ustar00rootroot00000000000000package 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() } ace-0.0.5/action.go000066400000000000000000000014741274633403000140600ustar00rootroot00000000000000package 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 } func (e *action) IsBlockElement() bool { return e.parent.IsBlockElement() } func (e *action) IsControlElement() bool { return true } // 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), } } ace-0.0.5/cmd/000077500000000000000000000000001274633403000130115ustar00rootroot00000000000000ace-0.0.5/cmd/ace/000077500000000000000000000000001274633403000135415ustar00rootroot00000000000000ace-0.0.5/cmd/ace/main.go000066400000000000000000000035051274633403000150170ustar00rootroot00000000000000package 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)) } } } ace-0.0.5/comment.go000066400000000000000000000007671274633403000142510ustar00rootroot00000000000000package 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), } } ace-0.0.5/compile.go000066400000000000000000000043251274633403000142310ustar00rootroot00000000000000package 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 } ace-0.0.5/compile_test.go000066400000000000000000000045421274633403000152710ustar00rootroot00000000000000package ace import ( "testing" ) func TestLineCompile(t *testing.T) { for i, this := range []struct { template string expect string }{ { template: `a href=./foo foo`, expect: `foo`, }, { template: `span.color-red red`, expect: `red`, }, { template: `span#ref1 text`, expect: `text`, }, { template: `span#ref1.color-red.text-big text`, expect: `text`, }, { template: `span.color-red#ref1.text-big text`, expect: `text`, }, { template: `#ref1 text`, expect: `
text
`, }, { template: `#ref1.color-red.text-big text`, expect: `
text
`, }, { template: `.color-red#ref1.text-big text`, expect: `
text
`, }, { template: "div class=\"dialog {{ if eq .Attr `important` }}color-red{{end}}\" text", expect: "
text
", }, { template: "div class=\"dialog {{ if eq .Attr `important` }}color-red text-big{{end}}\" text", expect: "
text
", }, { template: "div class=\"dialog {{ if eq .Attr \"important\" }}color-red{{end}}\" text", expect: "
text
", }, { template: "div class=\"dialog {{ if eq .Attr \"important\" }}color-red text-big{{end}}\" text", expect: "
text
", }, } { 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) } } } ace-0.0.5/doc.go000066400000000000000000000000751274633403000133440ustar00rootroot00000000000000// Package ace provides an HTML template engine. package ace ace-0.0.5/documentation/000077500000000000000000000000001274633403000151175ustar00rootroot00000000000000ace-0.0.5/documentation/README.md000066400000000000000000000002251274633403000163750ustar00rootroot00000000000000# Documentation * [Getting Started](getting-started.md) * [Syntax](syntax.md) * [Options](options.md) * [Projects using Ace](projects-using-ace.md) ace-0.0.5/documentation/getting-started.md000066400000000000000000000035431274633403000205530ustar00rootroot00000000000000# 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 Ace example

Base Template : Hello Ace

Inner Template - Main : Hello Ace

Inner Template - Sub : Hello Ace

Included Template : Hello Ace

``` ace-0.0.5/documentation/options.md000066400000000000000000000014061274633403000171350ustar00rootroot00000000000000# 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. ace-0.0.5/documentation/projects-using-ace.md000066400000000000000000000007751274633403000211540ustar00rootroot00000000000000# 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 * [ace-util](https://github.com/catinello/ace-util) - Command line utility for the Ace HTML template engine. ace-0.0.5/documentation/syntax.md000066400000000000000000000155061274633403000167760ustar00rootroot00000000000000# Syntax ## Indent A unit of an indent must be 2 spaces. ```ace html body div p ``` becomes ```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

This is interpreted as a text.

Go to GitHub
``` ID and classes can be defined with a head word of a line. ```ace p#foo.bar #container .wrapper ``` becomes ```html

``` 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

This is a block text.
BR tags are inserted
automatically.

``` ## 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
This is a single line.
This is a block line.
This is a
block line
with BR tags.
``` ## 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 | | | revealed | HTML | ```ace = conditionalComment hidden IE 6

You are using Internet Explorer 6.

= conditionalComment revealed !IE ``` becomes ```html ``` ### 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 ``` ### 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 | | | xml | | | transitional | | | strict | | | frameset | | | 1.1 | | | basic | | | mobile | | ```ace = doctype html ``` becomes ```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 ``` ### 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 ``` ## 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 string. ```ace {{"
"}} {{HTML "
"}} ``` becomes ```html <br>
``` ace-0.0.5/element.go000066400000000000000000000045721274633403000142360ustar00rootroot00000000000000package 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) IsBlockElement() bool IsControlElement() 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 } ace-0.0.5/element_base.go000066400000000000000000000037471274633403000152330ustar00rootroot00000000000000package 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 } func (e *elementBase) IsBlockElement() bool { return false } func (e *elementBase) IsControlElement() bool { return false } // 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 e.opts.formatter != nil { if i, err := e.opts.formatter.OpeningElement(bf, child); err != nil { return int64(i), err } } 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, } } ace-0.0.5/element_test.go000066400000000000000000000001071274633403000152630ustar00rootroot00000000000000package ace import "testing" func Test_newElement(t *testing.T) { } ace-0.0.5/empty_element.go000066400000000000000000000010461274633403000154450ustar00rootroot00000000000000package 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), } } ace-0.0.5/examples/000077500000000000000000000000001274633403000140645ustar00rootroot00000000000000ace-0.0.5/examples/README.md000066400000000000000000000015411274633403000153440ustar00rootroot00000000000000# 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) ace-0.0.5/examples/actions/000077500000000000000000000000001274633403000155245ustar00rootroot00000000000000ace-0.0.5/examples/actions/README.md000066400000000000000000000001051274633403000167770ustar00rootroot00000000000000# Actions This example shows how to embed actions in Ace templates. ace-0.0.5/examples/actions/example.ace000066400000000000000000000004111274633403000176250ustar00rootroot00000000000000= doctype html html lang=en head meta charset=utf-8 title {{.Title}} body h1 {{.Title}} ul {{range .Msgs}} li {{.}} {{end}} div {{"
Escaped String
"}} div {{HTML "
Non-Escaped String
"}} ace-0.0.5/examples/actions/main.go000066400000000000000000000011071274633403000167760ustar00rootroot00000000000000package 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) } ace-0.0.5/examples/base_inner_template/000077500000000000000000000000001274633403000200645ustar00rootroot00000000000000ace-0.0.5/examples/base_inner_template/README.md000066400000000000000000000001351274633403000213420ustar00rootroot00000000000000# Base and Inner Template This example shows how to use a base and inner Ace template file. ace-0.0.5/examples/base_inner_template/base.ace000066400000000000000000000002431274633403000214470ustar00rootroot00000000000000= 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 ace-0.0.5/examples/base_inner_template/inner.ace000066400000000000000000000002211274633403000216440ustar00rootroot00000000000000= 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. ace-0.0.5/examples/base_inner_template/main.go000066400000000000000000000007101274633403000213350ustar00rootroot00000000000000package 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) } ace-0.0.5/examples/cache_options/000077500000000000000000000000001274633403000167025ustar00rootroot00000000000000ace-0.0.5/examples/cache_options/README.md000066400000000000000000000002351274633403000201610ustar00rootroot00000000000000# 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. ace-0.0.5/examples/cache_options/main.go000066400000000000000000000010541274633403000201550ustar00rootroot00000000000000package 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) } ace-0.0.5/examples/cache_options/views/000077500000000000000000000000001274633403000200375ustar00rootroot00000000000000ace-0.0.5/examples/cache_options/views/example.ace000066400000000000000000000001561274633403000221460ustar00rootroot00000000000000= doctype html html lang=en head meta charset=utf-8 title Cache Options body h1 Cache Options ace-0.0.5/examples/change_action_delimiter/000077500000000000000000000000001274633403000207045ustar00rootroot00000000000000ace-0.0.5/examples/change_action_delimiter/README.md000066400000000000000000000001261274633403000221620ustar00rootroot00000000000000# Change the Action Delimiter This example shows how to change the action delimiter. ace-0.0.5/examples/change_action_delimiter/example.ace000066400000000000000000000001671274633403000230150ustar00rootroot00000000000000= doctype html html lang=en head meta charset=utf-8 title Change the Action Delimiter body h1 <%.Msg%> ace-0.0.5/examples/change_action_delimiter/main.go000066400000000000000000000010661274633403000221620ustar00rootroot00000000000000package 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) } ace-0.0.5/examples/comments/000077500000000000000000000000001274633403000157115ustar00rootroot00000000000000ace-0.0.5/examples/comments/README.md000066400000000000000000000000671274633403000171730ustar00rootroot00000000000000# Comments This example shows how to define comments. ace-0.0.5/examples/comments/example.ace000066400000000000000000000010511274633403000200130ustar00rootroot00000000000000= 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

You are using Internet Explorer 6.

= conditionalComment revealed !IE ace-0.0.5/examples/comments/main.go000066400000000000000000000007061274633403000171670ustar00rootroot00000000000000package 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) } ace-0.0.5/examples/css_javascript_helper_method/000077500000000000000000000000001274633403000220015ustar00rootroot00000000000000ace-0.0.5/examples/css_javascript_helper_method/README.md000066400000000000000000000001501274633403000232540ustar00rootroot00000000000000# CSS and Javascript Helper Method This example shows how to use the css and javascript helper method. ace-0.0.5/examples/css_javascript_helper_method/example.ace000066400000000000000000000006211274633403000241050ustar00rootroot00000000000000= 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); ace-0.0.5/examples/css_javascript_helper_method/main.go000066400000000000000000000007061274633403000232570ustar00rootroot00000000000000package 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) } ace-0.0.5/examples/dynamic_reload/000077500000000000000000000000001274633403000170365ustar00rootroot00000000000000ace-0.0.5/examples/dynamic_reload/README.md000066400000000000000000000004251274633403000203160ustar00rootroot00000000000000# 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. ace-0.0.5/examples/dynamic_reload/example.ace000066400000000000000000000001601274633403000211400ustar00rootroot00000000000000= doctype html html lang=en head meta charset=utf-8 title Dynamic Reload body h1 Dynamic Reload ace-0.0.5/examples/dynamic_reload/main.go000066400000000000000000000007441274633403000203160ustar00rootroot00000000000000package 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) } ace-0.0.5/examples/external_css_and_js/000077500000000000000000000000001274633403000200745ustar00rootroot00000000000000ace-0.0.5/examples/external_css_and_js/README.md000066400000000000000000000001541274633403000213530ustar00rootroot00000000000000# Single Template This example shows how to build a web application which uses a single Ace template file. ace-0.0.5/examples/external_css_and_js/example.ace000066400000000000000000000021721274633403000222030ustar00rootroot00000000000000= doctype html html lang=en head meta charset=utf-8 title External CSS and JS link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" body div.container h1. External CSS and JS div.row div.col-xs-12 label input#completed-css disabled="disabled" type="checkbox" span Load CSS from external source div.row div.col-xs-12 label input#completed-js disabled="disabled" type="checkbox" span Load JS from external source script src="http://code.jquery.com/jquery-2.1.4.min.js" type="text/javascript" = javascript // verification that we are using downloaded javascript window.onload = function() { if (jQuery !== undefined) { document.getElementById("completed-js").checked = true; } for (var i = 0; i < document.styleSheets.length; ++i) { if (document.styleSheets[i].href.indexOf("bootstrap.min.css") !== -1) { document.getElementById("completed-css").checked = true; } } }; ace-0.0.5/examples/external_css_and_js/main.go000066400000000000000000000007061274633403000213520ustar00rootroot00000000000000package 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) } ace-0.0.5/examples/html_tags/000077500000000000000000000000001274633403000160465ustar00rootroot00000000000000ace-0.0.5/examples/html_tags/README.md000066400000000000000000000001031274633403000173170ustar00rootroot00000000000000# HTML Tags This example shows how to generate various HTML tags. ace-0.0.5/examples/html_tags/example.ace000066400000000000000000000007771274633403000201660ustar00rootroot00000000000000= 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); ace-0.0.5/examples/html_tags/main.go000066400000000000000000000007061274633403000173240ustar00rootroot00000000000000package 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) } ace-0.0.5/examples/include_helper_method/000077500000000000000000000000001274633403000204065ustar00rootroot00000000000000ace-0.0.5/examples/include_helper_method/README.md000066400000000000000000000001211274633403000216570ustar00rootroot00000000000000# Include Helper Method This example shows how to use an include helper method. ace-0.0.5/examples/include_helper_method/example.ace000066400000000000000000000002221274633403000225070ustar00rootroot00000000000000= doctype html html lang=en head meta charset=utf-8 title Include Helper Method body h1 This is a base template = include inc ace-0.0.5/examples/include_helper_method/inc.ace000066400000000000000000000000411274633403000216240ustar00rootroot00000000000000h2 This is an included template. ace-0.0.5/examples/include_helper_method/main.go000066400000000000000000000007061274633403000216640ustar00rootroot00000000000000package 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) } ace-0.0.5/examples/load_templates_from_binary_data/000077500000000000000000000000001274633403000224415ustar00rootroot00000000000000ace-0.0.5/examples/load_templates_from_binary_data/README.md000066400000000000000000000007701274633403000237240ustar00rootroot00000000000000# 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.** ace-0.0.5/examples/load_templates_from_binary_data/asset.go000066400000000000000000000036151274633403000241140ustar00rootroot00000000000000package 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, } ace-0.0.5/examples/load_templates_from_binary_data/main.go000066400000000000000000000007511274633403000237170ustar00rootroot00000000000000package 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) } ace-0.0.5/examples/load_templates_from_binary_data/make_asset.sh000077500000000000000000000000471274633403000251150ustar00rootroot00000000000000#!/bin/sh go-bindata -o asset.go views ace-0.0.5/examples/load_templates_from_binary_data/views/000077500000000000000000000000001274633403000235765ustar00rootroot00000000000000ace-0.0.5/examples/load_templates_from_binary_data/views/example.ace000066400000000000000000000002221274633403000256770ustar00rootroot00000000000000= doctype html html lang=en head meta charset=utf-8 title Load templates from binary data body h1 Load templates from binary data ace-0.0.5/examples/pass_pipeline_to_included_template/000077500000000000000000000000001274633403000231635ustar00rootroot00000000000000ace-0.0.5/examples/pass_pipeline_to_included_template/README.md000066400000000000000000000002101274633403000244330ustar00rootroot00000000000000# Pass a Pipeline (Parameter) to the Included Template This example shows how to pass a pipeline (parameter) to the included template. ace-0.0.5/examples/pass_pipeline_to_included_template/example.ace000066400000000000000000000003771274633403000252770ustar00rootroot00000000000000= 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}} ace-0.0.5/examples/pass_pipeline_to_included_template/main.go000066400000000000000000000013541274633403000244410ustar00rootroot00000000000000package 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) } ace-0.0.5/examples/pass_pipeline_to_included_template/pet.ace000066400000000000000000000000451274633403000244240ustar00rootroot00000000000000li {{.Species}}, {{.Name}}, {{.Age}} ace-0.0.5/examples/plain_texts/000077500000000000000000000000001274633403000164165ustar00rootroot00000000000000ace-0.0.5/examples/plain_texts/README.md000066400000000000000000000000771274633403000177010ustar00rootroot00000000000000# Plain Texts This example shows how to generate plain texts. ace-0.0.5/examples/plain_texts/example.ace000066400000000000000000000004001274633403000205150ustar00rootroot00000000000000= 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. ace-0.0.5/examples/plain_texts/main.go000066400000000000000000000007061274633403000176740ustar00rootroot00000000000000package 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) } ace-0.0.5/examples/set_custom_functions/000077500000000000000000000000001274633403000203415ustar00rootroot00000000000000ace-0.0.5/examples/set_custom_functions/README.md000066400000000000000000000001101274633403000216100ustar00rootroot00000000000000# Set Custom Functions This example shows how to set custom functions. ace-0.0.5/examples/set_custom_functions/example.ace000066400000000000000000000001721274633403000224460ustar00rootroot00000000000000= doctype html html lang=en head meta charset=utf-8 title Set Custom Functions body h1 {{Greeting "Ace"}} ace-0.0.5/examples/set_custom_functions/main.go000066400000000000000000000011331274633403000216120ustar00rootroot00000000000000package 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) } ace-0.0.5/examples/set_default_value_to_the_yield_helper_method/000077500000000000000000000000001274633403000252065ustar00rootroot00000000000000ace-0.0.5/examples/set_default_value_to_the_yield_helper_method/README.md000066400000000000000000000001741274633403000264670ustar00rootroot00000000000000# Set a Default Value to the Yield Helper Method This example shows how to set a default value to the yield helper method. ace-0.0.5/examples/set_default_value_to_the_yield_helper_method/base.ace000066400000000000000000000005631274633403000265760ustar00rootroot00000000000000= 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. ace-0.0.5/examples/set_default_value_to_the_yield_helper_method/inner.ace000066400000000000000000000001111274633403000267640ustar00rootroot00000000000000= content main h2 This is a content named "main" of an inner template. ace-0.0.5/examples/set_default_value_to_the_yield_helper_method/main.go000066400000000000000000000007101274633403000264570ustar00rootroot00000000000000package 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) } ace-0.0.5/examples/single_template/000077500000000000000000000000001274633403000172405ustar00rootroot00000000000000ace-0.0.5/examples/single_template/README.md000066400000000000000000000001541274633403000205170ustar00rootroot00000000000000# Single Template This example shows how to build a web application which uses a single Ace template file. ace-0.0.5/examples/single_template/example.ace000066400000000000000000000001621274633403000213440ustar00rootroot00000000000000= doctype html html lang=en head meta charset=utf-8 title Single Template body h1 Single Template ace-0.0.5/examples/single_template/main.go000066400000000000000000000007061274633403000205160ustar00rootroot00000000000000package 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) } ace-0.0.5/file.go000066400000000000000000000003361274633403000135160ustar00rootroot00000000000000package 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, } } ace-0.0.5/formatter.go000066400000000000000000000022411274633403000145770ustar00rootroot00000000000000package ace import "bytes" import "strings" // File represents a file. type outputFormatter interface { OpeningElement(*bytes.Buffer, element) (int, error) ClosingElement(*bytes.Buffer, element) (int, error) WritingTextValue(*bytes.Buffer, element) (int, error) } type Formatter struct { indent string } func newFormatter(indent string) outputFormatter { f := &Formatter{ indent: indent, } return f } func (f *Formatter) OpeningElement(bf *bytes.Buffer, e element) (int, error) { if e.IsControlElement() { return 0, nil } base := e.Base() if base.parent != nil && base.parent.IsBlockElement() { return f.writeIndent(bf, base.ln.indent) } return 0, nil } func (f *Formatter) ClosingElement(bf *bytes.Buffer, e element) (int, error) { if e.IsBlockElement() { return f.writeIndent(bf, e.Base().ln.indent) } return 0, nil } func (f *Formatter) WritingTextValue(bf *bytes.Buffer, e element) (int, error) { if e.IsBlockElement() { return f.writeIndent(bf, e.Base().ln.indent+1) } return 0, nil } func (f *Formatter) writeIndent(bf *bytes.Buffer, depth int) (int, error) { indent := "\n" + strings.Repeat(f.indent, depth) return bf.WriteString(indent) } ace-0.0.5/formatter_test.go000066400000000000000000000025741274633403000156470ustar00rootroot00000000000000package ace import ( "bytes" "io/ioutil" "os" "testing" ) var innerTemplate string = ` = content inner section h1 hello ` func TestFormatter(t *testing.T) { inner, _ := ioutil.TempFile("", "inner") inner.WriteString(innerTemplate) inner.Close() os.Rename(inner.Name(), inner.Name()+".ace") defer os.Remove(inner.Name() + ".ace") for i, this := range []struct { template string expect string }{ { template: ` a span foo `, expect: `foo`, }, { template: ` div span foo `, expect: `
foo
`, }, { template: ` section div {{if true}} span foo {{end}} div text `, expect: `
foo
text
`, }, { template: ` body = yield inner `, expect: `

hello

`, }, } { base, _ := ioutil.TempFile("", "base") base.WriteString(this.template) base.Close() os.Rename(base.Name(), base.Name()+".ace") defer os.Remove(base.Name() + ".ace") tpl, _ := Load(base.Name(), inner.Name(), &Options{ Indent: " ", }) buf := new(bytes.Buffer) tpl.Execute(buf, map[string]string{}) compiled := buf.String() if compiled != this.expect { t.Errorf("[%d] Compiler didn't return an expected value, got %v but expected %v", i, compiled, this.expect) } } } ace-0.0.5/helper_method_conditional_comment.go000066400000000000000000000050631274633403000215250ustar00rootroot00000000000000package 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 } ace-0.0.5/helper_method_content.go000066400000000000000000000025701274633403000171520ustar00rootroot00000000000000package 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 } func (e *helperMethodContent) IsBlockElement() bool { return true } // 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 } ace-0.0.5/helper_method_css.go000066400000000000000000000020111274633403000162560ustar00rootroot00000000000000package 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), } } ace-0.0.5/helper_method_doctype.go000066400000000000000000000035741274633403000171540ustar00rootroot00000000000000package ace import ( "fmt" "io" ) // Doctypes var doctypes = map[string]string{ "html": ``, "xml": ``, "transitional": ``, "strict": ``, "frameset": ``, "1.1": ``, "basic": ``, "mobile": ``, } // 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 } ace-0.0.5/helper_method_include.go000066400000000000000000000022551274633403000171230ustar00rootroot00000000000000package 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 } ace-0.0.5/helper_method_javascript.go000066400000000000000000000021301274633403000176360ustar00rootroot00000000000000package 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), } } ace-0.0.5/helper_method_yield.go000066400000000000000000000026661274633403000166140ustar00rootroot00000000000000package 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 } ace-0.0.5/html_comment.go000066400000000000000000000025661274633403000152740ustar00rootroot00000000000000package 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), } } ace-0.0.5/html_tag.go000066400000000000000000000155601274633403000144030ustar00rootroot00000000000000package ace import ( "bytes" "fmt" "io" "strings" ) // Tag names const ( tagNameDiv = "div" ) // Attribute names const ( attributeNameID = "id" ) var inlineElements = map[string]bool{ "b": true, "big": true, "i": true, "small": true, "tt": true, "abbr": true, "acronym": true, "cite": true, "code": true, "dfn": true, "em": true, "kbd": true, "strong": true, "samp": true, "time": true, "var": true, "a": true, "bdo": true, "br": true, "img": true, "map": true, "object": true, "q": true, "script": true, "span": true, "sub": true, "sup": true, "button": true, "input": true, "label": true, "select": true, "textarea": true, } // 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 != "" { if e.opts.formatter != nil { e.opts.formatter.WritingTextValue(&bf, e) } 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() { if e.opts.formatter != nil { e.opts.formatter.ClosingElement(&bf, e) } 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 } // true if the element is inline one func (e *htmlTag) IsBlockElement() bool { if inline, found := inlineElements[e.tagName]; found { return !inline } else { return true } } // 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 } ace-0.0.5/line.go000066400000000000000000000050451274633403000135300ustar00rootroot00000000000000package 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 if 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 } ace-0.0.5/options.go000066400000000000000000000050401274633403000142670ustar00rootroot00000000000000package 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 // Indent string used for indentation. Indent string formatter outputFormatter // 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) } if opts.Indent != "" { opts.formatter = newFormatter(opts.Indent) } 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 } ace-0.0.5/parse.go000066400000000000000000000043311274633403000137100ustar00rootroot00000000000000package 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 } ace-0.0.5/plain_text.go000066400000000000000000000021561274633403000147500ustar00rootroot00000000000000package 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, } } ace-0.0.5/plain_text_inner.go000066400000000000000000000015701274633403000161420ustar00rootroot00000000000000package ace import "io" // HTML const ( htmlBr = "
" ) // 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, } } ace-0.0.5/read.go000066400000000000000000000060531274633403000135140ustar00rootroot00000000000000package 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 } ace-0.0.5/result.go000066400000000000000000000005671274633403000141230ustar00rootroot00000000000000package 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, } } ace-0.0.5/source.go000066400000000000000000000005041274633403000140740ustar00rootroot00000000000000package 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, } } ace-0.0.5/wercker.yml000066400000000000000000000015741274633403000144420ustar00rootroot00000000000000box: 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