为什么这是ERB的错误?

Dan*_*her 6 ruby erb

<div class='row'>
  <%= form.field_container :name do %>
    <%= form.label :name, raw('Name' + content_tag(:span, ' *', :class => 'required')) %>
    <%= form.text_field :name, :class => 'fullwidth' %>
    <%= form.error_message_on :name %>
  <% end %>
</div>
Run Code Online (Sandbox Code Playgroud)

为什么会产生以下错误?

$ erb -x -T - test.erb | ruby -c
-:3: syntax error, unexpected ')'
...form.field_container :name do ).to_s); _erbout.concat "\n"
...                               ^
-:9: syntax error, unexpected $end, expecting ')'
Run Code Online (Sandbox Code Playgroud)

Ror*_*ane 13

如果你看一下输出的代码erb -x -T - test.erb:

#coding:ASCII-8BIT
_erbout = ''; _erbout.concat "<div class='row'>\n  "
; _erbout.concat(( form.field_container :name do ).to_s); _erbout.concat "\n"
; _erbout.concat "    "; _erbout.concat(( form.label :name, raw('Name' + content_tag(:span, ' *', :class => 'required')) ).to_s); _erbout.concat "\n"
; _erbout.concat "    "; _erbout.concat(( form.text_field :name, :class => 'fullwidth' ).to_s); _erbout.concat "\n"
; _erbout.concat "    "; _erbout.concat(( form.error_message_on :name ).to_s); _erbout.concat "\n"
; _erbout.concat "  ";  end ; _erbout.concat "\n"
; _erbout.concat "</div>\n"
; _erbout.force_encoding(__ENCODING__)
Run Code Online (Sandbox Code Playgroud)

你可以看到在第三行,a do后跟一个).Ruby期待一个do... end块,但得到一个右括号.这是语法错误的直接原因.

erb输出错误代码的原因是您在使用<%=时正在使用<%.将代码更改为此修复了语法错误:

<div class='row'>
  <% form.field_container :name do %>
    <%= form.label :name, raw('Name' + content_tag(:span, ' *', :class => 'required')) %>
    <%= form.text_field :name, :class => 'fullwidth' %>
    <%= form.error_message_on :name %>
  <% end %>
</div>
Run Code Online (Sandbox Code Playgroud)

我无法运行此代码来测试它是否在我的更改后输出它应该输出的内容,但erb看起来它生成的代码将起作用:

#coding:ASCII-8BIT
_erbout = ''; _erbout.concat "<div class='row'>\n  "
;  form.field_container :name do ; _erbout.concat "\n"
; _erbout.concat "    "; _erbout.concat(( form.label :name, raw('Name' + content_tag(:span, ' *', :class => 'required')) ).to_s); _erbout.concat "\n"
# more...
Run Code Online (Sandbox Code Playgroud)

编辑

由于这个解决方案显然确实打破了输出,我调查了mu太短的建议.我查了一下,如果Erubis,其默认的Rails 3层的用途,从ERB表现不同.输出的代码erubis -x -T - test.erb(原文,未编辑test.erb):

_buf = ''; _buf << '<div class=\'row\'>
  '; _buf << ( form.field_container :name do ).to_s; _buf << '
'; _buf << '    '; _buf << ( form.label :name, raw('Name' + content_tag(:span, ' *', :class => 'required')) ).to_s; _buf << '
'; _buf << '    '; _buf << ( form.text_field :name, :class => 'fullwidth' ).to_s; _buf << '
'; _buf << '    '; _buf << ( form.error_message_on :name ).to_s; _buf << '
';   end 
 _buf << '</div>
';
_buf.to_s
Run Code Online (Sandbox Code Playgroud)

第三行有完全相同的问题,并erubis -x -T - test.erb | ruby -c输出相同的语法错误.所以ERB和Erubis之间的差异可能不是问题.

我还尝试从官方Rails文档中检查这段代码的语法:

<%= form_for(zone) do |f| %>
  <p>
    <b>Zone name</b><br />
    <%= f.text_field :name %>
  </p>
  <p>
    <%= f.submit %>
  </p>
<% end %>
Run Code Online (Sandbox Code Playgroud)

它得到相同的语法错误.所以这并不是你的ERB代码写得不好; 您的代码与该示例非常相似.

在这一点上,我最好的猜测是,将ERB模板转换为Ruby代码而不是直接评估它erb-x标志是有缺陷的,并且不支持它应该具备的某些功能.虽然现在我考虑到了这一点,但是当你输出一个本身输出文本应该有效的块的结果时,我很难想象应该输出什么样的Ruby代码.在什么时候应该写出每个输出 - 首先是结果,还是首先是块内容?

  • 很好的答案! (2认同)

Fre*_*ung 11

简短版:没有错; Rails做了一些疯狂的事情.

长版:你只能这样做

<%= some_method do %>
<% end %>
Run Code Online (Sandbox Code Playgroud)

(也就是说,<%=与一个块一起使用)因为Rails中存在巨大的飞行攻击.内容<%= %>应该是一个独立的表达式(或表达式):你应该能够坚持它eval并使它在语法上有效.

Rails 3中之前,人们有时的事实,与"正常助手"(迷惑link_to,number_to_currency等等),你必须使用<%=,但使用这些助手或助手像块形式的时候form_for,你不得不使用<%.这就是Rails 3制作<%=支持块的原因.

为了处理这种情况下的块,当你使用时<%=,Rails查看ERB代码,通过使用正则表达式查看它是否看起来像一个块,如果它确实重写了生成的代码,将其转换为有效的Ruby.

优秀的博客文章" Block in Helpers in Rails 3 " 中的更多血腥细节.