渲染前验证ERB模板

时间:2019-07-19 17:44:18

标签: ruby erb

如果模板中缺少值,则ruby ERB类将静默继续。

例如@config['bar']ENV['DOESNTEXIST']未定义。

require 'erb'
@config = {}
@config['foo'] = 42
template = ERB.new <<-EOF
  FOO=<%= @config['foo'] %>
  BAR=<%= @config['bar'] %>
  LANG=<%= ENV['LANG'] %>
  BAD=<%= ENV['DOESNTEXIST'] %>
EOF
template.result(binding)

输出

FOO=42
BAR=  
LANG=en_US.UTF-8
BAD=

所需的输出

Warning: @config['bar'] is undefined
Warning: ENV['DOESNTEXIST'] is undefined
FOO=42
BAR=  
LANG=en_US.UTF-8
BAD=

是否可以警告ERB模板是否为零或未定义值?
有没有办法显示erb模板中的所有变量,以便我可以遍历它们进行验证?

我不了解模板的所有值,因为用户可以指定自己的模板。


我尝试过的事情:

template.inspect给了我一个显示占位符的字符串。我可以过滤子字符串,但这是不可靠的。

template.inspect
 => "#<ERB:0x00007ff4e50b1288 @safe_level=nil, @src=\"#coding:UTF-8\\n_erbout = +''; _erbout.<< \\\"  FOO=\\\".freeze; _erbout.<<(( @config['foo'] ).to_s); _erbout.<< \\\"\\\\n  BAR=\\\".freeze\\n; _erbout.<<(( @config['bar'] ).to_s); _erbout.<< \\\"\\\\n  LANG=\\\".freeze\\n; _erbout.<<(( ENV['LANG'] ).to_s); _erbout.<< \\\"\\\\n  BAD=\\\".freeze\\n; _erbout.<<(( ENV['DOESNT EXIST'] ).to_s); _erbout.<< \\\"\\\\n\\\".freeze\\n; _erbout\", @encoding=#<Encoding:UTF-8>, @frozen_string=nil, @filename=nil, @lineno=0>"`

更新:我承认这个问题的措词不是很清楚。问题的实质是如何将像"@config['foo']"这样的字符串转换成像@config['foo']这样的变量? 我了解到,这通常是通过eval()完成的,它存在已知问题。 相关问题:how can i avoid eval in this situation

2 个答案:

答案 0 :(得分:1)

使用fetch。从文档中:

  

从散列返回给定键的值。如果找不到密钥,则有几种选择:没有其他参数,它将引发KeyError异常;如果给定默认值,则将返回该值;如果指定了可选代码块,则将运行该代码块并返回其结果。

具有您所描述的行为的不仅是ERB;还包括您所描述的行为。每当哈希中没有任何内容时,任何Ruby数组都将返回nil。我们希望它在出现某些缺失时“快速失败”。

2.5.1 :000 > {foo: "something"}[:foo]
 => "something"
2.5.1 :001 > {foo: "something"}[:bar]
 => nil

使用fetch访问@config中的项目:

2.5.1 :000 > @config = {foo: "something"}
 => "something"
2.5.1 :001 > @config.fetch(:foo)
 => "something"
2.5.1 :002 > @config.fetch(:bar)
KeyError (key not found: :bar)

在代码中使用fetch来检索不存在的bar将返回以下内容:

undefined local variable or method `‘bar’' for main:Object

这不是警告,因为最初的问题是错误。如果只需要警告,则fetch接受默认值作为可以充当该角色的第二个参数。

fetch docs

答案 1 :(得分:0)

一种选择是使用正则表达式过滤掉所有样式,例如<%= -%><%= %>

text.scan(/<%= ?([^>]+) -?%>/)

哪个会回来

 => [["@config['foo']"], ["@config['bar']"], ["ENV['LANG']"], ["ENV['DOESNTEXIST']"]]

然后使用eval()验证变量@config['foo']存在。因为eval()很危险,所以最好将允许计算的变量列入白名单。在这种情况下,仅允许使用@config[;ENV['之类的字符串。

require 'erb'
@config = {}
@config['foo'] = 42
text = <<-EOF
  FOO=<%= @config['foo'] %>
  BAR=<%= @config['bar'] %>
  LANG=<%= ENV['LANG'] %>
  BAD=<%= ENV['DOESNTEXIST'] %>
EOF
# Match strings like <%= -%> and <%= %>
variables = text.scan(/<%= ?([^>]+) -?%>/)
template = ERB.new text
template.result(binding)

# Only evaluate variables that start with '@config' or 'ENV'
variables.each do |variable|
  if variable.start_with?("@config")
    variable.sub!("@config", "")
    @config2 = builder.instance_variable_get("@config")
    puts "WARNING: Undefined variable: @config#{variable}" unless eval("@config2#{variable}")
  elsif variable.start_with?("ENV")
    puts "WARNING: Undefined variable: #{variable}" unless eval(variable)
  else
    raise "Variable '#{variable}' not allowed"
  end
end