From 0d39336f6c99de57357fa4a2aa150a5c048840ba Mon Sep 17 00:00:00 2001 From: matz Date: Thu, 25 Nov 2004 15:50:18 +0000 Subject: * io.c (io_read): move StringValue() check before GetOpenFile(). [ruby-dev:24959] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_8@7377 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- lib/erb.rb | 379 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 364 insertions(+), 15 deletions(-) (limited to 'lib/erb.rb') diff --git a/lib/erb.rb b/lib/erb.rb index 44529bf325..d9fdc7456a 100644 --- a/lib/erb.rb +++ b/lib/erb.rb @@ -1,19 +1,254 @@ -# Tiny eRuby --- ERB2 -# Copyright (c) 1999-2000,2002,2003 Masatoshi SEKI +# = ERB -- Ruby Templating +# +# Author:: Masatoshi SEKI +# Documentation:: James Edward Gray II and Gavin Sinclair +# +# 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. +# +# = ERB -- Ruby Templating +# +# == Introduction +# +# ERB provides an easy to use but powerful templating system for Ruby. Using +# ERB, actual Ruby code can be added to any plain text document for the +# purposes of generating document information details and/or flow control. +# +# A very simple example is this: +# +# require 'erb' +# +# x = 42 +# template = ERB.new <<-EOF +# The value of x is: <%= x %> +# EOF +# puts template.result(binding) +# +# Prints: The value of x is: 42 +# +# More complex examples are given below. +# +# +# == Recognized Tags +# +# ERB recognizes certain tags in the provided template and converts them based +# on the rules below: +# +# <% Ruby code -- inline with output %> +# <%= Ruby expression -- replace with result %> +# <%# comment -- ignored -- useful in testing %> +# % 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 +# +# All other text is passed through ERB filtering unchanged. +# +# +# == Options +# +# There are several settings you can change when you use ERB: +# * the nature of the tags that are recognized; +# * the value of $SAFE under which the template is run; +# * the binding used to resolve local variables in the template. +# +# See the ERB.new and ERB#result methods for more detail. +# +# +# == Examples +# +# === Plain Text +# +# ERB is useful for any generic templating situation. Note that in this example, we use the +# convenient "% at start of line" tag, and we quote the template literally with +# %q{...} to avoid trouble with the backslash. +# +# require "erb" +# +# # Create template. +# template = %q{ +# From: James Edward Gray II +# 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, 0, "%<>") +# +# # Set up template data. +# to = "Community Spokesman " +# priorities = [ "Run Ruby Quiz", +# "Document Modules", +# "Answer Questions on Ruby Talk" ] +# +# # Produce result. +# email = message.result +# puts email +# +# Generates: +# +# From: James Edward Gray II +# To: Community Spokesman +# 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 in HTML +# +# ERB is often used in .rhtml files (HTML with embedded Ruby). Notice the need in +# this example to provide a special binding when the template is run, so that the instance +# variables in the Product object can be resolved. +# +# 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 = %{ +# +# Ruby Toys -- <%= @name %> +# +# +#

<%= @name %> (<%= @code %>)

+#

<%= @desc %>

+# +#
    +# <% @features.each do |f| %> +#
  • <%= f %>
  • +# <% end %> +#
+# +#

+# <% if @cost < 10 %> +# Only <%= @cost %>!!! +# <% else %> +# Call for a price, today! +# <% end %> +#

+# +# +# +# }.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) +# +# Generates (some blank lines removed): +# +# +# Ruby Toys -- Rubysapien +# +# +#

Rubysapien (TZ-1002)

+#

Geek's Best Friend! Responds to Ruby commands...

+# +#
    +#
  • Listens for verbal commands in the Ruby language!
  • +#
  • Ignores Perl, Java, and all C variants.
  • +#
  • Karate-Chop Action!!!
  • +#
  • Matz signature on left leg.
  • +#
  • Gem studded eyes... Rubies, of course!
  • +#
+# +#

+# Call for a price, today! +#

+# +# +# +# +# +# == Notes +# +# There are a variety of templating solutions available in various Ruby projects: +# * ERB's big brother, eRuby, works the same but is written in C for speed; +# * Amrita (smart at producing HTML/XML); +# * cs/Template (written in C for speed); +# * RDoc, distributed with Ruby, uses its own template engine, which can be reused elsewhere; +# * and others; search the RAA. +# +# Rails, the web application framework, uses ERB to create views. +# class ERB Revision = '$Date$' #' + # Returns revision information for the erb.rb module. def self.version "erb.rb [2.0.4 #{ERB::Revision.split[1]}]" end end +#-- # ERB::Compiler class ERB - class Compiler - class PercentLine + class Compiler # :nodoc: + class PercentLine # :nodoc: def initialize(str) @value = str end @@ -21,7 +256,7 @@ class ERB alias :to_s :value end - class Scanner + class Scanner # :nodoc: SplitRegexp = /(<%%)|(%%>)|(<%=)|(<%#)|(<%)|(%>)|(\n)/ @scanner_map = {} @@ -47,7 +282,7 @@ class ERB def scan; end end - class TrimScanner < Scanner + class TrimScanner < Scanner # :nodoc: TrimSplitRegexp = /(<%%)|(%%>)|(<%=)|(<%#)|(<%)|(%>\n)|(%>)|(\n)/ def initialize(src, trim_mode, percent) @@ -155,7 +390,7 @@ class ERB Scanner.default_scanner = TrimScanner - class SimpleScanner < Scanner + class SimpleScanner < Scanner # :nodoc: def scan @src.each do |line| line.split(SplitRegexp).each do |token| @@ -170,7 +405,7 @@ class ERB begin require 'strscan' - class SimpleScanner2 < Scanner + class SimpleScanner2 < Scanner # :nodoc: def scan stag_reg = /(.*?)(<%%|<%=|<%#|<%|\n|\z)/ etag_reg = /(.*?)(%%>|%>|\n|\z)/ @@ -186,7 +421,7 @@ class ERB end Scanner.regist_scanner(SimpleScanner2, nil, false) - class PercentScanner < Scanner + class PercentScanner < Scanner # :nodoc: def scan new_line = true stag_reg = /(.*?)(<%%|<%=|<%#|<%|\n|\z)/ @@ -214,7 +449,7 @@ class ERB end Scanner.regist_scanner(PercentScanner, nil, true) - class ExplicitScanner < Scanner + class ExplicitScanner < Scanner # :nodoc: def scan new_line = true stag_reg = /(.*?)(<%%|<%=|<%#|<%-|<%|\n|\z)/ @@ -250,7 +485,7 @@ class ERB rescue LoadError end - class Buffer + class Buffer # :nodoc: def initialize(compiler) @compiler = compiler @line = [] @@ -380,8 +615,74 @@ class ERB end end +#-- # ERB class ERB + # + # Constructs a new ERB object with the template specified in _str_. + # + # An ERB object works by building a chunk of Ruby code that will output + # the completed template when run. If _safe_level_ is set to a non-nil value, + # ERB code will be run in a separate thread with $SAFE set to the + # provided level. + # + # If _trim_mode_ is passed a String containing one or more of the following + # modifiers, ERB will adjust its code generation as listed: + # + # % enables Ruby code processing for lines beginning with % + # <> omit newline for lines starting with <% and ending in %> + # > omit newline for lines ending in %> + # + # _eoutvar_ can be used to set the name of the variable ERB will build up + # its output in. 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. Pass the name of the variable to be used inside a String. + # + # === Example + # + # 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 variebles + # ERB.new(<<-'END_PRODUCT'.gsub(/^\s+/, ""), 0, "", "@product").result b + # <%= PRODUCT[:name] %> + # <%= PRODUCT[:desc] %> + # END_PRODUCT + # ERB.new(<<-'END_PRICE'.gsub(/^\s+/, ""), 0, "", "@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 + # + # _Generates_ + # + # Chicken Fried Steak + # A well messages pattie, breaded and fried. + # + # Chicken Fried Steak -- 9.95 + # A well messages pattie, breaded and fried. + # def initialize(str, safe_level=nil, trim_mode=nil, eoutvar='_erbout') @safe_level = safe_level compiler = ERB::Compiler.new(trim_mode) @@ -389,9 +690,19 @@ class ERB @src = compiler.compile(str) @filename = nil end + + # The Ruby code generated by ERB attr_reader :src + + # The optional _filename_ argument passed to Kernel#eval when the ERB code + # is run attr_accessor :filename + # + # Can be used to set _eoutvar_ as described in ERB#new. It's probably easier + # to just use the constructor though, since calling this method requires the + # setup of an ERB _compiler_ object. + # def set_eoutvar(compiler, eoutvar = '_erbout') compiler.put_cmd = "#{eoutvar}.concat" @@ -406,10 +717,19 @@ class ERB compiler.post_cmd = cmd end + # Generate results and print them. (see ERB#result) def run(b=TOPLEVEL_BINDING) print self.result(b) end + # + # Executes the generated ERB code to produce a completed template, returning + # the results of that code. (See ERB#new for details on how this process can + # be affected by _safe_level_.) + # + # _b_ accepts a Binding or Proc object which is used to set the context of + # code evaluation. + # def result(b=TOPLEVEL_BINDING) if @safe_level th = Thread.start { @@ -422,32 +742,58 @@ class ERB end end - def def_method(mod, methodname, fname='(ERB)') + def def_method(mod, methodname, fname='(ERB)') # :nodoc: mod.module_eval("def #{methodname}\n" + self.src + "\nend\n", fname, 0) end - def def_module(methodname='erb') + def def_module(methodname='erb') # :nodoc: mod = Module.new def_method(mod, methodname) mod end - def def_class(superklass=Object, methodname='result') + def def_class(superklass=Object, methodname='result') # :nodoc: cls = Class.new(superklass) def_method(cls, methodname) cls end end +#-- # ERB::Util class ERB + # A utility module for conversion routines, often handy in HTML generation. module Util public + # + # A utility method for escaping HTML tag characters in _s_. + # + # require "erb" + # include ERB::Util + # + # puts html_escape("is a > 0 & a < 10?") + # + # _Generates_ + # + # is a > 0 & a < 10? + # def html_escape(s) s.to_s.gsub(/&/, "&").gsub(/\"/, """).gsub(/>/, ">").gsub(/