4

我正在编写一个显示代码和输出的样式指南。它目前的结构使得代码只需要描述一次,并以原始版本和解释版本显示,如下所示:

<% code = <<PLACE_THE_EXAMPLE_CODE_BETWEEN_THESE_TWO_LINES_EXACTLY_AS_YOU_WANT_IT_TO_APPEAR
<div>
  #{ image_tag 'image.png' }
</div>  
PLACE_THE_EXAMPLE_CODE_BETWEEN_THESE_TWO_LINES_EXACTLY_AS_YOU_WANT_IT_TO_APPEAR
%>

<%= raw code %>
<%= content_tag :pre, code, class: "prettyprint linenums" %>

这很棒,而且相当容易维护。问题出现在 rails helpers 上,就像image_tag上面的例子一样。视图示例正确显示了 div 中的图像,代码示例显示了相关的 HTML。在这种情况下,相关的 HTML 包含一个锚标记 -image_tag方法的结果,而不是调用本身。

我更喜欢代码示例来显示辅助方法,而不是它们的结果。我可以通过在文件中指定示例代码并渲染或读取文件来完成这项工作。如上所述,我更愿意通过在变量中指定代码来完成这项工作,但我似乎无法让 ERB 分隔符在 erb 块内的字符串内工作。即使是最简单的情况<% foo = '<%= bar %>' %>也根本不起作用。我尝试过使用官方文档中的详细信息(例如),<%% %%>但没有取得多大成功。% %

我能找到的关于此事的唯一信息是here, using <%= "<" + "%=" %> link_to <%= image.css_tag.humanize %> <%= "%" + ">" %> %>,这在这个用例中不起作用(如果有的话)。

那么,有没有办法在 ERB 字符串中指定一个包含 ERB 结束分隔符 ( %>) 的字符串,还是我使用稍微笨拙的文件读取方法卡住了?谢谢!

编辑:

我想最终得到的是这个的工作版本:

<%# Idealized code - does not work %>
<% code = <<PLACE_THE_EXAMPLE_CODE_BETWEEN_THESE_TWO_LINES_EXACTLY_AS_YOU_WANT_IT_TO_APPEAR
<div>
  <% image_tag 'image.png' %>
</div>  
PLACE_THE_EXAMPLE_CODE_BETWEEN_THESE_TWO_LINES_EXACTLY_AS_YOU_WANT_IT_TO_APPEAR
%>

所以这<%= raw code %>将(继续)输出:

<div>
  <img src="/images/image.png" alt="Image" />
</div>

<%= content_tag :pre, code, class: "prettyprint linenums" %>会输出:

<pre class="prettyprint linenums">
  <div>
    <% image_tag 'image.png' %>
  </div>  
</pre>

而不是当前使用变量时所做的事情,即:

<pre class="prettyprint linenums">
  <div>
    <img src="/images/image.png" alt="Image" />
  </div>
</pre>

我希望用户能够复制代码示例并将其粘贴到新视图中,而无需将 HTML 转换回生成它们的帮助程序。我认为我基本上需要的是一个替代的 ERB 分隔符,就像'"(或什至%q{})为字符串而变化一样。似乎即使最终的 ERB 分隔符出现在字符串内部,它实际上也被作为块的结尾处理。最简单的例子在<% foo = '<%= bar %>' %>某种程度上展示了我想要完成的事情。在生成器中,您可以使用<% foo = '<%%= bar %>' %>(或类似的东西),告诉它不要立即作为 ERB 处理。这在从文件中读取时,甚至在纯 rb 文件(如助手)中都可以正常工作,但在这种情况下,将它放在视图中是最有意义的,因为它旨在由我们的设计师。

4

1 回答 1

5

如果我对您的理解正确,那么您真正的问题是,就插值而言,heredoc 的行为类似于双引号。因此,您所需要的只是一个行为类似于单引号的引用机制。Ruby 有很多字符串引用机制,特别是我们有%q{...}

<% code = %q{
<div>
  #{ image_tag 'image.png' }
</div>  
} %>

如果您愿意,可以使用其他分隔符:%q|...|,%q(...)等。当然还有一些变化,但至少您不必担心插值问题。

如果你真的想使用heredoc,你可以用引号指定heredoc终止符,相应的引用样式将应用于内容:

<% code = <<'PLACE_THE_EXAMPLE_CODE_BETWEEN_THESE_TWO_LINES_EXACTLY_AS_YOU_WANT_IT_TO_APPEAR'
<div>
  #{ image_tag 'image.png' }
</div>  
PLACE_THE_EXAMPLE_CODE_BETWEEN_THESE_TWO_LINES_EXACTLY_AS_YOU_WANT_IT_TO_APPEAR
%>

中的单引号<<'PLACE...'指定单引号规则(即无插值)适用于heredoc 的内容。


当然,这些东西都不能像这样与嵌入式 ERB 一起使用:

<% code = %q{
<div>
  <% ... %>
</div>  
} %>

因为 ERB 解析器会将第一个%>视为外部<% code...部分的结束分隔符。不要害怕,我想我有一个计划,可以在不涉及严重黑客或过多工作的情况下工作。

一些准备工作

使用上述方法,您可以添加自己的模板格式,即带有不同分隔符的 ERB,并且您可以让 Rails 使用这种新格式来处理您的“特殊”部分,然后正常的 ERB 处理可能会弄得一团糟。

首先,您需要连接 Tilt。如果你看一下lib/tilt/erb.rb你的 Tilt 安装,你会Tilt::ErubisTemplate在底部看到 Erubis 的东西。您应该能够子类化Tilt::ErubisTemplate并提供prepare覆盖,例如,将:pattern => '<!--% %-->'选项和平底船添加到超类。然后在 Rails 初始化程序中使用 Tilt 和 Sprockets 注册它,如下所示:

Tilt.register(Your::Template::Subclass, 'klerb') # "kl" for "kludge" :)
Rails.application.assets.register_engine('.klerb', Your::Template::Subclass)

现在您的应用程序应该能够处理.klerb带有<!--% ... %-->模板分隔符的文件。您还可以使用类似名称将您的 klerb 与 erb 链接起来pancakes.html.erb.klerb,文件将在 ERB 之前通过 klerb;这意味着像这样的模板(在一个名为 的文件中whatever.html.erb.klerb):

<!--% code = <<PLACE_THE_EXAMPLE_CODE_BETWEEN_THESE_TWO_LINES_EXACTLY_AS_YOU_WANT_IT_TO_APPEAR
<div>
  <% image_tag 'image.png' %>
</div>  
PLACE_THE_EXAMPLE_CODE_BETWEEN_THESE_TWO_LINES_EXACTLY_AS_YOU_WANT_IT_TO_APPEAR
%-->
<!--%= "code = escape_the_erb_as_needed(%q{#{code}})" %-->
<% do_normal_erb_stuff %>

会做正确的事。

当然,您需要一个助手来实现该escape_the_erb_as_needed功能;一些小实验应该可以帮助您理清需要逃避什么以及以什么方式逃避。

所有这些可能看起来有点复杂,但实际上非常简单。我使用 Tilt 和 Sprockets 添加了自定义模板处理步骤,结果证明它非常简单;弄清楚要做哪些简单的事情需要一些工作,但我已经为你完成了这项工作:

  1. Tilt::Template子类,你可以通过捎带得到这个Tilt::ErubisTemplate
  2. 通过调用 Tilt 注册Tilt.register
  3. 通过调用 Sprockets 注册Rails.application.assets.register_engine
  4. ...
  5. 利润。
于 2012-11-07T03:33:25.310 回答