From 02169c3483bc32640228664705e809560779449a Mon Sep 17 00:00:00 2001 From: AliSoftware Date: Sun, 30 Apr 2017 14:02:25 +0200 Subject: [PATCH] Tags & Filters documentation (#12) --- CHANGELOG.md | 3 ++ Documentation/filters-numbers.md | 33 +++++++++++++ Documentation/filters-strings.md | 79 ++++++++++++++++++++++++++++++++ Documentation/tag-call.md | 65 ++++++++++++++++++++++++++ Documentation/tag-macro.md | 45 ++++++++++++++++++ Documentation/tag-map.md | 51 +++++++++++++++++++++ Documentation/tag-set.md | 53 +++++++++++++++++++++ README.md | 36 +++++++-------- 8 files changed, 346 insertions(+), 19 deletions(-) create mode 100644 Documentation/filters-numbers.md create mode 100644 Documentation/filters-strings.md create mode 100644 Documentation/tag-call.md create mode 100644 Documentation/tag-macro.md create mode 100644 Documentation/tag-map.md create mode 100644 Documentation/tag-set.md diff --git a/CHANGELOG.md b/CHANGELOG.md index e110f8ff..d930fba1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,9 @@ _None_ * Update Stencil to 0.9.0 and update project to Xcode 8.3. [Diogo Tridapalli](https://github.com.diogot) [#32](https://github.com/SwiftGen/StencilSwiftKit/pull/32) +* Added documentation for tags and filters. + [David Jennes](https://github.com/djbe) + [#12](https://github.com/SwiftGen/StencilSwiftKit/pull/12) ## 1.0.1 diff --git a/Documentation/filters-numbers.md b/Documentation/filters-numbers.md new file mode 100644 index 00000000..1213f0de --- /dev/null +++ b/Documentation/filters-numbers.md @@ -0,0 +1,33 @@ +# Filters + +This is a list of filters that are added by StencilSwiftKit on top of the filters already provided by Stencil (which you can [find here](http://stencil.fuller.li/en/latest/builtins.html#built-in-filters)). + +## Filter: `int255toFloat` + +Accepts an integer and divides it by 255, resulting in a floating point number (usually) between 0.0 and 1.0. + +| Input | Output | +|-------|---------| +| 240 | 0.9412 | +| 128 | 0.5019 | + +## Filter: "hexToInt" + +Accepts a string with a number in hexadecimal format, and converts it into an integer number. Note that the string should NOT be prefixed with `0x`. + +| Input | Output | +|----------|-----------| +| FC | 252 | +| fcFf | 64767 | +| 01020304 | 16909060 | +| 0x1234 | nil / "" | + +## Filter: "percent" + +Accepts a floating point number and multiplies it by 100. The result is truncated into an integer, converted into a string and appended with the `%` character. + +| Input | Output | +|--------|---------| +| 0.23 | 23% | +| 0.779 | 77% | +| 1.234 | 123% | diff --git a/Documentation/filters-strings.md b/Documentation/filters-strings.md new file mode 100644 index 00000000..14b5b58b --- /dev/null +++ b/Documentation/filters-strings.md @@ -0,0 +1,79 @@ +# Filters + +This is a list of filters that are added by StencilSwiftKit on top of the filters already provided by Stencil (which you can [find here](http://stencil.fuller.li/en/latest/builtins.html#built-in-filters)). + +## Filter: `escapeReservedKeywords` + +Checks if the given string matches a reserved Swift keyword. If it does, wrap the string in escape characters (backticks). + +| Input | Output | +|-------|---------------------------------| +| hello | hello | +| self | \`self\` | +| Any | \`Any\` | + +## Filter: `lowerFirstWord` + +Transforms an arbitrary string so that only the first "word" is lowercased. + +- If the string starts with only one uppercase character, lowercase that first character. +- If the string starts with multiple uppercase character, lowercase those first characters up to the one before the last uppercase one, but only if the last one is followed by a lowercase character. This allows to support strings beginnng with an acronym, like `URL`. + +| Input | Output | +|--------------|--------------------------| +| PeoplePicker | peoplePicker | +| URLChooser | urlChooser | + +## Filter: `snakeToCamelCase` + +Transforms a string in "snake_case" format into one in "camelCase" format, following the steps below: + +- Separate the string in components using the `_` as separator. +- For each component, uppercase the first character and do not touch the other characters in the component. +- Join the components again into one string. + +If the whole starting "snake_case" string only contained uppercase characters, then each component will be capitalized: uppercase the first character and lowercase the other characters. + +| Input | Output | +|--------------|--------------------------| +| snake_case | SnakeCase | +| snAke_case | SnAkeCase | +| SNAKE_CASE | SnakeCase | +| __snake_case | __SnakeCase | + +This filter accepts a parameter (boolean, default `false`) that controls the prefixing behaviour. If set to `true`, it will trim empty components from the beginning of the string + +| Input | Output | +|--------------|--------------------------| +| snake_case | SnakeCase | +| snAke_case | SnAkeCase | +| SNAKE_CASE | SnakeCase | +| __snake_case | SnakeCase | + +## Filter: `swiftIdentifier` + +Transforms an arbitrary string into a valid Swift identifier (using only valid characters for a Swift identifier as defined in the Swift language reference). It will apply the following rules: + +- Uppercase the first character. +- Prefix with an underscore if the first character is a number. +- Replace invalid characters by an underscore (`_`). + +The list of allowed characters can be found here: +https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html + +| Input | Output | +|----------|------------------------------| +| hello | Hello | +| 42hello | _42hello | +| some$URL | Some_URL | + +## Filter: `titlecase` + +Simply uppercases the first character, leaving the other characters untouched. + +Note that even if very similar, this filter differs from the `capitalized` filter, which uppercases the first character but also lowercases the remaining characters. + +| Input | Output | +|---------------|-------------------------| +| hello | Hello | +| peopleChooser | PeopleChooser | diff --git a/Documentation/tag-call.md b/Documentation/tag-call.md new file mode 100644 index 00000000..b2f71632 --- /dev/null +++ b/Documentation/tag-call.md @@ -0,0 +1,65 @@ +# Tag: "Call" + +This tag _calls_ a macro previously defined using the [macro tag](tag-macro.md). + +## Node Information + +| Name | Description | +|-----------|-----------------------------------------------------------------| +| Tag Name | `call` | +| End Tag | N/A | +| Rendering | Immediately; output is the rendering of the called macro block | + +| Parameter | Description | +|------------|-----------------------------------------------------------| +| Block Name | The name of the block you want to invoke. | +| ... | A variable list of arguments, must match block definition | + +_Example:_ `{% call myBlock "Dave" %}` + +## When to use it + +This node only works together with the `macro` tag. You must define a macro block first, using the [macro tag](tag-macro.md), before you can call the define block using this `call` tag. + +The number of arguments in a `call` invocation must match the number of parameters in a `macro` definition. + +_Note: In contrast to the `set` tag, the `call` and `macro` tags can be used for delayed execution of blocks. When the renderer encounters a `macro` block, it won't render it immediately, but instead it's contents are stored as a body of the block. When the renderer encounters a `call` tag, it will look up any matching block (by name), and will then invoke it using the context from the invocation point._ + +Do note that, due to the delayed invocation, a `macro` block can contain `call` tags that invoke the `macro` block again, thus allowing for scenarios such as recursion. + +See the documentation for the [macro tag](tag-macro.md) for more information. + +## Usage example + +```stencil +{# define test1 #} +{% macro test1 %} + Hello world! (inside test) +{% endmacro %} + +{# define test2 #} +{% macro test2 a b c %} + Received parameters in test2: + - a = "{{a}}" + - b = "{{b}}" + - c = "{{c}}" + + // calling test1 + {% call test1 %} +{% endmacro %} + +{# calling test2 #} +{% call test2 "hey" 123 "world" %} +``` + +Will output: + +```text + Received parameters in test2: + - a = "hey" + - b = "123" + - c = "world" + + // calling test1 + Hello world! (inside test) +``` diff --git a/Documentation/tag-macro.md b/Documentation/tag-macro.md new file mode 100644 index 00000000..3f71d2c0 --- /dev/null +++ b/Documentation/tag-macro.md @@ -0,0 +1,45 @@ +# Tag: "Macro" + +This tag stores an entire content tree into a variable to be evaluated and rendered later (possibly multiple times). + +This can be thought like defining a function or macro. + +## Node Information + +| Name | Description | +|-----------|-----------------------------------------------------------------| +| Tag Name | `macro` | +| End Tag | `endmacro` | +| Rendering | None; content is stored unrendered in variable with block name | + +| Parameter | Description | +|------------|-------------------------------------------| +| Block Name | The name of the block you want to define. | +| ... | A variable list of parameters (optional). | + +_Example:_ `{% macro myBlock name %}Hello {{name}}!{% endmacro %}` + + +## When to use it + +This node only works together with the `call` tag. The `macro` tag on itself renders nothing as its output, it only stores it's unrendered template contents in a variable on the stack to be called later. + +The parameters in the definition will be available as variables in the context during invocation. Do note that a `macro` block's execution is scoped, thus any changes to the context inside of it will not be available once execution leaves the block's scope. + +## Usage example + +```stencil +{% macro hi name %} +Hello, {{name}}! How are you? +{% endmacro %} + +{% call hi Alice %} +{% call hi Bob %} +``` + +```text +Hello, Alice! How are you? +Hello, Bob! How are you? +``` + +See the documentation for the [call tag](tag-call.md) for a full and more complex usage example. diff --git a/Documentation/tag-map.md b/Documentation/tag-map.md new file mode 100644 index 00000000..2de2d908 --- /dev/null +++ b/Documentation/tag-map.md @@ -0,0 +1,51 @@ +# Tag: "Map" + +This tag iterates over an array, transforming each element, and storing the resulting array into a variable for later use. + +## Node Information + +| Name | Description | +|-----------|-----------------------------------------------------------------| +| Tag Name | `map` | +| End Tag | `endmap` | +| Rendering | Immediately; no output | + +| Parameter | Description | +|-------------|--------------------------------------------------------------------| +| Array Name | The name of the array you want to transform. | +| Result Name | The name of the variable you want to store into. | +| Item Name | Optional; name of the variable for accessing the iteration's value | + +_Example:_ `{% map myArray into myNewArray using myItem %}...{% endmap %}` + +## When to use it + +Handy when you have an array of items, but want to trasform them before applying other operations on the whole collection. For example, you can easily use this node to map an array of strings so that they're all uppercase and preprended with their index number in the collection. You can then join the resulting array into a string using the `join` filter. + +You must at least provide the name of the variable you're going to transform, and the name of the variable to store into. The block between the map/endmap tags will be executed once for each array item. Optionally you can provide a name for the variable of the iteration's element, that will be available during the block's execution. If you don't provide an item name, you can always access it using the `maploop` context variable. + +The `maploop` context variable is available during each iteration, similar to the `forloop` variable when using the `for` node. It contains the following properties: +- `counter`: the current iteration of the loop. +- `first`: true if this is the first time through the loop. +- `last`: true if this is the last time through the loop. +- `item`: the array item for this iteration. + +Keep in mind that, similar to the [set tag](tag-set.md), the result variable is scoped, meaning that if you set a variable while (for example) inside a `macro` call, the set variable will not exist outside the scope of that call. + +## Usage example + +```stencil +// we start with 'list' with as value ['a', 'b', 'c'] + +// map the list without item name +{% map list into result1 %}{{maploop.item|uppercase}}{% endmap %} +// result1 = ['A', 'B', 'C'] + +// map with item name +{% map list into result2 using item %}{{item}}{{item|uppercase}}{% endmap %} +// result2 = ['aA', 'bB', 'cC'] + +// map using the counter variable +{% map list into result3 using item %}{{maploop.counter}} - {{item}}{% endmap %} +// result3 = ['0 - a', '1 - b', '2 - c'] +``` diff --git a/Documentation/tag-set.md b/Documentation/tag-set.md new file mode 100644 index 00000000..c47fc204 --- /dev/null +++ b/Documentation/tag-set.md @@ -0,0 +1,53 @@ +# Tag: "Set" + +This tag stores a value into a variable for later use. + +## Node Information + +| Name | Description | +|-----------|----------------------------------| +| Tag Name | `set` | +| End Tag | `endset` | +| Rendering | Immediately; no output | + +| Parameter | Description | +|-----------|---------------------------------------------| +| Name | The name of the variable you want to store. | + +_Example:_ `{% set myVar %}hello{% endset %}` + +## When to use it + +Useful when you have a certain calculation you want to re-use in multiple places without repeating yourself. For example you can compute only once the result of multiple filters applied in sequence to a variable, store that result and reuse it later. + +The content between the the `set` and `endset` tags is rendered immediately using the available context, and stored on the stack into a variable with the provided name. + +Keep in mind that the variable is scoped, meaning that if you set a variable while (for example) inside a for loop, the set variable will not exist outside the scope of that for loop. + +## Usage example + +```stencil +// we start with 'x' and 'y' as empty variables + +// set value +{% set x %}hello{% endset %} +{% set y %}world{% endset %} +// x = "hello", y = "world" + +// Compute some complex expression once, and reuse it multiple times later +{% set greetings %}{{ x|uppercase }}, {{ y|titlecase }}{% endset %} +// greetings = "HELLO, World" + +// set inside for loop +{% for item in items %} + {% set x %}item #{{item}}{% endset %} + // x = "item #...", y = "world" + // greetings is still = "HELLO, World" (it isn't recomputed with new x) +{% endfor %} + +// after for loop +// x = "hello", y = "world" + +{{ greetings }}, {{ greetings }}, {{ greetings }}! +// HELLO World, HELLO World, HELLO World! +``` diff --git a/README.md b/README.md index 6b775ddf..9df5dfd7 100644 --- a/README.md +++ b/README.md @@ -7,36 +7,34 @@ `StencilSwiftKit` is a framework bringing additional [Stencil](https://github.com/kylef/Stencil) nodes & filters dedicated to Swift code generation. -## Nodes +## Tags -_TODO: [Write more extension Documentation](https://github.com/SwiftGen/StencilSwiftKit/issues/4)_ - -* `MacroNode` & `CallNode` +* [Macro](Documentation/tag-macro.md) & [Call](Documentation/tag-call.md) * `{% macro %}…{% endmacro %}` * Defines a macro that will be replaced by the nodes inside of this block later when called - * `{% call %}` - * Calls a previously defined macro, passing it some parameters -* `SetNode` + * `{% call %}` + * Calls a previously defined macro, passing it some arguments +* [Set](Documentation/tag-set.md) * `{% set %}…{% endset %}` * Renders the nodes inside this block immediately, and stores the result in the ` variable of the current context. -* `MapNode` +* [Map](Documentation/tag-map.md) * `{% map into using %}…{% endmap %}` * Apply a `map` operator to an array, and store the result into a new array variable `` in the current context. - * Inside the map loop, a `maploop` special variable is available (akin to the `forloop` variable in `for` nodes). It exposes `maploop.count`, `maploop.first`, `maploop.last` and `maploop.item`. + * Inside the map loop, a `maploop` special variable is available (akin to the `forloop` variable in `for` nodes). It exposes `maploop.counter`, `maploop.first`, `maploop.last` and `maploop.item`. ## Filters -_TODO: [Write more extension Documentation](https://github.com/SwiftGen/StencilSwiftKit/issues/4)_ - -* `swiftIdentifier`: Transforms an arbitrary string into a valid Swift identifier (using only valid characters for a Swift identifier as defined in the Swift language reference) * `join`: Deprecated. Will be removed now that the same filter exists in Stencil proper. -* `lowerFirstWord` -* `snakeToCamelCase` / `snakeToCamelCaseNoPrefix` -* `titlecase` -* `hexToInt` -* `int255toFloat` -* `percent` -* `escapeReservedKeywords`: Escape keywods reserved in the Swift language, by wrapping them inside backticks so that the can be used as regular escape keywords in Swift code. +* [String filters](Documentation/filters-strings.md): + * `escapeReservedKeywords`: Escape keywods reserved in the Swift language, by wrapping them inside backticks so that the can be used as regular escape keywords in Swift code. + * `lowerFirstWord` + * `snakeToCamelCase` / `snakeToCamelCaseNoPrefix` + * `swiftIdentifier`: Transforms an arbitrary string into a valid Swift identifier (using only valid characters for a Swift identifier as defined in the Swift language reference) + * `titlecase` +* [Number filters](Documentation/filters-numbers.md): + * `int255toFloat` + * `hexToInt` + * `percent` ## StencilSwiftTemplate