Skip to content

Commit

Permalink
update readme fix error on engine loading and bump version to 0.1.7
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidAlphaFox committed Jan 2, 2019
1 parent 4f28864 commit 7b4a05a
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 189 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
PROJECT = aihtml
PROJECT_DESCRIPTION = html tool for productions from ailink.io
PROJECT_VERSION = 0.1.6
PROJECT_VERSION = 0.1.7

ERLC_OPTS = -Werror +debug_info +warn_export_vars +warn_shadow_vars +warn_obsolete_guard
DEPS = ailib

dep_ailib = git https://github.com/DavidAlphaFox/ailib.git tag-0.1.6
dep_ailib = git https://github.com/DavidAlphaFox/ailib.git tag-0.2.0

include erlang.mk
213 changes: 26 additions & 187 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,213 +39,52 @@ included in the code path of projects that need it.
The simplest example involves using a string template and a context from the REPL.

Ctx = maps:from_list([{<<"planet">>, <<"World!">>}]).
{module,Mod} = ai_mustache_compiler:compile(simple,<<"Hello {{planet}}">>).
Mod:render(Ctx).
{IR,Partials} = ai_mustache_parser:parse(<<"Hello {{planet}}">>).
ai_mustache_runner:render({IR,Partials},Ctx).



In line 1 we created a context that contains a value bound to the `planet`
tag. In line 2 we generate a template module which will be named `simple_ai_mustache`.
tag. In line 2 we generate some IR code of the template.
In line 3 we render then template by passing in the context.


### Two-File Example


A more complex example consists of two files: the view and the template. The
view (logic) file is an Erlang module (simple.erl):

-module(simple).
-compile(export_all).
value() -> 10000.
get_value(<<"name">>,_Ctx)-> "Tome";
get_value(<<"value>>,_Ctx)-> value() ;
get_value(<<"taxed_value">>,_Ctx)->
value() - (value() * 0.4);
get_value(<<"in_ca">>,_Ctx)-> true.


In the view we define functions that will be called by the template. The names
of the functions correspond to the tag names that will be used in the
template. Some functions reference others, some return values, and some return
only booleans.

The template file (simple.mustache) looks like so:

Hello {{name}}
You have just won ${{value}}!
{{#in_ca}}
Well, ${{taxed_value}}, after taxes.
{{/in_ca}}

Notice that the template references the functions in the view module. The
return values from the view dictate how the template will be rendered. To get
the HTML output, make sure the `simple.beam` bytecode file is in your code
path and call the following function:

mustache:render(simple)

This tells Mustache to use the `simple` view and to look for a template named
`simple.mustache` in the same directory as the `simple.beam` bytecode file. If
all goes well, it returns the rendered HTML:

Hello Tom
You have just won $10000!
Well, $6000.00, after taxes.




### The Power of Context


You will often want to provide additional data to your template and view. You
can do this by passing in an initial context to the render function. During
rendering, tag lookups always hit the context first before looking for a view
function. In this way, the context can be used to override view functions.
Using the same template and view as above, we can replace the name tag with
different data by constructing a context and passing it to `render`:


Ctx = map:from_list([{<<"name">>, "Chris"}]).
{module,Mod} = ai_mustache_compiler:compile(simple,Body).
Mod:render(Ctx).

This will produce the following output:

Hello Chris
You have just won $10000!
Well, $6000.00, after taxes.

The context is also accessible from view functions, making it easy to pass in
initialization data. Consider a case where we want to pass in a user ID:

Ctx = map:from_list([{id, 42}])

View functions can get access to the context by accepting a single argument:

name(Ctx) ->
...

Now when this function is called, it will be handed the context. In order to
fetch data from the context, use `mustache:get/2`:

name(Ctx) ->
Id = maps:get(id, Ctx, undefined),
...

If the requested key does not exist in the context, the atom `undefined` will be returned.

### Using as View Engine

Tag Types
---------
please refer [examples/complex.erl](https://github.com/DavidAlphaFox/aihtml/blob/master/examples/complex.erl)

Tags are indicated by the double mustaches. `{{name}}` is a tag. Let's talk
about the different types of tags.
The engine will start a process named `ai_mustache_loader` to manage the templates under given directory.

### Variables
If a template is a parials, the filename must be prefixed with underscore.

The most basic tag is the variable. A `{{name}}` tag in a basic template will
try to call the `name` function on your view. By default a variable "miss"
returns an empty string.
The engine can load the templates without `ai_mustche:prepare`, but I recommand that call this fucation once after the tmeplate engine is started. Because the engine use only one `gen_server` to load template which may be very slow when the request is arrived.

All variables are HTML escaped by default. If you want to return unescaped
HTML, use the triple mustache: `{{{name}}}`.

### Boolean Sections

A section begins with a pound and ends with a slash. That is,
`{{#person}}` begins a "person" section while `{{/person}}` ends it.
## Erlang版Mustache编译器

If the `person` method exists and calling it returns `false`, the HTML
between the pound and slash will not be displayed.
Mustache是一款和框架无关的模板引擎系统,着重于将逻辑和视图分开。因此Mustache模板甚至没有任何内嵌的逻辑,因此可以跨多个语言使用。

If the `person` method exists and calling it returns `true`, the HTML
between the pound and slash will be rendered and displayed.
当前该版本是一个比较早期的版本,在内部虽然得到部分使用,但依然存在一定量的Bug,请谨慎使用


### List Sections
### 最简单的例子

List sections are syntactically identical to boolean sections in that they
begin with a pound and end with a slash. The difference, however, is in the
view: if the function called returns a list, the section is repeated as the
list is iterated over.
这个例子只是在Erlang的REPL中使用一个字符串和一个简单的context。

Each item in the enumerable is expected to be a map that will then become the
context of the corresponding iteration. In this way we can construct loops.

For example, imagine this template:

{{#repo}}
<b>{{name}}</b>
{{/repo}}

And this view code:

repo() ->
[map:from_list([{<<"name">>, Name}]) || Name <- ["Tom", "Chris", "PJ"]].

When rendered, our view will contain a list of each of the names in the source list. When left the section, the context will be restored, the view will not contain the names anymore.

### Inverted Sections
An inverted section begins with a caret (hat) and ends with a slash. That is {{^person}} begins a "person" inverted section while {{/person}} ends it.

While sections can be used to render text one or more times based on the value of the key, inverted sections may render text once based on the inverse value of the key. That is, they will be rendered if the key doesn't exist, is `false`, or is an empty list `[]`.

### Comments

Comments begin with a bang and are ignored. The following template:

<h1>Today{{! ignore me }}.</h1>

Will render as follows:

<h1>Today.</h1>

### Partials

Partials begin with a greater than sign, like {{> box}}.

Partials are rendered at runtime (as opposed to compile time), so recursive partials are possible. Just avoid infinite loops.

They also inherit the calling context.

In this way you may want to think of partials as includes, imports, template expansion, nested templates, or subtemplates, even though those aren't literally the case here.

For example, this template and partial:

base.mustache:

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

user.mustache:
Ctx = maps:from_list([{<<"planet">>, <<"World!">>}]).
{IR,Partials} = ai_mustache_parser:parse(<<"Hello {{planet}}">>).
ai_mustache_runner:render({IR,Partials},Ctx).

<strong>{{name}}</strong>
Can be thought of as a single, expanded template:

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

In Erlang REPL

{ok,BaseBody} = file:read_file("base.mustache").
{ok,UserBody} = file:read_file("user.mustache").
{module,UserMod} = ai_mustache_compiler:compile(user,UserBody).
{module,BaseMod} = ai_mustache_compiler:compile(base,BaseBody).
BaseMod:render(#{<<"_user">> => UserMod}).

### not support

aihtml doesn't support `Set Delimiter`.

### todo

`template manager`

第一行创建了一个非常简单的context,其中包行了 `planet` 标签。
第二行生成了模板的IR代码。而在第三行则使用IR代码和前面的context将木板进行了渲染。

template manger will manage all templates in your project, auto compile all templates and default context to handle partial template rendering.
### 做为视图引擎
请参考[examples/complex.erl](https://github.com/DavidAlphaFox/aihtml/blob/master/examples/complex.erl)

视图引擎将启动一个叫`ai_mustache_loader`的进行对指定目录下的所有模板进行管理

如果文件前面有一个`_`下划线,引擎则会认为这个文件为子模板。虽然引擎可以不使用`ai_mustche:prepare`进行预热,也可以按照需要逐个编译模板。
因为该引擎只使用了一个`gen_server`和两个ets表来管理所有的模板,所以当请求到达的时候才去遍历模板,并加载会比较缓慢,因此建议在引擎启动后立刻执行
`ai_mustche:prepare`进行预热,同时也可发现不正确的模板或引擎问题。

0 comments on commit 7b4a05a

Please sign in to comment.