ERB 类

ERB – Ruby 模板

简介

ERB 为 Ruby 提供了一个易于使用但功能强大的模板系统。使用 ERB,可以将实际的 Ruby 代码添加到任何纯文本文档中,以便生成文档信息详细信息和/或流程控制。

一个非常简单的示例如下

require 'erb'

x = 42
template = ERB.new <<-EOF
  The value of x is: <%= x %>
EOF
puts template.result(binding)

打印:x 的值为:42

下面给出更复杂的示例。

识别标记

ERB 识别提供的模板中的某些标记,并根据以下规则对其进行转换

<% Ruby code -- inline with output %>
<%= Ruby expression -- replace with result %>
<%# comment -- ignored -- useful in testing %> (`<% #` doesn't work. Don't use Ruby comments.)
% a line of Ruby code -- treated as <% line %> (optional -- see ERB.new)
%% replaced with % if first thing on a line and % processing is used
<%% or %%> -- replace with <% or %> respectively

所有其他文本都通过 ERB 过滤后保持不变。

选项

使用 ERB 时可以更改多个设置

有关更多详细信息,请参见 ERB.newERB#result 方法。

字符编码

ERB(或由 ERB 生成的 Ruby 代码)返回与输入字符串相同的字符编码的字符串。但是,当输入字符串有魔术注释时,它会返回以魔术注释指定的编码的字符串。

# -*- coding: utf-8 -*-
require 'erb'

template = ERB.new <<EOF
<%#-*- coding: Big5 -*-%>
  \_\_ENCODING\_\_ is <%= \_\_ENCODING\_\_ %>.
EOF
puts template.result

打印:_ENCODING_ 是 Big5。

示例

纯文本

ERB 适用于任何通用的模板化情况。请注意,在此示例中,我们使用便捷的“行首 %”标记,并且使用 %q{...} 对模板进行逐字引用,以避免反斜杠问题。

require "erb"

# Create template.
template = %q{
  From:  James Edward Gray II <[email protected]>
  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
}.gsub(/^  /, '')

message = ERB.new(template, trim_mode: "%<>")

# Set up template data.
to = "Community Spokesman <spokesman@ruby_community.org>"
priorities = [ "Run Ruby Quiz",
               "Document Modules",
               "Answer Questions on Ruby Talk" ]

# Produce result.
email = message.result
puts email

生成

From:  James Edward Gray II <[email protected]>
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

HTML 中的 Ruby

ERB 通常用于 .rhtml 文件(包含嵌入式 Ruby 的 HTML)。请注意,在此示例中需要在运行模板时提供特殊绑定,以便可以解析 Product 对象中的实例变量。

require "erb"

# Build template data class.
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

# Create 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>
}.gsub(/^  /, '')

rhtml = ERB.new(template)

# Set up template data.
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!")

# Produce result.
rhtml.run(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 使用其自己的模板引擎,该引擎可以在其他地方重复使用。

可以在 The Ruby Toolbox 的相应 类别 中找到其他流行的引擎。

常量

NOT_GIVEN
VERSION

属性

encoding[R]

要 eval 的编码

filename[RW]

运行 ERB 代码时传递给 Kernel#eval 的可选 filename 参数

lineno[RW]

运行 ERB 代码时传递给 Kernel#eval 的可选 lineno 参数

src[R]

ERB 生成的 Ruby 代码

公共类方法

new(str, safe_level=NOT_GIVEN, legacy_trim_mode=NOT_GIVEN, legacy_eoutvar=NOT_GIVEN, trim_mode: nil, eoutvar: '_erbout') 单击以切换源

使用 str 中指定的模板构建新的 ERB 对象。

ERB 对象通过构建一段 Ruby 代码来工作,该代码在运行时将输出已完成的模板。

如果 trim_mode 传递了一个包含以下一个或多个修饰符的 StringERB 将按如下所列调整其代码生成

%  enables Ruby code processing for lines beginning with %
<> omit newline for lines starting with <% and ending in %>
>  omit newline for lines ending in %>
-  omit blank lines ending in -%>

eoutvar 可用于设置 ERB 将在其内部构建输出的变量的名称。当您需要通过相同的绑定运行多个 ERB 模板和/或当您想要控制输出的最终位置时,这非常有用。将要用于 String 内部变量的名称传递过来。

示例

require "erb"

# build data class
class Listings
  PRODUCT = { :name => "Chicken Fried Steak",
              :desc => "A well messages pattie, breaded and fried.",
              :cost => 9.95 }

  attr_reader :product, :price

  def initialize( product = "", price = "" )
    @product = product
    @price = price
  end

  def build
    b = binding
    # create and run templates, filling member data variables
    ERB.new(<<~'END_PRODUCT', trim_mode: "", eoutvar: "@product").result b
      <%= PRODUCT[:name] %>
      <%= PRODUCT[:desc] %>
    END_PRODUCT
    ERB.new(<<~'END_PRICE', trim_mode: "", eoutvar: "@price").result b
      <%= PRODUCT[:name] %> -- <%= PRODUCT[:cost] %>
      <%= PRODUCT[:desc] %>
    END_PRICE
  end
end

# setup template data
listings = Listings.new
listings.build

puts listings.product + "\n" + listings.price

生成

Chicken Fried Steak
A well messages pattie, breaded and fried.

Chicken Fried Steak -- 9.95
A well messages pattie, breaded and fried.
# File lib/erb.rb, line 334
def initialize(str, safe_level=NOT_GIVEN, legacy_trim_mode=NOT_GIVEN, legacy_eoutvar=NOT_GIVEN, trim_mode: nil, eoutvar: '_erbout')
  # Complex initializer for $SAFE deprecation at [Feature #14256]. Use keyword arguments to pass trim_mode or eoutvar.
  if safe_level != NOT_GIVEN
    warn 'Passing safe_level with the 2nd argument of ERB.new is deprecated. Do not use it, and specify other arguments as keyword arguments.', uplevel: 1
  end
  if legacy_trim_mode != NOT_GIVEN
    warn 'Passing trim_mode with the 3rd argument of ERB.new is deprecated. Use keyword argument like ERB.new(str, trim_mode: ...) instead.', uplevel: 1
    trim_mode = legacy_trim_mode
  end
  if legacy_eoutvar != NOT_GIVEN
    warn 'Passing eoutvar with the 4th argument of ERB.new is deprecated. Use keyword argument like ERB.new(str, eoutvar: ...) instead.', uplevel: 1
    eoutvar = legacy_eoutvar
  end

  compiler = make_compiler(trim_mode)
  set_eoutvar(compiler, eoutvar)
  @src, @encoding, @frozen_string = *compiler.compile(str)
  @filename = nil
  @lineno = 0
  @_init = self.class.singleton_class
end
版本() 点击切换源

返回 erb.rb 模块的修订信息。

# File lib/erb.rb, line 266
def self.version
  VERSION
end

公共实例方法

def_class(superklass=Object, methodname='result') 点击切换源

定义一个未命名的类,它具有作为实例方法的methodname,并返回它。

示例

class MyClass_
  def initialize(arg1, arg2)
    @arg1 = arg1;  @arg2 = arg2
  end
end
filename = 'example.rhtml'  # @arg1 and @arg2 are used in example.rhtml
erb = ERB.new(File.read(filename))
erb.filename = filename
MyClass = erb.def_class(MyClass_, 'render()')
print MyClass.new('foo', 123).render()
# File lib/erb.rb, line 500
def def_class(superklass=Object, methodname='result')
  cls = Class.new(superklass)
  def_method(cls, methodname, @filename || '(ERB)')
  cls
end
def_method(mod, methodname, fname='(ERB)') 点击切换源

从编译的 Ruby 源中将methodname定义为mod的实例方法。

示例

filename = 'example.rhtml'   # 'arg1' and 'arg2' are used in example.rhtml
erb = ERB.new(File.read(filename))
erb.def_method(MyClass, 'render(arg1, arg2)', filename)
print MyClass.new.render('foo', 123)
# File lib/erb.rb, line 464
def def_method(mod, methodname, fname='(ERB)')
  src = self.src.sub(/^(?!#|$)/) {"def #{methodname}\n"} << "\nend\n"
  mod.module_eval do
    eval(src, binding, fname, -1)
  end
end
def_module(methodname='erb') 点击切换源

创建未命名的模块,将methodname定义为它的实例方法,并返回它。

示例

filename = 'example.rhtml'   # 'arg1' and 'arg2' are used in example.rhtml
erb = ERB.new(File.read(filename))
erb.filename = filename
MyModule = erb.def_module('render(arg1, arg2)')
class MyClass
  include MyModule
end
# File lib/erb.rb, line 481
def def_module(methodname='erb')
  mod = Module.new
  def_method(mod, methodname, @filename || '(ERB)')
  mod
end
location=((filename, lineno)) 点击切换源

设置将在 ERB 代码评估和错误报告中使用的可选文件名和行号。另请参见 filename=lineno=

erb = ERB.new('<%= some_x %>')
erb.render
# undefined local variable or method `some_x'
#   from (erb):1

erb.location = ['file.erb', 3]
# All subsequent error reporting would use new location
erb.render
# undefined local variable or method `some_x'
#   from file.erb:4
# File lib/erb.rb, line 394
def location=((filename, lineno))
  @filename = filename
  @lineno = lineno if lineno
end
make_compiler(trim_mode) 点击切换源

ERB 创建一个新的编译器。有关详细信息,请参阅 ERB::Compiler.new

# File lib/erb.rb, line 361
def make_compiler(trim_mode)
  ERB::Compiler.new(trim_mode)
end
result(b=new_toplevel) 点击切换源

执行生成的 ERB 代码以生成一个完整的模板,返回该代码的结果。(有关此过程如何受safe_level影响的详细信息,请参阅 ERB::new。)

b 接受一个 Binding 对象,该对象用于设置代码评估的上下文。

# File lib/erb.rb, line 424
def result(b=new_toplevel)
  unless @_init.equal?(self.class.singleton_class)
    raise ArgumentError, "not initialized"
  end
  eval(@src, b, (@filename || '(erb)'), @lineno)
end
result_with_hash(hash) 点击切换源

使用 Hash 对象指定的局部变量在新顶级绑定上呈现模板。

# File lib/erb.rb, line 433
def result_with_hash(hash)
  b = new_toplevel(hash.keys)
  hash.each_pair do |key, value|
    b.local_variable_set(key, value)
  end
  result(b)
end
run(b=new_toplevel) 点击切换源

生成结果并打印它们。(参见 ERB#result

# File lib/erb.rb, line 412
def run(b=new_toplevel)
  print self.result(b)
end
set_eoutvar(compiler, eoutvar = '_erbout') 单击以切换源

可用于将 eoutvar 设置为 ERB::new 中所述。不过,直接使用构造函数可能更简单,因为调用此方法需要设置 ERB compiler 对象。

# File lib/erb.rb, line 404
def set_eoutvar(compiler, eoutvar = '_erbout')
  compiler.put_cmd = "#{eoutvar}.<<"
  compiler.insert_cmd = "#{eoutvar}.<<"
  compiler.pre_cmd = ["#{eoutvar} = +''"]
  compiler.post_cmd = [eoutvar]
end

私有实例方法

new_toplevel(vars = nil) 单击以切换源

每次都返回一个新的绑定,该绑定对于未指定绑定的运行接近 TOPLEVEL_BINDING。

# File lib/erb.rb, line 445
def new_toplevel(vars = nil)
  b = TOPLEVEL_BINDING
  if vars
    vars = vars.select {|v| b.local_variable_defined?(v)}
    unless vars.empty?
      return b.eval("tap {|;#{vars.join(',')}| break binding}")
    end
  end
  b.dup
end