mirror of
https://github.com/ruby/ruby.git
synced 2026-01-26 20:19:19 +00:00
1176 lines
32 KiB
Ruby
1176 lines
32 KiB
Ruby
# -*- coding: us-ascii -*-
|
|
# frozen_string_literal: true
|
|
# = ERB -- Ruby Templating
|
|
#
|
|
# Author:: Masatoshi SEKI
|
|
# Documentation:: James Edward Gray II, Gavin Sinclair, and Simon Chiang
|
|
#
|
|
# See ERB for primary documentation and ERB::Util for a couple of utility
|
|
# routines.
|
|
#
|
|
# Copyright (c) 1999-2000,2002,2003 Masatoshi SEKI
|
|
#
|
|
# You can redistribute it and/or modify it under the same terms as Ruby.
|
|
|
|
# A NOTE ABOUT TERMS:
|
|
#
|
|
# Formerly: The documentation in this file used the term _template_ to refer to an ERB object.
|
|
#
|
|
# Now: The documentation in this file uses the term _template_
|
|
# to refer to the string input to ERB.new.
|
|
#
|
|
# The reason for the change: When documenting the ERB executable erb,
|
|
# we need a term that refers to its string input;
|
|
# _source_ is not a good idea, because ERB#src means something entirely different;
|
|
# the two different sorts of sources would bring confusion.
|
|
#
|
|
# Therefore we use the term _template_ to refer to:
|
|
#
|
|
# - The string input to ERB.new
|
|
# - The string input to executable erb.
|
|
#
|
|
|
|
require 'erb/version'
|
|
require 'erb/compiler'
|
|
require 'erb/def_method'
|
|
require 'erb/util'
|
|
|
|
# :markup: markdown
|
|
#
|
|
# Class **ERB** (the name stands for **Embedded Ruby**)
|
|
# is an easy-to-use, but also very powerful, [template processor][template processor].
|
|
#
|
|
# ## Usage
|
|
#
|
|
# Before you can use \ERB, you must first require it
|
|
# (examples on this page assume that this has been done):
|
|
#
|
|
# ```
|
|
# require 'erb'
|
|
# ```
|
|
#
|
|
# ## In Brief
|
|
#
|
|
# Here's how \ERB works:
|
|
#
|
|
# - You can create a *template*: a plain-text string that includes specially formatted *tags*..
|
|
# - You can create an \ERB object to store the template.
|
|
# - You can call instance method ERB#result to get the *result*.
|
|
#
|
|
# \ERB supports tags of three kinds:
|
|
#
|
|
# - [Expression tags][expression tags]:
|
|
# each begins with `'<%='`, ends with `'%>'`; contains a Ruby expression;
|
|
# in the result, the value of the expression replaces the entire tag:
|
|
#
|
|
# template = 'The magic word is <%= magic_word %>.'
|
|
# erb = ERB.new(template)
|
|
# magic_word = 'xyzzy'
|
|
# erb.result(binding) # => "The magic word is xyzzy."
|
|
#
|
|
# The above call to #result passes argument `binding`,
|
|
# which contains the binding of variable `magic_word` to its string value `'xyzzy'`.
|
|
#
|
|
# The below call to #result need not pass a binding,
|
|
# because its expression `Date::DAYNAMES` is globally defined.
|
|
#
|
|
# ERB.new('Today is <%= Date::DAYNAMES[Date.today.wday] %>.').result # => "Today is Monday."
|
|
#
|
|
# - [Execution tags][execution tags]:
|
|
# each begins with `'<%'`, ends with `'%>'`; contains Ruby code to be executed:
|
|
#
|
|
# template = '<% File.write("t.txt", "Some stuff.") %>'
|
|
# ERB.new(template).result
|
|
# File.read('t.txt') # => "Some stuff."
|
|
#
|
|
# - [Comment tags][comment tags]:
|
|
# each begins with `'<%#'`, ends with `'%>'`; contains comment text;
|
|
# in the result, the entire tag is omitted.
|
|
#
|
|
# template = 'Some stuff;<%# Note to self: figure out what the stuff is. %> more stuff.'
|
|
# ERB.new(template).result # => "Some stuff; more stuff."
|
|
#
|
|
# ## Some Simple Examples
|
|
#
|
|
# Here's a simple example of \ERB in action:
|
|
#
|
|
# ```
|
|
# template = 'The time is <%= Time.now %>.'
|
|
# erb = ERB.new(template)
|
|
# erb.result
|
|
# # => "The time is 2025-09-09 10:49:26 -0500."
|
|
# ```
|
|
#
|
|
# Details:
|
|
#
|
|
# 1. A plain-text string is assigned to variable `template`.
|
|
# Its embedded [expression tag][expression tags] `'<%= Time.now %>'` includes a Ruby expression, `Time.now`.
|
|
# 2. The string is put into a new \ERB object, and stored in variable `erb`.
|
|
# 4. Method call `erb.result` generates a string that contains the run-time value of `Time.now`,
|
|
# as computed at the time of the call.
|
|
#
|
|
# The
|
|
# \ERB object may be re-used:
|
|
#
|
|
# ```
|
|
# erb.result
|
|
# # => "The time is 2025-09-09 10:49:33 -0500."
|
|
# ```
|
|
#
|
|
# Another example:
|
|
#
|
|
# ```
|
|
# template = 'The magic word is <%= magic_word %>.'
|
|
# erb = ERB.new(template)
|
|
# magic_word = 'abracadabra'
|
|
# erb.result(binding)
|
|
# # => "The magic word is abracadabra."
|
|
# ```
|
|
#
|
|
# Details:
|
|
#
|
|
# 1. As before, a plain-text string is assigned to variable `template`.
|
|
# Its embedded [expression tag][expression tags] `'<%= magic_word %>'` has a variable *name*, `magic_word`.
|
|
# 2. The string is put into a new \ERB object, and stored in variable `erb`;
|
|
# note that `magic_word` need not be defined before the \ERB object is created.
|
|
# 3. `magic_word = 'abracadabra'` assigns a value to variable `magic_word`.
|
|
# 4. Method call `erb.result(binding)` generates a string
|
|
# that contains the *value* of `magic_word`.
|
|
#
|
|
# As before, the \ERB object may be re-used:
|
|
#
|
|
# ```
|
|
# magic_word = 'xyzzy'
|
|
# erb.result(binding)
|
|
# # => "The magic word is xyzzy."
|
|
# ```
|
|
#
|
|
# ## Bindings
|
|
#
|
|
# A call to method #result, which produces the formatted result string,
|
|
# requires a [Binding object][binding object] as its argument.
|
|
#
|
|
# The binding object provides the bindings for expressions in [expression tags][expression tags].
|
|
#
|
|
# There are three ways to provide the required binding:
|
|
#
|
|
# - [Default binding][default binding].
|
|
# - [Local binding][local binding].
|
|
# - [Augmented binding][augmented binding]
|
|
#
|
|
# ### Default Binding
|
|
#
|
|
# When you pass no `binding` argument to method #result,
|
|
# the method uses its default binding: the one returned by method #new_toplevel.
|
|
# This binding has the bindings defined by Ruby itself,
|
|
# which are those for Ruby's constants and variables.
|
|
#
|
|
# That binding is sufficient for an expression tag that refers only to Ruby's constants and variables;
|
|
# these expression tags refer only to Ruby's global constant `RUBY_COPYRIGHT` and global variable `$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.
|
|
# ```
|
|
#
|
|
# (The current process is `irb` because that's where we're doing these examples!)
|
|
#
|
|
# ### Local Binding
|
|
#
|
|
# The default binding is *not* sufficient for an expression
|
|
# that refers to a a constant or variable that is not defined there:
|
|
#
|
|
# ```
|
|
# 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)
|
|
# ```
|
|
#
|
|
# This call below raises `NameError` because although `Foo` and `foo` are defined locally,
|
|
# they are not defined in the default binding:
|
|
#
|
|
# ```
|
|
# erb.result # Raises NameError.
|
|
# ```
|
|
#
|
|
# To make the locally-defined constants and variables available,
|
|
# you can call #result with the local 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.
|
|
# ```
|
|
#
|
|
# ### Augmented Binding
|
|
#
|
|
# Another way to make variable bindings (but not constant bindings) available
|
|
# is to use method #result_with_hash(hash);
|
|
# the passed hash has name/value pairs that are to be used to define and assign variables
|
|
# in a copy of the default binding:
|
|
#
|
|
# ```
|
|
# 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)
|
|
# ```
|
|
#
|
|
# Both of these calls raise `NameError`, because `bar` and `baz`
|
|
# are not defined in either the default binding or the local binding.
|
|
#
|
|
# ```
|
|
# puts erb.result # Raises NameError.
|
|
# puts erb.result(binding) # Raises NameError.
|
|
# ```
|
|
#
|
|
# This call passes a hash that causes `bar` and `baz` to be defined
|
|
# in a new binding (derived from #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.
|
|
# ```
|
|
#
|
|
# ## Tags
|
|
#
|
|
# The examples above use expression tags.
|
|
# These are the tags available in \ERB:
|
|
#
|
|
# - [Expression tag][expression tags]: the tag contains a Ruby expression;
|
|
# in the result, the entire tag is to be replaced with the run-time value of the expression.
|
|
# - [Execution tag][execution tags]: the tag contains Ruby code;
|
|
# in the result, the entire tag is to be replaced with the run-time value of the code.
|
|
# - [Comment tag][comment tags]: the tag contains comment code;
|
|
# in the result, the entire tag is to be omitted.
|
|
#
|
|
# ### Expression Tags
|
|
#
|
|
# You can embed a Ruby expression in a template using an *expression tag*.
|
|
#
|
|
# Its syntax is `<%= _expression_ %>`,
|
|
# where *expression* is any valid Ruby expression.
|
|
#
|
|
# When you call method #result,
|
|
# the method evaluates the expression and replaces the entire expression tag with the expression's value:
|
|
#
|
|
# ```
|
|
# 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."
|
|
# ```
|
|
#
|
|
# Note that whitespace before and after the expression
|
|
# is allowed but not required,
|
|
# and that such whitespace is stripped from the result.
|
|
#
|
|
# ```
|
|
# 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."
|
|
# ```
|
|
#
|
|
# ### Execution Tags
|
|
#
|
|
# You can embed Ruby executable code in template using an *execution tag*.
|
|
#
|
|
# Its syntax is `<% _code_ %>`,
|
|
# where *code* is any valid Ruby code.
|
|
#
|
|
# When you call method #result,
|
|
# the method executes the code and removes the entire execution tag
|
|
# (generating no text in the result):
|
|
#
|
|
# ```
|
|
# ERB.new('foo <% Dir.chdir("C:/") %> bar').result # => "foo bar"
|
|
# ```
|
|
#
|
|
# Whitespace before and after the embedded code is optional:
|
|
#
|
|
# ```
|
|
# ERB.new('foo <%Dir.chdir("C:/")%> bar').result # => "foo bar"
|
|
# ```
|
|
#
|
|
# You can interleave text with execution tags to form a control structure
|
|
# such as a conditional, a loop, or a `case` statements.
|
|
#
|
|
# Conditional:
|
|
#
|
|
# ```
|
|
# 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"
|
|
# ```
|
|
#
|
|
# Note that the interleaved text may itself contain expression tags:
|
|
#
|
|
# Loop:
|
|
#
|
|
# ```
|
|
# 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"
|
|
# ```
|
|
#
|
|
# Other, non-control, lines of Ruby code may be interleaved with the text,
|
|
# and the Ruby code may itself contain regular Ruby comments:
|
|
#
|
|
# ```
|
|
# 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"
|
|
# ```
|
|
#
|
|
# The execution tag may also contain multiple lines of code:
|
|
#
|
|
# ```
|
|
# 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"
|
|
# ```
|
|
#
|
|
# #### Shorthand Format for Execution Tags
|
|
#
|
|
# You can use keyword argument `trim_mode: '%'` to enable a shorthand format for execution tags;
|
|
# this example uses the shorthand format `% _code_` instead of `<% _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
|
|
# ```
|
|
#
|
|
# Note that in the shorthand format, the character `'%'` must be the first character in the code line
|
|
# (no leading whitespace).
|
|
#
|
|
# #### Suppressing Unwanted Blank Lines
|
|
#
|
|
# With keyword argument `trim_mode` not given,
|
|
# all blank lines go into the result:
|
|
#
|
|
# ```
|
|
# template = <<TEMPLATE
|
|
# <% if true %>
|
|
# <%= RUBY_VERSION %>
|
|
# <% end %>
|
|
# TEMPLATE
|
|
# ERB.new(template).result.lines.each {|line| puts line.inspect }
|
|
# "\n"
|
|
# "3.4.5\n"
|
|
# "\n"
|
|
# ```
|
|
#
|
|
# You can give `trim_mode: '-'`, you can suppress each blank line
|
|
# whose source line ends with `-%>` (instead of `%>`):
|
|
#
|
|
# ```
|
|
# template = <<TEMPLATE
|
|
# <% if true -%>
|
|
# <%= RUBY_VERSION %>
|
|
# <% end -%>
|
|
# TEMPLATE
|
|
# ERB.new(template, trim_mode: '-').result.lines.each {|line| puts line.inspect }
|
|
# "3.4.5\n"
|
|
# ```
|
|
#
|
|
# It is an error to use the trailing `'-%>'` notation without `trim_mode: '-'`:
|
|
#
|
|
# ```
|
|
# ERB.new(template).result.lines.each {|line| puts line.inspect } # Raises SyntaxError.
|
|
# ```
|
|
#
|
|
# #### Suppressing Unwanted Newlines
|
|
#
|
|
# Consider this template:
|
|
#
|
|
# ```
|
|
# template = <<TEMPLATE
|
|
# <% RUBY_VERSION %>
|
|
# <%= RUBY_VERSION %>
|
|
# foo <% RUBY_VERSION %>
|
|
# foo <%= RUBY_VERSION %>
|
|
# TEMPLATE
|
|
# ```
|
|
#
|
|
# With keyword argument `trim_mode` not given, all newlines go into the result:
|
|
#
|
|
# ```
|
|
# ERB.new(template).result.lines.each {|line| puts line.inspect }
|
|
# "\n"
|
|
# "3.4.5\n"
|
|
# "foo \n"
|
|
# "foo 3.4.5\n"
|
|
# ```
|
|
#
|
|
# You can give `trim_mode: '>'` to suppress the trailing newline
|
|
# for each line that ends with `'%>'` (regardless of its beginning):
|
|
#
|
|
# ```
|
|
# ERB.new(template, trim_mode: '>').result.lines.each {|line| puts line.inspect }
|
|
# "3.4.5foo foo 3.4.5"
|
|
# ```
|
|
#
|
|
# You can give `trim_mode: '<>'` to suppress the trailing newline
|
|
# for each line that both begins with `'<%'` and ends with `'%>'`:
|
|
#
|
|
# ```
|
|
# ERB.new(template, trim_mode: '<>').result.lines.each {|line| puts line.inspect }
|
|
# "3.4.5foo \n"
|
|
# "foo 3.4.5\n"
|
|
# ```
|
|
#
|
|
# #### Combining Trim Modes
|
|
#
|
|
# You can combine certain trim modes:
|
|
#
|
|
# - `'%-'`: Enable shorthand and omit each blank line ending with `'-%>'`.
|
|
# - `'%>'`: Enable shorthand and omit newline for each line ending with `'%>'`.
|
|
# - `'%<>'`: Enable shorthand and omit newline for each line starting with `'<%'` and ending with `'%>'`.
|
|
#
|
|
# ### Comment Tags
|
|
#
|
|
# You can embed a comment in a template using a *comment tag*;
|
|
# its syntax is `<%# _text_ %>`,
|
|
# where *text* is the text of the comment.
|
|
#
|
|
# When you call method #result,
|
|
# it removes the entire comment tag
|
|
# (generating no text in the result).
|
|
#
|
|
# Example:
|
|
#
|
|
# ```
|
|
# template = 'Some stuff;<%# Note to self: figure out what the stuff is. %> more stuff.'
|
|
# ERB.new(template).result # => "Some stuff; more stuff."
|
|
# ```
|
|
#
|
|
# A comment tag may appear anywhere in the template.
|
|
#
|
|
# Note that the beginning of the tag must be `'<%#'`, not `'<% #'`.
|
|
#
|
|
# In this example, the tag begins with `'<% #'`, and so is an execution tag, not a comment tag;
|
|
# the cited code consists entirely of a Ruby-style comment (which is of course ignored):
|
|
#
|
|
# ```
|
|
# ERB.new('Some stuff;<% # Note to self: figure out what the stuff is. %> more stuff.').result
|
|
# # => "Some stuff;"
|
|
# ```
|
|
#
|
|
# ## Encodings
|
|
#
|
|
# An \ERB object has an [encoding][encoding],
|
|
# which is by default the encoding of the template string;
|
|
# the result string will also have that encoding.
|
|
#
|
|
# ```
|
|
# template = <<TEMPLATE
|
|
# <%# Comment. %>
|
|
# TEMPLATE
|
|
# erb = ERB.new(template)
|
|
# template.encoding # => #<Encoding:UTF-8>
|
|
# erb.encoding # => #<Encoding:UTF-8>
|
|
# erb.result.encoding # => #<Encoding:UTF-8>
|
|
# ```
|
|
#
|
|
# You can specify a different encoding by adding a [magic comment][magic comments]
|
|
# at the top of the given template:
|
|
#
|
|
# ```
|
|
# template = <<TEMPLATE
|
|
# <%#-*- coding: Big5 -*-%>
|
|
# <%# Comment. %>
|
|
# TEMPLATE
|
|
# erb = ERB.new(template)
|
|
# template.encoding # => #<Encoding:UTF-8>
|
|
# erb.encoding # => #<Encoding:Big5>
|
|
# erb.result.encoding # => #<Encoding:Big5>
|
|
# ```
|
|
#
|
|
# ## Error Reporting
|
|
#
|
|
# Consider this template (containing an error):
|
|
#
|
|
# ```
|
|
# template = '<%= nosuch %>'
|
|
# erb = ERB.new(template)
|
|
# ```
|
|
#
|
|
# When \ERB reports an error,
|
|
# it includes a file name (if available) and a line number;
|
|
# the file name comes from method #filename, the line number from method #lineno.
|
|
#
|
|
# Initially, those values are `nil` and `0`, respectively;
|
|
# these initial values are reported as `'(erb)'` and `1`, respectively:
|
|
#
|
|
# ```
|
|
# erb.filename # => nil
|
|
# erb.lineno # => 0
|
|
# erb.result
|
|
# (erb):1:in '<main>': undefined local variable or method 'nosuch' for main (NameError)
|
|
# ```
|
|
#
|
|
# You can use methods #filename= and #lineno= to assign values
|
|
# that are more meaningful in your context:
|
|
#
|
|
# ```
|
|
# erb.filename = 't.txt'
|
|
# erb.lineno = 555
|
|
# erb.result
|
|
# t.txt:556:in '<main>': undefined local variable or method 'nosuch' for main (NameError)
|
|
# ```
|
|
#
|
|
# You can use method #location= to set both values:
|
|
#
|
|
# ```
|
|
# erb.location = ['u.txt', 999]
|
|
# erb.result
|
|
# u.txt:1000:in '<main>': undefined local variable or method 'nosuch' for main (NameError)
|
|
# ```
|
|
#
|
|
# ## Plain Text with Embedded Ruby
|
|
#
|
|
# Here's a plain-text template;
|
|
# it uses the literal notation `'%q{ ... }'` to define the template
|
|
# (see [%q literals][%q literals]);
|
|
# this avoids problems with backslashes.
|
|
#
|
|
# ```
|
|
# 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
|
|
# }
|
|
# ```
|
|
#
|
|
# The template will need these:
|
|
#
|
|
# ```
|
|
# to = 'Community Spokesman <spokesman@ruby_community.org>'
|
|
# priorities = [ 'Run Ruby Quiz',
|
|
# 'Document Modules',
|
|
# 'Answer Questions on Ruby Talk' ]
|
|
# ```
|
|
#
|
|
# Finally, create the \ERB object and get the result
|
|
#
|
|
# ```
|
|
# 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
|
|
# ```
|
|
#
|
|
# ## HTML with Embedded Ruby
|
|
#
|
|
# This example shows an HTML template.
|
|
#
|
|
# First, here's a custom class, `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
|
|
# ```
|
|
#
|
|
# The template below will need these values:
|
|
#
|
|
# ```
|
|
# 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!')
|
|
# ```
|
|
#
|
|
# Here's the 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
|
|
# ```
|
|
#
|
|
# Finally, create the \ERB object and get the result (omitting some blank lines):
|
|
#
|
|
# ```
|
|
# 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>
|
|
# ```
|
|
#
|
|
#
|
|
# ## Other Template Processors
|
|
#
|
|
# Various Ruby projects have their own template processors.
|
|
# The Ruby Processing System [RDoc][rdoc], for example, has one that can be used elsewhere.
|
|
#
|
|
# Other popular template processors may found in the [Template Engines][template engines] page
|
|
# of the Ruby Toolbox.
|
|
#
|
|
# [%q literals]: https://docs.ruby-lang.org/en/master/syntax/literals_rdoc.html#label-25q-3A+Non-Interpolable+String+Literals
|
|
# [augmented binding]: rdoc-ref:ERB@Augmented+Binding
|
|
# [binding object]: https://docs.ruby-lang.org/en/master/Binding.html
|
|
# [comment tags]: rdoc-ref:ERB@Comment+Tags
|
|
# [default binding]: rdoc-ref:ERB@Default+Binding
|
|
# [encoding]: https://docs.ruby-lang.org/en/master/Encoding.html
|
|
# [execution tags]: rdoc-ref:ERB@Execution+Tags
|
|
# [expression tags]: rdoc-ref:ERB@Expression+Tags
|
|
# [kernel#binding]: https://docs.ruby-lang.org/en/master/Kernel.html#method-i-binding
|
|
# [local binding]: rdoc-ref:ERB@Local+Binding
|
|
# [magic comments]: https://docs.ruby-lang.org/en/master/syntax/comments_rdoc.html#label-Magic+Comments
|
|
# [rdoc]: https://ruby.github.io/rdoc
|
|
# [sprintf]: https://docs.ruby-lang.org/en/master/Kernel.html#method-i-sprintf
|
|
# [template engines]: https://www.ruby-toolbox.com/categories/template_engines
|
|
# [template processor]: https://en.wikipedia.org/wiki/Template_processor
|
|
#
|
|
class ERB
|
|
# :markup: markdown
|
|
#
|
|
# :call-seq:
|
|
# self.version -> string
|
|
#
|
|
# Returns the string \ERB version.
|
|
def self.version
|
|
VERSION
|
|
end
|
|
|
|
# :markup: markdown
|
|
#
|
|
# :call-seq:
|
|
# ERB.new(template, trim_mode: nil, eoutvar: '_erbout')
|
|
#
|
|
# Returns a new \ERB object containing the given string +template+.
|
|
#
|
|
# For details about `template`, its embedded tags, and generated results, see ERB.
|
|
#
|
|
# **Keyword Argument `trim_mode`**
|
|
#
|
|
# You can use keyword argument `trim_mode: '%'`
|
|
# to enable the [shorthand format][shorthand format] for execution tags.
|
|
#
|
|
# This value allows [blank line control][blank line control]:
|
|
#
|
|
# - `'-'`: Omit each blank line ending with `'%>'`.
|
|
#
|
|
# Other values allow [newline control][newline control]:
|
|
#
|
|
# - `'>'`: Omit newline for each line ending with `'%>'`.
|
|
# - `'<>'`: Omit newline for each line starting with `'<%'` and ending with `'%>'`.
|
|
#
|
|
# You can also [combine trim modes][combine trim modes].
|
|
#
|
|
# **Keyword Argument `eoutvar`**
|
|
#
|
|
# The string value of keyword argument `eoutvar` specifies the name of the variable
|
|
# that method #result uses to construct its result string;
|
|
# see #src.
|
|
#
|
|
# This is useful when you need to run multiple \ERB templates through the same binding
|
|
# and/or when you want to control where output ends up.
|
|
#
|
|
# It's good practice to choose a variable name that begins with an underscore: `'_'`.
|
|
#
|
|
# [blank line control]: rdoc-ref:ERB@Suppressing+Unwanted+Blank+Lines
|
|
# [combine trim modes]: rdoc-ref:ERB@Combining+Trim+Modes
|
|
# [newline control]: rdoc-ref:ERB@Suppressing+Unwanted+Newlines
|
|
# [shorthand format]: rdoc-ref:ERB@Shorthand+Format+for+Execution+Tags
|
|
#
|
|
def initialize(str, trim_mode: nil, eoutvar: '_erbout')
|
|
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
|
|
|
|
# :markup: markdown
|
|
#
|
|
# :call-seq:
|
|
# make_compiler -> erb_compiler
|
|
#
|
|
# Returns a new ERB::Compiler with the given `trim_mode`;
|
|
# for `trim_mode` values, see ERB.new:
|
|
#
|
|
# ```
|
|
# ERB.new('').make_compiler(nil)
|
|
# # => #<ERB::Compiler:0x000001cff9467678 @insert_cmd="print", @percent=false, @post_cmd=[], @pre_cmd=[], @put_cmd="print", @trim_mode=nil>
|
|
# ```
|
|
#
|
|
def make_compiler(trim_mode)
|
|
ERB::Compiler.new(trim_mode)
|
|
end
|
|
|
|
# :markup: markdown
|
|
#
|
|
# Returns the Ruby code that, when executed, generates the result;
|
|
# the code is executed by method #result,
|
|
# and by its wrapper methods #result_with_hash and #run:
|
|
#
|
|
# ```
|
|
# template = 'The time is <%= Time.now %>.'
|
|
# erb = ERB.new(template)
|
|
# erb.src
|
|
# # => "#coding:UTF-8\n_erbout = +''; _erbout.<< \"The time is \".freeze; _erbout.<<(( Time.now ).to_s); _erbout.<< \".\".freeze; _erbout"
|
|
# erb.result
|
|
# # => "The time is 2025-09-18 15:58:08 -0500."
|
|
# ```
|
|
#
|
|
# In a more readable format:
|
|
#
|
|
# ```
|
|
# # puts erb.src.split('; ')
|
|
# # #coding:UTF-8
|
|
# # _erbout = +''
|
|
# # _erbout.<< "The time is ".freeze
|
|
# # _erbout.<<(( Time.now ).to_s)
|
|
# # _erbout.<< ".".freeze
|
|
# # _erbout
|
|
# ```
|
|
#
|
|
# Variable `_erbout` is used to store the intermediate results in the code;
|
|
# the name `_erbout` is the default in ERB.new,
|
|
# and can be changed via keyword argument `eoutvar`:
|
|
#
|
|
# ```
|
|
# erb = ERB.new(template, eoutvar: '_foo')
|
|
# puts template.src.split('; ')
|
|
# #coding:UTF-8
|
|
# _foo = +''
|
|
# _foo.<< "The time is ".freeze
|
|
# _foo.<<(( Time.now ).to_s)
|
|
# _foo.<< ".".freeze
|
|
# _foo
|
|
# ```
|
|
#
|
|
attr_reader :src
|
|
|
|
# :markup: markdown
|
|
#
|
|
# Returns the encoding of `self`;
|
|
# see [Encodings][encodings]:
|
|
#
|
|
# [encodings]: rdoc-ref:ERB@Encodings
|
|
#
|
|
attr_reader :encoding
|
|
|
|
# :markup: markdown
|
|
#
|
|
# Sets or returns the file name to be used in reporting errors;
|
|
# see [Error Reporting][error reporting].
|
|
#
|
|
# [error reporting]: rdoc-ref:ERB@Error+Reporting
|
|
attr_accessor :filename
|
|
|
|
# :markup: markdown
|
|
#
|
|
# Sets or returns the line number to be used in reporting errors;
|
|
# see [Error Reporting][error reporting].
|
|
#
|
|
# [error reporting]: rdoc-ref:ERB@Error+Reporting
|
|
attr_accessor :lineno
|
|
|
|
# :markup: markdown
|
|
#
|
|
# :call-seq:
|
|
# location = [filename, lineno] => [filename, lineno]
|
|
# location = filename -> filename
|
|
#
|
|
# Sets the values of #filename and, if given, #lineno;
|
|
# see [Error Reporting][error reporting].
|
|
#
|
|
# [error reporting]: rdoc-ref:ERB@Error+Reporting
|
|
def location=((filename, lineno))
|
|
@filename = filename
|
|
@lineno = lineno if lineno
|
|
end
|
|
|
|
# :markup: markdown
|
|
#
|
|
# :call-seq:
|
|
# set_eoutvar(compiler, eoutvar = '_erbout') -> [eoutvar]
|
|
#
|
|
# Sets the `eoutvar` value in the ERB::Compiler object `compiler`;
|
|
# returns a 1-element array containing the value of `eoutvar`:
|
|
#
|
|
# ```
|
|
# template = ERB.new('')
|
|
# compiler = template.make_compiler(nil)
|
|
# pp compiler
|
|
# #<ERB::Compiler:0x000001cff8a9aa00
|
|
# @insert_cmd="print",
|
|
# @percent=false,
|
|
# @post_cmd=[],
|
|
# @pre_cmd=[],
|
|
# @put_cmd="print",
|
|
# @trim_mode=nil>
|
|
# template.set_eoutvar(compiler, '_foo') # => ["_foo"]
|
|
# pp compiler
|
|
# #<ERB::Compiler:0x000001cff8a9aa00
|
|
# @insert_cmd="_foo.<<",
|
|
# @percent=false,
|
|
# @post_cmd=["_foo"],
|
|
# @pre_cmd=["_foo = +''"],
|
|
# @put_cmd="_foo.<<",
|
|
# @trim_mode=nil>
|
|
# ```
|
|
#
|
|
def set_eoutvar(compiler, eoutvar = '_erbout')
|
|
compiler.put_cmd = "#{eoutvar}.<<"
|
|
compiler.insert_cmd = "#{eoutvar}.<<"
|
|
compiler.pre_cmd = ["#{eoutvar} = +''"]
|
|
compiler.post_cmd = [eoutvar]
|
|
end
|
|
|
|
# :markup: markdown
|
|
#
|
|
# :call-seq:
|
|
# run(binding = new_toplevel) -> nil
|
|
#
|
|
# Like #result, but prints the result string (instead of returning it);
|
|
# returns `nil`.
|
|
def run(b=new_toplevel)
|
|
print self.result(b)
|
|
end
|
|
|
|
# :markup: markdown
|
|
#
|
|
# :call-seq:
|
|
# result(binding = new_toplevel) -> new_string
|
|
#
|
|
# Returns the string result formed by processing \ERB tags found in the stored template in `self`.
|
|
#
|
|
# With no argument given, uses the default binding;
|
|
# see [Default Binding][default binding].
|
|
#
|
|
# With argument `binding` given, uses the local binding;
|
|
# see [Local Binding][local binding].
|
|
#
|
|
# See also #result_with_hash.
|
|
#
|
|
# [default binding]: rdoc-ref:ERB@Default+Binding
|
|
# [local binding]: rdoc-ref:ERB@Local+Binding
|
|
#
|
|
def result(b=new_toplevel)
|
|
unless @_init.equal?(self.class.singleton_class)
|
|
raise ArgumentError, "not initialized"
|
|
end
|
|
eval(@src, b, (@filename || '(erb)'), @lineno)
|
|
end
|
|
|
|
# :markup: markdown
|
|
#
|
|
# :call-seq:
|
|
# result_with_hash(hash) -> new_string
|
|
#
|
|
# Returns the string result formed by processing \ERB tags found in the stored string in `self`;
|
|
# see [Augmented Binding][augmented binding].
|
|
#
|
|
# See also #result.
|
|
#
|
|
# [augmented binding]: rdoc-ref:ERB@Augmented+Binding
|
|
#
|
|
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
|
|
|
|
# :markup: markdown
|
|
#
|
|
# :call-seq:
|
|
# new_toplevel(symbols) -> new_binding
|
|
#
|
|
# Returns a new binding based on `TOPLEVEL_BINDING`;
|
|
# used to create a default binding for a call to #result.
|
|
#
|
|
# See [Default Binding][default binding].
|
|
#
|
|
# Argument `symbols` is an array of symbols;
|
|
# each symbol `symbol` is defined as a new variable to hide and
|
|
# prevent it from overwriting a variable of the same name already
|
|
# defined within the binding.
|
|
#
|
|
# [default binding]: rdoc-ref:ERB@Default+Binding
|
|
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
|
|
private :new_toplevel
|
|
|
|
# :markup: markdown
|
|
#
|
|
# :call-seq:
|
|
# def_method(module, method_signature, filename = '(ERB)') -> method_name
|
|
#
|
|
# Creates and returns a new instance method in the given module `module`;
|
|
# returns the method name as a symbol.
|
|
#
|
|
# The method is created from the given `method_signature`,
|
|
# which consists of the method name and its argument names (if any).
|
|
#
|
|
# The `filename` sets the value of #filename;
|
|
# see [Error Reporting][error reporting].
|
|
#
|
|
# [error reporting]: rdoc-ref:ERB@Error+Reporting
|
|
#
|
|
# ```
|
|
# template = '<%= arg1 %> <%= arg2 %>'
|
|
# erb = ERB.new(template)
|
|
# MyModule = Module.new
|
|
# erb.def_method(MyModule, 'render(arg1, arg2)') # => :render
|
|
# class MyClass; include MyModule; end
|
|
# MyClass.new.render('foo', 123) # => "foo 123"
|
|
# ```
|
|
#
|
|
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
|
|
|
|
# :markup: markdown
|
|
#
|
|
# :call-seq:
|
|
# def_module(method_name = 'erb') -> new_module
|
|
#
|
|
# Returns a new nameless module that has instance method `method_name`.
|
|
#
|
|
# ```
|
|
# template = '<%= arg1 %> <%= arg2 %>'
|
|
# erb = ERB.new(template)
|
|
# MyModule = template.def_module('render(arg1, arg2)')
|
|
# class MyClass
|
|
# include MyModule
|
|
# end
|
|
# MyClass.new.render('foo', 123)
|
|
# # => "foo 123"
|
|
# ```
|
|
#
|
|
def def_module(methodname='erb')
|
|
mod = Module.new
|
|
def_method(mod, methodname, @filename || '(ERB)')
|
|
mod
|
|
end
|
|
|
|
# :markup: markdown
|
|
#
|
|
# :call-seq:
|
|
# def_class(super_class = Object, method_name = 'result') -> new_class
|
|
#
|
|
# Returns a new nameless class whose superclass is `super_class`,
|
|
# and which has instance method `method_name`.
|
|
#
|
|
# Create a template from HTML that has embedded expression tags that use `@arg1` and `@arg2`:
|
|
#
|
|
# ```
|
|
# html = <<TEMPLATE
|
|
# <html>
|
|
# <body>
|
|
# <p><%= @arg1 %></p>
|
|
# <p><%= @arg2 %></p>
|
|
# </body>
|
|
# </html>
|
|
# TEMPLATE
|
|
# template = ERB.new(html)
|
|
# ```
|
|
#
|
|
# Create a base class that has `@arg1` and `@arg2`:
|
|
#
|
|
# ```
|
|
# class MyBaseClass
|
|
# def initialize(arg1, arg2)
|
|
# @arg1 = arg1
|
|
# @arg2 = arg2
|
|
# end
|
|
# end
|
|
# ```
|
|
#
|
|
# Use method #def_class to create a subclass that has method `:render`:
|
|
#
|
|
# ```
|
|
# MySubClass = template.def_class(MyBaseClass, :render)
|
|
# ```
|
|
#
|
|
# Generate the result:
|
|
#
|
|
# ```
|
|
# puts MySubClass.new('foo', 123).render
|
|
# <html>
|
|
# <body>
|
|
# <p>foo</p>
|
|
# <p>123</p>
|
|
# </body>
|
|
# </html>
|
|
# ```
|
|
#
|
|
def def_class(superklass=Object, methodname='result')
|
|
cls = Class.new(superklass)
|
|
def_method(cls, methodname, @filename || '(ERB)')
|
|
cls
|
|
end
|
|
end
|