Grammar and stream of consciousness cleanup for Channel and Scheduler rdoc
[rbx.git] / lib / erb.rb
blob616435f21c0ca861a5b7416614e940301906f401
1 # = ERB -- Ruby Templating
3 # Author:: Masatoshi SEKI
4 # Documentation:: James Edward Gray II and Gavin Sinclair
6 # See ERB for primary documentation and ERB::Util for a couple of utility
7 # routines.
9 # Copyright (c) 1999-2000,2002,2003 Masatoshi SEKI
11 # You can redistribute it and/or modify it under the same terms as Ruby.
14 # = ERB -- Ruby Templating
16 # == Introduction
18 # ERB provides an easy to use but powerful templating system for Ruby.  Using
19 # ERB, actual Ruby code can be added to any plain text document for the
20 # purposes of generating document information details and/or flow control.
22 # A very simple example is this:
24 #   require 'erb'
26 #   x = 42
27 #   template = ERB.new <<-EOF
28 #     The value of x is: <%= x %>
29 #   EOF
30 #   puts template.result(binding)
32 # <em>Prints:</em> The value of x is: 42
34 # More complex examples are given below.
37 # == Recognized Tags
39 # ERB recognizes certain tags in the provided template and converts them based
40 # on the rules below:
42 #   <% Ruby code -- inline with output %>
43 #   <%= Ruby expression -- replace with result %>
44 #   <%# comment -- ignored -- useful in testing %>
45 #   % a line of Ruby code -- treated as <% line %> (optional -- see ERB.new)
46 #   %% replaced with % if first thing on a line and % processing is used
47 #   <%% or %%> -- replace with <% or %> respectively
49 # All other text is passed through ERB filtering unchanged.
52 # == Options
54 # There are several settings you can change when you use ERB:
55 # * the nature of the tags that are recognized;
56 # * the value of <tt>$SAFE</tt> under which the template is run;
57 # * the binding used to resolve local variables in the template.
59 # See the ERB.new and ERB#result methods for more detail.
62 # == Examples
64 # === Plain Text
66 # ERB is useful for any generic templating situation.  Note that in this example, we use the
67 # convenient "% at start of line" tag, and we quote the template literally with
68 # <tt>%q{...}</tt> to avoid trouble with the backslash.
70 #   require "erb"
72 #   # Create template.
73 #   template = %q{
74 #     From:  James Edward Gray II <james@grayproductions.net>
75 #     To:  <%= to %>
76 #     Subject:  Addressing Needs
78 #     <%= to[/\w+/] %>:
80 #     Just wanted to send a quick note assuring that your needs are being
81 #     addressed.
83 #     I want you to know that my team will keep working on the issues,
84 #     especially:
86 #     <%# ignore numerous minor requests -- focus on priorities %>
87 #     % priorities.each do |priority|
88 #       * <%= priority %>
89 #     % end
91 #     Thanks for your patience.
93 #     James Edward Gray II
94 #   }.gsub(/^  /, '')
96 #   message = ERB.new(template, 0, "%<>")
98 #   # Set up template data.
99 #   to = "Community Spokesman <spokesman@ruby_community.org>"
100 #   priorities = [ "Run Ruby Quiz",
101 #                  "Document Modules",
102 #                  "Answer Questions on Ruby Talk" ]
104 #   # Produce result.
105 #   email = message.result
106 #   puts email
108 # <i>Generates:</i>
110 #   From:  James Edward Gray II <james@grayproductions.net>
111 #   To:  Community Spokesman <spokesman@ruby_community.org>
112 #   Subject:  Addressing Needs
114 #   Community:
116 #   Just wanted to send a quick note assuring that your needs are being addressed.
118 #   I want you to know that my team will keep working on the issues, especially:
120 #       * Run Ruby Quiz
121 #       * Document Modules
122 #       * Answer Questions on Ruby Talk
124 #   Thanks for your patience.
126 #   James Edward Gray II
128 # === Ruby in HTML
130 # ERB is often used in <tt>.rhtml</tt> files (HTML with embedded Ruby).  Notice the need in
131 # this example to provide a special binding when the template is run, so that the instance
132 # variables in the Product object can be resolved.
134 #   require "erb"
136 #   # Build template data class.
137 #   class Product
138 #     def initialize( code, name, desc, cost )
139 #       @code = code
140 #       @name = name
141 #       @desc = desc
142 #       @cost = cost
144 #       @features = [ ]
145 #     end
147 #     def add_feature( feature )
148 #       @features << feature
149 #     end
151 #     # Support templating of member data.
152 #     def get_binding
153 #       binding
154 #     end
156 #     # ...
157 #   end
159 #   # Create template.
160 #   template = %{
161 #     <html>
162 #       <head><title>Ruby Toys -- <%= @name %></title></head>
163 #       <body>
165 #         <h1><%= @name %> (<%= @code %>)</h1>
166 #         <p><%= @desc %></p>
168 #         <ul>
169 #           <% @features.each do |f| %>
170 #             <li><b><%= f %></b></li>
171 #           <% end %>
172 #         </ul>
174 #         <p>
175 #           <% if @cost < 10 %>
176 #             <b>Only <%= @cost %>!!!</b>
177 #           <% else %>
178 #              Call for a price, today!
179 #           <% end %>
180 #         </p>
182 #       </body>
183 #     </html>
184 #   }.gsub(/^  /, '')
186 #   rhtml = ERB.new(template)
188 #   # Set up template data.
189 #   toy = Product.new( "TZ-1002",
190 #                      "Rubysapien",
191 #                      "Geek's Best Friend!  Responds to Ruby commands...",
192 #                      999.95 )
193 #   toy.add_feature("Listens for verbal commands in the Ruby language!")
194 #   toy.add_feature("Ignores Perl, Java, and all C variants.")
195 #   toy.add_feature("Karate-Chop Action!!!")
196 #   toy.add_feature("Matz signature on left leg.")
197 #   toy.add_feature("Gem studded eyes... Rubies, of course!")
199 #   # Produce result.
200 #   rhtml.run(toy.get_binding)
202 # <i>Generates (some blank lines removed):</i>
204 #    <html>
205 #      <head><title>Ruby Toys -- Rubysapien</title></head>
206 #      <body>
208 #        <h1>Rubysapien (TZ-1002)</h1>
209 #        <p>Geek's Best Friend!  Responds to Ruby commands...</p>
211 #        <ul>
212 #            <li><b>Listens for verbal commands in the Ruby language!</b></li>
213 #            <li><b>Ignores Perl, Java, and all C variants.</b></li>
214 #            <li><b>Karate-Chop Action!!!</b></li>
215 #            <li><b>Matz signature on left leg.</b></li>
216 #            <li><b>Gem studded eyes... Rubies, of course!</b></li>
217 #        </ul>
219 #        <p>
220 #             Call for a price, today!
221 #        </p>
223 #      </body>
224 #    </html>
227 # == Notes
229 # There are a variety of templating solutions available in various Ruby projects:
230 # * ERB's big brother, eRuby, works the same but is written in C for speed;
231 # * Amrita (smart at producing HTML/XML);
232 # * cs/Template (written in C for speed);
233 # * RDoc, distributed with Ruby, uses its own template engine, which can be reused elsewhere;
234 # * and others; search the RAA.
236 # Rails, the web application framework, uses ERB to create views.
238 class ERB
239   Revision = '$Date: 2007-02-12 15:01:19 -0800 (Mon, 12 Feb 2007) $'    #'
241   # Returns revision information for the erb.rb module.
242   def self.version
243     "erb.rb [2.0.4 #{ERB::Revision.split[1]}]"
244   end
248 # ERB::Compiler
249 class ERB
250   class Compiler # :nodoc:
251     class PercentLine # :nodoc:
252       def initialize(str)
253         @value = str
254       end
255       attr_reader :value
256       alias :to_s :value
257     end
259     class Scanner # :nodoc:
260       SplitRegexp = /(<%%)|(%%>)|(<%=)|(<%#)|(<%)|(%>)|(\n)/
262       @scanner_map = {}
263       def self.regist_scanner(klass, trim_mode, percent)
264         @scanner_map[[trim_mode, percent]] = klass
265       end
267       def self.default_scanner=(klass)
268         @default_scanner = klass
269       end
271       def self.make_scanner(src, trim_mode, percent)
272         klass = @scanner_map.fetch([trim_mode, percent], @default_scanner)
273         klass.new(src, trim_mode, percent)
274       end
276       def initialize(src, trim_mode, percent)
277         @src = src
278         @stag = nil
279       end
280       attr_accessor :stag
282       def scan; end
283     end
285     class TrimScanner < Scanner # :nodoc:
286       TrimSplitRegexp = /(<%%)|(%%>)|(<%=)|(<%#)|(<%)|(%>\n)|(%>)|(\n)/
288       def initialize(src, trim_mode, percent)
289         super
290         @trim_mode = trim_mode
291         @percent = percent
292         if @trim_mode == '>'
293           @scan_line = self.method(:trim_line1)
294         elsif @trim_mode == '<>'
295           @scan_line = self.method(:trim_line2)
296         elsif @trim_mode == '-'
297           @scan_line = self.method(:explicit_trim_line)
298         else
299           @scan_line = self.method(:scan_line)
300         end
301       end
302       attr_accessor :stag
304       def scan(&block)
305         @stag = nil
306         if @percent
307           @src.each do |line|
308             percent_line(line, &block)
309           end
310         else
311           @src.each do |line|
312             @scan_line.call(line, &block)
313           end
314         end
315         nil
316       end
318       def percent_line(line, &block)
319         if @stag || line[0] != ?%
320           return @scan_line.call(line, &block)
321         end
323         line[0] = ''
324         if line[0] == ?%
325           @scan_line.call(line, &block)
326         else
327           yield(PercentLine.new(line.chomp))
328         end
329       end
331       def scan_line(line)
332         line.split(SplitRegexp).each do |token|
333           next if token.empty?
334           yield(token)
335         end
336       end
338       def trim_line1(line)
339         line.split(TrimSplitRegexp).each do |token|
340           next if token.empty?
341           if token == "%>\n"
342             yield('%>')
343             yield(:cr)
344             break
345           end
346           yield(token)
347         end
348       end
350       def trim_line2(line)
351         head = nil
352         line.split(TrimSplitRegexp).each do |token|
353           next if token.empty?
354           head = token unless head
355           if token == "%>\n"
356             yield('%>')
357             if  is_erb_stag?(head)
358               yield(:cr)
359             else
360               yield("\n")
361             end
362             break
363           end
364           yield(token)
365         end
366       end
368       ExplicitTrimRegexp = /(^[ \t]*<%-)|(-%>\n?\z)|(<%-)|(-%>)|(<%%)|(%%>)|(<%=)|(<%#)|(<%)|(%>)|(\n)/
369       def explicit_trim_line(line)
370         line.split(ExplicitTrimRegexp).each do |token|
371           next if token.empty?
372           if @stag.nil? && /[ \t]*<%-/ =~ token
373             yield('<%')
374           elsif @stag && /-%>\n/ =~ token
375             yield('%>')
376             yield(:cr)
377           elsif @stag && token == '-%>'
378             yield('%>')
379           else
380             yield(token)
381           end
382         end
383       end
385       ERB_STAG = %w(<%= <%# <%)
386       def is_erb_stag?(s)
387         ERB_STAG.member?(s)
388       end
389     end
391     Scanner.default_scanner = TrimScanner
393     class SimpleScanner < Scanner # :nodoc:
394       def scan
395         @src.each do |line|
396           line.split(SplitRegexp).each do |token|
397             next if token.empty?
398             yield(token)
399           end
400         end
401       end
402     end
404     Scanner.regist_scanner(SimpleScanner, nil, false)
406     begin
407       require 'strscan'
408       class SimpleScanner2 < Scanner # :nodoc:
409         def scan
410           stag_reg = /(.*?)(<%%|<%=|<%#|<%|\n|\z)/
411           etag_reg = /(.*?)(%%>|%>|\n|\z)/
412           scanner = StringScanner.new(@src)
413           while ! scanner.eos?
414             scanner.scan(@stag ? etag_reg : stag_reg)
415             text = scanner[1]
416             elem = scanner[2]
417             yield(text) unless text.empty?
418             yield(elem) unless elem.empty?
419           end
420         end
421       end
422       Scanner.regist_scanner(SimpleScanner2, nil, false)
424       class PercentScanner < Scanner # :nodoc:
425         def scan
426           new_line = true
427           stag_reg = /(.*?)(<%%|<%=|<%#|<%|\n|\z)/
428           etag_reg = /(.*?)(%%>|%>|\n|\z)/
429           scanner = StringScanner.new(@src)
430           while ! scanner.eos?
431             if new_line && @stag.nil?
432               if scanner.scan(/%%/)
433                 yield('%')
434                 new_line = false
435                 next
436               elsif scanner.scan(/%/)
437                 yield(PercentLine.new(scanner.scan(/.*?(\n|\z)/).chomp))
438                 next
439               end
440             end
441             scanner.scan(@stag ? etag_reg : stag_reg)
442             text = scanner[1]
443             elem = scanner[2]
444             yield(text) unless text.empty?
445             yield(elem) unless elem.empty?
446             new_line = (elem == "\n")
447           end
448         end
449       end
450       Scanner.regist_scanner(PercentScanner, nil, true)
452       class ExplicitScanner < Scanner # :nodoc:
453         def scan
454           new_line = true
455           stag_reg = /(.*?)(<%%|<%=|<%#|<%-|<%|\n|\z)/
456           etag_reg = /(.*?)(%%>|-%>|%>|\n|\z)/
457           scanner = StringScanner.new(@src)
458           while ! scanner.eos?
459             if new_line && @stag.nil? && scanner.scan(/[ \t]*<%-/)
460               yield('<%')
461               new_line = false
462               next
463             end
464             scanner.scan(@stag ? etag_reg : stag_reg)
465             text = scanner[1]
466             elem = scanner[2]
467             new_line = (elem == "\n")
468             yield(text) unless text.empty?
469             if elem == '-%>'
470               yield('%>')
471               if scanner.scan(/(\n|\z)/)
472                 yield(:cr)
473                 new_line = true
474               end
475             elsif elem == '<%-'
476               yield('<%')
477             else
478               yield(elem) unless elem.empty?
479             end
480           end
481         end
482       end
483       Scanner.regist_scanner(ExplicitScanner, '-', false)
485     rescue LoadError
486     end
488     class Buffer # :nodoc:
489       def initialize(compiler)
490         @compiler = compiler
491         @line = []
492         @script = ""
493         @compiler.pre_cmd.each do |x|
494           push(x)
495         end
496       end
497       attr_reader :script
499       def push(cmd)
500         @line << cmd
501       end
503       def cr
504         @script << (@line.join('; '))
505         @line = []
506         @script << "\n"
507       end
509       def close
510         return unless @line
511         @compiler.post_cmd.each do |x|
512           push(x)
513         end
514         @script << (@line.join('; '))
515         @line = nil
516       end
517     end
519     def compile(s)
520       out = Buffer.new(self)
522       content = ''
523       scanner = make_scanner(s)
524       scanner.scan do |token|
525         if scanner.stag.nil?
526           case token
527           when PercentLine
528             out.push("#{@put_cmd} #{content.dump}") if content.size > 0
529             content = ''
530             out.push(token.to_s)
531             out.cr
532           when :cr
533             out.cr
534           when '<%', '<%=', '<%#'
535             scanner.stag = token
536             out.push("#{@put_cmd} #{content.dump}") if content.size > 0
537             content = ''
538           when "\n"
539             content << "\n"
540             out.push("#{@put_cmd} #{content.dump}")
541             out.cr
542             content = ''
543           when '<%%'
544             content << '<%'
545           else
546             content << token
547           end
548         else
549           case token
550           when '%>'
551             case scanner.stag
552             when '<%'
553               if content[-1] == ?\n
554                 content.chop!
555                 out.push(content)
556                 out.cr
557               else
558                 out.push(content)
559               end
560             when '<%='
561               out.push("#{@insert_cmd}((#{content}).to_s)")
562             when '<%#'
563               # out.push("# #{content.dump}")
564             end
565             scanner.stag = nil
566             content = ''
567           when '%%>'
568             content << '%>'
569           else
570             content << token
571           end
572         end
573       end
574       out.push("#{@put_cmd} #{content.dump}") if content.size > 0
575       out.close
576       out.script
577     end
579     def prepare_trim_mode(mode)
580       case mode
581       when 1
582         return [false, '>']
583       when 2
584         return [false, '<>']
585       when 0
586         return [false, nil]
587       when String
588         perc = mode.include?('%')
589         if mode.include?('-')
590           return [perc, '-']
591         elsif mode.include?('<>')
592           return [perc, '<>']
593         elsif mode.include?('>')
594           return [perc, '>']
595         else
596           [perc, nil]
597         end
598       else
599         return [false, nil]
600       end
601     end
603     def make_scanner(src)
604       Scanner.make_scanner(src, @trim_mode, @percent)
605     end
607     def initialize(trim_mode)
608       @percent, @trim_mode = prepare_trim_mode(trim_mode)
609       @put_cmd = 'print'
610       @insert_cmd = @put_cmd
611       @pre_cmd = []
612       @post_cmd = []
613     end
614     attr_reader :percent, :trim_mode
615     attr_accessor :put_cmd, :insert_cmd, :pre_cmd, :post_cmd
616   end
620 # ERB
621 class ERB
622   #
623   # Constructs a new ERB object with the template specified in _str_.
624   #
625   # An ERB object works by building a chunk of Ruby code that will output
626   # the completed template when run. If _safe_level_ is set to a non-nil value,
627   # ERB code will be run in a separate thread with <b>$SAFE</b> set to the
628   # provided level.
629   #
630   # If _trim_mode_ is passed a String containing one or more of the following
631   # modifiers, ERB will adjust its code generation as listed:
632   #
633   #     %  enables Ruby code processing for lines beginning with %
634   #     <> omit newline for lines starting with <% and ending in %>
635   #     >  omit newline for lines ending in %>
636   #
637   # _eoutvar_ can be used to set the name of the variable ERB will build up
638   # its output in.  This is useful when you need to run multiple ERB
639   # templates through the same binding and/or when you want to control where
640   # output ends up.  Pass the name of the variable to be used inside a String.
641   #
642   # === Example
643   #
644   #  require "erb"
645   #
646   #  # build data class
647   #  class Listings
648   #    PRODUCT = { :name => "Chicken Fried Steak",
649   #                :desc => "A well messages pattie, breaded and fried.",
650   #                :cost => 9.95 }
651   #
652   #    attr_reader :product, :price
653   #
654   #    def initialize( product = "", price = "" )
655   #      @product = product
656   #      @price = price
657   #    end
658   #
659   #    def build
660   #      b = binding
661   #      # create and run templates, filling member data variebles
662   #      ERB.new(<<-'END_PRODUCT'.gsub(/^\s+/, ""), 0, "", "@product").result b
663   #        <%= PRODUCT[:name] %>
664   #        <%= PRODUCT[:desc] %>
665   #      END_PRODUCT
666   #      ERB.new(<<-'END_PRICE'.gsub(/^\s+/, ""), 0, "", "@price").result b
667   #        <%= PRODUCT[:name] %> -- <%= PRODUCT[:cost] %>
668   #        <%= PRODUCT[:desc] %>
669   #      END_PRICE
670   #    end
671   #  end
672   #
673   #  # setup template data
674   #  listings = Listings.new
675   #  listings.build
676   #
677   #  puts listings.product + "\n" + listings.price
678   #
679   # _Generates_
680   #
681   #  Chicken Fried Steak
682   #  A well messages pattie, breaded and fried.
683   #
684   #  Chicken Fried Steak -- 9.95
685   #  A well messages pattie, breaded and fried.
686   #
687   def initialize(str, safe_level=nil, trim_mode=nil, eoutvar='_erbout')
688     @safe_level = safe_level
689     compiler = ERB::Compiler.new(trim_mode)
690     set_eoutvar(compiler, eoutvar)
691     @src = compiler.compile(str)
692     @filename = nil
693   end
695   # The Ruby code generated by ERB
696   attr_reader :src
698   # The optional _filename_ argument passed to Kernel#eval when the ERB code
699   # is run
700   attr_accessor :filename
702   #
703   # Can be used to set _eoutvar_ as described in ERB#new.  It's probably easier
704   # to just use the constructor though, since calling this method requires the
705   # setup of an ERB _compiler_ object.
706   #
707   def set_eoutvar(compiler, eoutvar = '_erbout')
708     compiler.put_cmd = "#{eoutvar}.concat"
709     compiler.insert_cmd = "#{eoutvar}.concat"
711     cmd = []
712     cmd.push "#{eoutvar} = ''"
714     compiler.pre_cmd = cmd
716     cmd = []
717     cmd.push(eoutvar)
719     compiler.post_cmd = cmd
720   end
722   # Generate results and print them. (see ERB#result)
723   def run(b=TOPLEVEL_BINDING)
724     print self.result(b)
725   end
727   #
728   # Executes the generated ERB code to produce a completed template, returning
729   # the results of that code.  (See ERB#new for details on how this process can
730   # be affected by _safe_level_.)
731   #
732   # _b_ accepts a Binding or Proc object which is used to set the context of
733   # code evaluation.
734   #
735   def result(b=TOPLEVEL_BINDING)
736     if @safe_level
737       th = Thread.start {
738         $SAFE = @safe_level
739         eval(@src, b, (@filename || '(erb)'), 1)
740       }
741       return th.value
742     else
743       return eval(@src, b, (@filename || '(erb)'), 1)
744     end
745   end
747   def def_method(mod, methodname, fname='(ERB)')  # :nodoc:
748     mod.module_eval("def #{methodname}\n" + self.src + "\nend\n", fname, 0)
749   end
751   def def_module(methodname='erb')  # :nodoc:
752     mod = Module.new
753     def_method(mod, methodname)
754     mod
755   end
757   def def_class(superklass=Object, methodname='result')  # :nodoc:
758     cls = Class.new(superklass)
759     def_method(cls, methodname)
760     cls
761   end
765 # ERB::Util
766 class ERB
767   # A utility module for conversion routines, often handy in HTML generation.
768   module Util
769     public
770     #
771     # A utility method for escaping HTML tag characters in _s_.
772     #
773     #   require "erb"
774     #   include ERB::Util
775     #
776     #   puts html_escape("is a > 0 & a < 10?")
777     #
778     # _Generates_
779     #
780     #   is a &gt; 0 &amp; a &lt; 10?
781     #
782     def html_escape(s)
783       s.to_s.gsub(/&/, "&amp;").gsub(/\"/, "&quot;").gsub(/>/, "&gt;").gsub(/</, "&lt;")
784     end
785     alias h html_escape
786     module_function :h
787     module_function :html_escape
789     #
790     # A utility method for encoding the String _s_ as a URL.
791     #
792     #   require "erb"
793     #   include ERB::Util
794     #
795     #   puts url_encode("Programming Ruby:  The Pragmatic Programmer's Guide")
796     #
797     # _Generates_
798     #
799     #   Programming%20Ruby%3A%20%20The%20Pragmatic%20Programmer%27s%20Guide
800     #
801     def url_encode(s)
802       s.to_s.gsub(/[^a-zA-Z0-9_\-.]/n){ sprintf("%%%02X", $&.unpack("C")[0]) }
803     end
804     alias u url_encode
805     module_function :u
806     module_function :url_encode
807   end
811 # ERB::DefMethod
812 class ERB
813   module DefMethod  # :nodoc:
814     public
815     def def_erb_method(methodname, erb)
816       if erb.kind_of? String
817         fname = erb
818         File.open(fname) {|f| erb = ERB.new(f.read) }
819         erb.def_method(self, methodname, fname)
820       else
821         erb.def_method(self, methodname)
822       end
823     end
824     module_function :def_erb_method
825   end