-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdoc.go
More file actions
252 lines (189 loc) · 6.62 KB
/
doc.go
File metadata and controls
252 lines (189 loc) · 6.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
/*
Package tmpl provides a lazy compilation block-based templating system.
When performing web development, page content and markup are easily visualized
in "blocks." These blocks may include: the header, navigation, main content, or
supporting content. Tmpl provides simple block-based templating system to
accommodate the needs of web development.
Statements
Template statements are surrounded by the tokens '{%' and '%}' and contain text
to specify the action the template system should take. All actions begin with
a keyword like "block" or "evoke", except in the case of printing a value from
a context, where just the selector is specified.
Contexts
Contexts are the origin for all of the values a template has access to. The main
context is passed in when calling Execute to render the final output. From there,
sub-contexts are derived by passing into blocks, using "evoke" or "with".
Statement - Selector
Values from contexts and sub-contexts are available through the use of
selectors. Selectors always begin with a dot (.), followed by the attribute
name. A single dot selector always references "this" value. Selectors may chain
together to delve deeper into any context, as seen below.
Sub-contexts may always reference their parent context through the use of
dollar signs ($), similar to referencing a parent directory using "..".
Additionally, the top-level context is always available with a leading forward
slash (/).
Given the following main context, represented in JSON format,
{
"foo": {
"bar": {
"baz": "bif"
}
}
}
and assuming the statements are being executed in a sub-context rooted at the
"baz" element, then the following selectors will all produce "bif":
{% . %}
{% $.baz %}
{% $$.bar.baz %}
{% $$$.foo.bar.baz %}
{% /.foo.bar.baz %}
Statement - Call
Call runs a function that is attached to the template before it is Executed with
the supplied arguments. For example if we had a function named "foo" that was
attached to the template, the action
{% call foo .Bar .Baz %}
corresponds to the function call
foo(.Bar, .Baz)
with the selectors .Bar and .Baz evaluated. See Template.Call for details on
how to attach a function.
{% call name [args...] %}
{% call titleCase .Title %}
{% call add .FirstNumber .SecondNumber %}
{% call not .Value %}
{% call equal .FirstName .OtherUser %}
Statement - Block
Defines a block with the name, myName. Block definitions must end with an
{% end block %} statement.
{% block myName %}...{% end block %}
{% block greeting %}Hello!{% end block %}
{% block fullName %}{% .FirstName %} {% .LastName %}{% end block %}
Statement - Evoke
Substitutes this statement with the contents of the block, myBlock. The
optional context argument pushes a sub-context into the block.
{% evoke myName [context] %}
{% evoke greeting %}
{% evoke fullName .LoggedInUser %}
Statement - Range
Iterates over the given value. A value can be either the result of a .Selector
or the result of a call statement. If "as keyName valueName" are present,
the selectors ".keyName" and ".valueName" are available within the range block.
Otherwise, the selectors ".key" and ".val" become available. Similar to the Go
built-in range, "_" is a valid name for either the key or value. Range
definitions must end with an {% end range %} statement. The types which range
will iterate are: map, slice, struct
{% range value [as keyName valueName]}...{% end range %}
{% range .LoggedInUser.Friends %}
{% evoke fullName .val %}
{% end range %}
{% range .LoggedInUser.Friends as _ friend %}
{% evoke fullName .friend %}
{% end range %}
{% range call someFunc [as keyName valueName] %}...{% end range %}
{% range call loggedInUsers %}
{% evoke fullName .val %}
{% end range %}
{% range call loggedInUsers as _ user %}
{% evoke fullName .user %}
{% end range %}
Statement - If
Evaluates the specified value which may be either a .Selector or the result of
a call statement. If the value is "truthy" it executes the postive template,
otherwise, it executes the negative template if given.
{% if value %}...[{% else %}...]{% end if %}
{% if .LoggedIn %}
Positive: {% evoke fullName .LoggedInUser %} is logged in!
{% end if %}
{% if .LoggedIn %}
Yep!
{% else %}
Negative: No one is logged in.
{% end if %}
Statement - With
With takes the specified selector and roots a sub-context at that position in
the context.
{% with .Selector %}...{% end with %}
{% with .LoggedInUser %}
Hello {% .FristName %},
How are you Ms. {% .LastName %}
{% end with %}
{% with /. %}
Now we're rooted back at the top level no matter what!
{% end with %}
{% with .LoggedInUser %}
{% with .FirstName %}
Hello {% . %},
How are you Ms. {% $.LastName %}
{% end with %}
{% end with %}
Modes
Tmpl has two modes, Production and Development, which can be changed at any time
with the CompileMode function. In Development mode, every block and template is
loaded from disk and compiled in Execute, so that the latest results are always
used. In Production mode, files are only compiled the first time they are needed
and the results are cached for subsequent access.
Full Implementation Example
The template, "base.tmpl", defined as,
//file: base.tmpl
<html>
<head>
<title>{% .Title %}</title>
{% evoke meta .Meta %}
</head>
<body>
{% evoke content %}
<hr>
{% evoke footer %}
</body>
</html>
with the set of blocks,
//file: footer.block
{% block footer %}
We're a footer!
{% end block %}
//file: meta.block
{% block meta %}
{% range .Javascripts as _ src %}
<script src="{% .src %}"></script>
{% end range %}
{% end block %}
//file: content.block
{% block content %}
Some foofy content with a {% .User.Name %}
{% end block %}
implementation with tmpl,
t := tmpl.Parse("base.tmpl")
t.Blocks("footer.block")
context := RequestContext(r)
if err := t.Execute(w, context, "meta.block", "content.block"); err != nil {
//handle err
}
and context, represented here as JSON,
{
"Meta": {
"Javascripts": ["one.js", "two.js"]
},
"Title": "templates!",
"User": {
"Name": "zeebo",
"Location": "USA"
}
}
would lead to the following output (with some whitespace difference):
<html>
<head>
<title>templates!</title>
<script src="one.js"></script>
<script src="two.js"></script>
</head>
<body>
Some foofy content with a zeebo
<hr>
We're a footer!
</body>
</html>
This requests that the "footer.block" file be compiled in for every Execute,
while the "meta.block" and "content.block" files are compiled in for that
specific execute. The block defintions are inserted into the evoke locations
with the current context passed in, or whatever is specified by the evoke.
*/
package tmpl