├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── main.go └── temple ├── README.md ├── assets └── bindata.go ├── build.go ├── build_test.go ├── doc.go ├── dom.go ├── funcs_test.go ├── templates └── generated.go.tmpl ├── temple.go ├── temple_test.go ├── test_files ├── layouts │ └── app.tmpl ├── partials │ └── todo.tmpl ├── run.go └── templates │ └── todos │ └── index.tmpl └── test_util.go /.gitignore: -------------------------------------------------------------------------------- 1 | temple/test_files/templates.go -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ------------ 3 | 4 | Feedback, bug reports, and pull requests are greatly appreciated :) 5 | 6 | ### Issues 7 | 8 | The following are all great reasons to submit an issue: 9 | 10 | 1. You found a bug in the code. 11 | 2. Something is missing from the documentation or the existing documentation is unclear. 12 | 3. You have an idea for a new feature. 13 | 14 | If you are thinking about submitting an issue please remember to: 15 | 16 | 1. Describe the issue in detail. 17 | 2. If applicable, describe the steps to reproduce the error, which probably should include some example code. 18 | 3. Mention details about your platform: OS, version of gopherjs, browser, etc. 19 | 20 | ### Pull Requests 21 | 22 | In order to submit a pull request: 23 | 24 | 1. Fork the repository. 25 | 2. Create a new "feature branch" with a descriptive name (e.g. fix-database-error). 26 | 3. Make your changes in the feature branch. 27 | 4. Run the tests to make sure that they still pass. Updated the tests if needed. 28 | 5. Submit a pull request to merge your feature branch into the *master* branch. 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (C) 2015, Alex Browne 4 | 5 | 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: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Humble/Temple 2 | ============= 3 | 4 | [![Version](https://img.shields.io/badge/version-0.1.3-5272B4.svg)](https://github.com/go-humble/temple/releases) 5 | [![GoDoc](https://godoc.org/github.com/go-humble/temple?status.svg)](https://godoc.org/github.com/go-humble/temple) 6 | 7 | A library and a command line tool for sanely managing go templates, with the ability 8 | to share them between client and server. The library and code generated by the cli are 9 | both compatible with [gopherjs](https://github.com/gopherjs/gopherjs), so you can compile 10 | to javascript and run in the browser. Temple works great as a stand-alone package or in 11 | combination with other packages in the [Humble Framework](https://github.com/go-humble/humble). 12 | 13 | This README is specific to the command line tool, which reads .tmpl files in your project and 14 | generates go source code. The command line tool uses the library (mainly the Build function). 15 | The README for the library can be found at 16 | [github.com/go-humble/temple/temple](https://github.com/go-humble/temple/tree/develop/temple). 17 | 18 | 19 | What Does the Command Line Tool Do? 20 | ----------------------------------- 21 | 22 | The command line tool first reads the contents of all the .tmpl files in the specified src directory 23 | (and it's sub-directories). Then it generates go code at the specified dest file which compiles the 24 | templates. The generated code uses strings which contain the content of source files as arguments to 25 | [`template.Parse`](http://golang.org/pkg/html/template/#Template.Parse). 26 | 27 | This is useful for a few reasons. If you're building and deploying binary executables, it means you 28 | no longer have to ship a templates folder with your binary distribution. It also makes it possible 29 | for you to use templates in code that has been compiled to javascript with gopherjs and is running 30 | in a browser. Since code generated by temple works exactly the same on the server and client, it also 31 | enables you to share templates between them. Check out 32 | [go-humble/examples/people](https://github.com/go-humble/examples/tree/master/people) for an example of 33 | how shared templates can work. 34 | 35 | 36 | Browser Support 37 | --------------- 38 | 39 | Temple is regularly tested with IE9+ and the latest versions of Chrome, Safari, and Firefox. 40 | 41 | Javascript code generated with gopherjs uses typed arrays, so in order to work with IE9, 42 | you will need a 43 | [polyfill for typed arrays](https://github.com/inexorabletash/polyfill/blob/master/typedarray.js). 44 | 45 | 46 | Installation 47 | ------------ 48 | 49 | Install the temple command line tool with the following. 50 | 51 | ```bash 52 | go get -u github.com/go-humble/temple 53 | ``` 54 | 55 | You may also need to install gopherjs. The latest version is recommended. Install 56 | gopherjs with: 57 | 58 | ```bash 59 | go get -u github.com/gopherjs/gopherjs 60 | ``` 61 | 62 | 63 | Usage Guide 64 | ----------- 65 | 66 | ### Basic Usage 67 | 68 | The temple command line tool has one main subcommand called `build`. Typical usage will look 69 | something like this: 70 | 71 | `temple build templates templates/templates.go` 72 | 73 | The first argument, in this case `templates`, is a directory which contains your .tmpl files. 74 | The second argument, in this case `templates/templates.go` is the name of a file where generated 75 | code will be written. If the file does not exist, temple will create it for you, and any previous 76 | content will be overwritten. 77 | 78 | You can run `temple help` to learn more about the possible commands and `temple help build` to 79 | learn more about the build command specifically. 80 | 81 | ### Generated Code 82 | 83 | The code generated by the temple command line tool will look something like this: 84 | 85 | ```go 86 | package templates 87 | 88 | // This package has been automatically generated with temple. 89 | // Do not edit manually! 90 | 91 | import ( 92 | "github.com/go-humble/temple/temple" 93 | ) 94 | 95 | var ( 96 | GetTemplate func(name string) (*temple.Template, error) 97 | GetPartial func(name string) (*temple.Partial, error) 98 | GetLayout func(name string) (*temple.Layout, error) 99 | MustGetTemplate func(name string) *temple.Template 100 | MustGetPartial func(name string) *temple.Partial 101 | MustGetLayout func(name string) *temple.Layout 102 | ) 103 | 104 | func init() { 105 | var err error 106 | g := temple.NewGroup() 107 | 108 | if err = g.AddPartial("head", `...`); err != nil { 109 | panic(err) 110 | } 111 | 112 | if err = g.AddLayout("app", `...`); err != nil { 113 | panic(err) 114 | } 115 | 116 | if err = g.AddTemplate("people/index", `...`); err != nil { 117 | panic(err) 118 | } 119 | 120 | GetTemplate = g.GetTemplate 121 | GetPartial = g.GetPartial 122 | GetLayout = g.GetLayout 123 | MustGetTemplate = g.MustGetTemplate 124 | MustGetPartial = g.MustGetPartial 125 | MustGetLayout = g.MustGetLayout 126 | } 127 | ``` 128 | 129 | The code creates a single template [`Group`](http://godoc.org/github.com/go-humble/temple/temple/#Group) 130 | and adds the templates, partials, and layouts (if applicable) to the group. Then it exposes the methods of 131 | the group for getting templates, partials, and layouts as exported global functions. 132 | 133 | The [temple.Template](http://godoc.org/github.com/go-humble/temple/temple/#Template), 134 | [temple.Partial](http://godoc.org/github.com/go-humble/temple/temple/#Partial), and 135 | [temple.Layout](http://godoc.org/github.com/go-humble/temple/temple/#Layout) types all inherit from 136 | the builtin 137 | [Template type from the html/template package](http://golang.org/pkg/html/template/#Template). That 138 | means you can render them like regular templates with the 139 | [`Execute`](http://golang.org/pkg/html/template/#Template.Execute) method. Temple also provides an 140 | additional method for rendering templates in the dom called 141 | [`ExecuteEl`](http://godoc.org/github.com/go-humble/temple/temple/#ExecuteEl). 142 | 143 | ### Naming conventions 144 | 145 | In go, every template needs to have a name. temple assigns a name to each template based on its 146 | filename and location relative to the src directory. So for example, if you had a template file 147 | located at `templates/people/show.tmpl` and `templates` was your src directory, the name assigned 148 | to the template would be `"people/show"`. 149 | 150 | ### Partials and Layouts 151 | 152 | Temple uses two optional groups called "partials" and "layouts" to help organize template files. 153 | You can specify a directory that contains partials with the `--partials` flag, and a directory 154 | that contains layouts with the `--layouts` flag. Any .tmpl files found in these directories will 155 | be treated specially, and they should not overlap with each other or the src directory with all 156 | your regular templates ("regular templates" is the name we'll use to refer to .tmpl files in the 157 | src directory that are neither partials or layouts). This organization feature is completely optional, 158 | so if you don't want to use it, you omit the `--partials` and `--layouts` flags and manage your 159 | templates any way you want. 160 | 161 | Before continuing, it is recommended that you read the documentation for the 162 | [text/template](http://golang.org/pkg/text/template/) and 163 | [html/template](http://golang.org/pkg/html/template/) packages. In addition, this 164 | article about 165 | [template inheritence in go](https://elithrar.github.io/article/approximating-html-template-inheritance/) 166 | will help explain some of the concepts that temple uses. 167 | 168 | #### Partials 169 | 170 | Partials are templates which typically represent only part of a full page. For example, you might 171 | have a partial for rendering a single model or a partial for the head section of your html. Partials 172 | are associated with (i.e., added to the parse tree of) all other partials, in addition to layouts and 173 | regular templates. That means you can render a partial inside of a regular template or layout with the 174 | `template` action. The name of the partial templates is based on its filename and location relative to the 175 | partials directory. [`PartialsPrefix`](http://godoc.org/github.com/albrow/prtty#pkg-variables) is added 176 | to the template name of all partials, which by default is simply `"partials/"`. 177 | 178 | So if your partials directory is `my-partials`, and you have the following partial template file located 179 | at `my-partials/head.tmpl`: 180 | 181 | ```handlebars 182 | 183 | Example Humble Application 184 | 185 | 186 | ``` 187 | 188 | You could render it inside of another template like so: 189 | 190 | ```handlebars 191 | 192 | 193 | {{ template "partials/head" }} 194 | 195 | 196 | 197 | ``` 198 | 199 | Check out the partials in 200 | [go-humble/examples/people](https://github.com/go-humble/examples/tree/master/people/shared/templates/partials) 201 | for a more in-depth example. 202 | 203 | #### Layouts 204 | 205 | Layouts are templates which define the structure for a page and require another template to fill in 206 | the details. Typically, layouts will use one or more `template` actions to render partials or regular 207 | templates inside of some structure. Layouts are associated with (i.e., added to the parse tree of) all 208 | regular templates and have access to partials. That means you can render layouts inside of a regular template, 209 | typically after declaring some sub-template that the layout expects to be defined. The name of the layout 210 | templates is based on its filename and location relative to the layouts directory. 211 | [`LayoutsPrefix`](http://godoc.org/github.com/go-humble/temple#pkg-variables) is added to the template 212 | name of all layouts, which by default is simply `"layouts/"`. 213 | 214 | For example, if your layouts directory is `my-layouts`, and you have the following layout template file 215 | located at `my-layouts/app.tmpl`: 216 | 217 | ```handlebars 218 | 219 | 220 | 221 | Example Humble Application 222 | 223 | 224 | {{ template "content" }} 225 | 226 | 227 | ``` 228 | 229 | You could then render a template inside of the layout by first defining the "content" sub-template and 230 | then rendering the layout: 231 | 232 | ```handlebars 233 | {{ define "content" }} 234 | Hello, Content! 235 | {{ end }} 236 | {{ template "layouts/app" }} 237 | ``` 238 | 239 | If you rendered the template (not the layout), the output would look like this: 240 | 241 | ```html 242 | 243 | 244 | 245 | Example Humble Application 246 | 247 | 248 | Hello, Content! 249 | 250 | 251 | ``` 252 | 253 | Check out the layouts in 254 | [go-humble/examples/people](https://github.com/go-humble/examples/tree/master/people/shared/templates/layouts) 255 | for a more in-depth example. 256 | 257 | Testing 258 | ------- 259 | 260 | Temple uses regular go testing, so you can run the all the tests with `go test ./...`. 261 | 262 | 263 | Contributing 264 | ------------ 265 | 266 | See [CONTRIBUTING.md](https://github.com/go-humble/temple/blob/master/CONTRIBUTING.md) 267 | 268 | 269 | License 270 | ------- 271 | 272 | Temple is licensed under the MIT License. See the [LICENSE](https://github.com/go-humble/temple/blob/master/LICENSE) 273 | file for more information. 274 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Alex Browne. 2 | // All rights reserved. Use of this source code is 3 | // governed by the MIT license, which can be found 4 | // in the LICENSE file. 5 | 6 | // Temple is a command-line tool for managing go templates which 7 | // supports sharing templates between a client and server. It generates 8 | // code which is compatible with gopherjs and can be compiled to 9 | // javascript to run in the browser. 10 | // 11 | // Version 0.1.3 12 | package main 13 | 14 | import ( 15 | "fmt" 16 | "io/ioutil" 17 | "os" 18 | 19 | "github.com/albrow/prtty" 20 | "github.com/go-humble/temple/temple" 21 | "github.com/spf13/cobra" 22 | ) 23 | 24 | const ( 25 | version = "temple version 0.1.1" 26 | ) 27 | 28 | var ( 29 | verbose = false 30 | ) 31 | 32 | // setQuiet effectively causes all loggers to print 33 | // to /dev/null. However, error will still be printed 34 | // out to stderr. 35 | func setQuiet() { 36 | prtty.AllLoggers.SetOutput(ioutil.Discard) 37 | prtty.Error.Output = os.Stderr 38 | } 39 | 40 | // setVerbose sets all loggers to print to stdout, 41 | // except for the Error logger, which will print to 42 | // stderr. 43 | func setVerbose() { 44 | prtty.AllLoggers.SetOutput(os.Stdout) 45 | prtty.Error.Output = os.Stderr 46 | } 47 | 48 | func main() { 49 | // Define build command 50 | cmdBuild := &cobra.Command{ 51 | Use: "build ", 52 | Short: "Compile the templates in the src directory and write generated go code to the dest file.", 53 | Long: ``, 54 | Run: func(cmd *cobra.Command, args []string) { 55 | if len(args) != 2 { 56 | prtty.Error.Fatal("temple build requires exactly 2 arguments: the src directory and the dest file.") 57 | } 58 | if verbose { 59 | setVerbose() 60 | } else { 61 | setQuiet() 62 | } 63 | partials := cmd.Flag("partials").Value.String() 64 | layouts := cmd.Flag("layouts").Value.String() 65 | packageName := cmd.Flag("package").Value.String() 66 | if err := temple.Build(args[0], args[1], partials, layouts, packageName); err != nil { 67 | prtty.Error.Fatal(err) 68 | } 69 | }, 70 | } 71 | cmdBuild.Flags().String("partials", "", "(optional) The directory to look for partials. Partials are .tmpl files that are associated with layouts and all other templates.") 72 | cmdBuild.Flags().String("layouts", "", "(optional) The directory to look for layouts. Layouts are .tmpl files which have access to partials and are associated with all other templates.") 73 | cmdBuild.Flags().String("package", "", "(optional) The package name for the generated go file. If not provided, the default will be the directory where the go file is created.") 74 | cmdBuild.Flags().BoolVarP(&verbose, "verbose", "v", false, "If set to true, temple will print out information while building.") 75 | 76 | // Define version command 77 | cmdVersion := &cobra.Command{ 78 | Use: "version", 79 | Short: "Print the current version number.", 80 | Run: func(cmd *cobra.Command, args []string) { 81 | fmt.Println(version) 82 | }, 83 | } 84 | 85 | // Define the root command 86 | rootCmd := &cobra.Command{ 87 | Use: "temple", 88 | Short: "A command line tool for sharing go templates between a client and server.", 89 | Long: ` 90 | A command line tool for sharing go templates between a client and server. 91 | Visit https://github.com/albrow/temple for source code, example usage, documentation, and more.`, 92 | } 93 | rootCmd.AddCommand(cmdBuild, cmdVersion) 94 | if err := rootCmd.Execute(); err != nil { 95 | prtty.Error.Fatal(err) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /temple/README.md: -------------------------------------------------------------------------------- 1 | Humble/Temple 2 | ============= 3 | 4 | [![GoDoc](https://godoc.org/github.com/go-humble/temple/temple?status.svg)](https://godoc.org/github.com/go-humble/temple/temple) 5 | 6 | Version 0.1.3 7 | 8 | A library and a command line tool for sanely managing go templates, with the ability 9 | to share them between client and server. The library and code generated by the cli are 10 | both compatible with [gopherjs](https://github.com/gopherjs/gopherjs), so you can compile 11 | to javascript and run in the browser. Temple works great as a stand-alone package or in 12 | combination with other packages in the [Humble Framework](https://github.com/go-humble/humble). 13 | 14 | This README is specific to the library, which offers helper functions and methods for 15 | managing templates and rendering them in the DOM. The README for the command line 16 | tool can be found at 17 | [github.com/go-humble/temple](https://github.com/go-humble/temple). 18 | 19 | 20 | What Does the Library Do? 21 | ------------------------- 22 | 23 | The temple library can be used on its own or in conjunction with code generated by the 24 | command line tool. It offers some utility functions for organizing templates into distinct 25 | categories: regular templates, partials, and layouts. It also lets you load templates from 26 | files or load inline templates from the DOM, and provides helper methods for rendering 27 | to the DOM. 28 | 29 | Temple uses the builtin [html/template package](http://golang.org/pkg/html/template/). 30 | If you are not already familiar with go's builtin templates, it is highly recommended 31 | that you read the documentation for them before continuing. 32 | 33 | 34 | Browser Support 35 | --------------- 36 | 37 | Temple is regularly tested with IE9+ and the latest versions of Chrome, Safari, and Firefox. 38 | 39 | Javascript code generated with gopherjs uses typed arrays, so in order to work with IE9, 40 | you will need a 41 | [polyfill for typed arrays](https://github.com/inexorabletash/polyfill/blob/master/typedarray.js). 42 | 43 | 44 | Installation 45 | ------------ 46 | 47 | Install the temple library with the following. 48 | 49 | ```bash 50 | go get github.com/go-humble/temple/temple` 51 | ``` 52 | 53 | You may also need to install gopherjs. The latest version is recommended. Install 54 | gopherjs with: 55 | 56 | ``` 57 | go get -u github.com/gopherjs/gopherjs 58 | ``` 59 | 60 | 61 | Quickstart Guide 62 | ---------------- 63 | 64 | ### Loading Templates 65 | 66 | All templates, partials, and layouts must belong to a group. To create a new group, use 67 | the `NewGroup` function: 68 | 69 | ```go 70 | g := NewGroup() 71 | ``` 72 | 73 | #### From a String 74 | 75 | To add template to the group, you can use the `AddTemplate` method. It takes 76 | two arguments, the name of the template and the source. 77 | 78 | ```go 79 | if err := g.AddTemplate("home", "

Home

"); err != nil { 80 | // Handle err 81 | } 82 | ``` 83 | 84 | #### From a File 85 | 86 | To add a template file, you can use the `AddTemplateFile` method, which takes the name of 87 | the template and the path to the file as an argument. 88 | 89 | ```go 90 | if err := g.AddTemplateFile("home", "templates/home.tmpl"); err != nil { 91 | // Handle err 92 | } 93 | ``` 94 | 95 | #### From a Directory 96 | 97 | You can also add multiple files in a directory at once with the `AddTemplateFiles` method. 98 | When you use this method, the templates created from the files will automatically get a 99 | name based on the filename and location relative to the given directory. So for example, 100 | if you had a template file located at `templates/people/show.tmpl` and passed `templates` 101 | as the `dir` argument, the name assigned to the template would be `"people/show"`. 102 | 103 | ```go 104 | if err := g.AddTemplateFiles("templates"); err != nil { 105 | // Handle err 106 | } 107 | ``` 108 | 109 | #### From the DOM 110 | 111 | Finally, if you compile to javascript with gopherjs, you can load inline templates from the 112 | DOM with the `AddInlineTemplate` method. 113 | 114 | ```go 115 | if err := g.AddInlineTemplate(document.QuerySelector("script[type='text/template']#home")); err != nil { 116 | // Handle err 117 | } 118 | ``` 119 | 120 | The method expects a `dom.Element` from the gopherjs 121 | [dom bindings](http://godoc.org/honnef.co/go/js/dom) as an argument. Typically this would be 122 | a script tag that looks something like: 123 | 124 | ``` 125 | 128 | ``` 129 | 130 | The `AddInlineTemplate` method will use the id property of the element as the template name, 131 | and the innerHTML of the given element as the template source. 132 | 133 | You can also load multiple inline templates (as well as partials and layouts) at once with 134 | the [`AddAllInline`](http://godoc.org/github.com/go-humble/temple/temple/#Group.AddAllInline) 135 | method, which scans the DOM for script tags with a `type` attribute of "text/template". 136 | 137 | ```go 138 | if err := g.AddAllInline(); err != nil { 139 | // Handle err 140 | } 141 | ``` 142 | 143 | It uses the id property as the template name, and the special `data-kind` attribute to 144 | distinguish between regular templates, partials, and layouts. 145 | 146 | 147 | ### Getting Templates 148 | 149 | Once a template has been added to a group, you can get it with the `GetTemplate` method: 150 | 151 | ```go 152 | homeTmpl, err := g.GetTemplate("home") 153 | if err != nil { 154 | // Handle error 155 | } 156 | ``` 157 | 158 | This returns a pointer to a [`temple.Template`](http://godoc.org/github.com/go-humble/temple/temple/#Template), 159 | which is merely a wrapper around the [`template.Template`](http://golang.org/pkg/html/template/#Template) 160 | type from the builtin html/template package. `temple.Template` has all the same methods as a 161 | builtin template, and also introduces an 162 | [`ExecuteEl`](http://godoc.org/github.com/go-humble/temple/temple/#Layout.ExecuteEl) method for 163 | rendering the template to the DOM. 164 | 165 | In some cases, you just want to fail fast if a template is not found. You can use the 166 | `MustGetTemplate` method, which will panic instead of returning an error. This is analogous 167 | to the [`Temlate.Must`](http://golang.org/pkg/text/template/#Must) method in the text/template 168 | and html/template packages in the standard library, and is useful for variable declarations. 169 | 170 | ```go 171 | var ( 172 | homeTmpl = g.MustGetTemplate("home") 173 | indexTmpl = g.MustGetTemplate("index") 174 | todoTmpl = g.MustGetTemplate("todo") 175 | ) 176 | ``` 177 | 178 | ### Rendering Templates 179 | 180 | #### To an io.Writer 181 | 182 | To render a template to an `io.Writer`, you can just use the `Execute` method. Since 183 | `http.ResponseWriter` implements `io.Writer` it is common to render templates this way on 184 | the server: 185 | 186 | ```go 187 | func HomeHandler(res http.ResponseWriter, req *http.Request) { 188 | homeTmpl, err := g.GetTemplate("home") 189 | if err != nil { 190 | // Handle error 191 | } 192 | if err := homeTmpl.Execute(res, nil); err != nil { 193 | // Handle error 194 | } 195 | } 196 | ``` 197 | 198 | The second argument to `Execute` is the data that will be passed into the template. 199 | 200 | #### To an Element in the DOM 201 | 202 | You can also render a template to an element in the DOM with the `ExecuteEl` method. This 203 | is the most common way to render templates for code which has been compiled to javascript 204 | with gopherjs and is running in the browser. 205 | 206 | ```go 207 | homeTmpl, err := g.GetTemplate("home") 208 | if err != nil { 209 | // Handle error 210 | } 211 | if err := homeTmpl.ExecuteEl(document.QuerySelector("body"), nil); err != nil { 212 | // Handle err 213 | } 214 | ``` 215 | 216 | ### Partials and Layouts 217 | 218 | Temple uses two optional groups called "partials" and "layouts" to help organize templates. 219 | To add partials or layouts, use the same methods for adding templates, but replace the word 220 | `Template` with `Partial` or `Layout` (e.g. `AddLayout`, `AddInlinePartials`, `AddLayoutFile`). 221 | 222 | Before continuing, it is recommended that you read the documentation for the 223 | [text/template](http://golang.org/pkg/text/template/) and 224 | [html/template](http://golang.org/pkg/html/template/) packages. In addition, this 225 | article about 226 | [template inheritence in go](https://elithrar.github.io/article/approximating-html-template-inheritance/) 227 | will help explain some of the concepts that temple uses. 228 | 229 | #### Partials 230 | 231 | Partials are templates which typically represent only part of a full page. For example, you might 232 | have a partial for rendering a single model or a partial for the head section of your html. Partials 233 | are associated with (i.e., added to the parse tree of) all other partials, in addition to layouts and 234 | regular templates. That means you can render a partial inside of a regular template or layout with the 235 | `template` action. [`PartialsPrefix`](http://godoc.org/github.com/albrow/prtty#pkg-variables) is added 236 | to the template name of all partials, which by default is simply `"partials/"`. 237 | 238 | So for example, you could have partials located in the `my-partials` directory and add them to a group 239 | with `g.AddPartialFiles("my-partials")`. Let's say you have the following partial file located at 240 | `my-partials/head.tmpl`: 241 | 242 | ```handlebars 243 | 244 | Example Humble Application 245 | 246 | 247 | ``` 248 | 249 | Let's also say that you have regular template files located in the `my-templates` directory, which you 250 | have added to the same group with `g.AddTemplateFiles("my-templates")`. And the following regular template 251 | located at `my-templates/index.tmpl`: 252 | 253 | ```handlebars 254 | 255 | 256 | {{ template "partials/head" }} 257 | 258 | 259 | 260 | ``` 261 | 262 | Then, if you rendered the template with the following code: 263 | 264 | ```go 265 | indexTmpl, err := g.GetTemplate("index") 266 | if err != nil { 267 | // Handle error 268 | } 269 | if err := indexTmpl.Execute(os.Stdout, nil); err != nil { 270 | // Handle err 271 | } 272 | ``` 273 | 274 | You would see the following output: 275 | 276 | ``` 277 | 278 | 279 | 280 | Example Humble Application 281 | 282 | 283 | 284 | 285 | 286 | ``` 287 | 288 | Check out the partials in 289 | [go-humble/examples/people](https://github.com/go-humble/examples/tree/master/people/shared/templates/partials) 290 | for a more in-depth example. 291 | 292 | #### Layouts 293 | 294 | Layouts are templates which define the structure for a page and require another template to fill in 295 | the details. Typically, layouts will use one or more `template` actions to render partials or regular 296 | templates inside of some structure. Layouts are associated with (i.e., added to the parse tree of) all 297 | regular templates and have access to partials. That means you can render layouts inside of a regular template, 298 | typically after declaring some sub-template that the layout expects to be defined. 299 | [`LayoutsPrefix`](http://godoc.org/github.com/go-humble/temple#pkg-variables) is added to the template 300 | name of all layouts, which by default is simply `"layouts/"`. 301 | 302 | So for example, you could have layouts located in the `my-layouts` directory, and add them to a group 303 | with `g.AddLayoutFiles("my-layouts")`. Let's say you have the following layout file located at 304 | `my-layouts/app.tmpl`: 305 | 306 | ```handlebars 307 | 308 | 309 | 310 | Example Humble Application 311 | 312 | 313 | {{ template "content" . }} 314 | 315 | 316 | ``` 317 | 318 | Let's also say that you have regular template files located in the `my-templates` directory, which you 319 | have added to the same group with `g.AddTemplateFiles("my-templates")`. And the following regular template 320 | located at `my-templates/hello.tmpl`: 321 | 322 | ```handlebars 323 | {{ define "content" }} 324 | Hello, {{ . }}! 325 | {{ end }} 326 | {{ template "layouts/app" . }} 327 | ``` 328 | 329 | Notice that we used the `.` in both the `{{ template "content" . }}` and the `{{ template "layouts/app" . }}` 330 | expressions to pass in the template data from the regular template to the layout. If you 331 | rendered the template with the following code: 332 | 333 | ```go 334 | helloTmpl, err := g.GetTemplate("hello") 335 | if err != nil { 336 | // Handle error 337 | } 338 | if err := helloTmpl.Execute(os.Stdout, "World"); err != nil { 339 | // Handle err 340 | } 341 | ``` 342 | 343 | You would see output that looked like this: 344 | 345 | ```html 346 | 347 | 348 | 349 | Example Humble Application 350 | 351 | 352 | Hello, World! 353 | 354 | 355 | ``` 356 | 357 | Check out the layouts in 358 | [go-humble/examples/people](https://github.com/go-humble/examples/tree/master/people/shared/templates/layouts) 359 | for a more in-depth example. 360 | 361 | Testing 362 | ------- 363 | 364 | Temple uses regular go testing, so you can run the all the tests with `go test .`. 365 | 366 | 367 | Contributing 368 | ------------ 369 | 370 | See [CONTRIBUTING.md](https://github.com/go-humble/temple/blob/master/CONTRIBUTING.md) 371 | 372 | 373 | License 374 | ------- 375 | 376 | Temple is licensed under the MIT License. See the [LICENSE](https://github.com/go-humble/temple/blob/master/LICENSE) 377 | file for more information. 378 | -------------------------------------------------------------------------------- /temple/assets/bindata.go: -------------------------------------------------------------------------------- 1 | package assets 2 | 3 | import ( 4 | "bytes" 5 | "compress/gzip" 6 | "fmt" 7 | "io" 8 | "strings" 9 | "os" 10 | "time" 11 | "io/ioutil" 12 | "path" 13 | "path/filepath" 14 | ) 15 | 16 | func bindata_read(data []byte, name string) ([]byte, error) { 17 | gz, err := gzip.NewReader(bytes.NewBuffer(data)) 18 | if err != nil { 19 | return nil, fmt.Errorf("Read %q: %v", name, err) 20 | } 21 | 22 | var buf bytes.Buffer 23 | _, err = io.Copy(&buf, gz) 24 | clErr := gz.Close() 25 | 26 | if err != nil { 27 | return nil, fmt.Errorf("Read %q: %v", name, err) 28 | } 29 | if clErr != nil { 30 | return nil, err 31 | } 32 | 33 | return buf.Bytes(), nil 34 | } 35 | 36 | type asset struct { 37 | bytes []byte 38 | info os.FileInfo 39 | } 40 | 41 | type bindata_file_info struct { 42 | name string 43 | size int64 44 | mode os.FileMode 45 | modTime time.Time 46 | } 47 | 48 | func (fi bindata_file_info) Name() string { 49 | return fi.name 50 | } 51 | func (fi bindata_file_info) Size() int64 { 52 | return fi.size 53 | } 54 | func (fi bindata_file_info) Mode() os.FileMode { 55 | return fi.mode 56 | } 57 | func (fi bindata_file_info) ModTime() time.Time { 58 | return fi.modTime 59 | } 60 | func (fi bindata_file_info) IsDir() bool { 61 | return false 62 | } 63 | func (fi bindata_file_info) Sys() interface{} { 64 | return nil 65 | } 66 | 67 | var _templates_generated_go_tmpl = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xb4\x53\x4d\x6f\xdb\x38\x10\x3d\x8b\xbf\x62\xe2\x93\x1c\x78\xa5\xdd\x05\xf6\x92\x45\x0e\x69\x1a\x04\x01\x12\x37\x40\xdc\x7b\x68\x6a\x2c\xb1\x95\x48\x81\x1c\xda\x31\x0c\xff\xf7\x0e\x25\xd9\xb1\xf3\xe1\x53\x7b\xb2\xc9\x79\x6f\xde\xe3\xcc\xd3\x66\x93\x9f\x8b\xe4\xda\xb6\x6b\xa7\xcb\x8a\xe0\xdf\xbf\xff\xf9\x0f\xae\x6a\x7c\x81\x2f\xce\xae\x0c\x66\x22\xb9\xaa\x6b\xe8\x8a\x1e\x1c\x7a\x74\x4b\x2c\x32\xf8\xee\x11\xec\x02\xa8\xd2\x1e\xbc\x0d\x4e\x21\x28\x5b\x20\x68\x2f\x92\xd2\x2e\xd1\x19\x2c\x60\xbe\x66\x00\xc2\xc3\xdd\x0c\x6a\xad\xd0\x78\x9c\xc0\xaa\xd2\xaa\x02\x25\x0d\xcc\x11\x16\x36\x98\x42\x24\xda\x74\xb8\xfb\xbb\xeb\x9b\xe9\xd3\x0d\x2c\x74\xcd\xba\x22\x99\x7e\x9b\xdd\x5c\xf4\x12\xf1\x8a\x7b\x03\x36\x73\x2c\x0a\x6e\xbd\xd4\x12\x4a\xfb\xd7\x5c\x9b\x42\x92\x84\xb4\x22\x6a\xfd\x45\x9e\x97\x9a\xaa\x30\xcf\x94\x6d\xf2\x1f\x84\x18\x56\x68\xf2\x57\xdc\x58\x24\x77\x0b\x58\xdb\x00\xaa\x92\xa6\xe4\x96\x34\x89\x3e\x7c\x70\x08\x64\xc1\x05\xc3\x5d\xa1\x44\x83\x4e\x12\x42\x96\x67\x59\xb6\xe7\x18\x64\x61\x46\x69\xe3\x49\xf2\x50\xa2\xe7\x03\x0f\xf8\x82\x2a\x90\x9c\xd7\xfc\xca\x7d\x23\x82\xd3\x8e\xc4\x79\xbe\xdd\x8a\x56\xaa\x9f\x92\xed\x6c\x36\x90\x3d\xf6\xff\xa7\xb2\x41\xe0\x92\xc8\x73\x98\xc5\x11\xec\x30\x95\xf4\x6c\x19\x0d\xc8\x40\xb6\x91\xa4\x15\x7b\x59\xef\x3d\x17\xb0\x62\x41\x20\x6c\xda\x38\x45\x66\x7f\xb5\x60\x2c\x01\x16\x9a\xa0\x91\x26\x44\xf8\x99\x10\xba\x69\xad\x23\x48\x45\x32\x3a\xb0\xc8\xce\xaa\xd0\xf0\x1b\xf2\xbe\xc3\xf0\x33\x12\x63\x21\x96\xd2\x45\xf8\x2d\xd2\x2c\x5e\xc6\x01\x2d\x82\x51\xa9\x89\x56\x3d\x39\x6d\xca\x31\xa4\xe7\x83\xf4\x0e\x33\x01\x74\xce\xba\x71\x47\x7c\x94\x8e\xb4\xac\x4f\xf1\x06\xc8\x11\xed\x5e\xf2\xfc\xe9\x14\xab\x47\xbc\x92\x1e\x82\xa7\xd3\x46\xdf\xfa\xdc\x73\x3e\xf7\xf8\xc6\xe2\x9e\xf1\xa9\xbd\x63\x77\x71\x86\x11\xc3\x01\xd2\x94\x8e\x61\x23\x92\x38\x52\xb6\xdc\xdb\xe6\x2f\x07\x2e\x2e\x77\xab\x9b\xe2\xea\xd6\xd9\xd0\xa6\xfc\x1a\xce\x85\xeb\xf2\xba\x93\xf6\x31\x1a\x89\x5e\x74\xe4\x4b\x28\xb3\xab\xa2\x18\x4a\xe9\x28\xa6\x68\x88\xcf\x68\x02\xcf\xf1\xf8\xe4\x14\x9f\x9e\xc7\xff\x77\x84\xb3\x4b\x30\xba\x8e\xfa\x49\x2b\x8d\x56\x29\x5f\xb2\xca\xb6\x13\x42\x53\x74\xb9\x3b\x10\xed\xed\x7f\xa4\xd9\x57\xfe\x80\xe4\x6e\x2b\x1f\x89\xee\x6a\xbf\x49\xf6\x28\xd0\x51\xe2\xe0\x7c\x14\xda\xa1\xb6\xdf\xfe\xeb\xe6\x87\xca\xb0\xe6\x77\xe1\x8b\xe5\x37\x77\xef\xd2\x76\x80\xf9\x24\x5e\x07\x88\x41\x68\xfb\x2b\x00\x00\xff\xff\x1e\x4e\x7e\x4b\xbe\x05\x00\x00") 68 | 69 | func templates_generated_go_tmpl_bytes() ([]byte, error) { 70 | return bindata_read( 71 | _templates_generated_go_tmpl, 72 | "templates/generated.go.tmpl", 73 | ) 74 | } 75 | 76 | func templates_generated_go_tmpl() (*asset, error) { 77 | bytes, err := templates_generated_go_tmpl_bytes() 78 | if err != nil { 79 | return nil, err 80 | } 81 | 82 | info := bindata_file_info{name: "templates/generated.go.tmpl", size: 1470, mode: os.FileMode(420), modTime: time.Unix(1432862983, 0)} 83 | a := &asset{bytes: bytes, info: info} 84 | return a, nil 85 | } 86 | 87 | // Asset loads and returns the asset for the given name. 88 | // It returns an error if the asset could not be found or 89 | // could not be loaded. 90 | func Asset(name string) ([]byte, error) { 91 | cannonicalName := strings.Replace(name, "\\", "/", -1) 92 | if f, ok := _bindata[cannonicalName]; ok { 93 | a, err := f() 94 | if err != nil { 95 | return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) 96 | } 97 | return a.bytes, nil 98 | } 99 | return nil, fmt.Errorf("Asset %s not found", name) 100 | } 101 | 102 | // MustAsset is like Asset but panics when Asset would return an error. 103 | // It simplifies safe initialization of global variables. 104 | func MustAsset(name string) []byte { 105 | a, err := Asset(name) 106 | if (err != nil) { 107 | panic("asset: Asset(" + name + "): " + err.Error()) 108 | } 109 | 110 | return a 111 | } 112 | 113 | // AssetInfo loads and returns the asset info for the given name. 114 | // It returns an error if the asset could not be found or 115 | // could not be loaded. 116 | func AssetInfo(name string) (os.FileInfo, error) { 117 | cannonicalName := strings.Replace(name, "\\", "/", -1) 118 | if f, ok := _bindata[cannonicalName]; ok { 119 | a, err := f() 120 | if err != nil { 121 | return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) 122 | } 123 | return a.info, nil 124 | } 125 | return nil, fmt.Errorf("AssetInfo %s not found", name) 126 | } 127 | 128 | // AssetNames returns the names of the assets. 129 | func AssetNames() []string { 130 | names := make([]string, 0, len(_bindata)) 131 | for name := range _bindata { 132 | names = append(names, name) 133 | } 134 | return names 135 | } 136 | 137 | // _bindata is a table, holding each asset generator, mapped to its name. 138 | var _bindata = map[string]func() (*asset, error){ 139 | "templates/generated.go.tmpl": templates_generated_go_tmpl, 140 | } 141 | 142 | // AssetDir returns the file names below a certain 143 | // directory embedded in the file by go-bindata. 144 | // For example if you run go-bindata on data/... and data contains the 145 | // following hierarchy: 146 | // data/ 147 | // foo.txt 148 | // img/ 149 | // a.png 150 | // b.png 151 | // then AssetDir("data") would return []string{"foo.txt", "img"} 152 | // AssetDir("data/img") would return []string{"a.png", "b.png"} 153 | // AssetDir("foo.txt") and AssetDir("notexist") would return an error 154 | // AssetDir("") will return []string{"data"}. 155 | func AssetDir(name string) ([]string, error) { 156 | node := _bintree 157 | if len(name) != 0 { 158 | cannonicalName := strings.Replace(name, "\\", "/", -1) 159 | pathList := strings.Split(cannonicalName, "/") 160 | for _, p := range pathList { 161 | node = node.Children[p] 162 | if node == nil { 163 | return nil, fmt.Errorf("Asset %s not found", name) 164 | } 165 | } 166 | } 167 | if node.Func != nil { 168 | return nil, fmt.Errorf("Asset %s not found", name) 169 | } 170 | rv := make([]string, 0, len(node.Children)) 171 | for childName := range node.Children { 172 | rv = append(rv, childName) 173 | } 174 | return rv, nil 175 | } 176 | 177 | type _bintree_t struct { 178 | Func func() (*asset, error) 179 | Children map[string]*_bintree_t 180 | } 181 | var _bintree = &_bintree_t{nil, map[string]*_bintree_t{ 182 | "templates": &_bintree_t{nil, map[string]*_bintree_t{ 183 | "generated.go.tmpl": &_bintree_t{templates_generated_go_tmpl, map[string]*_bintree_t{ 184 | }}, 185 | }}, 186 | }} 187 | 188 | // Restore an asset under the given directory 189 | func RestoreAsset(dir, name string) error { 190 | data, err := Asset(name) 191 | if err != nil { 192 | return err 193 | } 194 | info, err := AssetInfo(name) 195 | if err != nil { 196 | return err 197 | } 198 | err = os.MkdirAll(_filePath(dir, path.Dir(name)), os.FileMode(0755)) 199 | if err != nil { 200 | return err 201 | } 202 | err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) 203 | if err != nil { 204 | return err 205 | } 206 | err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) 207 | if err != nil { 208 | return err 209 | } 210 | return nil 211 | } 212 | 213 | // Restore assets under the given directory recursively 214 | func RestoreAssets(dir, name string) error { 215 | children, err := AssetDir(name) 216 | if err != nil { // File 217 | return RestoreAsset(dir, name) 218 | } else { // Dir 219 | for _, child := range children { 220 | err = RestoreAssets(dir, path.Join(name, child)) 221 | if err != nil { 222 | return err 223 | } 224 | } 225 | } 226 | return nil 227 | } 228 | 229 | func _filePath(dir, name string) string { 230 | cannonicalName := strings.Replace(name, "\\", "/", -1) 231 | return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) 232 | } 233 | 234 | -------------------------------------------------------------------------------- /temple/build.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Alex Browne. 2 | // All rights reserved. Use of this source code is 3 | // governed by the MIT license, which can be found 4 | // in the LICENSE file. 5 | 6 | package temple 7 | 8 | import ( 9 | "bytes" 10 | "errors" 11 | "github.com/albrow/prtty" 12 | "github.com/go-humble/temple/temple/assets" 13 | "go/format" 14 | "io/ioutil" 15 | "os" 16 | "path/filepath" 17 | "text/template" 18 | ) 19 | 20 | // Build is the function called when you run the build sub-command 21 | // in the command line tool. It compiles all the templates in the 22 | // src directory and generates go code in the dest file. If partials 23 | // and/or layouts are provided, it will add them to the generated file 24 | // with calls to AddPartial and AddLayout. If packageName is an empty 25 | // string, the package name will be the directory of the dest file. 26 | func Build(src, dest, partials, layouts, packageName string) error { 27 | prtty.Info.Println("--> building...") 28 | prtty.Default.Printf(" src: %s", src) 29 | prtty.Default.Printf(" dest: %s", dest) 30 | if partials != "" { 31 | prtty.Default.Printf(" partials: %s", partials) 32 | } 33 | if layouts != "" { 34 | prtty.Default.Printf(" layouts: %s", layouts) 35 | } 36 | if packageName != "" { 37 | prtty.Default.Printf(" package: %s", packageName) 38 | } 39 | dirs := sourceDirGroup{ 40 | templates: src, 41 | partials: partials, 42 | layouts: layouts, 43 | } 44 | if err := checkCompileTemplates(dirs); err != nil { 45 | return err 46 | } 47 | if err := generateFile(dirs, dest, packageName); err != nil { 48 | return err 49 | } 50 | prtty.Info.Println("--> done!") 51 | return nil 52 | } 53 | 54 | // checkCompileTemplates compiles the templates, partials, and layouts 55 | // in dirs with the correct associations to make sure that the templates 56 | // compile. If they don't, we can catch errors early and return them when 57 | // the command line tool is invoked, instead of at runtime. 58 | func checkCompileTemplates(dirs sourceDirGroup) error { 59 | prtty.Info.Println("--> checking for compilation errors...") 60 | if dirs.templates == "" { 61 | return errors.New("temple: templates dir cannot be an empty string.") 62 | } 63 | g := NewGroup() 64 | if dirs.partials != "" { 65 | prtty.Default.Println(" checking partials...") 66 | if err := g.AddPartialFiles(dirs.partials); err != nil { 67 | return err 68 | } 69 | } 70 | if dirs.layouts != "" { 71 | prtty.Default.Println(" checking layouts...") 72 | if err := g.AddLayoutFiles(dirs.layouts); err != nil { 73 | return err 74 | } 75 | } 76 | prtty.Default.Println(" checking templates...") 77 | if err := g.AddTemplateFiles(dirs.templates); err != nil { 78 | return err 79 | } 80 | return nil 81 | } 82 | 83 | // templateData is passed in to the template for the generated code. 84 | type templateData struct { 85 | PackageName string 86 | Templates []sourceFile 87 | Partials []sourceFile 88 | Layouts []sourceFile 89 | } 90 | 91 | // sourceFile represents the source file for a template, partial, or layout. 92 | type sourceFile struct { 93 | Name string 94 | Src string 95 | } 96 | 97 | // sourceDirGroup represents a group of source directories, consisting of a 98 | // directory for regular layouts and optionally for partials and layouts. 99 | // The directories for partials and layouts will be empty strings if they 100 | // were not provided. 101 | type sourceDirGroup struct { 102 | templates string 103 | partials string 104 | layouts string 105 | } 106 | 107 | // collectAllSourceFiles walks recursively through the directories in dirs 108 | // and collects all template, partial, and layout source files, adding them 109 | // to data. 110 | func (data *templateData) collectAllSourceFiles(dirs sourceDirGroup) error { 111 | if dirs.partials != "" { 112 | prtty.Info.Println("--> collecting partials...") 113 | partials, err := collectSourceFiles(dirs.partials) 114 | if err != nil { 115 | return err 116 | } 117 | data.Partials = partials 118 | } 119 | if dirs.layouts != "" { 120 | prtty.Info.Println("--> collecting layouts...") 121 | layouts, err := collectSourceFiles(dirs.layouts) 122 | if err != nil { 123 | return err 124 | } 125 | data.Layouts = layouts 126 | } 127 | prtty.Info.Println("--> collecting templates...") 128 | templates, err := collectSourceFiles(dirs.templates) 129 | if err != nil { 130 | return err 131 | } 132 | data.Templates = templates 133 | return nil 134 | } 135 | 136 | // generateFile generates go code containing the contents of all the 137 | // files in the sourceDirGroup and writes the code to the dest file. It 138 | // uses the given packageName if it is non-empty, and otherwise falls back 139 | // to the directory that dest is in. If a file already exists at dest, it 140 | // will be overwritten. 141 | func generateFile(dirs sourceDirGroup, dest, packageName string) error { 142 | prtty.Info.Println("--> generating go code...") 143 | if packageName == "" { 144 | packageName = filepath.Base(filepath.Dir(dest)) 145 | } 146 | data := &templateData{ 147 | PackageName: packageName, 148 | } 149 | if err := data.collectAllSourceFiles(dirs); err != nil { 150 | return err 151 | } 152 | if err := data.writeToFile(dest); err != nil { 153 | return err 154 | } 155 | return nil 156 | } 157 | 158 | //go:generate go-bindata --pkg=assets -o=assets/bindata.go templates/... 159 | 160 | // writeToFile writes the given templateData to the file located 161 | // at dest. It uses the template located at templates/generated.go.tmpl. 162 | // If there is already a file located at dest, it will be overwritten. 163 | func (data *templateData) writeToFile(dest string) error { 164 | tmplAsset, err := assets.Asset("templates/generated.go.tmpl") 165 | if err != nil { 166 | return err 167 | } 168 | generatedTmpl := template.Must(template.New("generated").Parse(string(tmplAsset))) 169 | buf := bytes.NewBuffer([]byte{}) 170 | if err := generatedTmpl.Execute(buf, data); err != nil { 171 | return err 172 | } 173 | formatted, err := format.Source(buf.Bytes()) 174 | if err != nil { 175 | return err 176 | } 177 | prtty.Success.Printf(" created %s", dest) 178 | return ioutil.WriteFile(dest, formatted, os.ModePerm) 179 | } 180 | 181 | // collectSourceFiles recursively walks through dir and its subdirectories 182 | // and returns an array of all the source files (files which end in .tmpl). 183 | func collectSourceFiles(dir string) ([]sourceFile, error) { 184 | sourceFiles := []sourceFile{} 185 | if err := collectTemplateFiles(dir, func(name, filename string) error { 186 | src, err := ioutil.ReadFile(filename) 187 | if err != nil { 188 | return err 189 | } 190 | prtty.Default.Printf(" %s", filename) 191 | sourceFiles = append(sourceFiles, sourceFile{ 192 | Name: name, 193 | Src: string(src), 194 | }) 195 | return nil 196 | }); err != nil { 197 | return nil, err 198 | } 199 | return sourceFiles, nil 200 | } 201 | -------------------------------------------------------------------------------- /temple/build_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Alex Browne. 2 | // All rights reserved. Use of this source code is 3 | // governed by the MIT license, which can be found 4 | // in the LICENSE file. 5 | 6 | package temple 7 | 8 | import ( 9 | "os/exec" 10 | "testing" 11 | ) 12 | 13 | const ( 14 | destFile = "test_files/templates.go" 15 | runFile = "test_files/run.go" 16 | ) 17 | 18 | func TestBuild(t *testing.T) { 19 | // Generate a go source file with build 20 | if err := Build("test_files/templates", destFile, "test_files/partials", "test_files/layouts", "main"); err != nil { 21 | t.Error(err) 22 | } 23 | // Use go run to run the file together with the run file 24 | cmd := exec.Command("go", "run", destFile, runFile) 25 | output, err := cmd.CombinedOutput() 26 | if err != nil { 27 | t.Error(err) 28 | } 29 | expected := "Todos" 30 | if string(output) != expected { 31 | t.Errorf("Output from generated code was not correct.\nExpected %s\nBut got: %s", expected, string(output)) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /temple/doc.go: -------------------------------------------------------------------------------- 1 | // Package temple is a library for managing go templates which 2 | // supports sharing templates between a client and server. It 3 | // is the library that powers the temple command line tool. 4 | // Temple provides lets you load templates from files or load 5 | // inline templates from the DOM. It also optionally organizes 6 | // templates into regular templates, partials, and layouts, associating 7 | // each template with other templates depending on which category 8 | // it belongs to. Temple is compatible with gopherjs and can be 9 | // compiled to javascript and run in a browser. 10 | // 11 | // Version 0.1.3 12 | package temple 13 | -------------------------------------------------------------------------------- /temple/dom.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Alex Browne. 2 | // All rights reserved. Use of this source code is 3 | // governed by the MIT license, which can be found 4 | // in the LICENSE file. 5 | 6 | package temple 7 | 8 | import ( 9 | "bytes" 10 | "honnef.co/go/js/dom" 11 | ) 12 | 13 | // ExecuteEl executes an Executor with the given data and then 14 | // writes the result to the innerHTML of el. It only works if 15 | // you have compiled this code to javascript with gopherjs and 16 | // it is running in a browser. 17 | func ExecuteEl(e Executor, el dom.Element, data interface{}) error { 18 | // TODO: use a buffer pool 19 | buf := bytes.NewBuffer([]byte{}) 20 | if err := e.Execute(buf, data); err != nil { 21 | return err 22 | } 23 | el.SetInnerHTML(buf.String()) 24 | return nil 25 | } 26 | 27 | // ExecuteEl executes the template with the given data and then 28 | // writes the result to the innerHTML of el. It only works if 29 | // you have compiled this code to javascript with gopherjs and 30 | // it is running in a browser. 31 | func (t *Template) ExecuteEl(el dom.Element, data interface{}) error { 32 | return ExecuteEl(t, el, data) 33 | } 34 | 35 | // ExecuteEl executes the partial with the given data and then 36 | // writes the result to the innerHTML of el. It only works if 37 | // you have compiled this code to javascript with gopherjs and 38 | // it is running in a browser. 39 | func (p *Partial) ExecuteEl(el dom.Element, data interface{}) error { 40 | return ExecuteEl(p, el, data) 41 | } 42 | 43 | // ExecuteEl executes the layout with the given data and then 44 | // writes the result to the innerHTML of el. It only works if 45 | // you have compiled this code to javascript with gopherjs and 46 | // it is running in a browser. 47 | func (l *Layout) ExecuteEl(el dom.Element, data interface{}) error { 48 | return ExecuteEl(l, el, data) 49 | } 50 | 51 | // AddAllInline scans the DOM for inline templates which 52 | // must be script tags with the type "text/template". The id property 53 | // will be used for the name of each template, and the special 54 | // property "data-kind" can be used to distinguish between regular 55 | // templates, partials, and layouts. So, to declare an inline partial 56 | // for use with the AddAllInline method, use an opening script 57 | // tag that looks like: 58 | //