【问题标题】:Defining a Chef ERB template method定义 Chef ERB 模板方法
【发布时间】:2015-07-22 04:40:36
【问题描述】:

Chef 非常适合配置基本模板,但我希望加入更高级的模板逻辑。这样的事情可能吗:

<%
def say_hi()
-%>
  <%= "hi" %>
<%
end
-%>

# Extraneous template data here

<% say_hi() -%> # Call to template method

在此示例中,我希望每当调用 say_hi() 时都会生成 hi,但它不起作用。

这种行为是否可能,如果没有,有没有办法产生类似的行为?我假设 Chef 模板遵循通用 ERB 模板规则。

谢谢!

更新:一点背景故事

我正在尝试模板化一个非常复杂的config.yml,它有自己的格式方案。它看起来像这样:

key: value

some other key:
  - some other value
  - another value

hash-like key:
  hash-like value:
    - element A
    - element B

其中有很多,我认为与其直接内联映射属性,不如将它们全部定义在一个公共键下并循环遍历它们:

<% node['section']['help'].each do |key, value| -%>
  <%= "#{key}: #{value}" %>
<% end -%>

这很好用!它使我不必定义所有内容两次(一次在属性文件中,一次在模板中),但是我觉得这种结构已经失控了,尤其是对于数组中的嵌套哈希:

<% node['section']['help'].each do |key, value| -%>
  <% if value.kind_of?(Hash) -%>
    <%= "#{key}: "%>
    <% value.each do |subkey, subvalue| -%>
      <%= "  #{subkey}: #{subvalue}" %>
    <% end -%>

  <% else -%>
    <%= "#{key}: #{value}" %>
  <% end -%>
<% end -%>

所以我想创建一个递归方法来识别三种类型(普通值、数组值和哈希值)并处理每种类型的格式。我原型化的完整方法如下所示:

<%
def determine_type(key, value, iteration)

  if value.kind_of?(Hash)
    -%>
    <%= %Q(#{" " * (iteration * 2)} #{key}:)  %>

    <% value.keys do |subkey, subvalue| -%>
      <% determine_type(subkey, subvalue, iteration + 1) -%>
    <% end -%>
    <%
  elsif value.kind_of?(Array)
    -%>
    <%= %Q(#{" " * (iteration * 2)} #{key}:)  %>

    <% value.each do |subvalue| -%>
      <%= %Q(#{" " * ((iteration + 1) * 2)} - #{subvalue})  %>
    <% end -%>
    <%
  else
    -%>
    <%= %Q(#{" " * (iteration * 2)} #{key}: #{value})  %>
    <%
  end
end
-%>

它在这里被调用:

<% node['section']['help'].each do |key, value| -%>
  <% determine_type(key, value, 1) -%>
<% end -%>

遗憾的是,如果只能返回值而不能直接输出到 ERB 模板,这个相对优雅的策略就失效了。

【问题讨论】:

  • 你试过了吗?输出是什么?用例是什么? (我真的看不出这在哪里有用)。由于对尝试目标的描述如此之少,因此无法知道是否可以使用变量或部分模板。
  • 当模板开始包含这么多逻辑时,我发现很难长期维护模板。当模板很简单并且逻辑位于配方或配方调用的库中时,阅读起来会容易得多。
  • 函数应该返回字符串,你应该用=打印函数的结果,就像&lt;%= say_hi %&gt;一样。但是在模板中包含逻辑和功能是一个坏主意。只需创建另一个模板,并在配方中包含使用哪个模板的逻辑。

标签: templates chef-infra erb


【解决方案1】:

这不是推荐的设计模式。逻辑应该保留在食谱和资源中。也就是说,有可能哪一个是你问题的核心,是什么引起了我的兴趣

<%
def say_hi()
-%>
  <% "hi" %>
<%
end
-%>
# Extraneous template data here

<%= say_hi() -%> # Call to template method

这将呈现以下文件

# Extraneous template data here

hi # Call to template method

注意&lt;%= 块如何获取say_hi 的返回值并打印它,而不是从say_hi 函数本身打印。

这意味着您的函数需要返回您希望它生成的整个字符串,并且您不能在方法中使用模板标签。我想说,仅此限制就违背了在 erb 模板中使用函数/方法的目的。

但是,如果render 是一个可以用来渲染不同部分模板的函数,那将会很有趣...我只是向上看,然后发现render 'other_template.erb' 就像一个魅力!

因此,鉴于此,我仍然建议坚持在配方中保留逻辑,并且仅在模板中使用循环、条件和打印。我不确定在模板中使用render 的准则是什么,但我有一种不鼓励这样做的感觉。

【讨论】:

  • 感谢您的回复!我以前也见过render,但我不确定这是否能提供我正在寻找的功能。我编辑了我的问题,以包含更多与我想要完成的目标相关的背景。
  • @JamesTaylor 如果只是 YAML,我通常只有一个节点属性的命名空间用于该配置文件,然后我只使用 YAML::dump(node['mycookbook']['myconfig'].to_hash) 转储该哈希值这通常是最简单的方法并且对于食谱的最终用户来说非常清楚,他们可能已经掌握了应用程序配置文件的结构。
  • 如果你走那条路,你需要处理一些技巧......问题在于深度嵌套的节点属性。您需要将节点属性转换为 JSON,然后再转换为基本哈希,然后再转换为 YAML... 很疯狂,但效果很好YAML.dump(JSON.parse(node['mycookbook']['myconfig'].to_hash.to_json))
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-09
  • 2012-04-16
  • 2015-12-25
相关资源
最近更新 更多