From 7b4a05ae84bc4675c3bbd6e6d70ea73add19b750 Mon Sep 17 00:00:00 2001 From: "David.Gao" Date: Wed, 2 Jan 2019 16:57:34 +0800 Subject: [PATCH] update readme fix error on engine loading and bump version to 0.1.7 --- Makefile | 4 +- README.md | 213 +++++++----------------------------------------------- 2 files changed, 28 insertions(+), 189 deletions(-) diff --git a/Makefile b/Makefile index e65ca16..f2b5087 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/README.md b/README.md index 1456c57..f89a365 100644 --- a/README.md +++ b/README.md @@ -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}} - {{name}} - {{/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: - -

Today{{! ignore me }}.

- -Will render as follows: - -

Today.

- -### 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: - -

Names

- {{#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). - {{name}} - Can be thought of as a single, expanded template: - -

Names

- {{#names}} - {{name}} - {{/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`进行预热,同时也可发现不正确的模板或引擎问题。 \ No newline at end of file