tornado: template 之 各种node/block

selectY 2013-02-08

template中的_parse方法是模板文法的解析器,而这个文件中一坨一坨的各种node以及block,就是解析结果的承载者,也就是说在经过parse处理过后,我们输入的tornado的html模板就变成了各种block的集合。

这些block和node的祖宗就是这个“抽象”类, _Node,它定义了三个方法定义,其中generate方法是必须由子类提供实现的(所以我叫它“抽象”类)。

理论上来说,当一个类成为祖宗类时,必定意味着这个类包含了一些在子类中通用的行为,那么,从_Node暴露出来的方法来看,即所有的子类理论上都会有如下特征:

1. 可作为容器 (each_child, find_named_blocks)

2. generate

当然了,理想总是丰满的,现实也总有那么点儿不对劲,对于某些子孙,它们的特征看上去不是那么靠谱,比如_Text。

_Text这个类只用到了generate这个方法,用于将文字(Html, JS)经过trim后添加到输入流中,如果调用它的each_child or find_named_blocks,当然你能这么做,但是没有什么意义。

前面反复说到_Parse方法,它返回的结果是一个_ChunkList的实例,而_ChunkList继承与_Node。这是一个体现了_Node容器特点的类,重写了generate方法和each_child方法,而基本上就是依次调用容器内所有元素的相关方法而已。

_Nodes众多子子孙孙中比较奇葩的是_ExtendsBlock这个类,丫什么事情都没做(That is true),看上去像是另外一个“抽象类”,但是居然会被_Parse初始化,用于处理Extends这个token(tornado术语)。我就纳闷了,一旦这货被generate,难道不会抛一个异常出来木?

 真正有意思的是另外几个方法,它们有共通的模式,用_ApplyBlock来举例

_ApplyBlock中,有趣的是generate方法

def generate(self, writer):
        method_name = "apply%d" % writer.apply_counter
        writer.apply_counter += 1
        writer.write_line("def %s():" % method_name, self.line)
        with writer.indent():
            writer.write_line("_buffer = []", self.line)
            writer.write_line("_append = _buffer.append", self.line)
            self.body.generate(writer)
            writer.write_line("return _utf8('').join(_buffer)", self.line)
        writer.write_line("_append(%s(%s()))" % (
            self.method, method_name), self.line)
 简单来说,这个函数做了两件事情:
  1. 定义了一个python文件全局函数叫做applyXXX():,其中的XXX是一个整形的,自增的值,返回值是一个utf8字符串。
  2. 执行这个applyXXX函数,将此函数的输出再作为self.method这个函数的输入。

所以,如果一个类似于这样的模板

{%apply linkify%} {{address}} {%end%}
 

会得到一个类似于如下的输出:

r = applyXXX()
r = linkify(r)
_append(r)
 

结合这篇文章 http://autumn-sea.appspot.com/page/agphdXR1bW4tc2VhcgwLEgRCbG9nGLKDfww (需科学上网),可以了解到,tornado的template机制,本质上讲,就是允许开发者已HTML + template marker的方式来编写视图模板,但是在背后,tornado会把这些视图模板通过template的处理,变成可编译的python代码。

还是那autumn-sea上面的代码作为例子,比较容易理解。

View Template

<html>
    <head>
        <title>{{ title }}</title>
    </head>
    <body>
        hello! {{ name }}
    </body>
</html>

处理后

_buffer = []
_buffer.append('<html>\\n<head>\\n<title>')

_tmp = title
if isinstance(_tmp, str): _buffer.append(_tmp)
elif isinstance(_tmp, unicode): _buffer.append(_tmp.encode('utf-8'))
else: _buffer.append(str(_tmp))

_buffer.append('</title>\\n</head>\\n<body>\\n')
_buffer.append('hello! ')

_tmp = name
if isinstance(_tmp, str): _buffer.append(_tmp)
elif isinstance(_tmp, unicode): _buffer.append(_tmp.encode('utf-8'))
else: _buffer.append(str(_tmp))

_buffer.append('\\n</body>\\n</html>\\n')
return ''.join(_buffer)\n"

简单吧?听说这就是传说中的CGI,不过咱没弄过。。。

相关推荐