[sinatra] 开一贴,专门讨论tilt
花花公子
2010-03-28
sinatra 1.0出来了,这里面最让我激动的是tilt,因为我前两天刚刚用过erb生成文本。才发现模板的渲染取值也是一门学问。
比如下面这段简单的erb代码 irb(main):001:0> require 'erb' => true irb(main):002:0> e = ERB.new(<%= x %>) irb(main):003:1" ^C irb(main):003:0> e = ERB.new('<%= x %>') => #<ERB:0xb7861ef8 @safe_level=nil, @src="_erbout = ''; _erbout.concat(( x ).to_s); _erbout", @filename=nil> irb(main):004:0> e.result NameError: undefined local variable or method `x' for main:Object from (erb):1 irb(main):005:0> x = 1 => 1 irb(main):006:0> e.result NameError: undefined local variable or method `x' for main:Object from (erb):1 需要改成如下代码才可以成功 irb(main):009:0> e.result(binding) => "1" erb的设计存在缺陷,result只能接受binding或者Proc作为参数,需要多加hack才能用的舒服。Tilt里面的操作我就不太理解。 haml和erb相比就好了一些,因为Haml::Engine的render方法接受第二个参数传入locals。而且Haml也直接提供了precompiled方法。所以如果你需要自己生成html就用haml,写起来方便。 |
|
花花公子
2010-03-28
下面该轮到Ryan Tomayko的tilt登场了。下面这段代码我在回ns的时候也贴过
require 'tilt' template = Tilt::ERBTemplate.new('foo.erb') # Slow. Uses Object#instance_eval to process template class Scope end scope = Scope.new template.render(scope) # Fast. Uses compiled template and Object#send to process template class Scope include Tilt::CompileSite end scope = Scope.new template.render(scope) # Also fast, though a bit a slower due to having to extend each time scope = Object.new scope.extend Tilt::CompileSite template.render(scope) |
|
花花公子
2010-03-28
这个CompileSite甚为神奇,因为它的代码只有两行
module CompileSite def __tilt__ end end 看后,我甚为震惊。“什么!这是在做什么?“ 在继续介绍之前,我先回顾一下ruby的module irb(main):001:0> class A;include B;end NameError: uninitialized constant A::B from (irb):1 from :0 irb(main):002:0> module B;def a;puts "a";end;end => nil irb(main):003:0> class A;include B;end => A irb(main):004:0> a = A.new => #<A:0xb7873e8c> irb(main):005:0> a.a a => nil irb(main):006:0> a.b NoMethodError: undefined method `b' for #<A:0xb7873e8c> from (irb):6 from :0 irb(main):007:0> module B;def b;puts "b";end;end => nil irb(main):008:0> a.b b => nil |
|
花花公子
2010-03-28
基于ruby的方法链,include以后只是增加了查找方法链的环节,并不是在新建对象的时候复制方法。所以你也想到了,CompileSite会持续的加入新方法。这里面的代码如下:
def compile_template_method(method_name, locals) source, offset = precompiled(locals) offset += 1 CompileSite.module_eval <<-RUBY, eval_file, line - offset def #{method_name}(locals) #{source} end RUBY ObjectSpace.define_finalizer self, Template.compiled_template_method_remover(CompileSite, method_name) end tilt原本还支持CoffeeScript这个Javascript template,但是人家已经用node.js重写了,估计下一版就可以放弃支持了。 |
|
night_stalker
2010-03-28
这段代码
require 'erb' e = ERB.new '<%= x %>' x = 1 e.result 的问题是:局部变量的可见域是从声明开始,而不是语句块内,e 声明的时候,x 还没出现呢。 但实例变量就不一样,实例变量是当前 object 内,可见域比局部变量要广: require 'erb' e = ERB.new '<%= @x %>' @x = 1 e.result 所以用 object scope 的 binding 比 local scope 的 binding 更接近人的习惯 …… |
|
花花公子
2010-03-28
多谢提醒,原来在生成ERB的时候binding就传入了的。一直以为是result的时候再去取的。
以后尽量用object scope,不用local scope, 比如 get '/items' do @items.map{ |item| haml :item, :locals => { :item => item } } end 就可以写成 get '/items' do @items.map{ |item| @item = item; haml :item } end |
|
mccxj
2010-04-22
CompileSite module的确很神亚,一开始也看傻瓜了,调用的过程原来是
render -> evaluate -> compile_template_method,回去再详细看看是怎么处理的 |