Skip to content

Commit

Permalink
doc edits; minor adjustment to calling tokens (#137)
Browse files Browse the repository at this point in the history
* doc edits; minor adjustment to calling tokens

* add test
  • Loading branch information
jverzani authored Nov 6, 2021
1 parent 1b900c9 commit 844c3f4
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 34 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "Mustache"
uuid = "ffc61752-8dc7-55ee-8c37-f3e9cdd09e70"
version = "1.0.10"
version = "1.0.11"

[deps]
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
Expand Down
124 changes: 106 additions & 18 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,15 @@ The `render` function pieces things together. Like `print`, the first
argument is for an optional `IO` instance. In the above example, where
one is not provided, a string is returned.

The flow is a template is parsed into tokens by `Mustache.parse`. This can be called directly, indirectly through the non-standard string literal `mt`, or when loading a file with `Mustache.load`. The templates use tags comprised of matching mustaches (`{}`), either two or three, to
The flow is

* a template is parsed into tokens by `Mustache.parse`. This can be called directly, indirectly through the non-standard string literal `mt`, or when loading a file with `Mustache.load`. The templates use tags comprised of matching mustaches (`{}`), either two or three, to
indicate a value to be substituted for. These tags may be adjusted when `parse` is called.
The `render` function takes tokens as its second argument. If this argument is a string, `parse` is called internally. The `render` function than reassambles the template, substituting values, as appropriate, from the "view" passed to it and writes the output to the specified `io` argument.

* The tokens and a view are `render`ed. The `render` function takes tokens as its second argument. If this argument is a string, `parse` is called internally. The `render` function than reassambles the template, substituting values, as appropriate, from the "view" passed to it and writes the output to the specified `io` argument.


There are only 4 exports: `mt` and `jmt` string literals to specify a template; and `render` and `render_from_file`.


The view used to provide values to substitute into the template can be
Expand Down Expand Up @@ -100,20 +106,95 @@ gives
"Beta distribution with alpha=1.0, beta=2.0"
```

### Rendering

The `render` function combines tokens and a view to fill in the template. The basic call is `render([io::IO], tokens, view)`, however there are variants:


* `render(tokens; kwargs...)`
* `render(string, view)` (`string` is parsed into tokens)
* `render(string; kwargs...)`

Finally, tokens are callable, so there are these variants to call `render`:

* `tokens([io::IO], view)`
* `tokens([io::UO]; kwargs...)`

### Views

Views are used to hold values for the templates variables. There are many possible objects that can be used for views:

* a dictionary
* a named tuple
* keyword arguments to `render`
* a module

For templates which iterate over a variable, these can be

* a `Tables.jl` compatible object with row iteration support (e.g., A `DataFrame`, a tuple of named tuples, ...)
* a vector or tuple (in which case "`.`" is used to match


### Templates and tokens

A template is parsed into tokens. The `render` function combines the tokens with the view to create the output.

* Parsing is done at compile time, if the `mt` string literal is used to define the template. If re-using a template, this is encouraged, as it will be more performant.

* If string interpolation is desired prior to the parsing into tokens, the `jmt` string literal can be used.

* As well, a string can be used to define a template. When `parse` is called, the string will be parsed into tokens. This is the flow if `render` is called on a string (and not tokens).




### Variables

Tags representing variables have the form `{{varname}}`,
Tags representing variables for substitution have the form `{{varname}}`,
`{{:symbol}}`, or their triple-braced versions `{{{varname}}}` or
`{{{:symbol}}}`. The triple brace prevents HTML substitution for
`{{{:symbol}}}`.


The `varname` version will match variables in a view such as a dictionary or a module.

The `:symbol` version will match variables passed in via named tuple or keyword arguments.

```julia
b = "be"
render(mt"a {{b}} c", Main) # "a be c"
render(mt"a {{:b}} c", b="bee") # "a bee c"
render(mt"a {{:b}} c", (b="bee", c="sea")) # "a bee c"
```


The triple brace prevents HTML substitution for
entities such as `<`. The following are escaped when only double
braces are used: "&", "<", ">", "'", "\", and "/".

If different tags are specified to `parse`,
```julia
render(mt"a {{:b}} c", b = "%< bee >%") # "a %&lt; bee &gt;% c"
render(mt"a {{{:b}}} c", b = "%< bee >%") # "a %< bee >% c"
```

If different tags are specified to `parse`,
say `<<` or `>>`, then `<<{` and `}>>` indicate the prevention of substitution.

```julia
tokens = Mustache.parse("a <<:b>> c", ("<<", ">>"))
render(tokens, b = "%< B >%") # a %&lt; B &gt;% c"

tokens = Mustache.parse("a <<{:b}>> c", ("<<", ">>"))
render(tokens, b = "%< B >%") # "a %< B >% c"
```


If the variable refers to a function, the value will be the result of
calling the function with no arguments passed in.

```julia
render(mt"a {{:b}} c", b = () -> "Bea") # "a Bea c"
```

### Sections

In the main example, the template included:
Expand All @@ -128,18 +209,19 @@ Tags beginning with `#varname` and closed with `/varname` create
sections. The part between them is used only if the variable is
defined.

```
```julia
Mustache.render("{{#:a}}one{{/:a}}", a=length) # "3"
```

If the section name refers to a function, that function will be passed
If the section name refers to a function, as above, that function will be passed
the unevaluated string within the section, as expected by the Mustache
specification. (If the tag "|" is used, the section value will be
rendered first, an enhancement to the specification.)
specification.

```
tpl = """{{|:lambda}}{{:value}}{{/:lambda}} dollars.""";
If the tag "|" is used, the section value will be rendered first, an enhancement to the specification.

```julia
fmt(txt) = "<b>" * string(round(parse(Float64, txt), digits=2)) * "</b>";
tpl = """{{|:lambda}}{{:value}}{{/:lambda}} dollars.""";
Mustache.render(tpl, value=1.23456789, lambda=fmt) # "<b>1.23</b> dollars."
```

Expand All @@ -154,7 +236,7 @@ defined or is `falsy`.

### Iteration

If the section variable, `{{#VARNAME}}`, binds to an iterable
If the section variable, `{{#varname}}`, binds to an iterable
collection, then the text in the section is repeated for each item in
the collection with the view used for the context of the template
given by the item.
Expand Down Expand Up @@ -246,7 +328,7 @@ end
In the above, a string is used above -- and not a `mt` macro -- so that string
interpolation can happen. The `jmt_str` string macro allows for substitution, so the above template could also have been more simply written as:

```
```julia
function df_to_table(df, label="label", caption="caption")
fmt = repeat("c", size(df,2))
header = join(string.(names(df)), " & ")
Expand All @@ -267,13 +349,14 @@ tpl = jmt"""
Mustache.render(tpl, DF=df)
end
```
### Iterating over vectors

#### Iterating over vectors

Though it isn't part of the Mustache specification, when iterating
over an unnamed vector, Mustache.jl uses `{{.}}` to refer to the item:
over an unnamed vector or tuple, `Mustache.jl uses` `{{.}}` to refer to the item:

```julia
tpl = "{{#:vec}}{{.}} {{/:vec}}"
tpl = mt"{{#:vec}}{{.}} {{/:vec}}"
render(tpl, vec = ["A1", "B2", "C3"]) # "A1 B2 C3 "
```

Expand All @@ -287,14 +370,14 @@ arithmetic on indices.)
To print commas one can use this pattern:

```julia
tpl = "{{#:vec}}{{.}}{{^.[end]}}, {{/.[end]}}{{/:vec}}"
tpl = mt"{{#:vec}}{{.}}{{^.[end]}}, {{/.[end]}}{{/:vec}}"
render(tpl, vec = ["A1", "B2", "C3"]) # "A1, B2, C3"
```

To put the first value in bold, but no others, say:

```julia
tpl = """
tpl = mt"""
{{#:vec}}
{{#.[1]}}<bold>{{.}}</bold>{{/.[1]}}
{{^.[1]}}{{.}}{{/.[1]}}
Expand Down Expand Up @@ -397,6 +480,11 @@ suited for many jobs:
[StringLiterals](https://github.com/JuliaString/StringLiterals.jl)
package are available.

* The
[HypertextLiteral](https://github.com/MechanicalRabbit/HypertextLiteral.jl)
package is useful when interpolating HTML, SVG, or SGML tagged
content.

## Differences from Mustache.js

This project deviates from Mustache.js in a few significant ways:
Expand Down
12 changes: 6 additions & 6 deletions src/context.jl
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,15 @@ function lookup(ctx::Context, key)
if occursin(r"\.", key)

value′ = lookup_dotted(context, key)
if value′ == nothing
if value′ == nothing
## do something with "."
## we use .[ind] to refer to value in parent of given index;
m = match(r"^\.\[(.*)\]$", key)
m === nothing && break

idx = m.captures[1]
vals = context.parent.view

# this has limited support for indices: "end", or a number, but no
# arithmetic, such as `end-1`.
## This is for an iterable; rather than
Expand All @@ -84,15 +84,15 @@ function lookup(ctx::Context, key)
else
value′ = lookup_in_view(context.view, stripWhitespace(key))
end

if value′ !== nothing
value = value′
!global_lookup && break
end

context = context.parent
end

## cache
ctx._cache[key] = value
return(value)
Expand Down
12 changes: 9 additions & 3 deletions src/render.jl
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,14 @@ render(tokens::MustacheTokens, view) = sprint(io -> render(io, tokens, view))
render(tokens::MustacheTokens; kwargs...) = sprint(io -> render(io, tokens, Dict(kwargs...)))

## make MustacheTokens callable for kwargs...
(m::MustacheTokens)(io::IO; kwargs...) = render(io, m; kwargs...)
(m::MustacheTokens)(; kwargs...) = sprint(io -> m(io; kwargs...))
function (m::MustacheTokens)(io::IO, args...; kwargs...)
if length(args) == 1
render(io, m, first(args))
else
render(io, m; kwargs...)
end
end
(m::MustacheTokens)(args...; kwargs...) = sprint(io -> m(io, args...; kwargs...))



Expand All @@ -56,7 +62,7 @@ render(template::AbstractString; kwargs...) = sprint(io -> render(io, template,
render_from_file(filepath, view)
render_from_file(filepath; kwargs...)
Renders a template from `filepath` and `view`.
Renders a template from `filepath` and `view`.
"""
render_from_file(filepath, view) = render(Mustache.load(filepath), view)
render_from_file(filepath::AbstractString; kwargs...) = render(Mustache.load(filepath); kwargs...)
13 changes: 7 additions & 6 deletions test/Mustache_test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,8 @@ tokens = Mustache.load(filepath)

## Test of MustacheTokens being callable
tpl = mt"""Hello {{:name}}"""
@test tpl(name="world") == "Hello world"
@test tpl(name="world") == "Hello world" # using kwargs...
@test tpl((name="world",)) == "Hello world" # using arg

@testset "closed issues" begin

Expand Down Expand Up @@ -200,8 +201,8 @@ tpl2 = mt"""
<input type="range" {{@:range}} min="{{start}}" step="{{step}}" max="{{stop}}" {{/:range}}>
"""
@test render(tpl, Issue123(1:2:3)) == "<input type=\"range\" min=\"1\" step=\"2\" max=\"3\" >\n"


## Issue 124 regression test"
tpl = mt"""
{{^dims}}<input type="text" value="">{{/dims}}
Expand All @@ -212,7 +213,7 @@ tpl2 = mt"""
@test render(tpl, Dict("dims"=>("1", "2"))) == "\n<textarea cols=\"1\" ></textarea><textarea rows=\"2\"></textarea>\n"
@test render(tpl, Dict("dims"=>(1, 2))) == "\n<textarea cols=\"1\" ></textarea><textarea rows=\"2\"></textarea>\n"
@test render(tpl, Dict("dims"=>1:2)) == "\n<textarea cols=\"1\" ></textarea><textarea rows=\"2\"></textarea>\n"

## issue 128 global versus local
d = Dict(:two=>Dict(:x=>3), :x=>2)
tpl = mt"""
Expand All @@ -232,7 +233,7 @@ tpl = mt"""
{{/:one}}
"""
@test render(tpl, one=d) == "2\n"
@test render(tpl, one=d, x=1) == "1\n"
@test render(tpl, one=d, x=1) == "1\n"

## Issue #133 triple brace with }
tpl = raw"\includegraphics{<<{:filename}>>}"
Expand All @@ -243,5 +244,5 @@ tokens = Mustache.parse(tpl, ("<<",">>"))
x = 1
tpl = jmt"$(2x) by {{:a}}"
@test tpl(a=2) == "2 by 2"

end

2 comments on commit 844c3f4

@jverzani
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/48319

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v1.0.11 -m "<description of version>" 844c3f47b73c95f11b78958964260a6d02c9c7b1
git push origin v1.0.11

Please sign in to comment.