類別 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

屬性

編碼[R]

要 eval 的編碼

檔名[RW]

執行 ERB 程式碼時,傳遞給 Kernel#eval 的選用 檔名 參數

行號[RW]

執行 ERB 程式碼時,傳遞給 Kernel#eval 的選用 行號 參數

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 傳遞一個 字串,其中包含下列一個或多個修飾詞,ERB 會依據所列調整其程式碼產生方式

%  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 範本時很有用,或者當您想要控制輸出結果時。在 字串 內傳遞要使用的變數名稱。

範例

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
version() 按一下以切換原始碼

傳回 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