Giter Club home page Giter Club logo

nim-mustache's Introduction

Nim-mustache

Mustache in Nim.

Nim-mustache is a Nim implementation of Mustache. Mustache is a logic-less templating system inspired by ctemplate and et. Mustache "emphasizes separating logic from presentation: it is impossible to embed application logic in this template language." Nim-mustache is a full implementation of mustache spec v1.2.1.

Build Status

Status

Nim-mustache is stable now. Welcome to contribute, comment, and report issues. ❤️

Features

  • ✨ Support Static Text.
  • ✨ Support {{Variables}}.
  • ✨ Support {{# Section }} {{/ Section }}.
  • ✨ Support {{^ InvertedSection }} {{/ InvertedSection }}.
  • ✨ Support {{! Comments }}.
  • ✨ Support {{=<% %>=}} <% SetDelimiter %>.
  • ✨ Support {{> Partial }}.
  • ✨ Support Inheritance {{< Parent}}{{$ foo}}Substitute{{/ foo}}{{/ Parent}}.
  • ✨ Support Lambda.
  • ✨ Passed all mustache specs.

Getting Started

Nim-mustache requires Nim >= 1.0.0.

$ nimble install mustache

Usage

# Step 1.
import mustache, tables

# Step 2.
var c = newContext()
c["i"] = 1
c["f"] = 1.0
c["s"] = "hello world"
c["a"] = @[{"k": "v"}.toTable]
c["t"] = {"k": "v"}.toTable
c["l"] = proc(s: string, c: Context): string = "<b>" & s.render(c) & "</b>"

# Step 3.
let s = """
{{i}} {{f}} {{s}}
{{#a}}
  {{k}}
{{/a}}

{{#t}}
  {{k}}
{{/t}}

{{#l}}
  {{s}}
{{/l}}
"""
echo(s.render(c))

Advanced Usage

Set Arbitrary Objects in Context

Consider you have your own object Stock.

type Stock = object
  name*: string
  price*: int

let stock = Stock(name: "NIM", price: 1000)

It would be convenient if you can set it to context:

let c = newContext()
c["stock"] = stock
let s = "{{#stock}}{{name}}: {{price}}{{/stock}}"
echo(s.render(c))

The trick is to define a castValue proc for your type. Below is an example of how to define a castValue for Stock.

proc castValue(value: Stock): Value =
  let newValue = new(Table[string, Value])
  result = Value(kind: vkTable, vTable: newValue)
  newValue["name"] = value.name.castValue
  newValue["price"] = value.price.castValue

Change Partials Searching Dir

By default, partials are located in cwd.

$ ls
main.mustache    partial.mustache
$ cat main.mustache
{{> partial}}

But what if mustache files are in other locations?

$ pwd
/tmp/path/to
$ ls -R
main.mustache
templates/partial.mustache

In this case, You can specify other searching dirs:

let c = newContext(searchDirs=@["/path/to/templates", "./"])
let s = readFile("main.mustache")
echo(s.render(c))

Read Partials From Memory

You can also read mustache partials from an in-memory table:

import tables
let partials = {
  "something": "This is something"
}.toTable()
let c = newContext(partials=partials)
let s = "something: {{> something}}"
echo(s.render(c))

Read Partials From Multiple Sources

You can read mustache partials from both directory and in-memory table in different orders. The first source that have the partial key wins.

import tables

let partials1 = {
  "something": "This is something"
}.toTable()

let partials2 = {
  "something": "This is something else"
}.toTable()
let c = newContext()
c.searchTable(partials1)
c.searchDirs(["/path/to/partials"])
c.searchTable(partials2)
let s = "result: {{> something}}"
echo(s.render(c)) # result: This is something

Use Mustache With Jester

It's recommended using Mustache with Jester, a sinatra-like web framework for Nim, when writing a web application.

Imagine you have a template file named hello.mustache. Rendering the template can be as simple as calling it in a route.

import jester
import mustache

routes:
  get "/hello/@name":
    let c = newContext()
    c["name"] = @"name"
    resp "{{ >hello }}".render(c)

In hello.mustache, it's just mustache template content.

<html>
  <body>
    <h1>Hello, {{ name }}</h1>
  </body>
</html>

If you have a dedicated directory template directory, you can specify it as:

let c = newContext(searchDirs = @["/path/to/templates"])

Develop

Build the binary.

$ nimble build

Run test cases.

$ nimble test

Changelog

  • v0.4.3, 9 Aug 2021, export searchDirs & searchTable.
  • v0.4.2, 10 Jun 2021, support casting types float32, float64, int8, int16, int32, int64.
  • v0.4.1, 16 May 2021, support mustache inheritance tag.
  • v0.4.0, 8 May 2021, build on github action && support mustache spec v1.2.1.
  • v0.3.3, 7 May 2021, add optional parameter values for newContext & support JsonNode as a value.
  • v0.3.2, 25 Jan 2021, support numeric indexing from list using dot syntax.
  • v0.3.1, 24 Jan 2021, fix version issue.
  • v0.3.0, 2 Dec 2020, support loading partials from multiple in-memory tables/directories.
  • v0.2.2, 1 Dec 2020, support in-memory table for partial resolving.
  • v0.2.1, 20 Sep 2019, fix locks level warnings.
  • v0.2.0, 20 Sep 2019, support setting arbitrary objects in context.
  • v0.1.0, 19 Sep 2019, initial release.

Alternatives

  • moustachu. Moustachu doesn't implement some mustache features, such as lambda, set delimiters, while Nim-mustache supports all mustache features.

References

  • spec, (see also a document with the results of running the specs using nim-mustache)
  • syntax, (see also a document reproducing the syntax using nim-mustache)

nim-mustache's People

Contributors

iffy avatar jdf-id-au avatar pietroppeter avatar soasme avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

nim-mustache's Issues

Initialize the context from JsonNode( kind: JObject ?)

Hi

My internal state is kept as a JsonNode structure with the root as a JObject JsonNode. I'm trying to do the minimal amount of magic on the way to transferring the state into nim-mustache and thought something like this should work:

var context = new_context(
  searchDirs = plugin.search_dirs(),
  values = my_context
)

Where my_context is my root object.

I have also tried several variations such as:

var context = new_context(
  searchDirs = plugin.search_dirs(),
  values = castValue(my_context)
)

or:

var context = new_context(
  searchDirs = plugin.search_dirs(),
  values = Table[string, Value](castValue(my_context))
)

but to no avail ...

I'm sure it is doable, but the compiler and I can't agree on how to annotate the type in question I guess 🙂 Could you tell me how to do it correctly @soasme ?

ENH: support int32 and float32

Hi, I am attempting to add seq[int32] and seq[float32] to a context.
I have made this work by doing this:

import mustache
import mustachepkg/values

proc castValue*(values:seq[float32]): Value =
  var vs: seq[Value]
  for v in values:
    vs.add(v.float.castValue)
  Value(kind: vkSeq, vSeq: vs)
  
var ctx = newContext()
ctx["a"] = @[23'f32, 33'f32]

but it would be nice if this were supported in the library.
thanks for the nice package.

Explicitly specify partial loaders

Issue
I want to have different types of loaders, and am able to organize the order of these loaders.
Each loader provides a way to resolve templates.

Example

ctx = newContext()
ctx.searchDir("./")
ctx.searchTable(table)

Or in another order

ctx = newContext()
ctx.searchTable(table1)
ctx.searchDir("./")
ctx.searchTable(table2)

Having problem in sending data

Row = seq[string]

I have a seq[Row] variable that I want to send to the mustache template but I don't know how. Can you show me how to send the data to the mustache template and how to show the data in a list.
Thank you for your help as I have just started to use the mustache library.

Support standalone indentation.

  - name: Standalone Indentation
    desc: Each line of the partial should be indented before rendering.
    data: { content: "<\n->" }
    template: |
      \
       {{>partial}}
      /
    partials:
      partial: |
        |
        {{{content}}}
        |
    expected: |
      \
       |
       <
      ->
       |
      /

some failures against latest specs

I made a document that shows the output of nim-mustache against the specs and I used the latest specs. Here is the document which shows some failures: https://pietroppeter.github.io/nblog/drafts/mustache_specs.html

The failures are due to changes in the latest releases (v1.2.0 of 9 days ago and v1.2.1 of 4 days ago) that introduced additional tests for existing features.

Here are the name of failing tests:

  • from interpolation.json:
    • Dotted Names - Context Precedence
    • Implicit Iterators - Basic Interpolation
    • Implicit Iterators - HTML Escaping
    • Implicit Iterators - Triple Mustache
    • Implicit Iterators - Ampersand
    • Implicit Iterators - Basic Integer Interpolation
  • from sections.json:
    • Variable test
    • Deeply Nested Contexts

The tests with names starting with Implicit Iterators are failures in the sense that the spec uses a data json that is not an object. The Context of nim-mustache must have as values field a table. Probably a fix would require to change the type of values to be a Value itself (and rename it to value)?

For the other 3 failing tests I report here for convenience the data with expected output and output from nim-mustache.

❌ Dotted Names - Context Precedence

Dotted names should be resolved against former resolutions.

Template:

{{#a}}{{b.c}}{{/a}}

Data:

{"a":{"b":{}},"b":{"c":"ERROR"}}

Expected:

Output:

ERROR

❌ Variable test

Non-false sections have their value at the top of context, accessible as {{.}} or through the parent context. This gives a simple way to display content conditionally if a variable exists.

Template:

"{{#foo}}{{.}} is {{foo}}{{/foo}}"

Data:

{"foo":"bar"}

Expected:

"bar is bar"

Output:

" is bar"

❌ Deeply Nested Contexts

All elements on the context stack should be accessible.

Template:

{{#a}}
{{one}}
{{#b}}
{{one}}{{two}}{{one}}
{{#c}}
{{one}}{{two}}{{three}}{{two}}{{one}}
{{#d}}
{{one}}{{two}}{{three}}{{four}}{{three}}{{two}}{{one}}
{{#five}}
{{one}}{{two}}{{three}}{{four}}{{five}}{{four}}{{three}}{{two}}{{one}}
{{one}}{{two}}{{three}}{{four}}{{.}}6{{.}}{{four}}{{three}}{{two}}{{one}}
{{one}}{{two}}{{three}}{{four}}{{five}}{{four}}{{three}}{{two}}{{one}}
{{/five}}
{{one}}{{two}}{{three}}{{four}}{{three}}{{two}}{{one}}
{{/d}}
{{one}}{{two}}{{three}}{{two}}{{one}}
{{/c}}
{{one}}{{two}}{{one}}
{{/b}}
{{one}}
{{/a}}

Data:

{"a":{"one":1},"b":{"two":2},"c":{"three":3,"d":{"four":4,"five":5}}}

Expected:

1
121
12321
1234321
123454321
12345654321
123454321
1234321
12321
121
1

Output:

1
121
12321
1234321
123454321
123464321
123454321
1234321
12321
121
1

unexpected whitespace when using lambda in a section

I was reproducing mustache man (5)
with nimib (see resulting document)
and I ran into this unexpected behaviour when using lambdas in a section:

import mustache

var tmpl = """
{{#wrapped}}
  {{name}} is awesome.
{{/wrapped}}"""
var context = newContext()
context["name"] = "Willy"
context["wrapped"] = proc(s: string, c: Context): string = "<b>" & s.render(c) & "</b>"
echo tmpl.render(context)
#[
  output:
<b>  Willy is awesome.
</b>  
  expected:
<b>Willy is awesome.</b>
]#

There is not test similar to this in the specs so probably it is not necessarily something that has a definite behaviour.

While looking in the specs I saw that there is a new version of specs with a new optional feature: inheritance, see this PR: mustache/spec#125

Also, I see that there is a ongoing PR to update musache man (5) to the most recent specs: mustache/mustache#266

Looking at this PR I found out that the lambda functionality has a simplified version: mustache/mustache#266

I think I will try to download the latest version of specs and produce an automatically generated nimib document running all tests and showing outputs.

Maybe we could add the above document (mustache man (5) for nim-mustache) and the one with the specs (when I do it) as link in the readme (in the references sections?)?

static context

I really like using nim-mustache with jester.

In the docs the jester example shown works really well for dev. But now I want to not read the context for each request.

I've done something like this with the idea to load everything to memory:

when defined(release):
  const account = staticRead"./tmpl/account.mustache"
  const addexercise = staticRead"./tmpl/stuff.mustache"
  const partials = {
    "account": account,
    "stuff": addexercise
  }.toTable()

# get "/":
  when not defined(release):
      let c = newContext(searchDirs = @["./tmpl"])
    else:
      let c = newContext(partials=partials)

Just wondering if this has come up and others are doing to optimize?

ENH: support float format

Given something like:

let ctx = newContext()
ctx["name"] = 1.234903242343234534
echo ctx["name"].castStr

I'd like to be able to specify to only output, for example 1.235. I tried this:

proc `$`*(f:float): string =
  return &"{f:.2f}"

but that didn't change the template output for me.

Access array element with index

Accessing an array element with its index seems to be unsupported.

import mustache
  
var c = newContext()
c["a"] = @[1, 2, 3]

echo "{{a.1}}".render(c)

It should output "2"

(for reference janl/mustache.js#158)

Also, "{{a}}" outputs "@[]" which does seem the right thing (I don't know if it is expected by the specification).

different formatting (missing newlines) when using partials

the following example is taken from mustache documentation where it says:

For example, this template and partial: [...] Can be thought of as a single, expanded template: [...]

As example below shows, currently we would have different output (the version using the partial is missing newlines).

I cannot find a reference in mustache documentation to rules about indentation and newlines but there is a test in specs that seems to cover similar stuff (partials.yml). I see that in the tests here there is the corresponding partials.json and I checked that it tests correctly. So maybe this is a different test case not covered by specs?

Or maybe I am making some silly mistake and this is not an issue at all...

Incidentally I found out that there is a test failing (in test_parser a newline is lost, see below for details).

Note that I am running all of this in windows (nim 1.4)

example

base.mustache:

<h2>Names</h2>
{{#names}}
  {{> user}}
{{/names}}

user.mustache:

<strong>{{name}}</strong>

single.mustache:

<h2>Names</h2>
{{#names}}
  <strong>{{name}}</strong>
{{/names}}

example.nim:

import mustache, json

var c = newContext()
c["names"] = %* [{"name": "soasme"}, {"name": "iffy"}, {"name": "pietroppeter"}]

echo "---base:\n", "{{> base}}".render(c), "\n---"
echo "---single:\n", "{{> single}}".render(c), "\n---"

output:

nim r example:

---base:
<h2>Names</h2>
  <strong>soasme</strong>  <strong>iffy</strong>  <strong>pietroppeter</strong>
---
---single:
<h2>Names</h2>
  <strong>soasme</strong>
  <strong>iffy</strong>
  <strong>pietroppeter</strong>

---

expected output

I would expect the formatting to be the same.

failing test

nim r tests\test_parser

[...]
mustache\tests\test_parser.nim(140, 20): Check failed: s.render(c) == "Shown\n"
s.render(c) was Shown

[FAILED] render partial

nim version

nim --version

Nim Compiler Version 1.4.0 [Windows: amd64]
Compiled at 2020-10-16
Copyright (c) 2006-2020 by Andreas Rumpf

active boot switches: -d:release

Support Dotted Names

/Users/soasme/space/github/mustache/tests/test_spec.nim(18, 24): Check failed: tpl.render(c) == expected
tpl.render(c) was "" == "Joe"
expected was "Joe" == "Joe"
[FAILED] Dotted Names - Basic Interpolation - Dotted names should be considered a form of shorthand for sections.
/Users/soasme/space/github/mustache/tests/test_spec.nim(18, 24): Check failed: tpl.render(c) == expected
tpl.render(c) was "" == "Joe"
expected was "Joe" == "Joe"
[FAILED] Dotted Names - Triple Mustache Interpolation - Dotted names should be considered a form of shorthand for sections.
/Users/soasme/space/github/mustache/tests/test_spec.nim(18, 24): Check failed: tpl.render(c) == expected
tpl.render(c) was "" == "Joe"
expected was "Joe" == "Joe"
[FAILED] Dotted Names - Ampersand Interpolation - Dotted names should be considered a form of shorthand for sections.
/Users/soasme/space/github/mustache/tests/test_spec.nim(18, 24): Check failed: tpl.render(c) == expected
tpl.render(c) was "" == "Phil"
expected was "Phil" == "Phil"
[FAILED] Dotted Names - Arbitrary Depth - Dotted names should be functional to any level of nesting.
/Users/soasme/space/github/mustache/tests/test_spec.nim(18, 24): Check failed: tpl.render(c) == expected
tpl.render(c) was "" == "Phil"
expected was "Phil" == "Phil"
[FAILED] Dotted Names - Initial Resolution - The first part of a dotted name should resolve as any other name.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.