diff --git a/documentation/Lambdas.md b/documentation/Lambdas.md new file mode 100644 index 0000000..5eb3bff --- /dev/null +++ b/documentation/Lambdas.md @@ -0,0 +1,30 @@ +### Lambda Implementation + +The library doesn't provide a lambda implementation but it does provide something akin to the lambda feature. + +Add a `HBMustacheLambda` to the object you want to be rendered and it can be used in a similar way to lambdas are used in Mustache. When you create a section referencing the lambda the contents of the section are passed as a template along with the current object to the lamdba function. This is slightly different from the standard implementation where the unprocessed text is passed to the lambda. + +Given the object `person` defined below +```swift +struct Person { + let name: String + let wrapped: HBMustacheLambda +} +let person = Person( + name: "John", + wrapped: HBMustacheLambda { object, template in + return "\(template.render(object))" + } +) + +``` +and the following mustache template +```swift +let mustache = "{{#wrapped}}{{name}} is awesome.{{/wrapped}}" +let template = try HBMustacheTemplate(string: mustache) +``` +Then `template.render(person)` will output +``` +John is awesome. +``` +In this example the template constructed from the contents of the `wrapped` section of the mustache is passed to my `wrapped` function inside the `Person` type. diff --git a/documentation/Mustache Syntax.md b/documentation/Mustache Syntax.md new file mode 100644 index 0000000..02cbf39 --- /dev/null +++ b/documentation/Mustache Syntax.md @@ -0,0 +1,35 @@ +# Mustache Syntax + +Mustache is a "logic-less" templating engine. The core language has no flow control statements. Instead it has tags that can be replaced with a value, nothing or a series of values. Below we document all the standard tags + +## Context + +Mustache renders a template with a context stack. A context is a list of key/value pairs. These can be represented by either a `Dictionary` or the reflection information from `Mirror`. For example the following two objects will render in the same way +```swift +let object = ["name": "John Smith", "age": 68] +``` +```swift +struct Person { + let name: String + let age: Int +} +let object = Person(name: "John Smith", age: 68) +``` + +Initially the stack will consist of the root context object you want to render. When we enter a section tag we push the associated value onto the context stack and when we leave the section we pop that value back off the stack. + +## Tags + +All tags are surrounded by a double curly bracket `{{}}`. When a tag has a reference to a key the associated value will be searched for from the context at the top of the context stack. If the value cannot be found then the next context down will be searched and so on until either a value is found or we have reached the bottom of the stack. If no value is found the output for that value is `nil`. + +## Tag types + +- `{{key}}`: Render value associated with `key` as text. By default this is HTML escaped. A `nil` value is rendered as an empty string. +- `{{{name}}}`: Acts the same as `{{name}}` except the resultant text is not HTML escaped. You can also use `{{&name}}` to avoid HTML escaping. +- `{{#section}}`: Section render blocks either render text once or multiple times depending on the value of the key in the current context. A section begins with `{{#section}}` and end with `{{/section}}`. If the key represents a `Bool` value it will only render if it is true. If the key represents an `Optional` it will only render if the object is non-nil. If the key represents an `Array` it will then render the internals of the section multiple times, once for each element of the `Array`. Otherwise it will render with the selected value pushed onto the top of the context stack. +- `{{^section}}`: An inverted section does the opposite of a section. If the key represents a `Bool` value it will render if it is false. If the key represents an `Optional` it will render if it is `nil`. If the key represents a `Array` it will render if the `Array` is empty. +- `{{! comment }}`: This is a comment tag and is ignored. +- `{{> partial}}`: A partial tag renders another mustache file, with the current context stack. In Hummingbird Mustache partial tags only work for templates that are a part of a library and the tag is the name of the referenced file without the ".mustache" extension. +- `{{=<% %>=}}`: The set delimiter tag allows you to change from using the double curly brackets as tag delimiters. In the example the delimiters have been changed to `<% %>` but you can change them to whatever you like. + +You can find out more about the standard Mustache tags in the [Mustache Manual](https://mustache.github.io/mustache.5.html). diff --git a/documentation/Transforms.md b/documentation/Transforms.md new file mode 100644 index 0000000..06858cf --- /dev/null +++ b/documentation/Transforms.md @@ -0,0 +1,76 @@ +# Transforms + +Transforms are specific to this implementation of Mustache. They are similar to Lambdas but instead of generating rendered text they allow you to transform an object into another. Transforms are formatted as a function call inside a tag eg +``` +{{uppercase(string)}} +``` +They can be applied to variable, section and inverted section tags. If you apply them to a section or inverted section tag the transform name should be included in the end section tag as well eg +``` +{{#sorted(array)}}{{.}}{{/sorted(array)}} +``` +The library comes with a series of transforms for the Swift standard objects. +- String/Substring + - capitalized: Return string with first letter capitalized + - lowercase: Return lowercased version of string + - uppercase: Return uppercased version of string + - reversed: Reverse string +- Int/UInt/Int8/Int16... + - equalzero: Returns if equal to zero + - plusone: Add one to integer + - minusone: Subtract one from integer + - odd: return if integer is odd + - even: return if integer is even +- Array + - first: Return first element of array + - last: Return last element of array + - count: Return number of elements in array + - empty: Returns if array is empty + - reversed: Reverse array + - sorted: If the elements of the array are comparable sort them +- Dictionary + - count: Return number of elements in dictionary + - empty: Returns if dictionary is empty + - enumerated: Return dictionary as array of key, value pairs + - sorted: If the keys are comparable return as array of key, value pairs sorted by key + +If a transform is applied to an object that doesn't recognise it then `nil` is returned. + +## Sequence context transforms + +Sequence context transforms are transforms applied to the current position in the sequence. They are formatted as a function that takes no parameter eg +``` +{{#array}}{{.}}{{^last()}}, {{/last()}}{{/array}} +``` +This will render an array as a comma separated list. The inverted section of the `last()` transform ensures we don't add a comma after the last element. + +The following sequence context transforms are available +- first: Is this the first element of the sequence +- last: Is this the last element of the sequence +- index: Returns the index of the element within the sequence +- odd: Returns if the index of the element is odd +- even: Returns if the index of the element is even + +## Custom transforms + +You can add transforms to your own objects. Conform the object to `HBMustacheTransformable` and provide an implementation of the function `transform`. eg +```swift +struct Object: HBMustacheTransformable { + let either: Bool + let or: Bool + + func transform(_ name: String) -> Any? { + switch name { + case "eitherOr": + return either || or + default: + break + } + return nil + } +} +``` +When we render an instance of this object with `either` or `or` set to true using the following template it will render "Success". +``` +{{#eitherOr(object)}}Success{{/eitherOr(object)}} +``` +With this we have got around the fact it is not possible to do logical OR statements in Mustache.