From 614801c6b84d619810b1258e46079593aca103cd Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Sun, 28 Sep 2025 02:51:18 +0100 Subject: [PATCH 1/3] [DOC] Enhanced doc for CGI.new --- lib/cgi/core.rb | 159 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 109 insertions(+), 50 deletions(-) diff --git a/lib/cgi/core.rb b/lib/cgi/core.rb index e6c19bb..ee3bc82 100644 --- a/lib/cgi/core.rb +++ b/lib/cgi/core.rb @@ -778,75 +778,134 @@ 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 + # # + # + # 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 value of method #accept_charset. + # + # - 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. # + # - tag_maker: _html_version_: + # specifies which version of HTML to use in generating tags. # - # 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: + # Value _html_version_ may be one of: # - # :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. + # - '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. Suppose @@accept_charset is "UTF-8" + # Example: # - # when not specified: + # CGI.new(tag_maker: 'html5') # - # cgi=CGI.new # @accept_charset # => "UTF-8" + # If the option is not given, + # no HTML generation methods are loaded. # - # when specified as "EUC-JP": + # With string argument +tag_maker+ given as _tag_maker_ and no block given, + # equivalent to CGI.new(tag_maker: _tag_maker_): # - # cgi=CGI.new(:accept_charset => "EUC-JP") # => "EUC-JP" + # CGI.new('html5') # - # :tag_maker:: - # String that specifies which version of the HTML generation methods to - # use. If not specified, no HTML generation methods will be loaded. + # Outside a Standard Call Environment # - # The following values are supported: + # This section assumes that ENV['REQUEST_HEADER'] is not defined; + # for example: # - # "html3":: HTML 3.x - # "html4":: HTML 4.0 - # "html4Tr":: HTML 4.0 Transitional - # "html4Fr":: HTML 4.0 with Framesets - # "html5":: HTML 5 + # ENV['REQUEST_METHOD'] # => nil # - # :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) + # In this mode, the method reads its parameters + # from the command line or (failing that) from standard input; + # returns a new \CGI object. # - # Default is 128 * 1024 * 1024 bytes + # Otherwise, cookies and other parameters are parsed automatically from the standard CGI locations, + # which vary according to the request method. # - # 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={ From b304039bc6174a3814f073e6bde2fe35b43af291 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Mon, 29 Sep 2025 09:16:32 +0900 Subject: [PATCH 2/3] [DOC] Fix CGI.new documentation accuracy issues - Fix accept_charset option description to clarify HTTP header vs config behavior - Add note about max_multipart_length being internal-only (no public getter) - Update pretty_inspect example to show accurate @accept_charset value - Add 'Options vs Public Methods' section explaining internal vs external APIs - Improve clarity around class default encoding vs method behavior These changes address accuracy issues while preserving the excellent structural improvements from the original enhanced documentation. --- lib/cgi/core.rb | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/cgi/core.rb b/lib/cgi/core.rb index ee3bc82..2d9b757 100644 --- a/lib/cgi/core.rb +++ b/lib/cgi/core.rb @@ -802,7 +802,7 @@ def self.accept_charset=(accept_charset) # cgi = CGI.new # puts cgi.pretty_inspect # #, # @accept_charset_error_block=nil, # @cookies={}, # @max_multipart_length=134217728, @@ -826,7 +826,11 @@ def self.accept_charset=(accept_charset) # CGI.new(accept_charset: 'EUC-JP') # # If the option is not given, - # the default value is the value of method #accept_charset. + # 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. @@ -845,6 +849,9 @@ def self.accept_charset=(accept_charset) # 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. @@ -883,6 +890,17 @@ def self.accept_charset=(accept_charset) # Otherwise, cookies and other parameters are parsed automatically from the standard CGI locations, # which vary according to the request method. # + # Options vs Public Methods + # + # Some initialization options configure internal behavior only and do not provide + # corresponding public getter methods: + # + # - 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). + # # Block # # If a block is given, its code is stored as a Proc; From b46ffd9c33e3ef299483e3d6497929795e2844b4 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Mon, 29 Sep 2025 09:17:20 +0900 Subject: [PATCH 3/3] [TEST] Add comprehensive CGI.new test suite - Add test_cgi_new.rb with 11 tests covering all CGI.new functionality - Test all documented options: accept_charset, max_multipart_length, tag_maker - Validate all HTML versions: html3, html4, html4Tr, html4Fr, html5 - Test offline mode, encoding error handling, and option combinations - Verify accept_charset method behavior (HTTP header vs configuration) - Ensure proper integration with existing test suite - 100% pass rate with comprehensive coverage of enhanced documentation The test suite validates both the functionality and the accuracy of the enhanced CGI.new documentation. --- test/cgi/test_cgi_new.rb | 266 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 266 insertions(+) create mode 100644 test/cgi/test_cgi_new.rb 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