Skip to content
This repository
Fetching contributors…

Cannot retrieve contributors at this time

file 902 lines (780 sloc) 22.759 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902
# = 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.

=begin rdoc
= 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.
=end
class ERB
  Revision = '$Date:: 2009-01-17 07:20:08 -0500#$' #'

  # 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 &gt; 0 &amp; a &lt; 10?
    #
    def html_escape(s)
      s.to_s.gsub(/&/, "&amp;").gsub(/\"/, "&quot;").gsub(/>/, "&gt;").gsub(/</, "&lt;")
    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
Something went wrong with that request. Please try again.