class ERB

Class ERB (名称代表 Embedded Ruby) 是一个易于使用但功能也非常强大的 模板处理器

用法

在使用 ERB 之前,必须先 require 它(本页面的示例假设已完成此操作)。

require 'erb'

简而言之

ERB 的工作原理如下:

ERB 支持三种类型的标签:

一些简单的例子

这是 ERB 运行的一个简单示例:

template = 'The time is <%= Time.now %>.'
erb = ERB.new(template)
erb.result
# => "The time is 2025-09-09 10:49:26 -0500."

详情

  1. 一个纯文本字符串被赋给变量 template。其嵌入的 表达式标签 '<%= Time.now %>' 包含一个 Ruby 表达式 Time.now

  2. 该字符串被放入一个新的 ERB 对象中,并存储在变量 erb 中。

  3. 方法调用 erb.result 生成一个包含 Time.now 运行时值的字符串,该值在调用时计算。

ERB 对象可以被重用。

erb.result
# => "The time is 2025-09-09 10:49:33 -0500."

另一个例子

template = 'The magic word is <%= magic_word %>.'
erb = ERB.new(template)
magic_word = 'abracadabra'
erb.result(binding)
# => "The magic word is abracadabra."

详情

  1. 和之前一样,一个纯文本字符串被赋给变量 template。其嵌入的 表达式标签 '<%= magic_word %>' 包含一个变量名称,即 magic_word

  2. 该字符串被放入一个新的 ERB 对象中,并存储在变量 erb 中;请注意,magic_word 在创建 ERB 对象之前不必定义。

  3. magic_word = 'abracadabra' 为变量 magic_word 赋一个值。

  4. 方法调用 erb.result(binding) 生成一个包含 magic_word的字符串。

和之前一样,ERB 对象可以被重用。

magic_word = 'xyzzy'
erb.result(binding)
# => "The magic word is xyzzy."

Binding(绑定)

调用方法 result(该方法生成格式化的结果字符串)需要一个 Binding 对象作为参数。

binding 对象为 表达式标签中的表达式提供绑定。

提供必需的 binding 有三种方式:

默认 Binding

当您将 no binding 参数传递给方法 result 时,该方法使用其默认绑定:由方法 new_toplevel 返回的那个。此绑定包含 Ruby 本身定义的绑定,即 Ruby 的常量和变量的绑定。

该绑定足以满足仅引用 Ruby 的常量和变量的表达式标签;这些表达式标签仅引用 Ruby 的全局常量 RUBY_COPYRIGHT 和全局变量 $0

template = <<TEMPLATE
The Ruby copyright is <%= RUBY_COPYRIGHT.inspect %>.
The current process is <%= $0 %>.
TEMPLATE
puts ERB.new(template).result
The Ruby copyright is "ruby - Copyright (C) 1993-2025 Yukihiro Matsumoto".
The current process is irb.

(当前进程是 irb,因为我们在这里做这些示例!)

局部 Binding

默认绑定足以满足引用不在该处定义的常量或变量的表达式。

Foo = 1 # Defines local constant Foo.
foo = 2 # Defines local variable foo.
template = <<TEMPLATE
The current value of constant Foo is <%= Foo %>.
The current value of variable foo is <%= foo %>.
The Ruby copyright is <%= RUBY_COPYRIGHT.inspect %>.
The current process is <%= $0 %>.
TEMPLATE
erb = ERB.new(template)

下面的调用将引发 NameError,因为尽管 Foofoo 是局部定义的,但在默认绑定中它们并未定义。

erb.result # Raises NameError.

为了使局部定义的常量和变量可用,您可以调用 result 并传入局部 binding。

puts erb.result(binding)
The current value of constant Foo is 1.
The current value of variable foo is 2.
The Ruby copyright is "ruby - Copyright (C) 1993-2025 Yukihiro Matsumoto".
The current process is irb.

增强 Binding

另一种使变量绑定(但不是常量绑定)可用的方法是使用方法 result_with_hash(hash);传递的哈希包含名称/值对,这些对将用于在默认绑定的副本中定义和赋值变量。

template = <<TEMPLATE
The current value of variable bar is <%= bar %>.
The current value of variable baz is <%= baz %>.
The Ruby copyright is <%= RUBY_COPYRIGHT.inspect %>.
The current process is <%= $0 %>.
TEMPLATE
erb = ERB.new(template)

这两个调用都会引发 NameError,因为 barbaz 在默认绑定或局部绑定中均未定义。

puts erb.result          # Raises NameError.
puts erb.result(binding) # Raises NameError.

此调用传递一个哈希,该哈希导致 barbaz 在新的绑定(派生自 new_toplevel)中定义。

hash = {bar: 3, baz: 4}
puts erb.result_with_hash(hash)
The current value of variable bar is 3.
The current value of variable baz is 4.
The Ruby copyright is "ruby - Copyright (C) 1993-2025 Yukihiro Matsumoto".
The current process is irb.

标签

上面的示例使用了表达式标签。以下是 ERB 中可用的标签:

表达式标签

您可以使用表达式标签将 Ruby 表达式嵌入到模板中。

其语法是 <%= expression %>,其中 expression 是任何有效的 Ruby 表达式。

当您调用方法 result 时,该方法将评估表达式,并用表达式的值替换整个表达式标签。

ERB.new('Today is <%= Date::DAYNAMES[Date.today.wday] %>.').result
# => "Today is Monday."
ERB.new('Tomorrow will be <%= Date::DAYNAMES[Date.today.wday + 1] %>.').result
# => "Tomorrow will be Tuesday."
ERB.new('Yesterday was <%= Date::DAYNAMES[Date.today.wday - 1] %>.').result
# => "Yesterday was Sunday."

请注意,表达式前后允许有空格,但不是必需的,并且这些空格将从结果中去除。

ERB.new('My appointment is on <%=Date::DAYNAMES[Date.today.wday + 2]%>.').result
# => "My appointment is on Wednesday."
ERB.new('My appointment is on <%=     Date::DAYNAMES[Date.today.wday + 2]    %>.').result
# => "My appointment is on Wednesday."

执行标签

您可以使用执行标签将 Ruby 可执行代码嵌入到模板中。

其语法是 <% code %>,其中 code 是任何有效的 Ruby 代码。

当您调用方法 result 时,该方法将执行代码并移除整个执行标签(在结果中不生成任何文本)。

ERB.new('foo <% Dir.chdir("C:/") %> bar').result # => "foo  bar"

嵌入代码前后允许有空格。

ERB.new('foo <%Dir.chdir("C:/")%> bar').result   # => "foo  bar"

您可以将文本与执行标签交错,形成如条件语句、循环或 case 语句等控制结构。

条件语句

template = <<TEMPLATE
<% if verbosity %>
An error has occurred.
<% else %>
Oops!
<% end %>
TEMPLATE
erb = ERB.new(template)
verbosity = true
erb.result(binding)
# => "\nAn error has occurred.\n\n"
verbosity = false
erb.result(binding)
# => "\nOops!\n\n"

请注意,交错的文本本身也可以包含表达式标签。

循环

template = <<TEMPLATE
<% Date::ABBR_DAYNAMES.each do |dayname| %>
<%= dayname %>
<% end %>
TEMPLATE
ERB.new(template).result
# => "\nSun\n\nMon\n\nTue\n\nWed\n\nThu\n\nFri\n\nSat\n\n"

其他非控制行的 Ruby 代码可以与文本交错,并且 Ruby 代码本身可以包含常规的 Ruby 注释。

template = <<TEMPLATE
<% 3.times do %>
<%= Time.now %>
<% sleep(1) # Let's make the times different. %>
<% end %>
TEMPLATE
ERB.new(template).result
# => "\n2025-09-09 11:36:02 -0500\n\n\n2025-09-09 11:36:03 -0500\n\n\n2025-09-09 11:36:04 -0500\n\n\n"

执行标签也可以包含多行代码。

template = <<TEMPLATE
<%
  (0..2).each do |i|
    (0..2).each do |j|
%>
* <%=i%>,<%=j%>
<%
    end
  end
%>
TEMPLATE
ERB.new(template).result
# => "\n* 0,0\n\n* 0,1\n\n* 0,2\n\n* 1,0\n\n* 1,1\n\n* 1,2\n\n* 2,0\n\n* 2,1\n\n* 2,2\n\n"

执行标签的简写格式

您可以使用关键字参数 trim_mode: '%' 来启用执行标签的简写格式;此示例使用简写格式 % code 而不是 <% code %>

template = <<TEMPLATE
% priorities.each do |priority|
  * <%= priority %>
% end
TEMPLATE
erb = ERB.new(template, trim_mode: '%')
priorities = [ 'Run Ruby Quiz',
               'Document Modules',
               'Answer Questions on Ruby Talk' ]
puts erb.result(binding)
  * Run Ruby Quiz
  * Document Modules
  * Answer Questions on Ruby Talk

请注意,在简写格式中,字符 '%' 必须是代码行的第一个字符(不允许有前导空格)。

抑制不需要的空白行

当未给出关键字参数 trim_mode 时,所有空白行都会进入结果。

template = <<TEMPLATE
<% if true %>
<%= RUBY_VERSION %>
<% end %>
TEMPLATE
ERB.new(template).result.lines.each {|line| puts line.inspect }
"\n"
"3.4.5\n"
"\n"

您可以指定 trim_mode: '-',可以抑制源行以 -%>(而不是 %>)结尾的每个空白行。

template = <<TEMPLATE
<% if true -%>
<%= RUBY_VERSION %>
<% end -%>
TEMPLATE
ERB.new(template, trim_mode: '-').result.lines.each {|line| puts line.inspect }
"3.4.5\n"

使用尾部 '-%>' 符号但不使用 trim_mode: '-' 是错误的。

ERB.new(template).result.lines.each {|line| puts line.inspect } # Raises SyntaxError.

抑制不需要的换行符

考虑这个模板:

template = <<TEMPLATE
<% RUBY_VERSION %>
<%= RUBY_VERSION %>
foo <% RUBY_VERSION %>
foo <%= RUBY_VERSION %>
TEMPLATE

当未给出关键字参数 trim_mode 时,所有换行符都会进入结果。

ERB.new(template).result.lines.each {|line| puts line.inspect }
"\n"
"3.4.5\n"
"foo \n"
"foo 3.4.5\n"

您可以指定 trim_mode: '>' 来抑制每行以 '%>' 结尾的尾部换行符(无论其开头是什么)。

ERB.new(template, trim_mode: '>').result.lines.each {|line| puts line.inspect }
"3.4.5foo foo 3.4.5"

您可以指定 trim_mode: '<>' 来抑制每行同时以 '<%' 开始并以 '%>' 结束的尾部换行符。

ERB.new(template, trim_mode: '<>').result.lines.each {|line| puts line.inspect }
"3.4.5foo \n"
"foo 3.4.5\n"

合并修剪模式

您可以组合某些修剪模式。

注释标签

您可以使用注释标签将注释嵌入到模板中;其语法是 <%# text %>,其中 text 是注释的文本。

当您调用方法 result 时,它会移除整个注释标签(在结果中不生成任何文本)。

示例

template = 'Some stuff;<%# Note to self: figure out what the stuff is. %> more stuff.'
ERB.new(template).result # => "Some stuff; more stuff."

注释标签可以出现在模板的任何位置。

请注意,标签的开头必须是 '<%#',而不是 '<% #'

在此示例中,标签以 '<% #' 开头,因此是一个执行标签,而不是注释标签;引用的代码完全由一个 Ruby 风格的注释组成(当然会被忽略)。

ERB.new('Some stuff;<% # Note to self: figure out what the stuff is. %> more stuff.').result
# => "Some stuff;"

编码

ERB 对象有一个编码,默认情况下是模板字符串的编码;结果字符串也将具有该编码。

template = <<TEMPLATE
<%# Comment. %>
TEMPLATE
erb = ERB.new(template)
template.encoding   # => #<Encoding:UTF-8>
erb.encoding        # => #<Encoding:UTF-8>
erb.result.encoding # => #<Encoding:UTF-8>

您可以通过在给定模板的顶部添加魔术注释来指定不同的编码。

template = <<TEMPLATE
<%#-*- coding: Big5 -*-%>
<%# Comment. %>
TEMPLATE
erb = ERB.new(template)
template.encoding   # => #<Encoding:UTF-8>
erb.encoding        # => #<Encoding:Big5>
erb.result.encoding # => #<Encoding:Big5>

错误报告

考虑这个模板(包含错误):

template = '<%= nosuch %>'
erb = ERB.new(template)

当 ERB 报告错误时,它会包含一个文件名(如果可用)和一个行号;文件名来自方法 filename,行号来自方法 lineno

最初,这些值分别是 nil0;这些初始值分别报告为 '(erb)'1

erb.filename # => nil
erb.lineno   # => 0
erb.result
(erb):1:in '<main>': undefined local variable or method 'nosuch' for main (NameError)

您可以使用方法 filename=lineno= 来分配对您的上下文更有意义的值。

erb.filename = 't.txt'
erb.lineno = 555
erb.result
t.txt:556:in '<main>': undefined local variable or method 'nosuch' for main (NameError)

您可以使用方法 location= 来同时设置这两个值。

erb.location = ['u.txt', 999]
erb.result
u.txt:1000:in '<main>': undefined local variable or method 'nosuch' for main (NameError)

带嵌入 Ruby 的纯文本

这是一个纯文本模板;它使用字面量表示法 '%q{ ... }' 来定义模板(请参阅 %q 字面量);这避免了反斜杠问题。

template = %q{
From:  James Edward Gray II <james@grayproductions.net>
To:  <%= to %>
Subject:  Addressing Needs

<%= to[/\w+/] %>:

Just wanted to send a quick note assuring that your needs are being
addressed.

I want you to know that my team will keep working on the issues,
especially:

<%# ignore numerous minor requests -- focus on priorities %>
% priorities.each do |priority|
  * <%= priority %>
% end

Thanks for your patience.

James Edward Gray II
}

模板将需要这些:

to = 'Community Spokesman <spokesman@ruby_community.org>'
priorities = [ 'Run Ruby Quiz',
               'Document Modules',
               'Answer Questions on Ruby Talk' ]

最后,创建 ERB 对象并获取结果。

erb = ERB.new(template, trim_mode: '%<>')
puts erb.result(binding)

From:  James Edward Gray II <james@grayproductions.net>
To:  Community Spokesman <spokesman@ruby_community.org>
Subject:  Addressing Needs

Community:

Just wanted to send a quick note assuring that your needs are being
addressed.

I want you to know that my team will keep working on the issues,
especially:

* Run Ruby Quiz
* Document Modules
* Answer Questions on Ruby Talk

Thanks for your patience.

James Edward Gray II

带嵌入 Ruby 的 HTML

此示例显示了一个 HTML 模板。

首先,这是一个自定义类 Product

class Product
  def initialize(code, name, desc, cost)
    @code = code
    @name = name
    @desc = desc
    @cost = cost
    @features = []
  end

  def add_feature(feature)
    @features << feature
  end

  # Support templating of member data.
  def get_binding
    binding
  end

end

下面的模板将需要这些值:

toy = Product.new('TZ-1002',
                  'Rubysapien',
                  "Geek's Best Friend!  Responds to Ruby commands...",
                  999.95
                  )
toy.add_feature('Listens for verbal commands in the Ruby language!')
toy.add_feature('Ignores Perl, Java, and all C variants.')
toy.add_feature('Karate-Chop Action!!!')
toy.add_feature('Matz signature on left leg.')
toy.add_feature('Gem studded eyes... Rubies, of course!')

这是 HTML:

template = <<TEMPLATE
<html>
  <head><title>Ruby Toys -- <%= @name %></title></head>
  <body>
    <h1><%= @name %> (<%= @code %>)</h1>
    <p><%= @desc %></p>
    <ul>
      <% @features.each do |f| %>
        <li><b><%= f %></b></li>
      <% end %>
    </ul>
    <p>
      <% if @cost < 10 %>
        <b>Only <%= @cost %>!!!</b>
      <% else %>
         Call for a price, today!
      <% end %>
    </p>
  </body>
</html>
TEMPLATE

最后,创建 ERB 对象并获取结果(省略一些空白行)。

erb = ERB.new(template)
puts erb.result(toy.get_binding)
<html>
  <head><title>Ruby Toys -- Rubysapien</title></head>
  <body>
    <h1>Rubysapien (TZ-1002)</h1>
    <p>Geek's Best Friend!  Responds to Ruby commands...</p>
    <ul>
        <li><b>Listens for verbal commands in the Ruby language!</b></li>
        <li><b>Ignores Perl, Java, and all C variants.</b></li>
        <li><b>Karate-Chop Action!!!</b></li>
        <li><b>Matz signature on left leg.</b></li>
        <li><b>Gem studded eyes... Rubies, of course!</b></li>
    </ul>
    <p>
         Call for a price, today!
    </p>
  </body>
</html>

其他模板处理器

各种 Ruby 项目都有自己的模板处理器。例如,Ruby 处理系统 RDoc 有一个可以在其他地方使用的处理器。

您可以在 Ruby Toolbox 的模板引擎页面上找到其他流行的模板处理器。