在这种情况下,instance_eval如何工作?到底是怎么回事?

时间:2017-04-03 20:20:27

标签: ruby

instance_eval传递绑定时会做什么?我对这段代码感到困惑:

require 'erb'
require 'ostruct'
namespace = OpenStruct.new(name: 'Joan', last: 'Maragall')
template = 'Name: <%= name %> <%= last %>'
result = ERB.new(template).result(namespace.instance_eval { binding })

我从instance_eval:z

的文档中读到
  

在接收器(obj)的上下文中计算包含Ruby源代码或给定块的字符串。为了设置上下文,在代码执行时将变量self设置为obj,使代码可以访问obj的实例变量。

所以binding在接收者的上下文中运行,OpenStruct。但是,模板如何获得namelast?我觉得我错过了一步。

即,我很困惑为什么会这样:

result = ERB.new(template).result(namespace)
TypeError: wrong argument type OpenStruct (expected binding)

2 个答案:

答案 0 :(得分:4)

  

所以绑定是在接收器的上下文中运行的,这是OpenStruct。

正确。

  

但是,模板如何获得namelast

不确定是什么让你感到困惑。当namespaceself时,方法namelast可用。因为他们是namespace上的方法。然后将该绑定传递给ERB(因为它是instance_eval的返回值)。这就是它如何得到它们。

答案 1 :(得分:1)

首先,#coding:UTF-8 _erbout = String.new _erbout.concat "Name: " _erbout.concat(( name ).to_s) _erbout.concat " " _erbout.concat(( last ).to_s) _erbout.force_encoding(__ENCODING__) 将模板编译为包含以下内容的字符串:

"Name: #{name} #{last}"

大致相当于包含以下内容的字符串:

namespace

我正在使用后者(因为它更短)以及示例中的require 'ostruct' namespace = OpenStruct.new(name: 'Joan', last: 'Maragall') 对象:

ERB#result

eval现在只是eval在给定绑定的上下文中使用此字符串:(注意双引号,eval('"Name: #{name} #{last}"', namespace.instance_eval { binding }) #=> "Name: Joan Maragall" 正在执行字符串插值)

eval

我们也可以将namespace.instance_eval { eval('"Name: #{name} #{last}"', binding) } #=> "Name: Joan Maragall" 移到块中:

binding

使显式namespace.instance_eval { eval('"Name: #{name} #{last}"') } #=> "Name: Joan Maragall" 多余:

namespace.instance_eval('"Name: #{name} #{last}"')
#=> "Name: Joan Maragall"

可以进一步简化为:

0