source: extensions/gsdl-video/trunk/installed/cmdline/lib/ruby/1.8/erb.rb@ 18425

Last change on this file since 18425 was 18425, checked in by davidb, 15 years ago

Video extension to Greenstone

File size: 19.9 KB
Line 
1# = ERB -- Ruby Templating
2#
3# Author:: Masatoshi SEKI
4# Documentation:: James Edward Gray II and Gavin Sinclair
5#
6# See ERB for primary documentation and ERB::Util for a couple of utility
7# routines.
8#
9# Copyright (c) 1999-2000,2002,2003 Masatoshi SEKI
10#
11# You can redistribute it and/or modify it under the same terms as Ruby.
12
13#
14# = ERB -- Ruby Templating
15#
16# == Introduction
17#
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.
21#
22# A very simple example is this:
23#
24# require 'erb'
25#
26# x = 42
27# template = ERB.new <<-EOF
28# The value of x is: <%= x %>
29# EOF
30# puts template.result(binding)
31#
32# <em>Prints:</em> The value of x is: 42
33#
34# More complex examples are given below.
35#
36#
37# == Recognized Tags
38#
39# ERB recognizes certain tags in the provided template and converts them based
40# on the rules below:
41#
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
48#
49# All other text is passed through ERB filtering unchanged.
50#
51#
52# == Options
53#
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.
58#
59# See the ERB.new and ERB#result methods for more detail.
60#
61#
62# == Examples
63#
64# === Plain Text
65#
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.
69#
70# require "erb"
71#
72# # Create template.
73# template = %q{
74# From: James Edward Gray II <[email protected]>
75# To: <%= to %>
76# Subject: Addressing Needs
77#
78# <%= to[/\w+/] %>:
79#
80# Just wanted to send a quick note assuring that your needs are being
81# addressed.
82#
83# I want you to know that my team will keep working on the issues,
84# especially:
85#
86# <%# ignore numerous minor requests -- focus on priorities %>
87# % priorities.each do |priority|
88# * <%= priority %>
89# % end
90#
91# Thanks for your patience.
92#
93# James Edward Gray II
94# }.gsub(/^ /, '')
95#
96# message = ERB.new(template, 0, "%<>")
97#
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" ]
103#
104# # Produce result.
105# email = message.result
106# puts email
107#
108# <i>Generates:</i>
109#
110# From: James Edward Gray II <[email protected]>
111# To: Community Spokesman <spokesman@ruby_community.org>
112# Subject: Addressing Needs
113#
114# Community:
115#
116# Just wanted to send a quick note assuring that your needs are being addressed.
117#
118# I want you to know that my team will keep working on the issues, especially:
119#
120# * Run Ruby Quiz
121# * Document Modules
122# * Answer Questions on Ruby Talk
123#
124# Thanks for your patience.
125#
126# James Edward Gray II
127#
128# === Ruby in HTML
129#
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.
133#
134# require "erb"
135#
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
143#
144# @features = [ ]
145# end
146#
147# def add_feature( feature )
148# @features << feature
149# end
150#
151# # Support templating of member data.
152# def get_binding
153# binding
154# end
155#
156# # ...
157# end
158#
159# # Create template.
160# template = %{
161# <html>
162# <head><title>Ruby Toys -- <%= @name %></title></head>
163# <body>
164#
165# <h1><%= @name %> (<%= @code %>)</h1>
166# <p><%= @desc %></p>
167#
168# <ul>
169# <% @features.each do |f| %>
170# <li><b><%= f %></b></li>
171# <% end %>
172# </ul>
173#
174# <p>
175# <% if @cost < 10 %>
176# <b>Only <%= @cost %>!!!</b>
177# <% else %>
178# Call for a price, today!
179# <% end %>
180# </p>
181#
182# </body>
183# </html>
184# }.gsub(/^ /, '')
185#
186# rhtml = ERB.new(template)
187#
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!")
198#
199# # Produce result.
200# rhtml.run(toy.get_binding)
201#
202# <i>Generates (some blank lines removed):</i>
203#
204# <html>
205# <head><title>Ruby Toys -- Rubysapien</title></head>
206# <body>
207#
208# <h1>Rubysapien (TZ-1002)</h1>
209# <p>Geek's Best Friend! Responds to Ruby commands...</p>
210#
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>
218#
219# <p>
220# Call for a price, today!
221# </p>
222#
223# </body>
224# </html>
225#
226#
227# == Notes
228#
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.
235#
236# Rails, the web application framework, uses ERB to create views.
237#
238class ERB
239 Revision = '$Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $' #'
240
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
245end
246
247#--
248# ERB::Compiler
249class 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
258
259 class Scanner # :nodoc:
260 SplitRegexp = /(<%%)|(%%>)|(<%=)|(<%#)|(<%)|(%>)|(\n)/
261
262 @scanner_map = {}
263 def self.regist_scanner(klass, trim_mode, percent)
264 @scanner_map[[trim_mode, percent]] = klass
265 end
266
267 def self.default_scanner=(klass)
268 @default_scanner = klass
269 end
270
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
275
276 def initialize(src, trim_mode, percent)
277 @src = src
278 @stag = nil
279 end
280 attr_accessor :stag
281
282 def scan; end
283 end
284
285 class TrimScanner < Scanner # :nodoc:
286 TrimSplitRegexp = /(<%%)|(%%>)|(<%=)|(<%#)|(<%)|(%>\n)|(%>)|(\n)/
287
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
303
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
317
318 def percent_line(line, &block)
319 if @stag || line[0] != ?%
320 return @scan_line.call(line, &block)
321 end
322
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
330
331 def scan_line(line)
332 line.split(SplitRegexp).each do |token|
333 next if token.empty?
334 yield(token)
335 end
336 end
337
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
349
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
367
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
384
385 ERB_STAG = %w(<%= <%# <%)
386 def is_erb_stag?(s)
387 ERB_STAG.member?(s)
388 end
389 end
390
391 Scanner.default_scanner = TrimScanner
392
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
403
404 Scanner.regist_scanner(SimpleScanner, nil, false)
405
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)
423
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)
451
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)
484
485 rescue LoadError
486 end
487
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
498
499 def push(cmd)
500 @line << cmd
501 end
502
503 def cr
504 @script << (@line.join('; '))
505 @line = []
506 @script << "\n"
507 end
508
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
518
519 def compile(s)
520 out = Buffer.new(self)
521
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
578
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
602
603 def make_scanner(src)
604 Scanner.make_scanner(src, @trim_mode, @percent)
605 end
606
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
617end
618
619#--
620# ERB
621class 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
694
695 # The Ruby code generated by ERB
696 attr_reader :src
697
698 # The optional _filename_ argument passed to Kernel#eval when the ERB code
699 # is run
700 attr_accessor :filename
701
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"
710
711 cmd = []
712 cmd.push "#{eoutvar} = ''"
713
714 compiler.pre_cmd = cmd
715
716 cmd = []
717 cmd.push(eoutvar)
718
719 compiler.post_cmd = cmd
720 end
721
722 # Generate results and print them. (see ERB#result)
723 def run(b=TOPLEVEL_BINDING)
724 print self.result(b)
725 end
726
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
746
747 def def_method(mod, methodname, fname='(ERB)') # :nodoc:
748 mod.module_eval("def #{methodname}\n" + self.src + "\nend\n", fname, 0)
749 end
750
751 def def_module(methodname='erb') # :nodoc:
752 mod = Module.new
753 def_method(mod, methodname)
754 mod
755 end
756
757 def def_class(superklass=Object, methodname='result') # :nodoc:
758 cls = Class.new(superklass)
759 def_method(cls, methodname)
760 cls
761 end
762end
763
764#--
765# ERB::Util
766class 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
788
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
808end
809
810#--
811# ERB::DefMethod
812class 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
826end
Note: See TracBrowser for help on using the repository browser.