Skip to content

Commit

Permalink
Issue 128 (#129)
Browse files Browse the repository at this point in the history
* fix bug

* Update src/context.jl

* add tests; bump version

Co-authored-by: Leo <cacate0129@gmail.com>
  • Loading branch information
jverzani and GiggleLiu authored Oct 22, 2020
1 parent 4ba61ce commit edd07f5
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 37 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.7"
version = "1.0.8"

[deps]
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
Expand Down
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,24 @@ a conditional check.

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.

### Non-eager finding of values

A view might have more than one variable bound to a symbol. The first one found is replaced in the template *unless* the variable is prefaced with `~`. This example illustrates:

```
d = Dict(:two=>Dict(:x=>3), :x=>2)
tpl = mt"""
{{#:one}}
{{#:two}}
{{~:x}}
{{/:two}}
{{/:one}}
"""
render(tpl, one=d) # "2\n"
render(tpl, one=d, x=1) # "1\n"
```
Were `{{:x}}` used, the value `3` would have been found within the dictionary `Dict(:x=>3)`; however, the presence of `{{~:x}}` is an instruction to keep looking up in the specified view to find other values, and use the last one found to substitute in. (This is hinted at in [this issue](https://github.com/janl/mustache.js/issues/399))

### Partials

Partials are used to include partial templates into a template.
Expand Down
89 changes: 54 additions & 35 deletions src/context.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ mutable struct Context
parent ## a context or nothing
_cache::Dict
end


function Context(view, parent=nothing)
d = Dict()
d["."] = isa(view, AnIndex) ? view.value : view
Expand Down Expand Up @@ -34,54 +36,65 @@ function lookup_dotted(ctx::Context, dotted)
ctx.view
end

## Lookup value by key in the context
# look up key in context
function lookup(ctx::Context, key)
if haskey(ctx._cache, key)
value = ctx._cache[key]
else
context = ctx
value = nothing
while value === nothing && context !== nothing
## does name have a .?
if occursin(r"\.", key)
value = lookup_dotted(context, key)
value !== nothing && break
return ctx._cache[key]
end

# use global lookup down
global_lookup = false
if startswith(key, "~")
global_lookup = true
key = replace(key, r"^~" => "")
end

# begin
context = ctx
value = nothing
while context !== nothing
value′ = nothing
## does name have a .?
if occursin(r"\.", key)

value′ = lookup_dotted(context, key)
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
# 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`.
## 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
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))
end
context = context.parent
else
value′ = lookup_in_view(context.view, stripWhitespace(key))
end

## cache
ctx._cache[key] = value

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

context = context.parent
end



## cache
ctx._cache[key] = value
return(value)
end

Expand Down Expand Up @@ -137,13 +150,19 @@ end

function _lookup_in_view(view::AbstractDict, key)
## is it a symbol?
k = startswith(key, ":") ? Symbol(key[2:end]) : key
get(view, k, nothing)

if occursin(r"^:", key)
key = Symbol(key[2:end])
end

get(view, key, nothing)
end

# support legacy use of `first` and `second` as variable names
# referring to piece, otherwise look up value
function _lookup_in_view(view::Pair, key)
## is it a symbol?
key == "first" && return view.first
key == "second" && return view.second
k = startswith(key, ":") ? Symbol(key[2:end]) : key
view.first == k ? view.second : nothing
end

function _lookup_in_view(view::NamedTuple, key)
Expand Down
25 changes: 24 additions & 1 deletion test/Mustache_test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -211,5 +211,28 @@ 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"
@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"""
{{#:one}}
{{#:two}}
{{:x}}
{{/:two}}
{{/:one}}
"""
@test render(tpl, one=d) == "3\n"

tpl = mt"""
{{#:one}}
{{#:two}}
{{~:x}}
{{/:two}}
{{/:one}}
"""
@test render(tpl, one=d) == "2\n"
@test render(tpl, one=d, x=1) == "1\n"


end

2 comments on commit edd07f5

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

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.8 -m "<description of version>" edd07f5c6ea7758546d6422538d31e217e5ab989
git push origin v1.0.8

Please sign in to comment.