--C7zPtVaVf+AK4Oqc
Content-Type: text/plain; charset=iso-8859-1
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

Hi Ruby-Core, I attach a path that solve this problem:

class String
  alias :old_gsub :gsub
    def gsub(*args, &block)
       old_gsub(*args, &block)
    end
end

require 'cgi'
CGI.escape("An Interview with Criteri…")

NoMethodError: undefined method `size' for nil:NilClass
  from /usr/lib/ruby/1.8/cgi.rb:343:in `escape'
  from (irb):4:in `old_gsub'
  from (irb):4:in `gsub'
  from /usr/lib/ruby/1.8/cgi.rb:342:in `escape'
from (irb):9

As you can see CGI.escape won't work well when we use gsub and blocks, this
issue was discussed in this thread: 

http://www.justskins.com/forums/bug-when-rerouting-string-gsub-with-a-block-using-1-a-52852.html

where Matz say "Then don't use ugly dollar variables" so, dollar variables are
problematic in this case, I changed the dollar variables by a block argument
variable and the problem was solved, I added 2 tests for CGI.escape and
CGI.unescape that reproduce this "problem", If you agree with this patch I could
do other new patch in order to avoid all dollar variables inside CGI lib.

And there is a bug in ActiveSupport realted with this issue:
https://rails.lighthouseapp.com/projects/8994/tickets/3475-activesupportmultibytecharsgsub-fails-while-stringgsub-works

Thanks, I'll be waiting your response.

-- 
Gast齊 Ramos

--C7zPtVaVf+AK4Oqc
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment; filename="cgi_escape_work_with_blocks_and_avoid_dollar_variables.patch"

Index: lib/cgi.rb
--- lib/cgi.rb	(revision 26114)
+++ lib/cgi.rb	(working copy)
@@ -1,14 +1,14 @@
-# 
+#
 # cgi.rb - cgi support library
-# 
+#
 # Copyright (C) 2000  Network Applied Communication Laboratory, Inc.
-# 
+#
 # Copyright (C) 2000  Information-technology Promotion Agency, Japan
 #
 # Author: Wakou Aoyama <wakou / ruby-lang.org>
 #
-# Documentation: Wakou Aoyama (RDoc'd and embellished by William Webber) 
-# 
+# Documentation: Wakou Aoyama (RDoc'd and embellished by William Webber)
+#
 # Overview
 #
 # The Common Gateway Interface (CGI) is a simple protocol
@@ -18,7 +18,7 @@
 # parameters of the request passed in either in the
 # environment (GET) or via $stdin (POST), and everything
 # it prints to $stdout is returned to the client.
-# 
+#
 # This file holds the +CGI+ class.  This class provides
 # functionality for retrieving HTTP request parameters,
 # managing cookies, and generating HTML output.  See the
@@ -77,18 +77,18 @@
 #
 #
 # For each of these variables, there is a corresponding attribute with the
-# same name, except all lower case and without a preceding HTTP_.  
+# same name, except all lower case and without a preceding HTTP_.
 # +content_length+ and +server_port+ are integers; the rest are strings.
 #
 #  Parameters
 #
 # The method #params() returns a hash of all parameters in the request as
 # name/value-list pairs, where the value-list is an Array of one or more
-# values.  The CGI object itself also behaves as a hash of parameter names 
-# to values, but only returns a single value (as a String) for each 
+# values.  The CGI object itself also behaves as a hash of parameter names
+# to values, but only returns a single value (as a String) for each
 # parameter name.
 #
-# For instance, suppose the request contains the parameter 
+# For instance, suppose the request contains the parameter
 # "favourite_colours" with the multiple values "blue" and "green".  The
 # following behaviour would occur:
 #
@@ -107,7 +107,7 @@
 #
 #  Multipart requests
 #
-# If a request's method is POST and its content type is multipart/form-data, 
+# If a request's method is POST and its content type is multipart/form-data,
 # then it may contain uploaded files.  These are stored by the QueryExtension
 # module in the parameters of the request.  The parameter name is the name
 # attribute of the file input field, as usual.  However, the value is not
@@ -138,7 +138,7 @@
 #
 # Each HTML element has a corresponding method for generating that
 # element as a String.  The name of this method is the same as that
-# of the element, all lowercase.  The attributes of the element are 
+# of the element, all lowercase.  The attributes of the element are
 # passed in as a hash, and the body as a no-argument block that evaluates
 # to a String.  The HTML generation module knows which elements are
 # always empty, and silently drops any passed-in body.  It also knows
@@ -152,57 +152,57 @@
 # as arguments, rather than via a hash.
 #
 # Examples of use
-# 
+#
 #  Get form values
-# 
+#
 #   require "cgi"
 #   cgi  GI.new
 #   value  gi['field_name']   # <value string for 'field_name'
 #     # if not 'field_name' included, then return "".
 #   fields  gi.keys            # <array of field names
-# 
+#
 #   # returns true if form has 'field_name'
 #   cgi.has_key?('field_name')
 #   cgi.has_key?('field_name')
 #   cgi.include?('field_name')
-# 
-# CAUTION! cgi['field_name'] returned an Array with the old 
+#
+# CAUTION! cgi['field_name'] returned an Array with the old
 # cgi.rb(included in ruby 1.6)
-# 
+#
 #  Get form values as hash
-# 
+#
 #   require "cgi"
 #   cgi  GI.new
 #   params  gi.params
-# 
+#
 # cgi.params is a hash.
-# 
+#
 #   cgi.params['new_field_name']  "value"]  # add new param
 #   cgi.params['field_name']  "new_value"]  # change value
 #   cgi.params.delete('field_name')           # delete param
 #   cgi.params.clear                          # delete all params
-# 
-# 
+#
+#
 #  Save form values to file
-# 
+#
 #   require "pstore"
 #   db  Store.new("query.db")
 #   db.transaction do
 #     db["params"]  gi.params
 #   end
-# 
-# 
+#
+#
 #  Restore form values from file
-# 
+#
 #   require "pstore"
 #   db  Store.new("query.db")
 #   db.transaction do
 #     cgi.params  b["params"]
 #   end
-# 
-# 
+#
+#
 #  Get multipart form values
-# 
+#
 #   require "cgi"
 #   cgi  GI.new
 #   value  gi['field_name']   # <value string for 'field_name'
@@ -210,37 +210,37 @@
 #   value.local_path            # <path to local file of value
 #   value.original_filename     # <original filename of value
 #   value.content_type          # <content_type of value
-# 
+#
 # and value has StringIO or Tempfile class methods.
-# 
+#
 #  Get cookie values
-# 
+#
 #   require "cgi"
 #   cgi  GI.new
 #   values  gi.cookies['name']  # <array of 'name'
 #     # if not 'name' included, then return [].
 #   names  gi.cookies.keys      # <array of cookie names
-# 
+#
 # and cgi.cookies is a hash.
-# 
+#
 #  Get cookie objects
-# 
+#
 #   require "cgi"
 #   cgi  GI.new
 #   for name, cookie in cgi.cookies
 #     cookie.expires  ime.now + 30
 #   end
 #   cgi.out("cookie" cgi.cookies) {"string"}
-# 
+#
 #   cgi.cookies # { "name1" cookie1, "name2" cookie2, ... }
-# 
+#
 #   require "cgi"
 #   cgi  GI.new
 #   cgi.cookies['name'].expires  ime.now + 30
 #   cgi.out("cookie" cgi.cookies['name']) {"string"}
-# 
+#
 #  Print http header and html string to $DEFAULT_OUTPUT ($>)
-# 
+#
 #   require "cgi"
 #   cgi  GI.new("html3")  # add HTML generation methods
 #   cgi.out() do
@@ -264,7 +264,7 @@
 #       end
 #     end
 #   end
-# 
+#
 #   # add HTML generation methods
 #   CGI.new("html3")    # html3.2
 #   CGI.new("html4")    # html4.01 (Strict)
@@ -286,7 +286,7 @@
 
   REVISION  $Id$' #:nodoc:
 
-  NEEDS_BINMODE  rue if /WIN/ni.match(RUBY_PLATFORM) 
+  NEEDS_BINMODE  rue if /WIN/ni.match(RUBY_PLATFORM)
 
   # Path separators in different environments.
   PATH_SEPARATOR  'UNIX'/', 'WINDOWS'\\', 'MACINTOSH':'}
@@ -321,7 +321,7 @@
 
   # :startdoc:
 
-  def env_table 
+  def env_table
     ENV
   end
 
@@ -339,8 +339,8 @@
   #   url_encoded_string  GI::escape("'Stop!' said Fred")
   #      # "%27Stop%21%27+said+Fred"
   def CGI::escape(string)
-    string.gsub(/([^ a-zA-Z0-9_.-]+)/n) do
-      '%' + $1.unpack('H2' * $1.size).join('%').upcase
+    string.gsub(/([^ a-zA-Z0-9_.-]+)/n) do |s|
+      '%' + s.unpack('H2' * s.size).join('%').upcase
     end.tr(' ', '+')
   end
 
@@ -349,8 +349,8 @@
   #   string  GI::unescape("%27Stop%21%27+said+Fred")
   #      # "'Stop!' said Fred"
   def CGI::unescape(string)
-    string.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n) do
-      [$1.delete('%')].pack('H*')
+    string.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n) do |s|
+      [s.delete('%')].pack('H*')
     end
   end
 
@@ -431,7 +431,7 @@
   #   print CGI::unescapeElement(
   #           CGI::escapeHTML('<BR><A HREFrl"></A>'), "A", "IMG")
   #     # "&lt;BR&gt;<A HREFrl"></A>"
-  # 
+  #
   #   print CGI::unescapeElement(
   #           CGI::escapeHTML('<BR><A HREFrl"></A>'), ["A", "IMG"])
   #     # "&lt;BR&gt;<A HREFrl"></A>"
@@ -475,7 +475,7 @@
   # status:: the HTTP status code, returned as the Status header.  See the
   #          list of available status codes below.
   # server:: the server software, returned as the Server header.
-  # connection:: the connection type, returned as the Connection header (for 
+  # connection:: the connection type, returned as the Connection header (for
   #              instance, "close".
   # length:: the length of the content that will be sent, returned as the
   #          Content-Length header.
@@ -485,19 +485,19 @@
   #           object, returned as the Expires header.
   # cookie:: a cookie or cookies, returned as one or more Set-Cookie headers.
   #          The value can be the literal string of the cookie; a CGI::Cookie
-  #          object; an Array of literal cookie strings or Cookie objects; or a 
+  #          object; an Array of literal cookie strings or Cookie objects; or a
   #          hash all of whose values are literal cookie strings or Cookie objects.
   #          These cookies are in addition to the cookies held in the
   #          @output_cookies field.
   #
   # Other header lines can also be set; they are appended as key: value.
-  # 
+  #
   #   header
   #     # Content-Type: text/html
-  # 
+  #
   #   header("text/plain")
   #     # Content-Type: text/plain
-  # 
+  #
   #   header("nph"        true,
   #          "status"     "OK",  # "200 OK"
   #            # "status"     "200 GOOD",
@@ -512,9 +512,9 @@
   #          "cookie"     [cookie1, cookie2],
   #          "my_header1" "my_value"
   #          "my_header2" "my_value")
-  # 
+  #
   # The status codes are:
-  # 
+  #
   #   "OK"                  --> "200 OK"
   #   "PARTIAL_CONTENT"     --> "206 Partial Content"
   #   "MULTIPLE_CHOICES"    --> "300 Multiple Choices"
@@ -533,9 +533,9 @@
   #   "NOT_IMPLEMENTED"     --> "501 Method Not Implemented"
   #   "BAD_GATEWAY"         --> "502 Bad Gateway"
   #   "VARIANT_ALSO_VARIES" --> "506 Variant Also Negotiates"
-  # 
-  # This method does not perform charset conversion. 
   #
+  # This method does not perform charset conversion.
+  #
   def header(options  text/html")
 
     buf  "
@@ -641,9 +641,9 @@
         when /^content-encoding$/ni
           Apache::request.content_encoding  alue
         when /^location$/ni
-	  if Apache::request.status 200
-	    Apache::request.status  02
-	  end
+    if Apache::request.status 200
+      Apache::request.status  02
+    end
           Apache::request.headers_out[name]  alue
         else
           Apache::request.headers_out[name]  alue
@@ -670,13 +670,13 @@
   #     # Content-Length: 6
   #     #
   #     # string
-  # 
+  #
   #   cgi.out("text/plain") { "string" }
   #     # Content-Type: text/plain
   #     # Content-Length: 6
   #     #
   #     # string
-  # 
+  #
   #   cgi.out("nph"        true,
   #           "status"     "OK",  # "200 OK"
   #           "server"     ENV['SERVER_SOFTWARE'],
@@ -689,16 +689,16 @@
   #           "cookie"     [cookie1, cookie2],
   #           "my_header1" "my_value",
   #           "my_header2" "my_value") { "string" }
-  # 
+  #
   # Content-Length is automatically calculated from the size of
   # the String returned by the content block.
   #
   # If ENV['REQUEST_METHOD'] "HEAD", then only the header
   # is outputted (the content block is still required, but it
   # is ignored).
-  # 
+  #
   # If the charset is "iso-2022-jp" or "euc-jp" or "shift_jis" then
-  # the content is converted to this charset, and the language is set 
+  # the content is converted to this charset, and the language is set
   # to "ja".
   def out(options  text/html") # :yield:
 
@@ -755,16 +755,16 @@
   #                              'expires' Time.now, # optional
   #                              'secure'  true      # optional
   #                             )
-  # 
+  #
   #   cgi.out("cookie" [cookie1, cookie2]) { "string" }
-  # 
+  #
   #   name     ookie1.name
   #   values   ookie1.value
   #   path     ookie1.path
   #   domain   ookie1.domain
   #   expires  ookie1.expires
   #   secure   ookie1.secure
-  # 
+  #
   #   cookie1.name     name'
   #   cookie1.value    'value1', 'value2', ...]
   #   cookie1.path     path'
@@ -787,7 +787,7 @@
     # domain:: the domain for which this cookie applies.
     # expires:: the time at which this cookie expires, as a +Time+ object.
     # secure:: whether this cookie is a secure cookie or not (default to
-    #          false).  Secure cookies are only transmitted to HTTPS 
+    #          false).  Secure cookies are only transmitted to HTTPS
     #          servers.
     #
     # These keywords correspond to attributes of the cookie object.
@@ -914,7 +914,7 @@
 
   # Mixin module. It provides the follow functionality groups:
   #
-  # 1. Access to CGI environment variables as methods.  See 
+  # 1. Access to CGI environment variables as methods.  See
   #    documentation to the CGI class for a list of these variables.
   #
   # 2. Access to cookies, including the cookies attribute.
@@ -1044,13 +1044,13 @@
         body.rewind
 
         /Content-Disposition:.* filename:"((?:\\.|[^\"])*)"|([^;\s]*))/ni.match(head)
-	filename  $1 or $2 or "")
-	if /Mac/ni.match(env_table['HTTP_USER_AGENT']) and
-	    /Mozilla/ni.match(env_table['HTTP_USER_AGENT']) and
-	    (not /MSIE/ni.match(env_table['HTTP_USER_AGENT']))
-	  filename  GI::unescape(filename)
-	end
-        
+  filename  $1 or $2 or "")
+  if /Mac/ni.match(env_table['HTTP_USER_AGENT']) and
+      /Mozilla/ni.match(env_table['HTTP_USER_AGENT']) and
+      (not /MSIE/ni.match(env_table['HTTP_USER_AGENT']))
+    filename  GI::unescape(filename)
+  end
+
         /Content-Type: ([^\s]*)/ni.match(head)
         content_type  $1 or "")
 
@@ -1164,12 +1164,12 @@
       def to_a
         @params || [self]
       end
-      alias to_ary to_a   	# to be rhs of multiple assignment
+      alias to_ary to_a     # to be rhs of multiple assignment
     end
 
     # Get the value for the parameter with a given key.
     #
-    # If the parameter has multiple values, only the first will be 
+    # If the parameter has multiple values, only the first will be
     # retrieved; use #params() to get the array of values.
     def [](key)
       params  params[key]
@@ -1216,7 +1216,7 @@
   #     #   <BODY>
   #     #   </BODY>
   #     # </HTML>
-  # 
+  #
   #   print CGI::pretty("<HTML><BODY></BODY></HTML>", "\t")
   #     # <HTML>
   #     #         <BODY>
@@ -1301,7 +1301,7 @@
   # Modules Http3, Http4, etc., contain more basic HTML-generation methods
   # (:title, :center, etc.).
   #
-  # See class CGI for a detailed example. 
+  # See class CGI for a detailed example.
   #
   module HtmlExtension
 
@@ -1334,7 +1334,7 @@
       end
     end
 
-    # Generate a Document Base URI element as a String. 
+    # Generate a Document Base URI element as a String.
     #
     # +href+ can either by a string, giving the base URL for the HREF
     # attribute, or it can be a has of the element's attributes.
@@ -1414,10 +1414,10 @@
     #
     #   checkbox("name")
     #     #  heckbox("NAME" "name")
-    # 
+    #
     #   checkbox("name", "value")
     #     #  heckbox("NAME" "name", "VALUE" "value")
-    # 
+    #
     #   checkbox("name", "value", true)
     #     #  heckbox("NAME" "name", "VALUE" "value", "CHECKED" true)
     def checkbox(name  ", value  il, checked  il)
@@ -1455,23 +1455,23 @@
     #     # <INPUT TYPE
heckbox" NAMEame" VALUEoo">foo # # <INPUT TYPE
heckbox" NAMEame" VALUE ar">bar # # <INPUT TYPE
heckbox" NAMEame" VALUE az">baz - # + # # checkbox_group("name", ["foo"], ["bar", true], "baz") # # <INPUT TYPE
heckbox" NAMEame" VALUEoo">foo # # <INPUT TYPE
heckbox" CHECKED NAMEame" VALUE ar">bar # # <INPUT TYPE
heckbox" NAMEame" VALUE az">baz - # + # # checkbox_group("name", ["1", "Foo"], ["2", "Bar", true], "Baz") # # <INPUT TYPE
heckbox" NAMEame" VALUE">Foo # # <INPUT TYPE
heckbox" CHECKED NAMEame" VALUE">Bar # # <INPUT TYPE
heckbox" NAMEame" VALUE az">Baz - # + # # checkbox_group("NAME" "name", # "VALUES" ["foo", "bar", "baz"]) - # + # # checkbox_group("NAME" "name", # "VALUES" [["foo"], ["bar", true], "baz"]) - # + # # checkbox_group("NAME" "name", # "VALUES" [["1", "Foo"], ["2", "Bar", true], "Baz"]) def checkbox_group(name ", *values) @@ -1507,13 +1507,13 @@ # # file_field("name") # # <INPUT TYPEile" NAMEame" SIZE0"> - # + # # file_field("name", 40) # # <INPUT TYPEile" NAMEame" SIZE0"> - # + # # file_field("name", 40, 100) # # <INPUT TYPEile" NAMEame" SIZE0" MAXLENGTH00"> - # + # # file_field("NAME" "name", "SIZE" 40) # # <INPUT TYPEile" NAMEame" SIZE0"> def file_field(name ", size 0, maxlength il) @@ -1533,7 +1533,7 @@ # # +method+ should be either "get" or "post", and defaults to the latter. # +action+ defaults to the current CGI script name. +enctype+ - # defaults to "application/x-www-form-urlencoded". + # defaults to "application/x-www-form-urlencoded". # # Alternatively, the attributes can be specified as a hash. # @@ -1541,19 +1541,19 @@ # # form{ "string" } # # <FORM METHODost" ENCTYPE pplication/x-www-form-urlencoded">string</FORM> - # + # # form("get") { "string" } # # <FORM METHODet" ENCTYPE pplication/x-www-form-urlencoded">string</FORM> - # + # # form("get", "url") { "string" } # # <FORM METHODet" ACTIONrl" ENCTYPE pplication/x-www-form-urlencoded">string</FORM> - # + # # form("METHOD" "post", "ENCTYPE" "enctype") { "string" } # # <FORM METHODost" ENCTYPE「セシッニマメヘセ ィ 「ャ ゜ャ ッュュュ「ゥ ョ゜ソィモゥ 「ヘナヤネマト「 ャ 「チテヤノマホ「 ャ ュ 「ナホテヤルミナ「 ォ 「ナホテヤルミナ「 ョ゜ソィ「ヘナヤネマト「ゥ ロ「ヘナヤネマト「ン 「 タタ ュアオクオャアー ォアオクオャアー タタ 」 」 ィ「「ゥ 」 」 シノホミユヤ ヤルミナ「 ホチヘナ「セ ュ 」 ォ 」 」 ィ「「ャ 「「ゥ 」 」 シノホミユヤ ヤルミナ「 ホチヘナ「 ヨチフユナ「セ ュ 」 ォ 」 」 ィ「ホチヘナ「 「「ャ 「ヨチフユナ「 「「ャ 「ノト「 「「ゥ 」 」 シノホミユヤ ヤルミナ「 ホチヘナ「 ヨチフユナ「 ノトoo"> def hidden(name ", value il) @@ -1611,36 +1611,36 @@ # should include the entire text of this tag, including angle brackets. # # The body of the html element is supplied as a block. - # + # # html{ "string" } # # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML>string</HTML> - # + # # html("LANG" "ja") { "string" } # # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML LANGa">string</HTML> - # + # # html("DOCTYPE" false) { "string" } # # <HTML>string</HTML> - # + # # html("DOCTYPE" '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">') { "string" } # # <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"><HTML>string</HTML> - # + # # html("PRETTY" " ") { "<BODY></BODY>" } # # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> # # <HTML> # # <BODY> # # </BODY> # # </HTML> - # + # # html("PRETTY" "\t") { "<BODY></BODY>" } # # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> # # <HTML> # # <BODY> # # </BODY> # # </HTML> - # + # # html("PRETTY") { "<BODY></BODY>" } # # tml("PRETTY" " ") { "<BODY></BODY>" } - # + # # html(if $VERBOSE then "PRETTY" end) { "HTML string" } # def html(attributes }) # :yield: @@ -1679,17 +1679,17 @@ # Generate an Image Button Input element as a string. # - # +src+ is the URL of the image to use for the button. +name+ + # +src+ is the URL of the image to use for the button. +name+ # is the input name. +alt+ is the alternative text for the image. # # Alternatively, the attributes can be specified as a hash. - # + # # image_button("url") # # <INPUT TYPEmage" SRCrl"> - # + # # image_button("url", "name", "string") # # <INPUT TYPEmage" SRCrl" NAMEame" ALTtring"> - # + # # image_button("SRC" "url", "ATL" "strng") # # <INPUT TYPEmage" SRCrl" ALTtring"> def image_button(src ", name il, alt il) @@ -1715,7 +1715,7 @@ # # img("src", "alt", 100, 50) # # <IMG SRCrc" ALT lt" WIDTH00" HEIGHT0"> - # + # # img("SRC" "src", "ALT" "alt", "WIDTH" 100, "HEIGHT" 50) # # <IMG SRCrc" ALT lt" WIDTH00" HEIGHT0"> def img(src ", alt ", width il, height il) @@ -1741,15 +1741,15 @@ # # multipart_form{ "string" } # # <FORM METHODost" ENCTYPEultipart/form-data">string</FORM> - # + # # multipart_form("url") { "string" } # # <FORM METHODost" ACTIONrl" ENCTYPEultipart/form-data">string</FORM> def multipart_form(action il, enctype multipart/form-data") attributes f action nil - { "METHOD" "post", "ENCTYPE" enctype } + { "METHOD" "post", "ENCTYPE" enctype } elsif action.kind_of?(String) { "METHOD" "post", "ACTION" action, - "ENCTYPE" enctype } + "ENCTYPE" enctype } else unless action.has_key?("METHOD") action["METHOD"] post" @@ -1777,13 +1777,13 @@ # # password_field("name") # # <INPUT TYPEassword" NAMEame" SIZE0"> - # + # # password_field("name", "value") # # <INPUT TYPEassword" NAMEame" VALUEalue" SIZE0"> - # + # # password_field("password", "value", 80, 200) # # <INPUT TYPEassword" NAMEame" VALUEalue" SIZE0" MAXLENGTH00"> - # + # # password_field("NAME" "name", "VALUE" "value") # # <INPUT TYPEassword" NAMEame" VALUEalue"> def password_field(name ", value il, size 0, maxlength il) @@ -1819,21 +1819,21 @@ # # <OPTION VALUE ar">bar</OPTION> # # <OPTION VALUE az">baz</OPTION> # # </SELECT> - # + # # popup_menu("name", ["foo"], ["bar", true], "baz") # # <SELECT NAMEame"> # # <OPTION VALUEoo">foo</OPTION> # # <OPTION VALUE ar" SELECTED>bar</OPTION> # # <OPTION VALUE az">baz</OPTION> # # </SELECT> - # + # # popup_menu("name", ["1", "Foo"], ["2", "Bar", true], "Baz") # # <SELECT NAMEame"> # # <OPTION VALUE">Foo</OPTION> # # <OPTION SELECTED VALUE">Bar</OPTION> # # <OPTION VALUE az">Baz</OPTION> # # </SELECT> - # + # # popup_menu("NAME" "name", "SIZE" 2, "MULTIPLE" true, # "VALUES" [["1", "Foo"], ["2", "Bar", true], "Baz"]) # # <SELECT NAMEame" MULTIPLE SIZE"> @@ -1884,10 +1884,10 @@ # # radio_button("name", "value") # # <INPUT TYPEadio" NAMEame" VALUEalue"> - # + # # radio_button("name", "value", true) # # <INPUT TYPEadio" NAMEame" VALUEalue" CHECKED> - # + # # radio_button("NAME" "name", "VALUE" "value", "ID" "foo") # # <INPUT TYPEadio" NAMEame" VALUEalue" IDoo"> def radio_button(name ", value il, checked il) @@ -1905,28 +1905,28 @@ # # This works the same as #checkbox_group(). However, it is not valid # to have more than one radiobutton in a group checked. - # + # # radio_group("name", "foo", "bar", "baz") # # <INPUT TYPEadio" NAMEame" VALUEoo">foo # # <INPUT TYPEadio" NAMEame" VALUE ar">bar # # <INPUT TYPEadio" NAMEame" VALUE az">baz - # + # # radio_group("name", ["foo"], ["bar", true], "baz") # # <INPUT TYPEadio" NAMEame" VALUEoo">foo # # <INPUT TYPEadio" CHECKED NAMEame" VALUE ar">bar # # <INPUT TYPEadio" NAMEame" VALUE az">baz - # + # # radio_group("name", ["1", "Foo"], ["2", "Bar", true], "Baz") # # <INPUT TYPEadio" NAMEame" VALUE">Foo # # <INPUT TYPEadio" CHECKED NAMEame" VALUE">Bar # # <INPUT TYPEadio" NAMEame" VALUE az">Baz - # + # # radio_group("NAME" "name", # "VALUES" ["foo", "bar", "baz"]) - # + # # radio_group("NAME" "name", # "VALUES" [["foo"], ["bar", true], "baz"]) - # + # # radio_group("NAME" "name", # "VALUES" [["1", "Foo"], ["2", "Bar", true], "Baz"]) def radio_group(name ", *values) @@ -1958,10 +1958,10 @@ # # reset # # <INPUT TYPEeset"> - # + # # reset("reset") # # <INPUT TYPEeset" VALUEeset"> - # + # # reset("VALUE" "reset", "ID" "foo") # # <INPUT TYPEeset" VALUEeset" IDoo"> def reset(value il, name il) @@ -1985,13 +1985,13 @@ # # submit # # <INPUT TYPEubmit"> - # + # # submit("ok") # # <INPUT TYPEubmit" VALUEk"> - # + # # submit("ok", "button1") # # <INPUT TYPEubmit" VALUEk" NAME utton1"> - # + # # submit("VALUE" "ok", "NAME" "button1", "ID" "foo") # # <INPUT TYPEubmit" VALUEk" NAME utton1" IDoo"> def submit(value il, name il) @@ -2014,16 +2014,16 @@ # # text_field("name") # # <INPUT TYPEext" NAMEame" SIZE0"> - # + # # text_field("name", "value") # # <INPUT TYPEext" NAMEame" VALUEalue" SIZE0"> - # + # # text_field("name", "value", 80) # # <INPUT TYPEext" NAMEame" VALUEalue" SIZE0"> - # + # # text_field("name", "value", 80, 200) # # <INPUT TYPEext" NAMEame" VALUEalue" SIZE0" MAXLENGTH00"> - # + # # text_field("NAME" "name", "VALUE" "value") # # <INPUT TYPEext" NAMEame" VALUEalue"> def text_field(name ", value il, size 0, maxlength il) Index: test/cgi/test_cgi.rb --- test/cgi/test_cgi.rb (revision 0) +++ test/cgi/test_cgi.rb (revision 0) @@ -0,0 +1,31 @@ +require "cgi" +require "test/unit" + +class String + alias :old_gsub :gsub + alias :old_delete :delete + + def gsub(*args, &block) + old_gsub(*args, &block) + end + + def delete(*args, &block) + old_delete(*args, &block) + end +end + +class TestCGI < Test::Unit::TestCase + + def setup + @original_string An Interview with Criteri&#8230;" + @escaped_string An+Interview+with+Criteri%26%238230%3B" + end + + def test_escape_should_work_inside_a_block + assert @escaped_string, CGI.escape(@original_string) + end + + def test_unescape_should_work_inside_a_block + assert @original_string, CGI.unescape(@escaped_string) + end +end --C7zPtVaVf+AK4Oqc--