Skip to content

Commit ae24fcc

Browse files
committed
fix: Restore README
1 parent 9e40ccd commit ae24fcc

File tree

1 file changed

+319
-3
lines changed

1 file changed

+319
-3
lines changed

README.md

Lines changed: 319 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,321 @@
1-
# SailthruSDK
1+
# FuManchu
22

3-
An opinionated .NET SDK built for the Sailthru API
3+
Text-templating using a Handlebars-like syntax, in your .NET Project. Tokenizer/Parser based on the Razor engine provided by Microsoft.
44

5-
[![.github/workflows/main.yml](https://github.com/IngeniumSE/SailthruSDK/actions/workflows/main.yml/badge.svg)](https://github.com/IngeniumSE/SailthruSDK/actions/workflows/main.yml) [![.github/workflows/release.yml](https://github.com/IngeniumSE/SailthruSDK/actions/workflows/release.yml/badge.svg)](https://github.com/IngeniumSE/SailthruSDK/actions/workflows/release.yml)
5+
[![NuGet](https://img.shields.io/nuget/v/FuManchu.svg)](https://www.nuget.org/packages/FuManchu/) [![.github/workflows/main.yml](https://github.com/IngeniumSE/FuManchu/actions/workflows/main.yml/badge.svg)](https://github.com/IngeniumSE/FuManchu/actions/workflows/main.yml) [![.github/workflows/release.yml](https://github.com/IngeniumSE/FuManchu/actions/workflows/release.yml/badge.svg)](https://github.com/IngeniumSE/FuManchu/actions/workflows/release.yml)
6+
7+
Quick Start
8+
-----------
9+
10+
First thing, install FuManchu from Nuget:
11+
12+
Install-Package FuManchu
13+
14+
Next, add a namespace using:
15+
16+
using FuManchu;
17+
18+
Then, you're good to go:
19+
20+
Handlebars.Compile("<template-name>", "Hello {{world}}!");
21+
string result = Handlebars.Run("<template-name>", new { world = "World" });
22+
23+
There is also a shorthand:
24+
25+
string result = Handlebars.CompileAndRun("<template-name>", "Hello {{world}}!", new { world = "World" });
26+
27+
Documentation
28+
-------------
29+
30+
Documentation site is coming soon. But for now, some easy instructions are provided below.
31+
32+
Compiling templates
33+
-------------------
34+
The static `Handlebars` class provides a singleton instance of the `IHandlebarsService` type. This API is modelled on the HandlebarsJS JavaScript API, so if you are familiar with that framework, you'll be fine with FuManchu.
35+
36+
Let's define a template and pass it to the service:
37+
38+
string template = "Hello {{name}}";
39+
var templateFunc = Handlebars.Compile("name", template);
40+
41+
The `Compile` function returns a `HandlebarTemplate` delegate which you can call, passing in your model to return you're result.
42+
43+
string result = templateFunc(new { name = "Matt" });
44+
45+
This is equivalent to the following:
46+
47+
string result = Handlebars.Run("name", new { name = "Matt" });
48+
49+
When you call `Compile` your template is parsed and can be executed multiple times with new data.
50+
51+
Block Tags
52+
------------
53+
Block tags are define using the `{{#tag}}{{/tag}}` syntax. There are three types of block tags; built-in tags, implicit tags and helper tags.
54+
55+
**Built-ins: if, elseif, else**
56+
57+
You can use the `if`, `elseif`, `else` tags to provide conditional logic to your templates.
58+
59+
{{#if value}}
60+
True
61+
{{/if}}
62+
63+
{{#if value}}
64+
True
65+
{{else}}
66+
False
67+
{{/if}}
68+
69+
{{#if value1}}
70+
Value 1
71+
{{#elseif value2}}
72+
Value 2
73+
{{else}}
74+
None
75+
{{/if}}
76+
77+
We resolve the truthfulness of values using the same semantics of *truthy/falsely* logic of JavaScript, therefore, values that are `null`, or the number zero, empty enumerables and empty strings, are all considered false. Everything else is considered true.
78+
79+
The `{{else}}` tag can be also be written as `{{^}}` in your templates
80+
81+
{{#if value}}
82+
True
83+
{{^}}
84+
False
85+
{{/if}}
86+
87+
**Built-ins: unless, else**
88+
89+
`unless` is the negated version of `if`. It will allow you to assume truthful values, and present output if the value is falsey.
90+
91+
{{#unless value}}
92+
Value is not true!
93+
{{/unless}}
94+
95+
{{#unless value}}
96+
Value is not true!
97+
{{else}}
98+
Value was true!
99+
{{/unless}}
100+
101+
The `{{else}}` tag can be also be written as `{{^}}` in your templates
102+
103+
**Built-ins: each, else**
104+
105+
The `each` tag allows you to iterate over enumerable objects.
106+
107+
<ul>
108+
{{#each items}}
109+
<li>{{value}}</li>
110+
{{/each}}
111+
</ul>
112+
113+
The `each` block tag creates a scope around the target model (therefore each child of `items`, above), to allow you to use the `{{value}}` expressions, where `value` is a property of a child of `items`. A more concrete example could be:
114+
115+
var people = new [] { new Person() { Name = "Matt" }, new Person() { Name = "Stuart" } };
116+
string template = "<ul>{{#each this}}<li>{{Name}}</li>{{/each}}</ul>";
117+
118+
// result: <ul><li>Matt</li><li>Stuart</li></ul>
119+
string result = Handlebars.CompileAndRun("name", template, people);
120+
121+
The `each` tag also supports the variables `@index`, `@first`, `@last`. If you enumerate over an `IDictionary`, you also have access to `@key`.
122+
123+
var people = new [] { new Person() { Name = "Matt" }, new Person() { Name = "Stuart" } };
124+
string template = "<ul>{{#each this}}<li>{{@index}}: {{Name}}</li>{{/each}}</ul>";
125+
126+
// result: <ul><li>0: Matt</li><li>1: Stuart</li></ul>
127+
string result = Handlebars.CompileAndRun("name", template, people);
128+
129+
`@index` tracks the current index of the item in the enumerable. `@first` and `@last` represent true/false values as to whether you are enumerating the first or last value in an enumerable. `@key` represents the dictionary key.
130+
131+
You can provided a `{{else}}` switch to provide an output when the enumerable is empty:
132+
133+
{{#each items}}
134+
Item {{@index}}
135+
{{else}}
136+
No items!
137+
{{/each}}
138+
139+
The `{{else}}` tag can be also be written as `{{^}}` in your templates
140+
141+
**Built-ins: with, else**
142+
The `with` block creates a scope around the parameter argument.
143+
144+
var model = new { person = new { forename = "Matt", surname = "Abbott" } };
145+
146+
{{#with person}}
147+
Name: {{forename}} {{surname}}
148+
{{/with}}
149+
150+
Again, as with `each`, you can use the `{{else}}` switch to provide an output if the value passed into the `with` tag is falsey:
151+
152+
{{#with person}}
153+
Name: {{forename}} {{surname}}
154+
{{else}}
155+
Nobody :-(
156+
{{/with}}
157+
158+
The `{{else}}` tag can be also be written as `{{^}}` in your templates
159+
160+
Implicit Block Tags
161+
-
162+
163+
You can use shorthand `{{#tag}}{{/tag}}` where "tag" is the name of a property on your model, instead of using full tags, e.g.
164+
165+
var model = new { person = new { forename = "Matt", surname = "Abbott" } };
166+
167+
{{#person}}
168+
Name: {{forename}} {{surname}}
169+
{{/person}}
170+
171+
The above is equivalent to:
172+
173+
{{#if person}}
174+
{{#with person}}
175+
Name: {{forename}} {{surname}}
176+
{{/with}}
177+
{{/if}}
178+
179+
If you're property also happens to be an enumerable, then the implicit block tag works like `each`:
180+
181+
{{#people}}
182+
<li>{{@index}}: {{forename}} {{surname}}</li>
183+
{{/people}}
184+
185+
Inverted Block Tags
186+
-
187+
Inverted block tags follow the rules for implicit block tags, but are used to provided content when the tag expression resolves to *falsey*.
188+
189+
{{^power}}
190+
You have no power here!
191+
{{/power}}
192+
193+
Block Helpers
194+
-
195+
196+
You can register custom helpers using the `Handlebars` service. You need to register a helper ahead of time, which you can then call from your template.
197+
198+
Handlebars.RegisterHelper("list", options => {
199+
var enumerable = options.Data as IEnumerable ?? new[] { (object)options.Data };
200+
201+
return "<ul>"
202+
+ string.Join("", enumerable.OfType<object>().Select(options.Fn))
203+
+ "</ul>";
204+
});
205+
206+
For block helpers, the `options` parameter provides access to the content of your block helper, therefore given the following usage:
207+
208+
{{#list people}}
209+
<li>{{forename}} {{surname}}</li>
210+
{{/list}}
211+
212+
When calling `options.Fn` (or `options.Render`), the content of your custom helper block is rendered, scoped to the value passed to the render function.
213+
214+
**Arguments and Hash parameters**
215+
You can pass additional information to your helpers using your helper block, e.g.:
216+
217+
var model = new {
218+
people = new List<People>(),
219+
message = "Hello World"
220+
};
221+
222+
Handlebars.RegisterHelper("list", options => {
223+
var people = options.Data as List<People>;
224+
string message = options.Arguments[1] as string;
225+
string cssClass = options.Hash["class"];
226+
227+
return "<ul class=\"" + cssClass + "\">"
228+
+ "<li>" + message + "</li>"
229+
+ string.Join("", enumerable.OfType<object>().Select(options.Fn))
230+
+ "</ul>";
231+
});
232+
233+
{{#list people message class="nav nav-pills"}}...{{/list}}
234+
235+
An instance of `HelperOptions` is passed as the value of `options`, which provides the input arguments (`people` and `message`) and a has (`IDictionary<string, object>`, `class="nav nav-pills"`) which are accessible. `options.Data` provides a shorthand access to `options.Arguments[0]` as `dynamic`, and `options.Hash` provides readonly access to `options.Parameters`. Both `options.Data` and `options.Hash` and provided for API compatability with HandlebarsJS.
236+
237+
Expression Tags
238+
-
239+
Expression tags are simple `{{value}}` type tags that allow you to render content into your templates, by binding values from your input models (or 'contexts' in HandlebarsJS speak). There are a variety of ways you can call these properties, so given:
240+
241+
var model = new {
242+
person = new {
243+
forename = "Matt",
244+
surname = "Abbott",
245+
age = 30,
246+
job = new {
247+
title = "Developer"
248+
}
249+
}
250+
};
251+
252+
{{person.forename}}
253+
{{./person.forename}}
254+
{{this.person.forename}}
255+
{{this/person/forename}}
256+
{{@root.person.forename}}
257+
258+
And also within scopes:
259+
260+
{{#with person}}
261+
{{#with job}}
262+
{{../forename}} {{../surname}}
263+
{{/with}}
264+
{{/with}}
265+
266+
You can use these same 'context paths' as arguments and hash parameters in your block tags too:
267+
268+
{{#if ../people}}
269+
{{#with @root.people}}
270+
271+
And with your custom helpers too:
272+
273+
{{#list people class=@root.cssClass}}
274+
275+
Expression Helpers
276+
-
277+
Just like Block Helpers, you can create Expression Helpers too, using the same function as before:
278+
279+
Handlebars.RegisterHelper("name", options => {
280+
return string.Format("{0} {1}", options.Data.forename, options.data.surname);
281+
});
282+
283+
Called using the expression syntax, this time with arguments:
284+
285+
var model = new { forename = "Matt", surname = "Abbott" };
286+
287+
{{name this}}
288+
289+
Partial Templates
290+
-
291+
Partial templates allow you to break your Handlebars templates into discreet units. To register a partial, you call the `Handlebars.RegisterPartial` method
292+
293+
Handlebars.RegisterPartial("name", "{{forename}} {{surname}}");
294+
295+
You can then call your partial from your template:
296+
297+
var model = new { forename = "Matt", surname = "Abbott" };
298+
299+
{{>name}}
300+
301+
You can override the model passed to your template, by providing an additional arguments:
302+
303+
var model = new { person = new { forename = "Matt", surname = "Abbott" } };
304+
305+
{{>name person}}
306+
307+
You can alternatively pass through parameters, if you need to provide a parameter-like experience:
308+
309+
var model = new { person = new { forename = "Matt", surname = "Abbott" } };
310+
311+
{{>name firstName=person.forename lastName=person.surname}}
312+
313+
Text Encoding
314+
-
315+
Like HandlebarsJS, FuManchu encodes values by default, therefore all calls, such as `{{forename}}` etc, will be encoded. If you need to render the raw value, you can use the triple-brace syntax:
316+
317+
{{{forename}}} {{{surname}}}
318+
319+
Singleton vs Instance Services
320+
-
321+
The `Handlebars` type provides singleton access to an instance of `IHandlebarsService`. If you need instance access instead (such as injection through IoC/DI), you can simply register `HandlebarsService` as an instance of `IHandlebarsService` in whatever appropriate lifetime scope you require. This will enable you to partition your helpers/partials and compiled templates per instances of `IHandlebarsService`.

0 commit comments

Comments
 (0)