Coder Social home page Coder Social logo

muttonchop's Introduction

MuttonChop

Swift Platform License Codebeat

Mutton Chops are synonymous to Sideburns. Sideburns are kind of similar to Mustaches.

Mustache templates in Swift. 100% spec compliant. Linux and macOS supported.

Table of Contents

Features

  • Conforms entirely to the official Mustache specification.
  • Compiles its templates, meaning that it only parses them once. This makes it very fast.
  • Supports template inheritance, conforming to the optional specification. Big thanks to @groue for providing the inheritance algorithm.
  • Has template collections to make work with multiple templates convenient
  • More coming soon! Do keep your eyes open.

Installation

// Package.swift
.Package(url: "https://github.com/Danappelxx/MuttonChop.git", majorVersion: 0, minor: 5),

MuttonChop works best with Swift 5.4. Compatibility with previous/future snapshots/releases is not guaranteed.

Usage

Basic template usage

To compile a template, simply call the Template initializer with your template string. How you get that is up to you.

let template = try Template("Hello, {{name}}!")

Before we can render your template, we first have to create the context.

let context: Context = [
    "name": "Dan"
]

Notice how the type of context is Context. Context is a JSON-like tree structure which conforms to Codable, so you can either build it by hand or from an external source.

We can then render the template with the context like such:

let rendered = template.render(with: context)
print(rendered) // -> Hello, Dan!

Template Collections

Template collections are useful when there are multiple templates which are meant to work together (for example, views in a web application).

Creation

Template collections can be created in several ways:

// label and load templates manually
let collection = TemplateCollection(templates: [
    "one": try Template(...),
    "two": try Template(...)
])
// search directory for .mustache files
let collection = try TemplateCollection(directory: "/path/to/Views")
// search directory for .mustache and .html files
let collection = try TemplateCollection(
    directory: "/path/to/Views",
    fileExtensions: ["mustache", "html"]
)

Fetching

Templates can be extracted from collections in one of two ways:

collection.templates["one"] // Template?
try collection.get(template: "one") // Template

Rendering

Templates rendered in collections have the rest of the templates (including the template itself) registered as partials. Templates can be rendered like so:

try collection.render(template: "one", with: ["name": "Dan"])

Mustache Language

Below is a comprehensive reference of the Mustache language features supported by Muttonchop. Examples are included with in each section.

Tags

Tags are denoted by two opening braces {{, followed by some content, delimited by two closing braces }}. The first non-whitespace character inside the tag denotes the type of the tag.

The format for this section is: template, context, result. The syntax for doing all three is shown in the section above.

Variables (Interpolation)

Interpolation tags do not have a type-denoting character. The tag is replaced with the result of its content looked up in the context stack. If the context stack does not contain its value, the tag is simply ignored.

The variable dot {{.}} is special, displaying the current topmost value of the context stack.

{{ greeting }}, {{ location }}!
let context: Context = [
    "greeting": "Hello",
    "location": "world"
]
Hello, world!

Sections

Section tags start with an opening tag ({{# section }}) and end with a closing tag ({{/ section }}). Everything between the open and closing tags is the content.

The content is only rendered if the value inside the opening/closing tags is truthy. If the value is an array, the array is iterated and the content of the section is rendered for every value. If the value is a dictionary, it is added to the context stack.

{{# person }}
  {{ name }} is {{ age }} years old.
  Some numbers he knows are{{# numbers }} {{.}}{{/ numbers }}.
{{/ person }}
let context: Context = [
    "person": [
        "name": "Dan",
        "age": 16,
        "numbers": [1, 2, 3, 4, 5]
    ]
]
Dan is 16 years old.
Some numbers he knows are 1 2 3 4 5.

Inverted Sections

An inverted section begins with an opening tag ({{^ section }}) and end with a closing tag ({{/ section }}). Everything between the tags is the content.

The content is only rendered if the value inside the section tag is falsy. A falsy value is either one that is not found in the context stack, or the boolean value false.

{{^ person }}
  {{ message }}
{{/ person }}
let context: Context = [
    "message": "There is no person!"
]
There is no person!

Comments

Comment tags ({{! Some comment about something }}) are not rendered, and are very useful for documenting your templates.

Partials

A partial tag ({{> partial }}) inserts the contents of the template with the name of the partial in place of the tag. The partial has access to the same context stack.

Partials are passed to the template in the form of [String:Template] when rendering.

let person = try Template(...)
let people = try Template(...)
let partials = [
    "person": person,
    "people": people, // recursive partials are supported!
]
let rendered = people.render(with: context, partials: partials)
{{! person.mustache }}
{{ name }} is {{ age }} years old.
Some numbers he knows are{{# numbers }} {{.}}{{/ numbers }}.

{{! people.mustache }}
{{# people }}
{{> person }}
{{/ people }}
let context: Context = [
    "people": [
        [
            "name": "Dan",
            "age": 16,
            "numbers": [1, 2, 3, 4, 5]
        ],
        [
            "name": "Kyle",
            "age": 16,
            "numbers": [6, 7, 8, 9, 10]
        ]
    ]
]
Dan is 16 years old.
Some numbers he knows are 1 2 3 4 5.
Kyle is 16 years old.
Some numbers he knows are 6 7 8 9 10.

Inheritance

MuttonChop supports template inheritance. This is done through a combination of two tags: the partial override tag ({{< partial }}{{/ partial }}) and the block tag ({{$ block }}{{/ block }}).

The partial override tag is similar to normal partial tag, except that blocks inside the contents override the tags inside the included partial.

Blocks simply render their content. The only thing that makes them special is that they can override and be overriden by other blocks.

The code to render them is exactly the same as to render partials - put the templates in a dictionary and pass it to the render call.

{{! layout.mustache }}
The title of the page is: {{$ title }}Default title{{/ title }}!
{{$ content}}
Looks like somebody forgot to add meaningful content!
{{/ content}}

{{! page.mustache }}
{{< layout }}
    {{$ title }}{{ number }} reasons NOT to drink bleach! Number {{ special-number }} will blow your mind!{{/ title }}
{{/ layout }}
let context: Context [
    "number": 11,
    "special-number": 9
]
The title of the page is: 11 reasons NOT to drink bleach! Number 9 will blow your mind!!
Looks like somebody forgot to add meaningful content!

Tips

Parsing is slow and unoptimized. Rendering is fast and optimized. Take advantage of the fact that MuttonChop compiles the templates and only create a single instance of a template (which you can render many times).

Contributing

Any and all help is very welcome, I promise I won't bite. Contributing is more than just code! If you have any ideas at all, please do make an issue and/or pull request.

License

MIT - more information is in the LICENSE file.

muttonchop's People

Contributors

danappelxx avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

tomohisa ratranqu

muttonchop's Issues

Pass the final test case!

Test case.

This one is particularly tricky because it requires proper indentation of partials before they are rendered. This is difficult because partials are already compiled (but not rendered) which means that splitting on each newline and indenting is not trivial.

Use bytes instead of Characters

Strings are infamously slow in Swift. Switching to raw bytes (UInt8) should be fairly trivial while boosting the performance significantly.

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.