mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@22784 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
		
			
				
	
	
		
			902 lines
		
	
	
	
		
			22 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
			
		
		
	
	
			902 lines
		
	
	
	
		
			22 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
# = 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)
 | 
						|
#
 | 
						|
# <em>Prints:</em> 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 <tt>$SAFE</tt> 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.
 | 
						|
#
 | 
						|
# == Character encodings
 | 
						|
#
 | 
						|
# ERB (or ruby code generated by ERB) returns a string in the same
 | 
						|
# character encoding as the input string.  When the input string has
 | 
						|
# a magic comment, however, it returns a string in the encoding specified
 | 
						|
# by the magic comment.
 | 
						|
#
 | 
						|
#   # -*- coding: UTF-8 -*-
 | 
						|
#   require 'erb'
 | 
						|
#
 | 
						|
#   template = ERB.new <<EOF
 | 
						|
#   <%#-*- coding: Big5 -*-%>
 | 
						|
#     \_\_ENCODING\_\_ is <%= \_\_ENCODING\_\_ %>.
 | 
						|
#   EOF
 | 
						|
#   puts template.result
 | 
						|
#
 | 
						|
# <em>Prints:</em> \_\_ENCODING\_\_ is Big5.
 | 
						|
#
 | 
						|
#
 | 
						|
# == 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
 | 
						|
# <tt>%q{...}</tt> to avoid trouble with the backslash.
 | 
						|
#
 | 
						|
#   require "erb"
 | 
						|
#
 | 
						|
#   # Create template.
 | 
						|
#   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
 | 
						|
#   }.gsub(/^  /, '')
 | 
						|
#
 | 
						|
#   message = ERB.new(template, 0, "%<>")
 | 
						|
#
 | 
						|
#   # 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
 | 
						|
#
 | 
						|
# <i>Generates:</i>
 | 
						|
#
 | 
						|
#   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 in HTML
 | 
						|
#
 | 
						|
# ERB is often used in <tt>.rhtml</tt> 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 = %{
 | 
						|
#     <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)
 | 
						|
#
 | 
						|
# <i>Generates (some blank lines removed):</i>
 | 
						|
#
 | 
						|
#    <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>
 | 
						|
#
 | 
						|
#
 | 
						|
# == 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.1.0 #{ERB::Revision.split[1]}]"
 | 
						|
  end
 | 
						|
end
 | 
						|
 | 
						|
#--
 | 
						|
# ERB::Compiler
 | 
						|
class ERB
 | 
						|
  class Compiler # :nodoc:
 | 
						|
    class PercentLine # :nodoc:
 | 
						|
      def initialize(str)
 | 
						|
        @value = str
 | 
						|
      end
 | 
						|
      attr_reader :value
 | 
						|
      alias :to_s :value
 | 
						|
 | 
						|
      def empty?
 | 
						|
        @value.empty?
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    class Scanner # :nodoc:
 | 
						|
      @scanner_map = {}
 | 
						|
      def self.regist_scanner(klass, trim_mode, percent)
 | 
						|
	@scanner_map[[trim_mode, percent]] = klass
 | 
						|
      end
 | 
						|
 | 
						|
      def self.default_scanner=(klass)
 | 
						|
	@default_scanner = klass
 | 
						|
      end
 | 
						|
 | 
						|
      def self.make_scanner(src, trim_mode, percent)
 | 
						|
	klass = @scanner_map.fetch([trim_mode, percent], @default_scanner)
 | 
						|
	klass.new(src, trim_mode, percent)
 | 
						|
      end
 | 
						|
 | 
						|
      def initialize(src, trim_mode, percent)
 | 
						|
	@src = src
 | 
						|
	@stag = nil
 | 
						|
      end
 | 
						|
      attr_accessor :stag
 | 
						|
 | 
						|
      def scan; end
 | 
						|
    end
 | 
						|
 | 
						|
    class TrimScanner < Scanner # :nodoc:
 | 
						|
      def initialize(src, trim_mode, percent)
 | 
						|
	super
 | 
						|
	@trim_mode = trim_mode
 | 
						|
	@percent = percent
 | 
						|
	if @trim_mode == '>'
 | 
						|
	  @scan_line = self.method(:trim_line1)
 | 
						|
	elsif @trim_mode == '<>'
 | 
						|
	  @scan_line = self.method(:trim_line2)
 | 
						|
	elsif @trim_mode == '-'
 | 
						|
	  @scan_line = self.method(:explicit_trim_line)
 | 
						|
	else
 | 
						|
	  @scan_line = self.method(:scan_line)
 | 
						|
	end
 | 
						|
      end
 | 
						|
      attr_accessor :stag
 | 
						|
 | 
						|
      def scan(&block)
 | 
						|
	@stag = nil
 | 
						|
	if @percent
 | 
						|
	  @src.each_line do |line|
 | 
						|
	    percent_line(line, &block)
 | 
						|
	  end
 | 
						|
	else
 | 
						|
          @scan_line.call(@src, &block)
 | 
						|
	end
 | 
						|
	nil
 | 
						|
      end
 | 
						|
 | 
						|
      def percent_line(line, &block)
 | 
						|
	if @stag || line[0] != ?%
 | 
						|
	  return @scan_line.call(line, &block)
 | 
						|
	end
 | 
						|
 | 
						|
	line[0] = ''
 | 
						|
	if line[0] == ?%
 | 
						|
	  @scan_line.call(line, &block)
 | 
						|
	else
 | 
						|
          yield(PercentLine.new(line.chomp))
 | 
						|
	end
 | 
						|
      end
 | 
						|
 | 
						|
      def scan_line(line)
 | 
						|
        line.scan(/(.*?)(<%%|%%>|<%=|<%#|<%|%>|\n|\z)/m) do |tokens|
 | 
						|
          tokens.each do |token|
 | 
						|
            next if token.empty?
 | 
						|
            yield(token)
 | 
						|
          end
 | 
						|
	end
 | 
						|
      end
 | 
						|
 | 
						|
      def trim_line1(line)
 | 
						|
        line.scan(/(.*?)(<%%|%%>|<%=|<%#|<%|%>\n|%>|\n|\z)/m) do |tokens|
 | 
						|
          tokens.each do |token|
 | 
						|
            next if token.empty?
 | 
						|
            if token == "%>\n"
 | 
						|
              yield('%>')
 | 
						|
              yield(:cr)
 | 
						|
            else
 | 
						|
              yield(token)
 | 
						|
            end
 | 
						|
          end
 | 
						|
	end
 | 
						|
      end
 | 
						|
 | 
						|
      def trim_line2(line)
 | 
						|
	head = nil
 | 
						|
        line.scan(/(.*?)(<%%|%%>|<%=|<%#|<%|%>\n|%>|\n|\z)/m) do |tokens|
 | 
						|
          tokens.each do |token|
 | 
						|
            next if token.empty?
 | 
						|
            head = token unless head
 | 
						|
            if token == "%>\n"
 | 
						|
              yield('%>')
 | 
						|
              if is_erb_stag?(head)
 | 
						|
                yield(:cr)
 | 
						|
              else
 | 
						|
                yield("\n")
 | 
						|
              end
 | 
						|
              head = nil
 | 
						|
            else
 | 
						|
              yield(token)
 | 
						|
              head = nil if token == "\n"
 | 
						|
            end
 | 
						|
          end
 | 
						|
	end
 | 
						|
      end
 | 
						|
 | 
						|
      def explicit_trim_line(line)
 | 
						|
        line.scan(/(.*?)(^[ \t]*<%\-|<%\-|<%%|%%>|<%=|<%#|<%|-%>\n|-%>|%>|\z)/m) do |tokens|
 | 
						|
          tokens.each do |token|
 | 
						|
            next if token.empty?
 | 
						|
            if @stag.nil? && /[ \t]*<%-/ =~ token
 | 
						|
              yield('<%')
 | 
						|
            elsif @stag && token == "-%>\n"
 | 
						|
              yield('%>')
 | 
						|
              yield(:cr)
 | 
						|
            elsif @stag && token == '-%>'
 | 
						|
              yield('%>')
 | 
						|
            else
 | 
						|
              yield(token)
 | 
						|
            end
 | 
						|
          end
 | 
						|
        end
 | 
						|
      end
 | 
						|
 | 
						|
      ERB_STAG = %w(<%= <%# <%)
 | 
						|
      def is_erb_stag?(s)
 | 
						|
	ERB_STAG.member?(s)
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    Scanner.default_scanner = TrimScanner
 | 
						|
 | 
						|
    class SimpleScanner < Scanner # :nodoc:
 | 
						|
      def scan
 | 
						|
        @src.scan(/(.*?)(<%%|%%>|<%=|<%#|<%|%>|\n|\z)/m) do |tokens|
 | 
						|
          tokens.each do |token|
 | 
						|
            next if token.empty?
 | 
						|
            yield(token)
 | 
						|
          end
 | 
						|
	end
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    Scanner.regist_scanner(SimpleScanner, nil, false)
 | 
						|
 | 
						|
    begin
 | 
						|
      require 'strscan'
 | 
						|
      class SimpleScanner2 < Scanner # :nodoc:
 | 
						|
        def scan
 | 
						|
          stag_reg = /(.*?)(<%%|<%=|<%#|<%|\z)/m
 | 
						|
          etag_reg = /(.*?)(%%>|%>|\z)/m
 | 
						|
          scanner = StringScanner.new(@src)
 | 
						|
          while ! scanner.eos?
 | 
						|
            scanner.scan(@stag ? etag_reg : stag_reg)
 | 
						|
            yield(scanner[1])
 | 
						|
            yield(scanner[2])
 | 
						|
          end
 | 
						|
        end
 | 
						|
      end
 | 
						|
      Scanner.regist_scanner(SimpleScanner2, nil, false)
 | 
						|
 | 
						|
      class ExplicitScanner < Scanner # :nodoc:
 | 
						|
	def scan
 | 
						|
          stag_reg = /(.*?)(^[ \t]*<%-|<%%|<%=|<%#|<%-|<%|\z)/m
 | 
						|
          etag_reg = /(.*?)(%%>|-%>|%>|\z)/m
 | 
						|
          scanner = StringScanner.new(@src)
 | 
						|
          while ! scanner.eos?
 | 
						|
	    scanner.scan(@stag ? etag_reg : stag_reg)
 | 
						|
            yield(scanner[1])
 | 
						|
 | 
						|
            elem = scanner[2]
 | 
						|
            if /[ \t]*<%-/ =~ elem
 | 
						|
              yield('<%')
 | 
						|
            elsif elem == '-%>'
 | 
						|
	      yield('%>')
 | 
						|
	      yield(:cr) if scanner.scan(/(\n|\z)/)
 | 
						|
	    else
 | 
						|
	      yield(elem)
 | 
						|
	    end
 | 
						|
          end
 | 
						|
        end
 | 
						|
      end
 | 
						|
      Scanner.regist_scanner(ExplicitScanner, '-', false)
 | 
						|
 | 
						|
    rescue LoadError
 | 
						|
    end
 | 
						|
 | 
						|
    class Buffer # :nodoc:
 | 
						|
      def initialize(compiler, enc=nil)
 | 
						|
	@compiler = compiler
 | 
						|
	@line = []
 | 
						|
        @script = enc ? "#coding:#{enc.to_s}\n" : ""
 | 
						|
	@compiler.pre_cmd.each do |x|
 | 
						|
	  push(x)
 | 
						|
	end
 | 
						|
      end
 | 
						|
      attr_reader :script
 | 
						|
 | 
						|
      def push(cmd)
 | 
						|
	@line << cmd
 | 
						|
      end
 | 
						|
 | 
						|
      def cr
 | 
						|
	@script << (@line.join('; '))
 | 
						|
	@line = []
 | 
						|
	@script << "\n"
 | 
						|
      end
 | 
						|
 | 
						|
      def close
 | 
						|
	return unless @line
 | 
						|
	@compiler.post_cmd.each do |x|
 | 
						|
	  push(x)
 | 
						|
	end
 | 
						|
	@script << (@line.join('; '))
 | 
						|
	@line = nil
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    def content_dump(s)
 | 
						|
      n = s.count("\n")
 | 
						|
      if n > 0
 | 
						|
        s.dump + "\n" * n
 | 
						|
      else
 | 
						|
        s.dump
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    def compile(s)
 | 
						|
      enc = s.encoding
 | 
						|
      raise ArgumentError, "#{enc} is not ASCII compatible" if enc.dummy?
 | 
						|
      s = s.dup.force_encoding("ASCII-8BIT") # don't use constant Enoding::ASCII_8BIT for miniruby
 | 
						|
      enc = detect_magic_comment(s) || enc
 | 
						|
      out = Buffer.new(self, enc)
 | 
						|
 | 
						|
      content = ''
 | 
						|
      scanner = make_scanner(s)
 | 
						|
      scanner.scan do |token|
 | 
						|
        next if token.nil?
 | 
						|
        next if token == ''
 | 
						|
	if scanner.stag.nil?
 | 
						|
	  case token
 | 
						|
          when PercentLine
 | 
						|
	    out.push("#{@put_cmd} #{content_dump(content)}") if content.size > 0
 | 
						|
	    content = ''
 | 
						|
            out.push(token.to_s)
 | 
						|
            out.cr
 | 
						|
	  when :cr
 | 
						|
	    out.cr
 | 
						|
	  when '<%', '<%=', '<%#'
 | 
						|
	    scanner.stag = token
 | 
						|
	    out.push("#{@put_cmd} #{content_dump(content)}") if content.size > 0
 | 
						|
	    content = ''
 | 
						|
	  when "\n"
 | 
						|
	    content << "\n"
 | 
						|
	    out.push("#{@put_cmd} #{content_dump(content)}")
 | 
						|
	    content = ''
 | 
						|
	  when '<%%'
 | 
						|
	    content << '<%'
 | 
						|
	  else
 | 
						|
	    content << token
 | 
						|
	  end
 | 
						|
	else
 | 
						|
	  case token
 | 
						|
	  when '%>'
 | 
						|
	    case scanner.stag
 | 
						|
	    when '<%'
 | 
						|
	      if content[-1] == ?\n
 | 
						|
		content.chop!
 | 
						|
		out.push(content)
 | 
						|
		out.cr
 | 
						|
	      else
 | 
						|
		out.push(content)
 | 
						|
	      end
 | 
						|
	    when '<%='
 | 
						|
	      out.push("#{@insert_cmd}((#{content}).to_s)")
 | 
						|
	    when '<%#'
 | 
						|
	      # out.push("# #{content_dump(content)}")
 | 
						|
	    end
 | 
						|
	    scanner.stag = nil
 | 
						|
	    content = ''
 | 
						|
	  when '%%>'
 | 
						|
	    content << '%>'
 | 
						|
	  else
 | 
						|
	    content << token
 | 
						|
	  end
 | 
						|
	end
 | 
						|
      end
 | 
						|
      out.push("#{@put_cmd} #{content_dump(content)}") if content.size > 0
 | 
						|
      out.close
 | 
						|
      return out.script, enc
 | 
						|
    end
 | 
						|
 | 
						|
    def prepare_trim_mode(mode)
 | 
						|
      case mode
 | 
						|
      when 1
 | 
						|
	return [false, '>']
 | 
						|
      when 2
 | 
						|
	return [false, '<>']
 | 
						|
      when 0
 | 
						|
	return [false, nil]
 | 
						|
      when String
 | 
						|
	perc = mode.include?('%')
 | 
						|
	if mode.include?('-')
 | 
						|
	  return [perc, '-']
 | 
						|
	elsif mode.include?('<>')
 | 
						|
	  return [perc, '<>']
 | 
						|
	elsif mode.include?('>')
 | 
						|
	  return [perc, '>']
 | 
						|
	else
 | 
						|
	  [perc, nil]
 | 
						|
	end
 | 
						|
      else
 | 
						|
	return [false, nil]
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    def make_scanner(src)
 | 
						|
      Scanner.make_scanner(src, @trim_mode, @percent)
 | 
						|
    end
 | 
						|
 | 
						|
    def initialize(trim_mode)
 | 
						|
      @percent, @trim_mode = prepare_trim_mode(trim_mode)
 | 
						|
      @put_cmd = 'print'
 | 
						|
      @insert_cmd = @put_cmd
 | 
						|
      @pre_cmd = []
 | 
						|
      @post_cmd = []
 | 
						|
    end
 | 
						|
    attr_reader :percent, :trim_mode
 | 
						|
    attr_accessor :put_cmd, :insert_cmd, :pre_cmd, :post_cmd
 | 
						|
 | 
						|
    private
 | 
						|
    def detect_magic_comment(s)
 | 
						|
      if /\A<%#(.*)%>/ =~ s or (@percent and /\A%#(.*)/ =~ s)
 | 
						|
	comment = $1
 | 
						|
	comment = $1 if comment[/-\*-\s*(.*?)\s*-*-$/]
 | 
						|
	if %r"coding\s*[=:]\s*([[:alnum:]\-_]+)" =~ comment
 | 
						|
	  enc = $1.sub(/-(?:mac|dos|unix)/i, '')
 | 
						|
	  enc = Encoding.find(enc)
 | 
						|
	end
 | 
						|
      end
 | 
						|
    end
 | 
						|
  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 <b>$SAFE</b> 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 variables
 | 
						|
  #      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)
 | 
						|
    set_eoutvar(compiler, eoutvar)
 | 
						|
    @src, @enc = *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"
 | 
						|
    compiler.insert_cmd = "#{eoutvar}.concat"
 | 
						|
 | 
						|
    cmd = []
 | 
						|
    cmd.push "#{eoutvar} = ''"
 | 
						|
 | 
						|
    compiler.pre_cmd = cmd
 | 
						|
 | 
						|
    cmd = []
 | 
						|
    cmd.push("#{eoutvar}.force_encoding(__ENCODING__)")
 | 
						|
 | 
						|
    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
 | 
						|
      proc {
 | 
						|
	$SAFE = @safe_level
 | 
						|
	eval(@src, b, (@filename || '(erb)'), 0)
 | 
						|
      }.call
 | 
						|
    else
 | 
						|
      eval(@src, b, (@filename || '(erb)'), 0)
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  # Define _methodname_ as instance method of _mod_ from compiled ruby source.
 | 
						|
  #
 | 
						|
  # example:
 | 
						|
  #   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)
 | 
						|
  def def_method(mod, methodname, fname='(ERB)')
 | 
						|
    src = self.src
 | 
						|
    magic_comment = "#coding:#{@enc}\n"
 | 
						|
    mod.module_eval do
 | 
						|
      eval(magic_comment + "def #{methodname}\n" + src + "\nend\n", binding, fname, -2)
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  # Create unnamed module, define _methodname_ as instance method of it, and return it.
 | 
						|
  #
 | 
						|
  # example:
 | 
						|
  #   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
 | 
						|
  def def_module(methodname='erb')
 | 
						|
    mod = Module.new
 | 
						|
    def_method(mod, methodname, @filename || '(ERB)')
 | 
						|
    mod
 | 
						|
  end
 | 
						|
 | 
						|
  # Define unnamed class which has _methodname_ as instance method, and return it.
 | 
						|
  #
 | 
						|
  # example:
 | 
						|
  #   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()
 | 
						|
  def def_class(superklass=Object, methodname='result')
 | 
						|
    cls = Class.new(superklass)
 | 
						|
    def_method(cls, methodname, @filename || '(ERB)')
 | 
						|
    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(/</, "<")
 | 
						|
    end
 | 
						|
    alias h html_escape
 | 
						|
    module_function :h
 | 
						|
    module_function :html_escape
 | 
						|
 | 
						|
    #
 | 
						|
    # A utility method for encoding the String _s_ as a URL.
 | 
						|
    #
 | 
						|
    # 	require "erb"
 | 
						|
    # 	include ERB::Util
 | 
						|
    #
 | 
						|
    # 	puts url_encode("Programming Ruby:  The Pragmatic Programmer's Guide")
 | 
						|
    #
 | 
						|
    # _Generates_
 | 
						|
    #
 | 
						|
    # 	Programming%20Ruby%3A%20%20The%20Pragmatic%20Programmer%27s%20Guide
 | 
						|
    #
 | 
						|
    def url_encode(s)
 | 
						|
      s.to_s.dup.force_encoding("ASCII-8BIT").gsub(/[^a-zA-Z0-9_\-.]/n) {
 | 
						|
        sprintf("%%%02X", $&.unpack("C")[0])
 | 
						|
      }
 | 
						|
    end
 | 
						|
    alias u url_encode
 | 
						|
    module_function :u
 | 
						|
    module_function :url_encode
 | 
						|
  end
 | 
						|
end
 | 
						|
 | 
						|
#--
 | 
						|
# ERB::DefMethod
 | 
						|
class ERB
 | 
						|
  # Utility module to define eRuby script as instance method.
 | 
						|
  #
 | 
						|
  # === Example
 | 
						|
  #
 | 
						|
  # example.rhtml:
 | 
						|
  #   <% for item in @items %>
 | 
						|
  #   <b><%= item %></b>
 | 
						|
  #   <% end %>
 | 
						|
  #
 | 
						|
  # example.rb:
 | 
						|
  #   require 'erb'
 | 
						|
  #   class MyClass
 | 
						|
  #     extend ERB::DefMethod
 | 
						|
  #     def_erb_method('render()', 'example.rhtml')
 | 
						|
  #     def initialize(items)
 | 
						|
  #       @items = items
 | 
						|
  #     end
 | 
						|
  #   end
 | 
						|
  #   print MyClass.new([10,20,30]).render()
 | 
						|
  #
 | 
						|
  # result:
 | 
						|
  #
 | 
						|
  #   <b>10</b>
 | 
						|
  #
 | 
						|
  #   <b>20</b>
 | 
						|
  #
 | 
						|
  #   <b>30</b>
 | 
						|
  #
 | 
						|
  module DefMethod
 | 
						|
    public
 | 
						|
  # define _methodname_ as instance method of current module, using ERB object or eRuby file
 | 
						|
    def def_erb_method(methodname, erb_or_fname)
 | 
						|
      if erb_or_fname.kind_of? String
 | 
						|
        fname = erb_or_fname
 | 
						|
        erb = ERB.new(File.read(fname))
 | 
						|
        erb.def_method(self, methodname, fname)
 | 
						|
      else
 | 
						|
        erb = erb_or_fname
 | 
						|
        erb.def_method(self, methodname, erb.filename || '(ERB)')
 | 
						|
      end
 | 
						|
    end
 | 
						|
    module_function :def_erb_method
 | 
						|
  end
 | 
						|
end
 |