Ruby ERB类报"未定义局部变量或方法"错误,但erb命令解析正常
ERB模板用erb命令正常执行,但Ruby ERB类调用报错
undefined local variable or method 'i' 问题重现
有如下test.erb模板:
% for i in 0..1 foo<%=i%>; % end
用系统自带的erb命令运行完全正常:
$ erb test.erb foo0; foo1;
但用Ruby代码直接调用ERB类执行时,却抛出变量未定义的错误:
test.rb代码:
require 'erb' e = ERB.new(File.read("test.erb")) File.open("test.out", 'w') do |f| f.write e.result(binding) end
运行后报错(Ruby 1.9.1及更高版本错误信息类似):
$ ruby test.rb (erb):2:in `block in <main>': undefined local variable or method `i' for main:Object (NameError) from <...>/lib/ruby/1.9.1/erb.rb:838:in `eval' from <...>/lib/ruby/1.9.1/erb.rb:838:in `result' from test.rb:5:in `block in <main>' from test.rb:4:in `open' from test.rb:4:in `<main>'
问题原因
erb命令默认启用了特殊的trim_mode编译选项,内部模板编译逻辑会自动处理循环变量的作用域。而直接调用ERB.new时,默认编译方式会将模板内的循环变量i隔离在局部代码块中,我们传递的binding属于外部主作用域,导致模板无法找到这个变量。
解决思路
方案1:给ERB实例添加trim_mode: '-'参数
初始化ERB对象时指定trim_mode: '-',该选项会调整模板编译规则,让循环变量能被正确识别:
require 'erb' # 启用trim_mode的'-'模式 e = ERB.new(File.read("test.erb"), trim_mode: '-') File.open("test.out", 'w') do |f| f.write e.result(binding) end
方案2:提前在作用域中声明变量(不推荐)
在调用result(binding)之前,在当前作用域提前声明i变量(哪怕只是赋值为nil),让ERB编译时能识别到该变量的存在:
require 'erb' i = nil # 提前声明变量 e = ERB.new(File.read("test.erb")) File.open("test.out", 'w') do |f| f.write e.result(binding) end
这种方式不够优雅,模板内的循环变量理应由模板自身管理,因此优先推荐方案1。
方案3:使用result_with_hash方法(Ruby 2.5+)
如果你的Ruby版本在2.5及以上,可以使用result_with_hash方法,无需传递binding,直接传空哈希即可,模板内部的变量会在独立作用域中处理:
require 'erb' e = ERB.new(File.read("test.erb")) File.open("test.out", 'w') do |f| f.write e.result_with_hash({}) end
内容的提问来源于stack exchange,提问作者user20183737
相关产品推荐
相关产品推荐

