Ruby应该是自我描述的,但我无法通过阅读它来弄清楚这些代码.有嵌套的类逃逸.为什么?为什么定义一个方法然后解除绑定?
为什么代码编译了一堆模板并保留它们以便重复使用如此复杂的外观?
另外:如果有任何GitHub员工正在查看此问题,您能否向GitHub添加一些功能,允许用户在代码片段中插入问题?
(此代码从https://github.com/rtomayko/tilt/blob/master/lib/tilt.rb解除)
def compile_template_method(locals) source,offset = precompiled(locals) offset += 5 method_name = "__tilt_#{Thread.current.object_id.abs}" Object.class_eval <<-RUBY,eval_file,line - offset #{extract_magic_comment source} TOPOBJECT.class_eval do def #{method_name}(locals) Thread.current[:tilt_vars] = [self,locals] class << self this,locals = Thread.current[:tilt_vars] this.instance_eval do #{source} end end end end RUBY unbind_compiled_method(method_name) end
解决方法
There are nested class evals. Why?
这种方法看起来像是来自战争伤痕累累,修复和修补的生产代码,而不是优雅的自描述代码(所以也许我们可以原谅他们一点).
那为什么两个人逃避?在第二个嵌套的“真实”模板方法代码可以被评估之前,要评估的代码必须以正确的源编码作为前缀,该编码可能已被定义为模板文件中的“魔术注释”.
一旦正确设置了字符串编码,就可以尝试真正的class_eval.另一种说法可能是“这是编写源代码的源代码的源代码”!
据推测,这是为了修复Ruby 1.9中可能出现的兼容性问题,其中正在编译的模板可能包含与Tilt库源代码本身(US-ASCII编码)的编码不同的字符编码(UTF-8),会导致模板字符串的错误评估(因为字符串编码已经在调用模板文件的主机代码中设置).
Why is a method being defined and then unbound?
澄清一下:在Ruby中,unbound与undefined不同.
未绑定方法作为可以调用的UnboundMethod类型的自由方法对象存在,尽管它们不再与特定对象关联.未绑定的方法不再具有接收器.
为了创建一个未绑定的方法,它首先被绑定(定义为)一个对象.这就是为什么编译模板方法可以从顶级对象中快速删除的原因,因为它只是生成未绑定方法所必需的临时安排.
此技术用于使用可以使用特定于给定类的不同实例的编译模板,而无需以任何可见或永久的方式更改根对象或第三方开发人员的客户端类.
通过将编译的模板方法与特定的客户端代码对象解除关联,在以后调用使用该类型的对象的模板期间,编译的模板方法可以稍后反弹到该对象的类的新实例.
例如,给定以下ERB模板:
<p>Hello <%= @name %></p>
scott = Person.new scott.name = "Scott" output = template.render(scott) => "<p>Hello Scott</p>"
在第一次渲染期间,模板被评估并针对TOPOBJECT对象的实例进行编译.编译的模板方法将命名为“__tilt_2151955260”.然后,此方法将被解除绑定,以便再次对TOPOBJECT类型的所有实例(根据Ruby版本只是Object或BasicObject)使用,因此可以用于任何客户端对象类型.
下次渲染模板时,编译的模板方法与TOPOBJECT的’baq’实例绑定:
baq = Person.new baq.name = "Baq" output = template.render(baq)
在引擎盖下,当调用template.render(baq)时,未绑定的编译模板方法将绑定到Person的’baq’实例:
__tilt_2151955260.bind(baq).call
Why is code that compiles a bunch of templates and keeps them around for
re-use so complicated looking?
我的评估是,尽管代码实现确实看起来确实看起来不那么复杂,但是这些间接层通常在框架代码中是必需的,这些代码旨在使公共API非常简单和甜蜜地为成千上万的其他开发人员消费,即使它是以少数必须维护它的开发人员为代价的.
由于在许多不同的语言环境中消耗的API引起的实际问题以及来自世界各地的许多编码,代码复杂性(双eval嵌套)也增加了.
脚注:
此后,问题中引用的Template类已重构为单独的文件github.com/rtomayko/tilt/blob/master/lib/tilt/template.rb