2017-04-03 64 views
0

instance_eval传递绑定时会做什么?我很困惑在此代码: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

求值包含Ruby源代码的字符串,或者给定块时,接收机的(范围内OBJ)。为了设置上下文,变量self在代码执行时被设置为obj,从而赋予代码访问obj的实例变量的权限。

因此binding运行在接收器的上下文中,它是OpenStruct。但那么模板如何得到namelast?我觉得我错过了一步。

也就是说,我很困惑,为什么出现这种情况:

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

回答

4

所以结合在接收器这是OpenStruct的上下文中运行。

正确。

但是,那么模板如何得到namelast

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

+0

我很困惑,为什么我需要绑定。 – Jwan622

+2

@ Jwan622你需要一个绑定,因为'ERB#result'需要一个绑定作为参数。您无法传递当前上下文的绑定(即'ERB.new(template).result(binding)'),因为'name'和'last'将不可用。你需要一个来自'namespace'的绑定。你怎样才能从其他对象获取绑定? 'obj.instance_eval {binding}'。 – Aetherus

1

首先,ERB.new('Name: <%= name %> <%= last %>')编译模板到含有的字符串:

#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" 
相关问题