Skip to content

Commit

Permalink
Issue 123 (#126)
Browse files Browse the repository at this point in the history
* close #125, address issue #123

* allow tuple or abstractvector

* add a new test

* add docs

Co-authored-by: GiggleLiu <cacate0129@gmail.com>
  • Loading branch information
jverzani and GiggleLiu authored Oct 19, 2020
1 parent be94446 commit 4ba61ce
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 21 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,10 @@ implemented for now -- does not allow for iteration. That is
constructs like `{{#.[1]}}` don't introduce iteration, but only offer
a conditional check.

### Conditional checking without iteration

The section tag, `#`, check for existence; pushes the object into the view; and then iterates over the object. For cases where iteration is not desirable; the tag type `@` can be used.

### Partials

Partials are used to include partial templates into a template.
Expand Down
21 changes: 12 additions & 9 deletions src/context.jl
Original file line number Diff line number Diff line change
Expand Up @@ -45,28 +45,31 @@ function lookup(ctx::Context, key)
## does name have a .?
if occursin(r"\.", key)
value = lookup_dotted(context, key)
value != nothing && break
value !== nothing && break

## do something with "."
## we use .[ind] to refer to value in parent of given index;
m = match(r"^\.\[(.*)\]$", key)
m == nothing && break
m === nothing && break
# m == nothing && error("Not implemented. Can use Composite Kinds in the view.")

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`.
if isa(vals, Vector) # supports getindex(v, i)?
if idx == "end"
value = AnIndex(-1, vals[end])
else
ind = Base.parse(Int, idx)
value = AnIndex(ind, vals[ind])
end
## This is for an iterable; rather than
## limit the type, we let non-iterables error.
if true # isa(vals, AbstractVector) || isa(vals, Tuple) # supports getindex(v, i)?
if idx == "end"
value = AnIndex(-1, vals[end])
else
ind = Base.parse(Int, idx)
value = AnIndex(ind, string(vals[ind]))
end
break
end
break
else
## strip leading, trailing whitespace in key
value = lookup_in_view(context.view, stripWhitespace(key))
Expand Down
29 changes: 24 additions & 5 deletions src/tokens.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ collector::Vector
SectionToken(_type, value, ltag, rtag) = new(_type, value, ltag, rtag, Any[])
end

mutable struct BooleanToken <: Token
_type::String
value::String
ltag::String
rtag::String
collector::Vector
BooleanToken(_type, value, ltag, rtag) = new(_type, value, ltag, rtag, Any[])
end

mutable struct MustacheTokens
tokens::Vector{Token}
end
Expand Down Expand Up @@ -53,7 +62,7 @@ end



mutable struct AnIndex
struct AnIndex
ind::Int
value::String
end
Expand Down Expand Up @@ -258,7 +267,7 @@ function make_tokens(template, tags)
# what kinda tag did we get?
token_value = stripWhitespace(token_value)
_type = token_value[1:1]
if _type in ("#", "^", "/", ">", "<", "!", "|", "=", "{", "&")
if _type in ("#", "^", "/", ">", "<", "!", "|", "=", "{", "&", "@")
# type is first, we peel it off, also strip trailing = and },
# as necessary

Expand Down Expand Up @@ -297,6 +306,10 @@ function make_tokens(template, tags)

tag_token = SectionToken(_type, token_value, ltag, rtag)

elseif _type in ("@",)

tag_token = BooleanToken(_type, token_value, ltag, rtag)

else

tag_token = TagToken(_type, token_value, ltag, rtag)
Expand Down Expand Up @@ -339,7 +352,7 @@ function make_tokens(template, tags)
push!(tokens, tag_token)

# account for matching/nested sections
if _type == "#" || _type == "^" || _type == "|"
if _type == "#" || _type == "^" || _type == "|" || _type == "@"
push!(sections, tag_token)
elseif _type == "/"
## section nestinng
Expand Down Expand Up @@ -379,7 +392,7 @@ function nestTokens(tokens)
## a {{#name}}...{{/name}} will iterate over name
## a {{^name}}...{{/name}} does ... if we have no name
## start nesting
if token._type == "^" || token._type == "#" || token._type == "|"
if token._type == "^" || token._type == "#" || token._type == "|" || token._type == "@"
push!(sections, token)
push!(collector, token)
token.collector = Array{Any}(undef, 0)
Expand Down Expand Up @@ -497,7 +510,7 @@ function _renderTokensByValue(value::AbstractDict, io, token, writer, context, t
renderTokens(io, token.collector, writer, ctx_push(context, value), template, args...)
end

function _renderTokensByValue(value::AbstractArray, io, token, writer, context, template, args...)
function _renderTokensByValue(value::Union{AbstractArray, Tuple}, io, token, writer, context, template, args...)
inverted = token._type == "^"
if (inverted && falsy(value))
renderTokens(io, token.collector, writer, ctx_push(context, ""), template, args...)
Expand Down Expand Up @@ -690,6 +703,12 @@ function renderTokens(io, tokens, writer, context, template, idx=(0,0))
print(io, val)
end

elseif token._type == "@"
value = lookup(context, tokenValue)
if !falsy(value)
print(io, render(MustacheTokens(token.collector), value))
end

elseif token._type == "name"
value = lookup(context, tokenValue)
if !falsy(value)
Expand Down
28 changes: 21 additions & 7 deletions test/Mustache_test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ mutable struct ThrowAway
y
end

struct Issue123
range
end

@test render(tpl, Main) == "a:ex b:why"
@test render(tpl, d) == "a:ex b:why"
@test render(tpl, ThrowAway(x,y)) == "a:ex b:why"
Expand Down Expand Up @@ -191,11 +195,21 @@ tpl2 = mt"""
"""
@test Mustache.render(tpl, vec = ["A1", "B2", "C3"]) == "<bold>A1</bold>B2 C3 \n"

end

@testset "#124 regression test" begin
tpl = """{{^dims}}<input type="text" value="">{{/dims}}
{{#dims}}<textarea {{#.[1]}}cols="{{.}}"{{/.[1]}} {{#.[2]}}rows="{{.}}"{{/.[2]}}></textarea>{{/dims}}"""
@test render(tpl, Dict("dims"=>missing)) == "<input type=\"text\" value=\"\">\n"
@test render(tpl, Dict("dims"=>["1", "2"])) == "\n<textarea cols=\"1\" ></textarea><textarea rows=\"2\"></textarea>"
##
tpl = 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}}
{{#dims}}<textarea {{#.[1]}}cols="{{.}}"{{/.[1]}} {{#.[2]}}rows="{{.}}"{{/.[2]}}></textarea>{{/dims}}
"""
@test render(tpl, Dict("dims"=>missing)) == "<input type=\"text\" value=\"\">\n\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"
@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"
end

2 comments on commit 4ba61ce

@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/23258

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.7 -m "<description of version>" 4ba61ce761d9429847efc6ec547e3138963f084e
git push origin v1.0.7

Please sign in to comment.