mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	 62f7424640
			
		
	
	
		62f7424640
		
	
	
	
	
		
			
			* lib/erb.rb (ERB#run, ERB#result): eval under isolated bindings for safe concurrent use. [ruby-core:47638] [Bug #7046] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37594 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
		
			
				
	
	
		
			997 lines
		
	
	
	
		
			26 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
			
		
		
	
	
			997 lines
		
	
	
	
		
			26 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
| # -*- coding: us-ascii -*-
 | |
| # = 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.
 | |
| 
 | |
| require "cgi/util"
 | |
| 
 | |
| #
 | |
| # = 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
 | |
|   # = ERB::Compiler
 | |
|   #
 | |
|   # Compiles ERB templates into Ruby code; the compiled code produces the
 | |
|   # template result when evaluated. ERB::Compiler provides hooks to define how
 | |
|   # generated output is handled.
 | |
|   #
 | |
|   # Internally ERB does something like this to generate the code returned by
 | |
|   # ERB#src:
 | |
|   #
 | |
|   #   compiler = ERB::Compiler.new('<>')
 | |
|   #   compiler.pre_cmd    = ["_erbout=''"]
 | |
|   #   compiler.put_cmd    = "_erbout.concat"
 | |
|   #   compiler.insert_cmd = "_erbout.concat"
 | |
|   #   compiler.post_cmd   = ["_erbout"]
 | |
|   #
 | |
|   #   code, enc = compiler.compile("Got <%= obj %>!\n")
 | |
|   #   puts code
 | |
|   #
 | |
|   # <i>Generates</i>:
 | |
|   #
 | |
|   #   #coding:UTF-8
 | |
|   #   _erbout=''; _erbout.concat "Got "; _erbout.concat(( obj ).to_s); _erbout.concat "!\n"; _erbout
 | |
|   #
 | |
|   # By default the output is sent to the print method.  For example:
 | |
|   #
 | |
|   #   compiler = ERB::Compiler.new('<>')
 | |
|   #   code, enc = compiler.compile("Got <%= obj %>!\n")
 | |
|   #   puts code
 | |
|   #
 | |
|   # <i>Generates</i>:
 | |
|   #
 | |
|   #   #coding:UTF-8
 | |
|   #   print "Got "; print(( obj ).to_s); print "!\n"
 | |
|   #
 | |
|   # == Evaluation
 | |
|   #
 | |
|   # The compiled code can be used in any context where the names in the code
 | |
|   # correctly resolve. Using the last example, each of these print 'Got It!'
 | |
|   #
 | |
|   # Evaluate using a variable:
 | |
|   #
 | |
|   #   obj = 'It'
 | |
|   #   eval code
 | |
|   #
 | |
|   # Evaluate using an input:
 | |
|   #
 | |
|   #   mod = Module.new
 | |
|   #   mod.module_eval %{
 | |
|   #     def get(obj)
 | |
|   #       #{code}
 | |
|   #     end
 | |
|   #   }
 | |
|   #   extend mod
 | |
|   #   get('It')
 | |
|   #
 | |
|   # Evaluate using an accessor:
 | |
|   #
 | |
|   #   klass = Class.new Object
 | |
|   #   klass.class_eval %{
 | |
|   #     attr_accessor :obj
 | |
|   #     def initialize(obj)
 | |
|   #       @obj = obj
 | |
|   #     end
 | |
|   #     def get_it
 | |
|   #       #{code}
 | |
|   #     end
 | |
|   #   }
 | |
|   #   klass.new('It').get_it
 | |
|   #
 | |
|   # Good! See also ERB#def_method, ERB#def_module, and ERB#def_class.
 | |
|   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) # :nodoc:
 | |
|       n = s.count("\n")
 | |
|       if n > 0
 | |
|         s.dump + "\n" * n
 | |
|       else
 | |
|         s.dump
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     # Compiles an ERB template into Ruby code.  Returns an array of the code
 | |
|     # and encoding like ["code", Encoding].
 | |
|     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) # :nodoc:
 | |
|       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) # :nodoc:
 | |
|       Scanner.make_scanner(src, @trim_mode, @percent)
 | |
|     end
 | |
| 
 | |
|     # Construct a new compiler using the trim_mode. See ERB#new for available
 | |
|     # trim modes.
 | |
|     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
 | |
| 
 | |
|     # The command to handle text that ends with a newline
 | |
|     attr_accessor :put_cmd
 | |
| 
 | |
|     # The command to handle text that is inserted prior to a newline
 | |
|     attr_accessor :insert_cmd
 | |
| 
 | |
|     # An array of commands prepended to compiled code
 | |
|     attr_accessor :pre_cmd
 | |
| 
 | |
|     # An array of commands appended to compiled code
 | |
|     attr_accessor :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=new_toplevel)
 | |
|     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=new_toplevel)
 | |
|     if @safe_level
 | |
|       proc {
 | |
|         $SAFE = @safe_level
 | |
|         eval(@src, b, (@filename || '(erb)'), 0)
 | |
|       }.call
 | |
|     else
 | |
|       eval(@src, b, (@filename || '(erb)'), 0)
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   def new_toplevel
 | |
|     # New binding each time *near* toplevel for unspecified runs
 | |
|     TOPLEVEL_BINDING.dup
 | |
|   end
 | |
|   private :new_toplevel
 | |
| 
 | |
|   # 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)
 | |
|       CGI.escapeHTML(s.to_s)
 | |
|     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
 |