diff --git a/lib/cgi/core.rb b/lib/cgi/core.rb
index e6c19bb..2d9b757 100644
--- a/lib/cgi/core.rb
+++ b/lib/cgi/core.rb
@@ -778,75 +778,152 @@ def self.accept_charset=(accept_charset)
#
@@max_multipart_length= 128 * 1024 * 1024
- # Create a new CGI instance.
- #
# :call-seq:
- # CGI.new(tag_maker) { block }
- # CGI.new(options_hash = {}) { block }
+ # CGI.new(options = {}) -> new_cgi
+ # CGI.new(tag_maker) -> new_cgi
+ # CGI.new(options = {}) {|name, value| ... } -> new_cgi
+ # CGI.new(tag_maker) {|name, value| ... } -> new_cgi
+ #
+ # Returns a new \CGI object.
+ #
+ # The behavior of this method depends _strongly_ on whether it is called
+ # within a standard \CGI call environment;
+ # that is, whether ENV['REQUEST_HEADER'] is defined.
+ #
+ # Within a Standard Call Environment
+ #
+ # This section assumes that ENV['REQUEST_HEADER'] is defined;
+ # for example:
+ #
+ # ENV['REQUEST_METHOD'] # => "GET"
+ #
+ # With no argument and no block given, returns a new \CGI object with default values:
+ #
+ # cgi = CGI.new
+ # puts cgi.pretty_inspect
+ # #,
+ # @accept_charset_error_block=nil,
+ # @cookies={},
+ # @max_multipart_length=134217728,
+ # @multipart=false,
+ # @output_cookies=nil,
+ # @output_hidden=nil,
+ # @params={}>
+ #
+ # With hash argument +options+ given and no block given,
+ # returns a new \CGI object with the given options.
+ #
+ # The options may be:
+ #
+ # - accept_charset: _encoding_:
+ # specifies the encoding of the received query string.
+ #
+ # Value _encoding_ may be
+ # an {Encoding object}[https://docs.ruby-lang.org/en/master/encodings_rdoc.html#label-Encoding+Objects]
+ # or an {encoding name}[https://docs.ruby-lang.org/en/master/encodings_rdoc.html#label-Names+and+Aliases]:
+ #
+ # CGI.new(accept_charset: 'EUC-JP')
+ #
+ # If the option is not given,
+ # the default value is the class default encoding.
+ #
+ # Note: The accept_charset method returns the HTTP Accept-Charset
+ # header value, not the configured encoding. The configured encoding is used
+ # internally for query string parsing.
+ #
+ # - max_multipart_length: _size_:
+ # specifies maximum size (in bytes) of multipart data.
+ #
+ # The _size_ may be:
+ #
+ # - A positive integer.
+ #
+ # CGI.new(max_multipart_length: 1024 * 1024)
+ #
+ #
+ # - A lambda to be evaluated when the request is parsed.
+ # This is useful when determining whether to accept multipart data
+ # (e.g. by consulting a registered user's upload allowance).
+ #
+ # CGI.new(max_multipart_length: -> {check_filesystem})
+ #
+ # If the option is not given, the default is +134217728+, specifying a maximum size of 128 megabytes.
+#
+# Note: This option configures internal behavior only.
+# There is no public method to retrieve this value after initialization.
+ #
+ # - tag_maker: _html_version_:
+ # specifies which version of HTML to use in generating tags.
+ #
+ # Value _html_version_ may be one of:
+ #
+ # - 'html3': {HTML version 3}[https://en.wikipedia.org/wiki/HTML#HTML_3].
+ # - 'html4': {HTML version 4}[https://en.wikipedia.org/wiki/HTML#HTML_4].
+ # - 'html4Tr': HTML 4.0 Transitional.
+ # - 'html4Fr': HTML 4.0 with Framesets.
+ # - 'html5': {HTML version 5}[https://en.wikipedia.org/wiki/HTML#HTML_5].
#
+ # Example:
#
- # tag_maker::
- # This is the same as using the +options_hash+ form with the value {
- # :tag_maker => tag_maker } Note that it is recommended to use the
- # +options_hash+ form, since it also allows you specify the charset you
- # will accept.
- # options_hash::
- # A Hash that recognizes three options:
+ # CGI.new(tag_maker: 'html5')
#
- # :accept_charset::
- # specifies encoding of received query string. If omitted,
- # @@accept_charset is used. If the encoding is not valid, a
- # CGI::InvalidEncoding will be raised.
+ # If the option is not given,
+ # no HTML generation methods are loaded.
#
- # Example. Suppose @@accept_charset is "UTF-8"
+ # With string argument +tag_maker+ given as _tag_maker_ and no block given,
+ # equivalent to CGI.new(tag_maker: _tag_maker_):
#
- # when not specified:
+ # CGI.new('html5')
#
- # cgi=CGI.new # @accept_charset # => "UTF-8"
+ # Outside a Standard Call Environment
#
- # when specified as "EUC-JP":
+ # This section assumes that ENV['REQUEST_HEADER'] is not defined;
+ # for example:
#
- # cgi=CGI.new(:accept_charset => "EUC-JP") # => "EUC-JP"
+ # ENV['REQUEST_METHOD'] # => nil
#
- # :tag_maker::
- # String that specifies which version of the HTML generation methods to
- # use. If not specified, no HTML generation methods will be loaded.
+ # In this mode, the method reads its parameters
+ # from the command line or (failing that) from standard input;
+ # returns a new \CGI object.
#
- # The following values are supported:
+ # Otherwise, cookies and other parameters are parsed automatically from the standard CGI locations,
+ # which vary according to the request method.
#
- # "html3":: HTML 3.x
- # "html4":: HTML 4.0
- # "html4Tr":: HTML 4.0 Transitional
- # "html4Fr":: HTML 4.0 with Framesets
- # "html5":: HTML 5
+ # Options vs Public Methods
#
- # :max_multipart_length::
- # Specifies maximum length of multipart data. Can be an Integer scalar or
- # a lambda, that will be evaluated when the request is parsed. This
- # allows more complex logic to be set when determining whether to accept
- # multipart data (e.g. consult a registered users upload allowance)
+ # Some initialization options configure internal behavior only and do not provide
+ # corresponding public getter methods:
#
- # Default is 128 * 1024 * 1024 bytes
+ # - accept_charset: Configures internal encoding for parsing.
+ # The accept_charset method returns the HTTP Accept-Charset header.
+ # - max_multipart_length: Configures internal multipart size limits.
+ # No public getter method is available.
+ # - tag_maker: Loads HTML generation methods (publicly accessible).
#
- # cgi=CGI.new(:max_multipart_length => 268435456) # simple scalar
+ # Block
#
- # cgi=CGI.new(:max_multipart_length => -> {check_filesystem}) # lambda
+ # If a block is given, its code is stored as a Proc;
+ # whenever CGI::InvalidEncoding would be raised, the proc is called instead.
#
- # block::
- # If provided, the block is called when an invalid encoding is
- # encountered. For example:
+ # In this example, the proc simply saves the error:
#
- # encoding_errors={}
- # cgi=CGI.new(:accept_charset=>"EUC-JP") do |name,value|
- # encoding_errors[name] = value
- # end
+ # encoding_errors={}
+ # CGI.new(accept_charset: 'EUC-JP') do |name,value|
+ # encoding_errors[name] = value
+ # end
+ # # =>
+ # #,
+ # @cookies={},
+ # @max_multipart_length=134217728,
+ # @multipart=false,
+ # @options={accept_charset: "EUC-JP", max_multipart_length: 134217728},
+ # @output_cookies=nil,
+ # @output_hidden=nil,
+ # @params={}>
#
- # Finally, if the CGI object is not created in a standard CGI call
- # environment (that is, it can't locate REQUEST_METHOD in its environment),
- # then it will run in "offline" mode. In this mode, it reads its parameters
- # from the command line or (failing that) from standard input. Otherwise,
- # cookies and other parameters are parsed automatically from the standard
- # CGI locations, which varies according to the REQUEST_METHOD.
def initialize(options = {}, &block) # :yields: name, value
@accept_charset_error_block = block_given? ? block : nil
@options={
diff --git a/test/cgi/test_cgi_new.rb b/test/cgi/test_cgi_new.rb
new file mode 100644
index 0000000..fffc1c8
--- /dev/null
+++ b/test/cgi/test_cgi_new.rb
@@ -0,0 +1,266 @@
+# frozen_string_literal: true
+require 'test/unit'
+require 'cgi'
+require 'stringio'
+require_relative 'update_env'
+
+# Test suite for CGI.new method functionality and documentation validation.
+# Ensures the enhanced documentation matches actual implementation behavior.
+class CGINewTest < Test::Unit::TestCase
+ include UpdateEnv
+
+ def setup
+ @environ = {}
+ @original_stdin = $stdin
+ end
+
+ def teardown
+ ENV.update(@environ)
+ $stdin = @original_stdin
+ end
+
+ # Test basic CGI object creation with all documented call sequences
+ def test_basic_object_creation
+ update_env(
+ 'REQUEST_METHOD' => 'GET',
+ 'QUERY_STRING' => '',
+ 'SERVER_SOFTWARE' => 'Apache 2.2.0',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ )
+
+ # CGI.new(options = {}) -> new_cgi
+ cgi1 = CGI.new({})
+ assert_instance_of(CGI, cgi1)
+
+ # CGI.new(tag_maker) -> new_cgi
+ cgi2 = CGI.new('html5')
+ assert_instance_of(CGI, cgi2)
+
+ # With blocks - should not raise errors
+ assert_nothing_raised { CGI.new({}) { |name, value| } }
+ assert_nothing_raised { CGI.new('html5') { |name, value| } }
+ end
+
+ # Test tag_maker functionality for all documented HTML versions
+ def test_tag_maker_functionality
+ update_env(
+ 'REQUEST_METHOD' => 'GET',
+ 'QUERY_STRING' => '',
+ 'SERVER_SOFTWARE' => 'Apache 2.2.0',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ )
+
+ html_versions = {
+ 'html3' => '',
+ 'html4' => '',
+ 'html4Tr' => '',
+ 'html4Fr' => '',
+ 'html5' => ''
+ }
+
+ html_versions.each do |version, expected_doctype|
+ cgi = CGI.new(tag_maker: version)
+ assert_respond_to(cgi, :doctype, "HTML generation methods should be loaded for #{version}")
+ assert_equal(expected_doctype, cgi.doctype)
+ end
+
+ # Test that without tag_maker, HTML methods are not loaded
+ cgi = CGI.new
+ assert_raise(NoMethodError) { cgi.doctype }
+ end
+
+ # Test string tag_maker argument equivalence to hash option
+ def test_string_tag_maker_equivalent
+ update_env(
+ 'REQUEST_METHOD' => 'GET',
+ 'QUERY_STRING' => '',
+ 'SERVER_SOFTWARE' => 'Apache 2.2.0',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ )
+
+ cgi1 = CGI.new('html5')
+ cgi2 = CGI.new(tag_maker: 'html5')
+
+ # Both should have HTML generation methods loaded
+ assert_respond_to(cgi1, :doctype)
+ assert_respond_to(cgi2, :doctype)
+
+ # Both should produce the same doctype
+ assert_equal(cgi1.doctype, cgi2.doctype)
+ assert_equal('', cgi1.doctype)
+ end
+
+ # Test offline mode (when REQUEST_METHOD is not defined)
+ def test_offline_mode
+ ENV.delete('REQUEST_METHOD')
+ ENV.delete('QUERY_STRING')
+ ENV.delete('SERVER_SOFTWARE')
+ ENV.delete('SERVER_PROTOCOL')
+
+ # Create test input
+ test_input = "name=value&test=123"
+ $stdin = StringIO.new(test_input)
+
+ cgi = CGI.new
+
+ # In offline mode, it should read from stdin
+ assert_equal("value", cgi['name'])
+ assert_equal("123", cgi['test'])
+ end
+
+ # Test that all documented options are accepted without errors
+ def test_options_acceptance
+ update_env(
+ 'REQUEST_METHOD' => 'GET',
+ 'QUERY_STRING' => '',
+ 'SERVER_SOFTWARE' => 'Apache 2.2.0',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ )
+
+ # Test accept_charset option
+ assert_nothing_raised { CGI.new(accept_charset: 'EUC-JP') }
+ assert_nothing_raised { CGI.new(accept_charset: Encoding::UTF_8) }
+
+ # Test max_multipart_length options
+ assert_nothing_raised { CGI.new(max_multipart_length: 1024 * 1024) }
+ assert_nothing_raised { CGI.new(max_multipart_length: -> { 2 * 1024 * 1024 }) }
+
+ # Test combined options
+ assert_nothing_raised do
+ CGI.new(
+ accept_charset: 'ISO-8859-1',
+ max_multipart_length: 64 * 1024 * 1024,
+ tag_maker: 'html5'
+ )
+ end
+ end
+
+ # Test basic object structure and public methods
+ def test_object_structure
+ update_env(
+ 'REQUEST_METHOD' => 'GET',
+ 'QUERY_STRING' => 'foo=bar',
+ 'SERVER_SOFTWARE' => 'Apache 2.2.0',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ )
+
+ cgi = CGI.new
+
+ # Test documented instance variables and methods exist
+ assert_kind_of(Hash, cgi.cookies)
+ assert_kind_of(Hash, cgi.params)
+ assert_equal(false, cgi.multipart?)
+ assert_equal("bar", cgi['foo']) # Verify param parsing works
+ end
+
+ # Test accept_charset method behavior (HTTP header vs configuration)
+ def test_accept_charset_method_behavior
+ update_env(
+ 'REQUEST_METHOD' => 'GET',
+ 'QUERY_STRING' => '',
+ 'SERVER_SOFTWARE' => 'Apache 2.2.0',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ )
+
+ # Test without HTTP_ACCEPT_CHARSET header - method should return nil
+ cgi1 = CGI.new(accept_charset: 'EUC-JP')
+ assert_nil(cgi1.accept_charset, "accept_charset method should return HTTP header, not config")
+ assert_equal('EUC-JP', cgi1.instance_variable_get(:@accept_charset))
+
+ # Test with HTTP_ACCEPT_CHARSET header - method should return header value
+ update_env('HTTP_ACCEPT_CHARSET' => 'ISO-8859-1')
+ cgi2 = CGI.new(accept_charset: 'UTF-8')
+ assert_equal('ISO-8859-1', cgi2.accept_charset, "accept_charset method should return HTTP header")
+ assert_equal('UTF-8', cgi2.instance_variable_get(:@accept_charset))
+ end
+
+ # Test encoding error block handling
+ def test_encoding_error_block_handling
+ # Test that a block can be provided (even if encoding errors don't occur in this simple case)
+ test_input = "name=value"
+ update_env(
+ 'REQUEST_METHOD' => 'POST',
+ 'CONTENT_TYPE' => 'application/x-www-form-urlencoded',
+ 'CONTENT_LENGTH' => test_input.length.to_s,
+ 'SERVER_SOFTWARE' => 'Apache 2.2.0',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ )
+
+ $stdin = StringIO.new(test_input)
+
+ encoding_errors = {}
+ assert_nothing_raised do
+ cgi = CGI.new(accept_charset: 'UTF-8') do |name, value|
+ encoding_errors[name] = value
+ end
+ assert_equal("value", cgi['name'])
+ end
+ end
+
+ # Test class vs instance charset behavior
+ def test_class_vs_instance_charset
+ update_env(
+ 'REQUEST_METHOD' => 'GET',
+ 'QUERY_STRING' => '',
+ 'SERVER_SOFTWARE' => 'Apache 2.2.0',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ )
+
+ # Class default should be UTF-8
+ assert_equal(Encoding::UTF_8, CGI.accept_charset)
+
+ # Instance with no option should use class default internally
+ cgi = CGI.new
+ assert_equal(Encoding::UTF_8, cgi.instance_variable_get(:@accept_charset))
+ end
+
+ # Test max_multipart_length configuration (no public getter available)
+ def test_max_multipart_length_configuration
+ update_env(
+ 'REQUEST_METHOD' => 'GET',
+ 'QUERY_STRING' => '',
+ 'SERVER_SOFTWARE' => 'Apache 2.2.0',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ )
+
+ # Test with integer value - should not raise error
+ custom_size = 1024 * 1024 # 1 MB
+ cgi = CGI.new(max_multipart_length: custom_size)
+ assert_equal(custom_size, cgi.instance_variable_get(:@max_multipart_length))
+
+ # Test with lambda - should not raise error
+ check_lambda = -> { 2 * 1024 * 1024 } # 2 MB
+ cgi = CGI.new(max_multipart_length: check_lambda)
+ assert_equal(check_lambda, cgi.instance_variable_get(:@max_multipart_length))
+ end
+
+ # Test that configuration options don't interfere with each other
+ def test_option_assignment
+ update_env(
+ 'REQUEST_METHOD' => 'GET',
+ 'QUERY_STRING' => '',
+ 'SERVER_SOFTWARE' => 'Apache 2.2.0',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ )
+
+ # Create CGI instances with different combinations of options
+ cgi1 = CGI.new(accept_charset: 'EUC-JP')
+ cgi2 = CGI.new(max_multipart_length: 512 * 1024)
+ cgi3 = CGI.new(tag_maker: 'html4')
+ cgi4 = CGI.new(
+ accept_charset: 'ISO-8859-1',
+ max_multipart_length: 256 * 1024,
+ tag_maker: 'html5'
+ )
+
+ # Verify each has the expected configuration
+ assert_equal('EUC-JP', cgi1.instance_variable_get(:@accept_charset))
+ assert_equal(512 * 1024, cgi2.instance_variable_get(:@max_multipart_length))
+ assert_respond_to(cgi3, :doctype)
+
+ assert_equal('ISO-8859-1', cgi4.instance_variable_get(:@accept_charset))
+ assert_equal(256 * 1024, cgi4.instance_variable_get(:@max_multipart_length))
+ assert_respond_to(cgi4, :doctype)
+ assert_equal('', cgi4.doctype)
+ end
+end
\ No newline at end of file