commit
3902da7abc
@ -0,0 +1,2 @@
|
||||
pgot
|
||||
*.result
|
@ -0,0 +1,24 @@
|
||||
Copyright (c) 2020 Jason T. Lenz. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@ -0,0 +1,156 @@
|
||||
.POSIX:
|
||||
|
||||
PNAME = pgot
|
||||
|
||||
RTEMPLATE ?= ../repo-template
|
||||
|
||||
all: goUtil doc
|
||||
|
||||
doc: docMain docMan
|
||||
|
||||
clean: cleanGoUtil cleanCheck
|
||||
|
||||
cleanDoc: cleanDocMain cleanDocMan
|
||||
|
||||
install: installGoUtil installMan
|
||||
|
||||
uninstall: uninstallGoUtil uninstallMan
|
||||
|
||||
.DEFAULT_GOAL := all
|
||||
|
||||
.PHONY: all doc clean cleanDoc install uninstall
|
||||
|
||||
#---Helper Macros to Remove Files---
|
||||
|
||||
RMDIR_IF_EMPTY := sh -c '\
|
||||
if test -d $$0 && ! ls -1qA $$0 | grep -q . ; then \
|
||||
rmdir $$0; \
|
||||
fi'
|
||||
|
||||
RM ?= rm -f
|
||||
|
||||
#---Generate Golang Utility---
|
||||
|
||||
PREFIX ?= /usr/local
|
||||
_INSTDIR = $(DESTDIR)$(PREFIX)
|
||||
BINDIR ?= $(_INSTDIR)/bin
|
||||
GO ?= go
|
||||
GOFLAGS ?=
|
||||
|
||||
VERSION != git describe --first-parent 2> /dev/null
|
||||
|
||||
GOSRC != find . -name '*.go'
|
||||
|
||||
goUtil: $(PNAME)
|
||||
|
||||
$(PNAME): $(GOSRC)
|
||||
$(GO) build $(GOFLAGS) \
|
||||
-ldflags "-X main.Version=$(VERSION)" \
|
||||
-o $@
|
||||
|
||||
cleanGoUtil:
|
||||
$(RM) $(PNAME)
|
||||
|
||||
installGoUtil: $(PNAME)
|
||||
strip $(PNAME)
|
||||
mkdir -m755 -p $(BINDIR)
|
||||
install -m755 $(PNAME) $(BINDIR)/$(PNAME)
|
||||
|
||||
uninstallGoUtil:
|
||||
$(RM) $(BINDIR)/$(PNAME)
|
||||
$(RMDIR_IF_EMPTY) $(BINDIR)
|
||||
|
||||
.PHONY: goUtil cleanGoUtil installGoUtil uninstallGoUtil
|
||||
|
||||
#---Generate Man Page(s)---
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .1 .5 .1.scd .5.scd
|
||||
|
||||
PREFIX ?= /usr/local
|
||||
_INSTDIR = $(DESTDIR)$(PREFIX)
|
||||
MANDIR ?= $(_INSTDIR)/share/man
|
||||
|
||||
DOCMAN := doc/$(PNAME).1 doc/$(PNAME).5
|
||||
|
||||
.1.scd.1:
|
||||
scdoc < $< > $@
|
||||
|
||||
.5.scd.5:
|
||||
scdoc < $< > $@
|
||||
|
||||
docMan: $(DOCMAN)
|
||||
|
||||
cleanDocMan:
|
||||
$(RM) $(DOCMAN)
|
||||
|
||||
installMan: $(DOCMAN)
|
||||
mkdir -m755 -p $(MANDIR)/man1 $(MANDIR)/man5
|
||||
install -m644 doc/$(PNAME).1 $(MANDIR)/man1/$(PNAME).1
|
||||
install -m644 doc/$(PNAME).5 $(MANDIR)/man5/$(PNAME).5
|
||||
|
||||
uninstallMan:
|
||||
$(RM) $(MANDIR)/man1/$(PNAME).1
|
||||
$(RM) $(MANDIR)/man5/$(PNAME).5
|
||||
$(RMDIR_IF_EMPTY) $(MANDIR)/man1
|
||||
$(RMDIR_IF_EMPTY) $(MANDIR)/man5
|
||||
$(RMDIR_IF_EMPTY) $(MANDIR)
|
||||
|
||||
.PHONY: installMan uninstallMan
|
||||
|
||||
#---Test/Check Section---
|
||||
|
||||
TESTDIR = tests
|
||||
|
||||
check: $(PNAME)
|
||||
cd $(TESTDIR) && go test -v
|
||||
|
||||
cleanCheck:
|
||||
find $(TESTDIR) -name '*.result' -delete
|
||||
|
||||
.PHONY: check cleanCheck
|
||||
|
||||
#---Generate Main Documents---
|
||||
|
||||
DOCMAIN := README.md LICENSE
|
||||
|
||||
README.md: template/README.md.got
|
||||
pgot -i ":$(RTEMPLATE)" -o $@ $<
|
||||
|
||||
LICENSE: $(RTEMPLATE)/LICENSE.src/BSD-2-clause.got
|
||||
pgot -i ":$(RTEMPLATE)" -o $@ $<
|
||||
|
||||
docMain: $(DOCMAIN)
|
||||
|
||||
cleanDocMain:
|
||||
$(RM) $(DOCMAIN)
|
||||
|
||||
.PHONY: docMain, cleanDocMain
|
||||
|
||||
#---Generate Makefile---
|
||||
|
||||
Makefile: template/Makefile.got
|
||||
pgot -i ":$(RTEMPLATE)" -o $@ $<
|
||||
|
||||
mkFile: Makefile
|
||||
|
||||
regenMkFile:
|
||||
pgot -i ":$(RTEMPLATE)" -o Makefile template/Makefile.got
|
||||
|
||||
.PHONY: mkFile regenMkFile
|
||||
|
||||
#---Lint Helper Target---
|
||||
|
||||
lint:
|
||||
@find . -path ./.git -prune -or \
|
||||
-type f -and -not -name 'Makefile' \
|
||||
-exec grep -Hn '<no value>' '{}' ';'
|
||||
|
||||
#---TODO Helper Target---
|
||||
|
||||
todo:
|
||||
@find . -path ./.git -prune -or \
|
||||
-type f -and -not -name 'Makefile' \
|
||||
-exec grep -Hn TODO '{}' ';'
|
||||
|
||||
# vim:set noet tw=80:
|
@ -0,0 +1,286 @@
|
||||
# pgot
|
||||
|
||||
**pgot** (process golang template) is a lightweight command line [template
|
||||
processing](https://en.wikipedia.org/wiki/Template_processor) utility. It
|
||||
directly leverages the [golang template
|
||||
package](https://golang.org/pkg/text/template) from the standard library. pgot
|
||||
processes files that contain a combination of frontmatter followed by a
|
||||
template. The frontmatter consists of
|
||||
[JSON](https://en.wikipedia.org/wiki/JSON) formatted data, and the template that
|
||||
follows is free form text containing
|
||||
[actions](https://golang.org/pkg/text/template/#hdr-Actions). By convention,
|
||||
files to be processed by pgot are referred to as "got" (golang template) files
|
||||
and have a ".got" filename extension although this is not a requirement.
|
||||
|
||||
The pgot utility implements a "pgotInclude" keyword that can be defined in
|
||||
frontmatter to include (aka import) other got files. Each of these included
|
||||
files can also include other got files if desired. The number of nested
|
||||
includes is limited only by memory and the number of open files allowed by the
|
||||
operating system.
|
||||
|
||||
Both the frontmatter and the template from an included got file can be leveraged
|
||||
by the originating got file. The imported template is accessed via a [template
|
||||
action](https://golang.org/pkg/text/template/#hdr-Actions) using the name of the
|
||||
file minus its extension. By default, imported frontmatter is merged into the
|
||||
global namespace (aka "."), however pgotInclude also has the option of importing
|
||||
into a separate namespace if desired to avoid data naming collisions. Although
|
||||
pgot is small in size these features along with the comprehensive actions
|
||||
available in the standard template library enable a large range of applications
|
||||
from simple to large scale complex text file hierarchies.
|
||||
|
||||
The range of uses is quite large, but a few examples include:
|
||||
|
||||
* A simple README, LICENSE, or CONTRIBUTING template with standard sections that
|
||||
an author can leverage across various git repositories (the pgot source code
|
||||
[repository](https://git.lenzplace.org/lenzj/pgot) is an example).
|
||||
|
||||
* A static website using imported pgot templates to standardize website
|
||||
appearance, headers, footers, etc. (<https://blog.lenzplace.org> is an example).
|
||||
|
||||
* A mail merge script to generate tailored messages for each recipient.
|
||||
|
||||
* A large book written in markdown or latex with metadata in frontmatter for
|
||||
authors, chapters, references, contributors, keywords, etc.
|
||||
|
||||
## Command line synopsis
|
||||
|
||||
```text
|
||||
Usage: pgot [OPTION]... [FILE]...
|
||||
Read a got (golang template) file and send transformed text to output.
|
||||
Options:
|
||||
-d string
|
||||
string of json frontmatter to include
|
||||
-i paths
|
||||
colon separated list of paths to search with pgotInclude
|
||||
-o file
|
||||
output file path (default "-")
|
||||
```
|
||||
|
||||
If no files are specified on the command line, then pgot reads from sdtin.
|
||||
Similarly if the -o option is not used, the output is sent to stdout (aka "-").
|
||||
If multiple files are specified on the command line the last file listed is the
|
||||
primary or root file with previous files being included in a way similar to
|
||||
how the "pgotInclude" keyword works in frontmatter.
|
||||
|
||||
The -d option can be used to insert frontmatter from the command line. This
|
||||
frontmatter is merged into the global (aka ".") namespace and takes precedence
|
||||
or overwrites any frontmatter read in from the main got file or included files.
|
||||
|
||||
The path(s) specified with the -i option can include absolute or relative paths.
|
||||
An empty path in the -i option (the default) indicates that the local path
|
||||
relative to the respective got file should be searched. A few examples below
|
||||
shed further light on this:
|
||||
|
||||
```text
|
||||
pgot -i "" myfile.got
|
||||
This is the same as the default for the -i option. For any relative files
|
||||
specified using pgotInclude, the base dir of the calling got file will be
|
||||
where the relative path starts.
|
||||
|
||||
pgot -i ":/home/veronica/pgot-inc" myfile.got
|
||||
The folder of the calling got file will be searched first (nothing before
|
||||
the first ":") and if the file is not found there it will then search the
|
||||
"/home/veronica/pgot-inc" folder.
|
||||
|
||||
pgot -i "/home/larry/inc1::/home/larry/inc2" myfile.got
|
||||
The inc1 folder will be searched first, followed by the folder the calling
|
||||
got file is in, followed by the inc2 folder.
|
||||
```
|
||||
|
||||
## "Got" file format
|
||||
|
||||
A got file must include a frontmatter JSON section bounded by three semicolons
|
||||
each on their own line before and after the JSON data. Anything after this
|
||||
frontmatter section is considered to be the "template" which is processed by the
|
||||
golang template package. The full documentation of the template actions provided
|
||||
by the golang library is available [here](https://golang.org/pkg/text/template).
|
||||
Note that it is acceptable to have an empty frontmatter section or template
|
||||
section or both.
|
||||
|
||||
### Got file examples
|
||||
|
||||
The following examples also exist in the "tests" sub folder within this
|
||||
repository. A minimal (although admittedly useless) got file consists of the
|
||||
following:
|
||||
|
||||
```text
|
||||
;;;
|
||||
;;;
|
||||
```
|
||||
|
||||
A slightly more relevant got file:
|
||||
|
||||
```text
|
||||
;;;
|
||||
{ "name" : "Jason" }
|
||||
;;;
|
||||
Hello world, my name is {{.name}}
|
||||
```
|
||||
|
||||
And a more advanced example leveraged from
|
||||
[here](https://golang.org/pkg/text/template/#example_Template):
|
||||
|
||||
```text
|
||||
;;;
|
||||
{
|
||||
"guests" : [
|
||||
["Aunt Mildred", "bone china tea set", true],
|
||||
["Uncle John", "moleskin pants", false],
|
||||
["Cousin Rodney", "", false]
|
||||
]
|
||||
}
|
||||
;;;
|
||||
{{range .guests -}}
|
||||
Dear {{index . 0}},
|
||||
{{if index . 2}}
|
||||
It was a pleasure to see you at the wedding.
|
||||
{{- else}}
|
||||
It is a shame you couldn't make it to the wedding.
|
||||
{{- end}}
|
||||
{{with index . 1 -}}
|
||||
Thank you for the lovely {{.}}.
|
||||
{{end}}
|
||||
Best wishes,
|
||||
Josie
|
||||
---
|
||||
{{end -}}
|
||||
```
|
||||
|
||||
### Frontmatter key word
|
||||
|
||||
Any valid JSON structure can be used in the frontmatter section. There is
|
||||
however one keyword that is reserved when used in frontmatter.
|
||||
|
||||
**pgotInclude**
|
||||
|
||||
```text
|
||||
"pgotInclude" : [ "file1.got", "file2.got", "file3.got" ]
|
||||
"pgotInclude" : [ { "f":"file1.got", "n":"name1" },
|
||||
{ "f":"file2.got", "n":"name2" },
|
||||
{ "f":"file3.got", "n":"name3" } ]
|
||||
```
|
||||
|
||||
The pgotInclude keyword is used to include (aka import) other pgot files and can
|
||||
be used in one of two variations as shown above. Note that three files are
|
||||
showdn in the above variations, however any number of files can be included
|
||||
(from one to the limits of memory and the filesystem).
|
||||
|
||||
The first variation above imports each got file in the order defined in the
|
||||
array. The frontmatter is merged directly into the top level "." namespace. If
|
||||
two imported got files contain the same data element name, the last file
|
||||
included takes precedence. In other words, file3 would take precedence over
|
||||
file2, and file1. Lastly, any frontmatter defined in the originating got file
|
||||
(the file called directly by pgot) is included last and takes precedence
|
||||
(overwrites) any included data structures that have the same element name.
|
||||
|
||||
The second variation can be used to import frontmatter into the specified
|
||||
namespace which can be useful to avoid data collisions or overwriting. For
|
||||
example if file1 defines an element "color" : "Purple", and file2 defines
|
||||
"color" : "Green", they can both be accessed separately via
|
||||
{{.name1.color}}, and {{.name2.color}}.
|
||||
|
||||
It's important to note that the included got filenames may be specified with
|
||||
relative or absolute paths. For relative paths the the included got file is
|
||||
relative to the location of the originating got file.
|
||||
|
||||
Note that future keywords may be created which start with "pgot", so it is
|
||||
advisable to avoid naming any elements in frontmatter starting with "pgot".
|
||||
|
||||
## Custom pgot actions
|
||||
|
||||
In addition to the standard [template
|
||||
actions](https://golang.org/pkg/text/template/#hdr-Actions), there is currently
|
||||
one custom template action defined by pgot (below). This is included more as a
|
||||
simple example rather than a critical feature. For those interested in
|
||||
implementing custom template actions the
|
||||
[gotFuncs.go](/lenzj/pgot/src/branch/master/lib/gotFuncs.go) source
|
||||
code demonstrates how custom template actions can be added.
|
||||
|
||||
```text
|
||||
{{lnp "label" "url"}}
|
||||
```
|
||||
|
||||
lnp (link new page) generates the specified url as an html link which opens a
|
||||
new page when the user selects the link. If label is nil (aka "") then the url
|
||||
is displayed as the clickable link, otherwise the label itself is displayed.
|
||||
|
||||
## Compiling from source
|
||||
|
||||
### Dependencies
|
||||
|
||||
* Go compiler (v1.12 or later).
|
||||
* Go package [chunkio](https://git.lenzplace.org/lenzj/chunkio)
|
||||
* Go package [testcli](https://git.lenzplace.org/lenzj/testcli) to run tests.
|
||||
* [Scdoc](https://git.sr.ht/~sircmpwn/scdoc/) utility to generate the man page.
|
||||
Only needed if changes to man page sources are made.
|
||||
* [pgot](https://git.lenzplace.org/lenzj/pgot) (this utility) to process files in the templates sub
|
||||
folder. Only needed if changes to README.md, LICENCE, Makefile etc. are
|
||||
needed.
|
||||
|
||||
### Installing
|
||||
|
||||
```text
|
||||
$ make
|
||||
# make install
|
||||
```
|
||||
|
||||
## Running the tests
|
||||
|
||||
```text
|
||||
$ make check
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
If you have a bugfix, update, issue or feature enhancement the best way to reach
|
||||
me is by following the instructions in the link below. Thank you!
|
||||
|
||||
<https://blog.lenzplace.org/about/contact.html>
|
||||
|
||||
|
||||
## Versioning
|
||||
|
||||
I follow the [SemVer](http://semver.org/) strategy for versioning. The latest
|
||||
version is listed in the [releases](/lenzj/pgot/releases) section.
|
||||
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under a BSD two clause license - see the
|
||||
[LICENSE](LICENSE) file for details.
|
||||
|
||||
|
||||
## FAQ
|
||||
|
||||
**Q**: What causes the following error message: "pgot : invalid character '}'
|
||||
looking for beginning of object key string"?
|
||||
|
||||
**A**: This is an error message from the golang JSON library. The frontmatter
|
||||
is likely not valid structured [JSON](https://json.org/). Make sure you don't
|
||||
have something like the following. The last comma in the "copyright" row should
|
||||
be deleted.
|
||||
|
||||
```text
|
||||
;;;
|
||||
{
|
||||
"author" : "Jose",
|
||||
"date" : "March 6th, 2019",
|
||||
"copyright" : "2019",
|
||||
}
|
||||
;;;
|
||||
```
|
||||
|
||||
**Q**: I keep getting '\<no value\>' in parts of my output?
|
||||
|
||||
**A**: This is what the golang template engine outputs when a
|
||||
[pipeline](https://golang.org/pkg/text/template/#hdr-Actions) is undefined. It
|
||||
can sometimes be handy when debugging golang templates to include
|
||||
{{.}} somewhere in the template. This displays __all__ the elements
|
||||
in the global pipeline defined at time of template execution.
|
||||
|
||||
## Other similar projects
|
||||
|
||||
* <https://github.com/hairyhenderson/gomplate>
|
||||
* <https://github.com/gliderlabs/sigil>
|
||||
|
||||
<!-- vim:set ts=4 sw=4 et tw=80: -->
|
@ -0,0 +1,132 @@
|
||||
.\" Generated by scdoc 1.9.7
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.nh
|
||||
.ad l
|
||||
.\" Begin generated content:
|
||||
.TH "pgot" "1" "2020-04-26"
|
||||
.P
|
||||
.SH NAME
|
||||
.P
|
||||
pgot- a command line template processing utility
|
||||
.P
|
||||
.SH SYNOPSIS
|
||||
.P
|
||||
\fIpgot\fR [\fIoption\fR] [\fIfile\fR] \fI...\fR
|
||||
.P
|
||||
.SH OPTIONS
|
||||
.P
|
||||
\fB-d\fR \fIstring\fR
|
||||
.RS 4
|
||||
A string of json frontmatter to include
|
||||
.P
|
||||
.RE
|
||||
\fB-i\fR \fIpaths\fR
|
||||
.RS 4
|
||||
A colon separated list of paths to search with gotInclude
|
||||
.P
|
||||
.RE
|
||||
\fB-o\fR \fIfile\fR
|
||||
.RS 4
|
||||
Output file path (default "-")
|
||||
.P
|
||||
.RE
|
||||
.SH DESCRIPTION
|
||||
.P
|
||||
\fBpgot\fR (process golang template) is a command line template processing utility.
|
||||
It directly leverages the powerful golang template package. \fBpgot\fR processes
|
||||
files containing frontmatter followed by a template. The frontmatter consists
|
||||
of JSON formatted data, and the template that follows is free form text
|
||||
containing template actions. By convention, files to be processed by pgot are
|
||||
referred to as "got" (golang template) files and have a ".got" filename
|
||||
extension although this is not a requirement.
|
||||
.P
|
||||
The pgot utility implements a "pgotInclude" keyword that can be defined in
|
||||
frontmatter to include (aka import) other got files. Each of these included
|
||||
files can also include other got files if desired. The number of nested
|
||||
includes is limited only by memory and the number of open files allowed by the
|
||||
operating system (a pretty large number).
|
||||
.P
|
||||
Both the frontmatter and the template from the included got file can be
|
||||
leveraged by the originating got file. This enables pgot to be used both in
|
||||
simple text processing applications as well as complex large scale text
|
||||
processing. As one example, the author of pgot used it to create a static blog
|
||||
website with uniform html page headers, footers, and other features while
|
||||
avoiding repetitive data entry (aka following the DRY principle).
|
||||
.P
|
||||
A got file must include a frontmatter JSON section bounded by three semicolons
|
||||
each on their own line before and after the JSON data. It is acceptable to
|
||||
have an empty frontmatter section or template section or both.
|
||||
.P
|
||||
.SH EXAMPLE
|
||||
.P
|
||||
A minimal (although admittedly useless) got file consists of the following:
|
||||
.P
|
||||
.nf
|
||||
.RS 4
|
||||
;;;
|
||||
;;;
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
A slightly more relevant got file:
|
||||
.P
|
||||
.nf
|
||||
.RS 4
|
||||
;;;
|
||||
{ "name" : "Jason" }
|
||||
;;;
|
||||
Hello world, my name is {{\&.name}}
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
And a more advanced example leveraged from the golang template website:
|
||||
.P
|
||||
.nf
|
||||
.RS 4
|
||||
;;;
|
||||
{
|
||||
"guests" : [
|
||||
["Aunt Mildred", "bone china tea set", true],
|
||||
["Uncle John", "moleskin pants", false],
|
||||
["Cousin Rodney", "", false]
|
||||
]
|
||||
}
|
||||
;;;
|
||||
{{range \&.guests -}}
|
||||
Dear {{index \&. 0}},
|
||||
{{if index \&. 2}}
|
||||
It was a pleasure to see you at the wedding\&.
|
||||
{{- else}}
|
||||
It is a shame you couldn't make it to the wedding\&.
|
||||
{{- end}}
|
||||
{{with index \&. 1 -}}
|
||||
Thank you for the lovely {{\&.}}\&.
|
||||
{{end}}
|
||||
Best wishes,
|
||||
Josie
|
||||
---
|
||||
{{end -}}
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
.SH BUGS
|
||||
.P
|
||||
Bug reporting instructions:
|
||||
.br
|
||||
<https://blog.lenzplace.org/about/contact.html>
|
||||
.P
|
||||
.SH SEE ALSO
|
||||
.P
|
||||
\fBpgot\fR(5)
|
||||
.P
|
||||
Website for pgot:
|
||||
.br
|
||||
<https://git.lenzplace.org/lenzj/pgot>
|
||||
.P
|
||||
Other useful references:
|
||||
.br
|
||||
<https://golang.org/pkg/text/template>
|
||||
<https://en.wikipedia.org/wiki/Template_processor>
|
||||
<https://en.wikipedia.org/wiki/JSON>
|
||||
.P
|
@ -0,0 +1,114 @@
|
||||
pgot(1)
|
||||
; This is a scdoc source file for generating a man page
|
||||
; See https://git.sr.ht/~sircmpwn/scdoc
|
||||
|
||||
# NAME
|
||||
|
||||
pgot- a command line template processing utility
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
_pgot_ [_option_] [_file_] _..._
|
||||
|
||||
# OPTIONS
|
||||
|
||||
*-d* _string_
|
||||
A string of json frontmatter to include
|
||||
|
||||
*-i* _paths_
|
||||
A colon separated list of paths to search with gotInclude
|
||||
|
||||
*-o* _file_
|
||||
Output file path (default "-")
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
*pgot* (process golang template) is a command line template processing utility.
|
||||
It directly leverages the powerful golang template package. *pgot* processes
|
||||
files containing frontmatter followed by a template. The frontmatter consists
|
||||
of JSON formatted data, and the template that follows is free form text
|
||||
containing template actions. By convention, files to be processed by pgot are
|
||||
referred to as "got" (golang template) files and have a ".got" filename
|
||||
extension although this is not a requirement.
|
||||
|
||||
The pgot utility implements a "pgotInclude" keyword that can be defined in
|
||||
frontmatter to include (aka import) other got files. Each of these included
|
||||
files can also include other got files if desired. The number of nested
|
||||
includes is limited only by memory and the number of open files allowed by the
|
||||
operating system (a pretty large number).
|
||||
|
||||
Both the frontmatter and the template from the included got file can be
|
||||
leveraged by the originating got file. This enables pgot to be used both in
|
||||
simple text processing applications as well as complex large scale text
|
||||
processing. As one example, the author of pgot used it to create a static blog
|
||||
website with uniform html page headers, footers, and other features while
|
||||
avoiding repetitive data entry (aka following the DRY principle).
|
||||
|
||||
A got file must include a frontmatter JSON section bounded by three semicolons
|
||||
each on their own line before and after the JSON data. It is acceptable to
|
||||
have an empty frontmatter section or template section or both.
|
||||
|
||||
# EXAMPLE
|
||||
|
||||
A minimal (although admittedly useless) got file consists of the following:
|
||||
|
||||
```
|
||||
;;;
|
||||
;;;
|
||||
```
|
||||
|
||||
A slightly more relevant got file:
|
||||
|
||||
```
|
||||
;;;
|
||||
{ "name" : "Jason" }
|
||||
;;;
|
||||
Hello world, my name is {{.name}}
|
||||
```
|
||||
|
||||
And a more advanced example leveraged from the golang template website:
|
||||
|
||||
```
|
||||
;;;
|
||||
{
|
||||
"guests" : [
|
||||
["Aunt Mildred", "bone china tea set", true],
|
||||
["Uncle John", "moleskin pants", false],
|
||||
["Cousin Rodney", "", false]
|
||||
]
|
||||
}
|
||||
;;;
|
||||
{{range .guests -}}
|
||||
Dear {{index . 0}},
|
||||
{{if index . 2}}
|
||||
It was a pleasure to see you at the wedding.
|
||||
{{- else}}
|
||||
It is a shame you couldn't make it to the wedding.
|
||||
{{- end}}
|
||||
{{with index . 1 -}}
|
||||
Thank you for the lovely {{.}}.
|
||||
{{end}}
|
||||
Best wishes,
|
||||
Josie
|
||||
---
|
||||
{{end -}}
|
||||
```
|
||||
|
||||
# BUGS
|
||||
|
||||
Bug reporting instructions:++
|
||||
<https://blog.lenzplace.org/about/contact.html>
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
*pgot*(5)
|
||||
|
||||
Website for pgot:++
|
||||
<https://git.lenzplace.org/lenzj/pgot>
|
||||
|
||||
Other useful references:++
|
||||
<https://golang.org/pkg/text/template>
|
||||
<https://en.wikipedia.org/wiki/Template_processor>
|
||||
<https://en.wikipedia.org/wiki/JSON>
|
||||
|
||||
; vim:set noet tw=80:
|
@ -0,0 +1,698 @@
|
||||
.\" Generated by scdoc 1.9.7
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.nh
|
||||
.ad l
|
||||
.\" Begin generated content:
|
||||
.TH "pgot" "5" "2020-04-26"
|
||||
.P
|
||||
.SH NAME
|
||||
.P
|
||||
pgot - structure of got (golang template) files used by pgot.
|
||||
.P
|
||||
.SH DESCRIPTION
|
||||
.P
|
||||
By convention, files to be processed by pgot are referred to as "got" (golang
|
||||
template) files and have a ".got" filename extension although this is not a
|
||||
requirement.
|
||||
.P
|
||||
All got files contain frontmatter followed by a template. The frontmatter
|
||||
contains formatted data, and the template that follows is free form text
|
||||
containing actions defined by the golang template package.
|
||||
.P
|
||||
The pgot utility implements a "pgotInclude" keyword that can be defined in
|
||||
frontmatter to include (aka import) other got files. Each of these included
|
||||
files can also include other got files if desired. The number of nested
|
||||
includes is limited only by memory and the number of open files allowed by the
|
||||
operating system (a pretty large number).
|
||||
.P
|
||||
Both the frontmatter and the template from the included got file can be
|
||||
leveraged by the originating got file. This enables pgot to be used both in
|
||||
simple text processing applications as well as complex large scale text
|
||||
processing.
|
||||
.P
|
||||
.SH FRONTMATTER SYNTAX
|
||||
.P
|
||||
Frontmatter in got files is defined by a JSON data structure. The JSON data
|
||||
structure is bounded before and after by three semicolons and a newline and must
|
||||
exist at the beginning of the got file. An example is shown below:
|
||||
.P
|
||||
.nf
|
||||
.RS 4
|
||||
;;;
|
||||
{ "name" : "Jason" }
|
||||
;;;
|
||||
Hello world, my name is {{\&.name}}\&.
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
In the example above, the string "Hello world, my name is {{.name}}" is the
|
||||
template portion of the file and the lines prior make up the JSON frontmatter.
|
||||
.P
|
||||
Note that if and when other data structure languages are included in the golang
|
||||
standard library in the future they may be added to pgot frontmatter syntax as
|
||||
optional methods with their own bounded signature. (I.E. YAML bounded with
|
||||
"---" or TOML with "+
|
||||
+")
|
||||
.P
|
||||
.SS Frontmatter keywords
|
||||
.P
|
||||
\fBpgotInclude\fR
|
||||
.P
|
||||
.nf
|
||||
.RS 4
|
||||
"pgotInclude" : [ "file1\&.got", "file2\&.got", "file3\&.got" ]
|
||||
"pgotInclude" : [ { "f":"file1\&.got", "n":"name1" },
|
||||
{ "f":"file2\&.got", "n":"name2" },
|
||||
{ "f":"file3\&.got", "n":"name3" } ]
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
The pgotInclude keyword is used to include (aka import) other pgot files and can
|
||||
be used in one of two variations as shown above. Note that three files are
|
||||
showdn in the above variations, however any number of files can be included
|
||||
(from one to the limits of memory and the filesystem).
|
||||
.P
|
||||
The first variation above imports each got file in the order defined in the
|
||||
array. The frontmatter is merged directly into the top level "." namespace. If
|
||||
two imported got files contain the same data element name, the last file
|
||||
included takes precedence. In other words, file3 would take precedence over
|
||||
file2, and file1. Lastly, any frontmatter defined in the originating got file
|
||||
(the file called directly by pgot) is included last and takes precedence
|
||||
(overwrites) any included data structures that have the same element name.
|
||||
.P
|
||||
The second variation can be used to import frontmatter into the specified
|
||||
namespace which can be useful to avoid data collisions or overwriting. For
|
||||
example if file1 defines an element "color" : "Purple", and file2 defines
|
||||
"color" : "Green", they can both be accessed separately via {{.name1.color}},
|
||||
and {{.name2.color}}.
|
||||
.P
|
||||
It's important to note that the included got filenames may be specified with
|
||||
relative or absolute paths. For relative paths the the included got file is
|
||||
relative to the location of the originating got file.
|
||||
.P
|
||||
Note that future keywords may be created which start with "pgot", so it is
|
||||
advisable to avoid naming any elements in frontmatter starting with "pgot".
|
||||
.P
|
||||
.SH TEMPLATE SYNTAX
|
||||
.P
|
||||
Templates are executed by applying them to the data structure from the
|
||||
associated frontmatter. Annotations in the template refer to elements of the
|
||||
data structure (typically a field of a struct or a key in a map) to control
|
||||
execution and derive values to be displayed. Execution of the template walks
|
||||
the structure and sets the cursor, represented by a period '.' and called "dot",
|
||||
to the value at the current location in the structure as execution proceeds.
|
||||
.P
|
||||
The input text for a template is UTF-8-encoded text in any format.
|
||||
"Actions"--data evaluations or control structures--are delimited by "{{" and
|
||||
"}}"; all text outside actions is copied to the output unchanged. Except for raw
|
||||
strings, actions may not span newlines, although comments can.
|
||||
.P
|
||||
.SS Imported templates
|
||||
.P
|
||||
When a got file is included using the \fBpgotInclude\fR keyword in frontmatter, the
|
||||
associated template is included also and its name is the same as the associated
|
||||
filename minus its extension. This template can be accessed using one of the
|
||||
two {{template ...}} methods mentioned in the \fBActions\fR section below. A short
|
||||
example is as follows:
|
||||
.P
|
||||
file1.got
|
||||
.nf
|
||||
.RS 4
|
||||
;;;
|
||||
{ "date" : "2020-04-19" }
|
||||
;;;
|
||||
I am typing this on {{\&.date}}
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
file2.got
|
||||
.nf
|
||||
.RS 4
|
||||
;;;
|
||||
{
|
||||
"date" : "Sunday",
|
||||
"pgotInclude" : [ "file1\&.got" ]
|
||||
}
|
||||
;;;
|
||||
{{template "file1" \&.}}
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
Output from "pgot file2.got"
|
||||
.nf
|
||||
.RS 4
|
||||
I am typing this on Sunday
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
.SS Custom pgot actions
|
||||
.P
|
||||
There is currently one custom template action defined by pgot (below). This is
|
||||
included more as an example rather than a critical feature. For those
|
||||
interested in implementing custom template actions the source code at
|
||||
<https://git.lenzplace.org/lenzj/pgot/src/branch/master/lib/gotFuncs.go>
|
||||
demonstrates this simple example.
|
||||
.P
|
||||
{{lnp label url}}
|
||||
.RS 4
|
||||
lnp (link new page) generates the specified html url in a format which
|
||||
opens a new page when the user selects the link. If label is nil (aka
|
||||
"") then the url is displayed as the clickable link, otherwise the label
|
||||
itself is displayed.
|
||||
.P
|
||||
.RE
|
||||
.SS Text and spaces
|
||||
.P
|
||||
By default, all text between actions is copied verbatim when the template is
|
||||
executed. However, to aid in formatting template source code, if an action's
|
||||
left delimiter (by default "{{") is followed immediately by a minus sign and
|
||||
ASCII space character ("{{- "), all trailing white space is trimmed from the
|
||||
immediately preceding text. Similarly, if the right delimiter ("}}") is preceded
|
||||
by a space and minus sign (" -}}"), all leading white space is trimmed from the
|
||||
immediately following text. In these trim markers, the ASCII space must be
|
||||
present; "{{-3}}" parses as an action containing the number -3.
|
||||
.P
|
||||
For instance, when executing the template whose source is
|
||||
.P
|
||||
.RS 4
|
||||
"{{23 -}} < {{- 45}}"
|
||||
.P
|
||||
.RE
|
||||
the generated output would be
|
||||
.P
|
||||
.RS 4
|
||||
"23<45"
|
||||
.P
|
||||
.RE
|
||||
For this trimming, the definition of white space characters is the same as in
|
||||
Go: space, horizontal tab, carriage return, and newline.
|
||||
.P
|
||||
.SS Actions
|
||||
.P
|
||||
Here is the list of actions. "Arguments" and "pipelines" are evaluations of
|
||||
data, defined in detail in the corresponding sections that follow.
|
||||
.P
|
||||
{{/* a comment */}}
|
||||
.br
|
||||
{{- /* a comment with white space trimmed from preceding and following text */ -}}
|
||||
.RS 4
|
||||
A comment; discarded. May contain newlines. Comments do not nest and
|
||||
must start and end at the delimiters, as shown here.
|
||||
.P
|
||||
.RE
|
||||
{{pipeline}}
|
||||
.RS 4
|
||||
The default textual representation (the same as would be printed by
|
||||
fmt.Print) of the value of the pipeline is copied to the output.
|
||||
.P
|
||||
.RE
|
||||
{{if pipeline}} T1 {{end}}
|
||||
.RS 4
|
||||
If the value of the pipeline is empty, no output is generated;
|
||||
otherwise, T1 is executed. The empty values are false, 0, any nil
|
||||
pointer or interface value, and any array, slice, map, or string of
|
||||
length zero. Dot is unaffected.
|
||||
.P
|
||||
.RE
|
||||
{{if pipeline}} T1 {{else}} T0 {{end}}
|
||||
.RS 4
|
||||
If the value of the pipeline is empty, T0 is executed; otherwise, T1 is
|
||||
executed. Dot is unaffected.
|
||||
.P
|
||||
.RE
|
||||
{{if pipeline}} T1 {{else if pipeline}} T0 {{end}}
|
||||
.RS 4
|
||||
To simplify the appearance of if-else chains, the else action of an if
|
||||
may include another if directly; the effect is exactly the same as
|
||||
writing {{if pipeline}} T1 {{else}}{{if pipeline}} T0 {{end}}{{end}}
|
||||
.P
|
||||
.RE
|
||||
{{range pipeline}} T1 {{end}}
|
||||
.RS 4
|
||||
The value of the pipeline must be an array, slice, map, or channel. If
|
||||
the value of the pipeline has length zero, nothing is output; otherwise,
|
||||
dot is set to the successive elements of the array, slice, or map and T1
|
||||
is executed. If the value is a map and the keys are of basic type with
|
||||
a defined order, the elements will be visited in sorted key order.
|
||||
.P
|
||||
.RE
|
||||
{{range pipeline}} T1 {{else}} T0 {{end}}
|
||||
.RS 4
|
||||
The value of the pipeline must be an array, slice, map, or channel. If
|
||||
the value of the pipeline has length zero, dot is unaffected and T0 is
|
||||
executed; otherwise, dot is set to the successive elements of the array,
|
||||
slice, or map and T1 is executed.
|
||||
.P
|
||||
.RE
|
||||
{{template "name"}}
|
||||
.RS 4
|
||||
The template with the specified name is executed with nil data.
|
||||
.P
|
||||
.RE
|
||||
{{template "name" pipeline}}
|
||||
.RS 4
|
||||
The template with the specified name is executed with dot set to the
|
||||
value of the pipeline.
|
||||
.P
|
||||
.RE
|
||||
{{block "name" pipeline}} T1 {{end}}
|
||||
.RS 4
|
||||
A block is shorthand for defining a template {{define "name"}} T1
|
||||
{{end}} and then executing it in place {{template "name" pipeline}} The
|
||||
typical use is to define a set of root templates that are then
|
||||
customized by redefining the block templates within.
|
||||
.P
|
||||
.RE
|
||||
{{with pipeline}} T1 {{end}}
|
||||
.RS 4
|
||||
If the value of the pipeline is empty, no output is generated;
|
||||
otherwise, dot is set to the value of the pipeline and T1 is executed.
|
||||
.P
|
||||
.RE
|
||||
{{with pipeline}} T1 {{else}} T0 {{end}}
|
||||
.RS 4
|
||||
If the value of the pipeline is empty, dot is unaffected and T0 is
|
||||
executed; otherwise, dot is set to the value of the pipeline and T1 is
|
||||
executed.
|
||||
.P
|
||||
.RE
|
||||
.SS Arguments
|
||||
.P
|
||||
An argument is a simple value, denoted by one of the following.
|
||||
.P
|
||||
.nf
|
||||
.RS 4
|
||||
- A boolean, string, character, integer, floating-point, imaginary
|
||||
or complex constant in Go syntax\&. These behave like Go's untyped
|
||||
constants\&. Note that, as in Go, whether a large integer constant
|
||||
overflows when assigned or passed to a function can depend on whether
|
||||
the host machine's ints are 32 or 64 bits\&.
|
||||
|
||||
- The keyword nil, representing an untyped Go nil\&.
|
||||
|
||||
- The character '\&.' (period):
|
||||
\&.
|
||||
The result is the value of dot\&.
|
||||
|
||||
- A variable name, which is a (possibly empty) alphanumeric string
|
||||
preceded by a dollar sign, such as
|
||||
$piOver2
|
||||
or
|
||||
$
|
||||
The result is the value of the variable\&. Variables are described below\&.
|
||||
|
||||
- The name of a field of the data, which must be a struct, preceded
|
||||
by a period, such as
|
||||
\&.Field
|
||||
The result is the value of the field\&. Field invocations may be chained:
|
||||
\&.Field1\&.Field2
|
||||
Fields can also be evaluated on variables, including chaining:
|
||||
$x\&.Field1\&.Field2
|
||||
|
||||
- The name of a key of the data, which must be a map, preceded by a period, such
|
||||
as
|
||||
\&.Key
|
||||
The result is the map element value indexed by the key\&. Key invocations may
|
||||
be chained and combined with fields to any depth:
|
||||
\&.Field1\&.Key1\&.Field2\&.Key2
|
||||
Although the key must be an alphanumeric identifier, unlike with field names
|
||||
they do not need to start with an upper case letter\&. Keys can also be
|
||||
evaluated on variables, including chaining:
|
||||
$x\&.key1\&.key2
|
||||
|
||||
- The name of a niladic method of the data, preceded by a period, such as
|
||||
\&.Method
|
||||
The result is the value of invoking the method with dot as the receiver,
|
||||
dot\&.Method()\&. Such a method must have one return value (of any type) or two
|
||||
return values, the second of which is an error\&. If it has two and the
|
||||
returned error is non-nil, execution terminates and an error is returned to
|
||||
the caller as the value of Execute\&. Method invocations may be chained and
|
||||
combined with fields and keys to any depth:
|
||||
\&.Field1\&.Key1\&.Method1\&.Field2\&.Key2\&.Method2
|
||||
Methods can also be evaluated on variables, including chaining:
|
||||
$x\&.Method1\&.Field
|
||||
|
||||
- The name of a niladic function, such as
|
||||
fun
|
||||
The result is the value of invoking the function, fun()\&. The return types and
|
||||
values behave as in methods\&. Functions and function names are described below\&.
|
||||
|
||||
- A parenthesized instance of one the above, for grouping\&. The result may be
|
||||
accessed by a field or map key invocation\&.
|
||||
print (\&.F1 arg1) (\&.F2 arg2)
|
||||
(\&.StructValuedMethod "arg")\&.Field
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
Arguments may evaluate to any type; if they are pointers the implementation
|
||||
automatically indirects to the base type when required. If an evaluation
|
||||
yields a function value, such as a function-valued field of a struct, the
|
||||
function is not invoked automatically, but it can be used as a truth value for
|
||||
an if action and the like. To invoke it, use the call function, defined below.
|
||||
.P
|
||||
.SS Pipelines
|
||||
.P
|
||||
A pipeline is a possibly chained sequence of "commands". A command is a simple
|
||||
value (argument) or a function or method call, possibly with multiple arguments:
|
||||
.P
|
||||
.nf
|
||||
.RS 4
|
||||
Argument
|
||||
The result is the value of evaluating the argument\&.
|
||||
|
||||
\&.Method [Argument\&.\&.\&.]
|
||||
The method can be alone or the last element of a chain but,
|
||||
unlike methods in the middle of a chain, it can take arguments\&.
|
||||
The result is the value of calling the method with the
|
||||
arguments:
|
||||
dot\&.Method(Argument1, etc\&.)
|
||||
|
||||
functionName [Argument\&.\&.\&.]
|
||||
The result is the value of calling the function associated
|
||||
with the name:
|
||||
function(Argument1, etc\&.)
|
||||
Functions and function names are described below\&.
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
A pipeline may be "chained" by separating a sequence of commands with pipeline
|
||||
characters '|'. In a chained pipeline, the result of each command is passed as
|
||||
the last argument of the following command. The output of the final command in
|
||||
the pipeline is the value of the pipeline.
|
||||
.P
|
||||
The output of a command will be either one value or two values, the second of
|
||||
which has type error. If that second value is present and evaluates to non-nil,
|
||||
execution terminates and the error is returned to the caller of Execute.
|
||||
.P
|
||||
.SS Variables
|
||||
.P
|
||||
A pipeline inside an action may initialize a variable to capture the result. The
|
||||
initialization has syntax
|
||||
.P
|
||||
.nf
|
||||
.RS 4
|
||||
$variable := pipeline
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
where $variable is the name of the variable. An action that declares a variable
|
||||
produces no output.
|
||||
.P
|
||||
Variables previously declared can also be assigned, using the syntax
|
||||
.P
|
||||
.nf
|
||||
.RS 4
|
||||
$variable = pipeline
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
If a "range" action initializes a variable, the variable is set to the
|
||||
successive elements of the iteration. Also, a "range" may declare two variables,
|
||||
separated by a comma:
|
||||
.P
|
||||
.nf
|
||||
.RS 4
|
||||
range $index, $element := pipeline
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
in which case $index and $element are set to the successive values of the
|
||||
array/slice index or map key and element, respectively. Note that if there is
|
||||
only one variable, it is assigned the element; this is opposite to the
|
||||
convention in Go range clauses.
|
||||
.P
|
||||
A variable's scope extends to the "end" action of the control structure ("if",
|
||||
"with", or "range") in which it is declared, or to the end of the template if
|
||||
there is no such control structure. A template invocation does not inherit
|
||||
variables from the point of its invocation.
|
||||
.P
|
||||
When execution begins, $ is set to the data argument passed to Execute, that is,
|
||||
to the starting value of dot.
|
||||
.P
|
||||
Here are some example one-line templates demonstrating pipelines and variables.
|
||||
All produce the quoted word "output":
|
||||
.P
|
||||
.nf
|
||||
.RS 4
|
||||
{{""output""}}
|
||||
A string constant\&.
|
||||
{{`"output"`}}
|
||||
A raw string constant\&.
|
||||
{{printf "%q" "output"}}
|
||||
A function call\&.
|
||||
{{"output" | printf "%q"}}
|
||||
A function call whose final argument comes from the previous
|
||||
command\&.
|
||||
{{printf "%q" (print "out" "put")}}
|
||||
A parenthesized argument\&.
|
||||
{{"put" | printf "%s%s" "out" | printf "%q"}}
|
||||
A more elaborate call\&.
|
||||
{{"output" | printf "%s" | printf "%q"}}
|
||||
A longer chain\&.
|
||||
{{with "output"}}{{printf "%q" \&.}}{{end}}
|
||||
A with action using dot\&.
|
||||
{{with $x := "output" | printf "%q"}}{{$x}}{{end}}
|
||||
A with action that creates and uses a variable\&.
|
||||
{{with $x := "output"}}{{printf "%q" $x}}{{end}}
|
||||
A with action that uses the variable in another action\&.
|
||||
{{with $x := "output"}}{{$x | printf "%q"}}{{end}}
|
||||
The same, but pipelined\&.
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
.SS Functions
|
||||
.P
|
||||
During execution functions are found in two function maps: first in the
|
||||
template, then in the global function map. By default, no functions are defined
|
||||
in the template but the Funcs method can be used to add them.
|
||||
.P
|
||||
Predefined global functions are named as follows.
|
||||
.P
|
||||
.nf
|
||||
.RS 4
|
||||
and
|
||||
Returns the boolean AND of its arguments by returning the
|
||||
first empty argument or the last argument, that is,
|
||||
"and x y" behaves as "if x then y else x"\&. All the
|
||||
arguments are evaluated\&.
|
||||
|
||||
call
|
||||
Returns the result of calling the first argument, which
|
||||
must be a function, with the remaining arguments as parameters\&.
|
||||
Thus "call \&.X\&.Y 1 2" is, in Go notation, dot\&.X\&.Y(1, 2) where
|
||||
Y is a func-valued field, map entry, or the like\&.
|
||||
The first argument must be the result of an evaluation
|
||||
that yields a value of function type (as distinct from
|
||||
a predefined function such as print)\&. The function must
|
||||
return either one or two result values, the second of which
|
||||
is of type error\&. If the arguments don't match the function
|
||||
or the returned error value is non-nil, execution stops\&.
|
||||
|
||||
html
|
||||
Returns the escaped HTML equivalent of the textual
|
||||
representation of its arguments\&. This function is unavailable
|
||||
in html/template, with a few exceptions\&.
|
||||
|
||||
index
|
||||
Returns the result of indexing its first argument by the
|
||||
following arguments\&. Thus "index x 1 2 3" is, in Go syntax,
|
||||
x[1][2][3]\&. Each indexed item must be a map, slice, or array\&.
|
||||
|
||||
slice
|
||||
slice returns the result of slicing its first argument by the
|
||||
remaining arguments\&. Thus "slice x 1 2" is, in Go syntax, x[1:2],
|
||||
while "slice x" is x[:], "slice x 1" is x[1:], and "slice x 1 2 3"
|
||||
is x[1:2:3]\&. The first argument must be a string, slice, or array\&.
|
||||
|
||||
js
|
||||
Returns the escaped JavaScript equivalent of the textual
|
||||
representation of its arguments\&.
|
||||
|
||||
len
|
||||
Returns the integer length of its argument\&.
|
||||
|
||||
not
|
||||
Returns the boolean negation of its single argument\&.
|
||||
|
||||
or
|
||||
Returns the boolean OR of its arguments by returning the
|
||||
first non-empty argument or the last argument, that is,
|
||||
"or x y" behaves as "if x then x else y"\&. All the
|
||||
arguments are evaluated\&.
|
||||
|
||||
print
|
||||
An alias for fmt\&.Sprint
|
||||
|
||||
printf
|
||||
An alias for fmt\&.Sprintf
|
||||
|
||||
println
|
||||
An alias for fmt\&.Sprintln
|
||||
|
||||
urlquery
|
||||
Returns the escaped value of the textual representation of
|
||||
its arguments in a form suitable for embedding in a URL query\&.
|
||||
This function is unavailable in html/template, with a few
|
||||
exceptions\&.
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
The boolean functions take any zero value to be false and a non-zero value to be
|
||||
true.
|
||||
.P
|
||||
There is also a set of binary comparison operators defined as functions:
|
||||
.nf
|
||||
.RS 4
|
||||
eq
|
||||
Returns the boolean truth of arg1 == arg2
|
||||
|
||||
ne
|
||||
Returns the boolean truth of arg1 != arg2
|
||||
|
||||
lt
|
||||
Returns the boolean truth of arg1 < arg2
|
||||
|
||||
le
|
||||
Returns the boolean truth of arg1 <= arg2
|
||||
|
||||
gt
|
||||
Returns the boolean truth of arg1 > arg2
|
||||
|
||||
ge
|
||||
Returns the boolean truth of arg1 >= arg2
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
For simpler multi-way equality tests, eq (only) accepts two or more arguments
|
||||
and compares the second and subsequent to the first, returning in effect
|
||||
.P
|
||||
.nf
|
||||
.RS 4
|
||||
arg1==arg2 || arg1==arg3 || arg1==arg4 \&.\&.\&.
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
(Unlike with || in Go, however, eq is a function call and all the arguments will
|
||||
be evaluated.)
|
||||
.P
|
||||
The comparison functions work on any values whose type Go defines as comparable.
|
||||
For basic types such as integers, the rules are relaxed: size and exact type are
|
||||
ignored, so any integer value, signed or unsigned, may be compared with any
|
||||
other integer value. (The arithmetic value is compared, not the bit pattern, so
|
||||
all negative integers are less than all unsigned integers.) However, as usual,
|
||||
one may not compare an int with a float32 and so on.
|
||||
.P
|
||||
.SS Associated templates
|
||||
.P
|
||||
Each template is named by a string specified when it is created. Also, each
|
||||
template is associated with zero or more other templates that it may invoke by
|
||||
name; such associations are transitive and form a name space of templates.
|
||||
.P
|
||||
A template may use a template invocation to instantiate another associated
|
||||
template; see the explanation of the "template" action above. The name must be
|
||||
that of a template associated with the template that contains the invocation.
|
||||
.P
|
||||
.SS Nested template definitions
|
||||
.P
|
||||
When parsing a template, another template may be defined and associated with the
|
||||
template being parsed. Template definitions must appear at the top level of the
|
||||
template, much like global variables in a Go program.
|
||||
.P
|
||||
The syntax of such definitions is to surround each template declaration with a
|
||||
"define" and "end" action.
|
||||
.P
|
||||
The define action names the template being created by providing a string
|
||||
constant. Here is a simple example:
|
||||
.P
|
||||
.nf
|
||||
.RS 4
|
||||
{{define "T1"}}ONE{{end}}
|
||||
{{define "T2"}}TWO{{end}}
|
||||
{{define "T3"}}{{template "T1"}} {{template "T2"}}{{end}}
|
||||
{{template "T3"}}
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
This defines two templates, T1 and T2, and a third T3 that invokes the other two
|
||||
when it is executed. Finally it invokes T3. If executed this template will
|
||||
produce the text
|
||||
.P
|
||||
.nf
|
||||
.RS 4
|
||||
ONE TWO
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
.SH EXAMPLE
|
||||
.P
|
||||
A minimal (although admittedly useless) got file consists of the following:
|
||||
.P
|
||||
.nf
|
||||
.RS 4
|
||||
;;;
|
||||
;;;
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
A slightly more relevant got file:
|
||||
.P
|
||||
.nf
|
||||
.RS 4
|
||||
;;;
|
||||
{ "name" : "Jason" }
|
||||
;;;
|
||||
Hello world, my name is {{\&.name}}
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
And a more advanced example leveraged from the golang template website:
|
||||
.P
|
||||
.nf
|
||||
.RS 4
|
||||
;;;
|
||||
{
|
||||
"guests" : [
|
||||
["Aunt Mildred", "bone china tea set", true],
|
||||
["Uncle John", "moleskin pants", false],
|
||||
["Cousin Rodney", "", false]
|
||||
]
|
||||
}
|
||||
;;;
|
||||
{{range \&.guests -}}
|
||||
Dear {{index \&. 0}},
|
||||
{{if index \&. 2}}
|
||||
It was a pleasure to see you at the wedding\&.
|
||||
{{- else}}
|
||||
It is a shame you couldn't make it to the wedding\&.
|
||||
{{- end}}
|
||||
{{with index \&. 1 -}}
|
||||
Thank you for the lovely {{\&.}}\&.
|
||||
{{end}}
|
||||
Best wishes,
|
||||
Josie
|
||||
---
|
||||
{{end -}}
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
.SH BUGS
|
||||
.P
|
||||
Bug reporting instructions:
|
||||
.br
|
||||
<https://blog.lenzplace.org/about/contact.html>
|
||||
.P
|
||||
.SH SEE ALSO
|
||||
.P
|
||||
\fBpgot\fR(1)
|
||||
.P
|
||||
Website for pgot:
|
||||
.br
|
||||
<https://git.lenzplace.org/lenzj/pgot>
|
||||
.P
|
||||
Other useful references:
|
||||
.br
|
||||
<https://golang.org/pkg/text/template>
|
||||
<https://en.wikipedia.org/wiki/Template_processor>
|
||||
<https://en.wikipedia.org/wiki/JSON>
|
||||
.P
|
@ -0,0 +1,625 @@
|
||||
pgot(5)
|
||||
; This is a scdoc source file for generating a man page
|
||||
; See https://git.sr.ht/~sircmpwn/scdoc
|
||||
|
||||
# NAME
|
||||
|
||||
pgot - structure of got (golang template) files used by pgot.
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
By convention, files to be processed by pgot are referred to as "got" (golang
|
||||
template) files and have a ".got" filename extension although this is not a
|
||||
requirement.
|
||||
|
||||
All got files contain frontmatter followed by a template. The frontmatter
|
||||
contains formatted data, and the template that follows is free form text
|
||||
containing actions defined by the golang template package.
|
||||
|
||||
The pgot utility implements a "pgotInclude" keyword that can be defined in
|
||||
frontmatter to include (aka import) other got files. Each of these included
|
||||
files can also include other got files if desired. The number of nested
|
||||
includes is limited only by memory and the number of open files allowed by the
|
||||
operating system (a pretty large number).
|
||||
|
||||
Both the frontmatter and the template from the included got file can be
|
||||
leveraged by the originating got file. This enables pgot to be used both in
|
||||
simple text processing applications as well as complex large scale text
|
||||
processing.
|
||||
|
||||
# FRONTMATTER SYNTAX
|
||||
|
||||
Frontmatter in got files is defined by a JSON data structure. The JSON data
|
||||
structure is bounded before and after by three semicolons and a newline and must
|
||||
exist at the beginning of the got file. An example is shown below:
|
||||
|
||||
```
|
||||
;;;
|
||||
{ "name" : "Jason" }
|
||||
;;;
|
||||
Hello world, my name is {{.name}}.
|
||||
```
|
||||
|
||||
In the example above, the string "Hello world, my name is {{.name}}" is the
|
||||
template portion of the file and the lines prior make up the JSON frontmatter.
|
||||
|
||||
Note that if and when other data structure languages are included in the golang
|
||||
standard library in the future they may be added to pgot frontmatter syntax as
|
||||
optional methods with their own bounded signature. (I.E. YAML bounded with
|
||||
"---" or TOML with "+++")
|
||||
|
||||
## Frontmatter keywords
|
||||
|
||||
*pgotInclude*
|
||||
|
||||
```
|
||||
"pgotInclude" : [ "file1.got", "file2.got", "file3.got" ]
|
||||
"pgotInclude" : [ { "f":"file1.got", "n":"name1" },
|
||||
{ "f":"file2.got", "n":"name2" },
|
||||
{ "f":"file3.got", "n":"name3" } ]
|
||||
```
|
||||
|
||||
The pgotInclude keyword is used to include (aka import) other pgot files and can
|
||||
be used in one of two variations as shown above. Note that three files are
|
||||
showdn in the above variations, however any number of files can be included
|
||||
(from one to the limits of memory and the filesystem).
|
||||
|
||||
The first variation above imports each got file in the order defined in the
|
||||
array. The frontmatter is merged directly into the top level "." namespace. If
|
||||
two imported got files contain the same data element name, the last file
|
||||
included takes precedence. In other words, file3 would take precedence over
|
||||
file2, and file1. Lastly, any frontmatter defined in the originating got file
|
||||
(the file called directly by pgot) is included last and takes precedence
|
||||
(overwrites) any included data structures that have the same element name.
|
||||
|
||||
The second variation can be used to import frontmatter into the specified
|
||||
namespace which can be useful to avoid data collisions or overwriting. For
|
||||
example if file1 defines an element "color" : "Purple", and file2 defines
|
||||
"color" : "Green", they can both be accessed separately via {{.name1.color}},
|
||||
and {{.name2.color}}.
|
||||
|
||||
It's important to note that the included got filenames may be specified with
|
||||
relative or absolute paths. For relative paths the the included got file is
|
||||
relative to the location of the originating got file.
|
||||
|
||||
Note that future keywords may be created which start with "pgot", so it is
|
||||
advisable to avoid naming any elements in frontmatter starting with "pgot".
|
||||
|
||||
# TEMPLATE SYNTAX
|
||||
; Note that much of the content in this section of the man page is derived from
|
||||
; the golang template package documentation.
|
||||
; See https://golang.org/pkg/text/template
|
||||
|
||||
Templates are executed by applying them to the data structure from the
|
||||
associated frontmatter. Annotations in the template refer to elements of the
|
||||
data structure (typically a field of a struct or a key in a map) to control
|
||||
execution and derive values to be displayed. Execution of the template walks
|
||||
the structure and sets the cursor, represented by a period '.' and called "dot",
|
||||
to the value at the current location in the structure as execution proceeds.
|
||||
|
||||
The input text for a template is UTF-8-encoded text in any format.
|
||||
"Actions"--data evaluations or control structures--are delimited by "{{" and
|
||||
"}}"; all text outside actions is copied to the output unchanged. Except for raw
|
||||
strings, actions may not span newlines, although comments can.
|
||||
|
||||
## Imported templates
|
||||
|
||||
When a got file is included using the *pgotInclude* keyword in frontmatter, the
|
||||
associated template is included also and its name is the same as the associated
|
||||
filename minus its extension. This template can be accessed using one of the
|
||||
two {{template ...}} methods mentioned in the *Actions* section below. A short
|
||||
example is as follows:
|
||||
|
||||
file1.got
|
||||
```
|
||||
;;;
|
||||
{ "date" : "2020-04-19" }
|
||||
;;;
|
||||
I am typing this on {{.date}}
|
||||
```
|
||||
|
||||
file2.got
|
||||
```
|
||||
;;;
|
||||
{
|
||||
"date" : "Sunday",
|
||||
"pgotInclude" : [ "file1.got" ]
|
||||
}
|
||||
;;;
|
||||
{{template "file1" .}}
|
||||
```
|
||||
|
||||
Output from "pgot file2.got"
|
||||
```
|
||||
I am typing this on Sunday
|
||||
```
|
||||
|
||||
## Custom pgot actions
|
||||
|
||||
There is currently one custom template action defined by pgot (below). This is
|
||||
included more as an example rather than a critical feature. For those
|
||||
interested in implementing custom template actions the source code at
|
||||
<https://git.lenzplace.org/lenzj/pgot/src/branch/master/lib/gotFuncs.go>
|
||||
demonstrates this simple example.
|
||||
|
||||
{{lnp label url}}
|
||||
lnp (link new page) generates the specified html url in a format which
|
||||
opens a new page when the user selects the link. If label is nil (aka
|
||||
"") then the url is displayed as the clickable link, otherwise the label
|
||||
itself is displayed.
|
||||
|
||||
## Text and spaces
|
||||
|
||||
By default, all text between actions is copied verbatim when the template is
|
||||
executed. However, to aid in formatting template source code, if an action's
|
||||
left delimiter (by default "{{") is followed immediately by a minus sign and
|
||||
ASCII space character ("{{- "), all trailing white space is trimmed from the
|
||||
immediately preceding text. Similarly, if the right delimiter ("}}") is preceded
|
||||
by a space and minus sign (" -}}"), all leading white space is trimmed from the
|
||||
immediately following text. In these trim markers, the ASCII space must be
|
||||
present; "{{-3}}" parses as an action containing the number -3.
|
||||
|
||||
For instance, when executing the template whose source is
|
||||
|
||||
"{{23 -}} < {{- 45}}"
|
||||
|
||||
the generated output would be
|
||||
|
||||
"23<45"
|
||||
|
||||
For this trimming, the definition of white space characters is the same as in
|
||||
Go: space, horizontal tab, carriage return, and newline.
|
||||
|
||||
## Actions
|
||||
|
||||
Here is the list of actions. "Arguments" and "pipelines" are evaluations of
|
||||
data, defined in detail in the corresponding sections that follow.
|
||||
|
||||
{{/\* a comment \*/}}++
|
||||
{{- /\* a comment with white space trimmed from preceding and following text \*/ -}}
|
||||
A comment; discarded. May contain newlines. Comments do not nest and
|
||||
must start and end at the delimiters, as shown here.
|
||||
|
||||
{{pipeline}}
|
||||
The default textual representation (the same as would be printed by
|
||||
fmt.Print) of the value of the pipeline is copied to the output.
|
||||
|
||||
{{if pipeline}} T1 {{end}}
|
||||
If the value of the pipeline is empty, no output is generated;
|
||||
otherwise, T1 is executed. The empty values are false, 0, any nil
|
||||
pointer or interface value, and any array, slice, map, or string of
|
||||
length zero. Dot is unaffected.
|
||||
|
||||
{{if pipeline}} T1 {{else}} T0 {{end}}
|
||||
If the value of the pipeline is empty, T0 is executed; otherwise, T1 is
|
||||
executed. Dot is unaffected.
|
||||
|
||||
{{if pipeline}} T1 {{else if pipeline}} T0 {{end}}
|
||||
To simplify the appearance of if-else chains, the else action of an if
|
||||
may include another if directly; the effect is exactly the same as
|
||||
writing {{if pipeline}} T1 {{else}}{{if pipeline}} T0 {{end}}{{end}}
|
||||
|
||||
{{range pipeline}} T1 {{end}}
|
||||
The value of the pipeline must be an array, slice, map, or channel. If
|
||||
the value of the pipeline has length zero, nothing is output; otherwise,
|
||||
dot is set to the successive elements of the array, slice, or map and T1
|
||||
is executed. If the value is a map and the keys are of basic type with
|
||||
a defined order, the elements will be visited in sorted key order.
|
||||
|
||||
{{range pipeline}} T1 {{else}} T0 {{end}}
|
||||
The value of the pipeline must be an array, slice, map, or channel. If
|
||||
the value of the pipeline has length zero, dot is unaffected and T0 is
|
||||
executed; otherwise, dot is set to the successive elements of the array,
|
||||
slice, or map and T1 is executed.
|
||||
|
||||
{{template "name"}}
|
||||
The template with the specified name is executed with nil data.
|
||||
|
||||
{{template "name" pipeline}}
|
||||
The template with the specified name is executed with dot set to the
|
||||
value of the pipeline.
|
||||
|
||||
{{block "name" pipeline}} T1 {{end}}
|
||||
A block is shorthand for defining a template {{define "name"}} T1
|
||||
{{end}} and then executing it in place {{template "name" pipeline}} The
|
||||
typical use is to define a set of root templates that are then
|
||||
customized by redefining the block templates within.
|
||||
|
||||
{{with pipeline}} T1 {{end}}
|
||||
If the value of the pipeline is empty, no output is generated;
|
||||
otherwise, dot is set to the value of the pipeline and T1 is executed.
|
||||
|
||||
{{with pipeline}} T1 {{else}} T0 {{end}}
|
||||
If the value of the pipeline is empty, dot is unaffected and T0 is
|
||||
executed; otherwise, dot is set to the value of the pipeline and T1 is
|
||||
executed.
|
||||
|
||||
## Arguments
|
||||
|
||||
An argument is a simple value, denoted by one of the following.
|
||||
|
||||
```
|
||||
- A boolean, string, character, integer, floating-point, imaginary
|
||||
or complex constant in Go syntax. These behave like Go's untyped
|
||||
constants. Note that, as in Go, whether a large integer constant
|
||||
overflows when assigned or passed to a function can depend on whether
|
||||
the host machine's ints are 32 or 64 bits.
|
||||
|
||||
- The keyword nil, representing an untyped Go nil.
|
||||
|
||||
- The character '.' (period):
|
||||
.
|
||||
The result is the value of dot.
|
||||
|
||||
- A variable name, which is a (possibly empty) alphanumeric string
|
||||
preceded by a dollar sign, such as
|
||||
$piOver2
|
||||
or
|
||||
$
|
||||
The result is the value of the variable. Variables are described below.
|
||||
|
||||
- The name of a field of the data, which must be a struct, preceded
|
||||
by a period, such as
|
||||
.Field
|
||||
The result is the value of the field. Field invocations may be chained:
|
||||
.Field1.Field2
|
||||
Fields can also be evaluated on variables, including chaining:
|
||||
$x.Field1.Field2
|
||||
|
||||
- The name of a key of the data, which must be a map, preceded by a period, such
|
||||
as
|
||||
.Key
|
||||
The result is the map element value indexed by the key. Key invocations may
|
||||
be chained and combined with fields to any depth:
|
||||
.Field1.Key1.Field2.Key2
|
||||
Although the key must be an alphanumeric identifier, unlike with field names
|
||||
they do not need to start with an upper case letter. Keys can also be
|
||||
evaluated on variables, including chaining:
|
||||
$x.key1.key2
|
||||
|
||||
- The name of a niladic method of the data, preceded by a period, such as
|
||||
.Method
|
||||
The result is the value of invoking the method with dot as the receiver,
|
||||
dot.Method(). Such a method must have one return value (of any type) or two
|
||||
return values, the second of which is an error. If it has two and the
|
||||
returned error is non-nil, execution terminates and an error is returned to
|
||||
the caller as the value of Execute. Method invocations may be chained and
|
||||
combined with fields and keys to any depth:
|
||||
.Field1.Key1.Method1.Field2.Key2.Method2
|
||||
Methods can also be evaluated on variables, including chaining:
|
||||
$x.Method1.Field
|
||||
|
||||
- The name of a niladic function, such as
|
||||
fun
|
||||
The result is the value of invoking the function, fun(). The return types and
|
||||
values behave as in methods. Functions and function names are described below.
|
||||
|
||||
- A parenthesized instance of one the above, for grouping. The result may be
|
||||
accessed by a field or map key invocation.
|
||||
print (.F1 arg1) (.F2 arg2)
|
||||
(.StructValuedMethod "arg").Field
|
||||
```
|
||||
|
||||
Arguments may evaluate to any type; if they are pointers the implementation
|
||||
automatically indirects to the base type when required. If an evaluation
|
||||
yields a function value, such as a function-valued field of a struct, the
|
||||
function is not invoked automatically, but it can be used as a truth value for
|
||||
an if action and the like. To invoke it, use the call function, defined below.
|
||||
|
||||
## Pipelines
|
||||
|
||||
A pipeline is a possibly chained sequence of "commands". A command is a simple
|
||||
value (argument) or a function or method call, possibly with multiple arguments:
|
||||
|
||||
```
|
||||
Argument
|
||||
The result is the value of evaluating the argument.
|
||||
|
||||
.Method [Argument...]
|
||||
The method can be alone or the last element of a chain but,
|
||||
unlike methods in the middle of a chain, it can take arguments.
|
||||
The result is the value of calling the method with the
|
||||
arguments:
|
||||
dot.Method(Argument1, etc.)
|
||||
|
||||
functionName [Argument...]
|
||||
The result is the value of calling the function associated
|
||||
with the name:
|
||||
function(Argument1, etc.)
|
||||
Functions and function names are described below.
|
||||
```
|
||||
|
||||
A pipeline may be "chained" by separating a sequence of commands with pipeline
|
||||
characters '|'. In a chained pipeline, the result of each command is passed as
|
||||
the last argument of the following command. The output of the final command in
|
||||
the pipeline is the value of the pipeline.
|
||||
|
||||
The output of a command will be either one value or two values, the second of
|
||||
< |