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