From 5cfd49fee4febedf59b53439199f02b40239ab02 Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Sat, 13 Sep 2025 22:49:34 -0500 Subject: [PATCH 1/2] [ruby/erb] [DOC] Enhanced doc for ERB.new (https://github.com/ruby/erb/pull/68) https://github.com/ruby/erb/commit/9591b5d23b --- lib/erb.rb | 92 +++++++++++++++++++++++++----------------------------- 1 file changed, 42 insertions(+), 50 deletions(-) diff --git a/lib/erb.rb b/lib/erb.rb index c8aae4fd15239c..d4f43e0772341b 100644 --- a/lib/erb.rb +++ b/lib/erb.rb @@ -259,10 +259,9 @@ # # => "\n* 0,0\n\n* 0,1\n\n* 0,2\n\n* 1,0\n\n* 1,1\n\n* 1,2\n\n* 2,0\n\n* 2,1\n\n* 2,2\n\n" # ``` # -# You can use keyword argument `trim_mode` to make certain adjustments to the processing; -# see ERB.new. +# #### Shorthand Format for Execution Tags # -# In particular, you can give `trim_mode: '%'` to enable a shorthand format for execution tags; +# You can give `trim_mode: '%'` to enable a shorthand format for execution tags; # this example uses the shorthand format `% _code_` instead of `<% _code_ %>`: # # ``` @@ -527,69 +526,62 @@ def self.version VERSION end + # :markup: markdown # - # Constructs a new ERB object with the template specified in _str_. + # :call-seq: + # ERB.new(string, trim_mode: nil, eoutvar: '_erbout') # - # An ERB object works by building a chunk of Ruby code that will output - # the completed template when run. + # Returns a new \ERB object containing the given +string+. # - # If _trim_mode_ is passed a String containing one or more of the following - # modifiers, ERB will adjust its code generation as listed: + # For details about `string`, its embedded tags, and generated results, see ERB. # - # % enables Ruby code processing for lines beginning with % - # <> omit newline for lines starting with <% and ending in %> - # > omit newline for lines ending in %> - # - omit blank lines ending in -%> + # **Keyword Argument `trim_mode`** # - # _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. + # When keyword argument `trim_mode` has a string value, + # that value may be one of: # - # ### Example + # - `'%'`: Enable [shorthand format][shorthand format] for execution tags. + # - `'-'`: Omit each blank line ending with `'%>'`. + # - `'>'`: Omit newline for each line ending with `'%>'`. + # - `'<>'`: Omit newline for each line starting with `'<%'` and ending with `'%>'`. # - # require "erb" + # The value may also be certain combinations of the above. # - # # build data class - # class Listings - # PRODUCT = { :name => "Chicken Fried Steak", - # :desc => "A well messaged pattie, breaded and fried.", - # :cost => 9.95 } + # - `'%-'`: Enable shorthand and omit each blank line ending with `'%>'`. + # - `'%>'`: Enable shorthand and omit newline for each line ending with `'%>'`. + # - `'%<>'`: Enable shorthand and omit newline for each line starting with `'<%'` and ending with `'%>'`. # - # attr_reader :product, :price + # **Keyword Argument `eoutvar`** # - # def initialize( product = "", price = "" ) - # @product = product - # @price = price - # end + # The string value of keyword argument `eoutvar` specifies the name of the variable + # that method #result uses to construct its result string. + # 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. # - # def build - # b = binding - # # create and run templates, filling member data variables - # ERB.new(<<~'END_PRODUCT', trim_mode: "", eoutvar: "@product").result b - # <%= PRODUCT[:name] %> - # <%= PRODUCT[:desc] %> - # END_PRODUCT - # ERB.new(<<~'END_PRICE', trim_mode: "", eoutvar: "@price").result b - # <%= PRODUCT[:name] %> -- <%= PRODUCT[:cost] %> - # <%= PRODUCT[:desc] %> - # END_PRICE - # end - # end + # It's good practice to choose a variable name that begins with an underscore: `'_'`. # - # # setup template data - # listings = Listings.new - # listings.build + # Backward Compatibility # - # puts listings.product + "\n" + listings.price + # The calling sequence given above -- which is the one you should use -- + # is a simplified version of the complete formal calling sequence, + # which is: # - # _Generates_ + # ``` + # ERB.new(string, + # safe_level=NOT_GIVEN, legacy_trim_mode=NOT_GIVEN, legacy_eoutvar=NOT_GIVEN, + # trim_mode: nil, eoutvar: '_erbout') + # ``` # - # Chicken Fried Steak - # A well massaged pattie, breaded and fried. + # The second, third, and fourth positional arguments (those in the second line above) are deprecated; + # this method issues warnings if they are given. # - # Chicken Fried Steak -- 9.95 - # A well massaged pattie, breaded and fried. + # However, their values, if given, are handled thus: + # + # - `safe_level`: ignored. + # - `legacy_trim_mode: overrides keyword argument `trim_mode`. + # - `legacy_eoutvar: overrides keyword argument `eoutvar`. + # + # [shorthand format]: rdoc-ref:ERB@Shorthand+Format+for+Execution+Tags # def initialize(str, safe_level=NOT_GIVEN, legacy_trim_mode=NOT_GIVEN, legacy_eoutvar=NOT_GIVEN, trim_mode: nil, eoutvar: '_erbout') # Complex initializer for $SAFE deprecation at [Feature #14256]. Use keyword arguments to pass trim_mode or eoutvar. From 67cc574dfb1943a9cb052478cc84d0c8823d0527 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 14 Sep 2025 12:25:25 +0900 Subject: [PATCH 2/2] Remove tests do not test Regexp methods The backslashs in these tests are parsed as escapes in string literals by the parser, but not passed to the `Regexp` methods. --- spec/ruby/core/regexp/shared/new.rb | 241 +--------------------------- 1 file changed, 1 insertion(+), 240 deletions(-) diff --git a/spec/ruby/core/regexp/shared/new.rb b/spec/ruby/core/regexp/shared/new.rb index 921736d2990853..12c3d7c9c265e9 100644 --- a/spec/ruby/core/regexp/shared/new.rb +++ b/spec/ruby/core/regexp/shared/new.rb @@ -195,190 +195,14 @@ def obj.to_int() ScratchPad.record(:called) end -> { Regexp.send(@method, "\\\\") }.should_not raise_error(RegexpError) end - it "accepts a backspace followed by a character" do + it "accepts a backspace followed by a non-special character" do Regexp.send(@method, "\\N").should == /#{"\x5c"+"N"}/ end - it "accepts a one-digit octal value" do - Regexp.send(@method, "\0").should == /#{"\x00"}/ - end - - it "accepts a two-digit octal value" do - Regexp.send(@method, "\11").should == /#{"\x09"}/ - end - - it "accepts a one-digit hexadecimal value" do - Regexp.send(@method, "\x9n").should == /#{"\x09n"}/ - end - - it "accepts a two-digit hexadecimal value" do - Regexp.send(@method, "\x23").should == /#{"\x23"}/ - end - - it "interprets a digit following a two-digit hexadecimal value as a character" do - Regexp.send(@method, "\x420").should == /#{"\x420"}/ - end - it "raises a RegexpError if \\x is not followed by any hexadecimal digits" do -> { Regexp.send(@method, "\\" + "xn") }.should raise_error(RegexpError, Regexp.new(Regexp.escape("invalid hex escape: /\\xn/"))) end - it "accepts an escaped string interpolation" do - Regexp.send(@method, "\#{abc}").should == /#{"\#{abc}"}/ - end - - it "accepts '\\n'" do - Regexp.send(@method, "\n").should == /#{"\x0a"}/ - end - - it "accepts '\\t'" do - Regexp.send(@method, "\t").should == /#{"\x09"}/ - end - - it "accepts '\\r'" do - Regexp.send(@method, "\r").should == /#{"\x0d"}/ - end - - it "accepts '\\f'" do - Regexp.send(@method, "\f").should == /#{"\x0c"}/ - end - - it "accepts '\\v'" do - Regexp.send(@method, "\v").should == /#{"\x0b"}/ - end - - it "accepts '\\a'" do - Regexp.send(@method, "\a").should == /#{"\x07"}/ - end - - it "accepts '\\e'" do - Regexp.send(@method, "\e").should == /#{"\x1b"}/ - end - - it "accepts '\\C-\\n'" do - Regexp.send(@method, "\C-\n").should == /#{"\x0a"}/ - end - - it "accepts '\\C-\\t'" do - Regexp.send(@method, "\C-\t").should == /#{"\x09"}/ - end - - it "accepts '\\C-\\r'" do - Regexp.send(@method, "\C-\r").should == /#{"\x0d"}/ - end - - it "accepts '\\C-\\f'" do - Regexp.send(@method, "\C-\f").should == /#{"\x0c"}/ - end - - it "accepts '\\C-\\v'" do - Regexp.send(@method, "\C-\v").should == /#{"\x0b"}/ - end - - it "accepts '\\C-\\a'" do - Regexp.send(@method, "\C-\a").should == /#{"\x07"}/ - end - - it "accepts '\\C-\\e'" do - Regexp.send(@method, "\C-\e").should == /#{"\x1b"}/ - end - - it "accepts multiple consecutive '\\' characters" do - Regexp.send(@method, "\\\\\\N").should == /#{"\\\\\\"+"N"}/ - end - - it "accepts characters and escaped octal digits" do - Regexp.send(@method, "abc\076").should == /#{"abc\x3e"}/ - end - - it "accepts escaped octal digits and characters" do - Regexp.send(@method, "\076abc").should == /#{"\x3eabc"}/ - end - - it "accepts characters and escaped hexadecimal digits" do - Regexp.send(@method, "abc\x42").should == /#{"abc\x42"}/ - end - - it "accepts escaped hexadecimal digits and characters" do - Regexp.send(@method, "\x3eabc").should == /#{"\x3eabc"}/ - end - - it "accepts escaped hexadecimal and octal digits" do - Regexp.send(@method, "\061\x42").should == /#{"\x31\x42"}/ - end - - it "accepts \\u{H} for a single Unicode codepoint" do - Regexp.send(@method, "\u{f}").should == /#{"\x0f"}/ - end - - it "accepts \\u{HH} for a single Unicode codepoint" do - Regexp.send(@method, "\u{7f}").should == /#{"\x7f"}/ - end - - it "accepts \\u{HHH} for a single Unicode codepoint" do - Regexp.send(@method, "\u{07f}").should == /#{"\x7f"}/ - end - - it "accepts \\u{HHHH} for a single Unicode codepoint" do - Regexp.send(@method, "\u{0000}").should == /#{"\x00"}/ - end - - it "accepts \\u{HHHHH} for a single Unicode codepoint" do - Regexp.send(@method, "\u{00001}").should == /#{"\x01"}/ - end - - it "accepts \\u{HHHHHH} for a single Unicode codepoint" do - Regexp.send(@method, "\u{000000}").should == /#{"\x00"}/ - end - - it "accepts characters followed by \\u{HHHH}" do - Regexp.send(@method, "abc\u{3042}").should == /#{"abc\u3042"}/ - end - - it "accepts \\u{HHHH} followed by characters" do - Regexp.send(@method, "\u{3042}abc").should == /#{"\u3042abc"}/ - end - - it "accepts escaped hexadecimal digits followed by \\u{HHHH}" do - Regexp.send(@method, "\x42\u{3042}").should == /#{"\x42\u3042"}/ - end - - it "accepts escaped octal digits followed by \\u{HHHH}" do - Regexp.send(@method, "\056\u{3042}").should == /#{"\x2e\u3042"}/ - end - - it "accepts a combination of escaped octal and hexadecimal digits and \\u{HHHH}" do - Regexp.send(@method, "\056\x42\u{3042}\x52\076").should == /#{"\x2e\x42\u3042\x52\x3e"}/ - end - - it "accepts \\uHHHH for a single Unicode codepoint" do - Regexp.send(@method, "\u3042").should == /#{"\u3042"}/ - end - - it "accepts characters followed by \\uHHHH" do - Regexp.send(@method, "abc\u3042").should == /#{"abc\u3042"}/ - end - - it "accepts \\uHHHH followed by characters" do - Regexp.send(@method, "\u3042abc").should == /#{"\u3042abc"}/ - end - - it "accepts escaped hexadecimal digits followed by \\uHHHH" do - Regexp.send(@method, "\x42\u3042").should == /#{"\x42\u3042"}/ - end - - it "accepts escaped octal digits followed by \\uHHHH" do - Regexp.send(@method, "\056\u3042").should == /#{"\x2e\u3042"}/ - end - - it "accepts a combination of escaped octal and hexadecimal digits and \\uHHHH" do - Regexp.send(@method, "\056\x42\u3042\x52\076").should == /#{"\x2e\x42\u3042\x52\x3e"}/ - end - - it "accepts a multiple byte character which need not be escaped" do - Regexp.send(@method, "\§").should == /#{"§"}/ - end - it "raises a RegexpError if less than four digits are given for \\uHHHH" do -> { Regexp.send(@method, "\\" + "u304") }.should raise_error(RegexpError, Regexp.new(Regexp.escape("invalid Unicode escape: /\\u304/"))) end @@ -433,69 +257,6 @@ def obj.to_int() ScratchPad.record(:called) end describe :regexp_new_string_binary, shared: true do describe "with escaped characters" do - it "accepts a three-digit octal value" do - Regexp.send(@method, "\315").should == /#{"\xcd"}/ - end - - it "interprets a digit following a three-digit octal value as a character" do - Regexp.send(@method, "\3762").should == /#{"\xfe2"}/ - end - - it "accepts '\\M-\\n'" do - Regexp.send(@method, "\M-\n").should == /#{"\x8a"}/ - end - - it "accepts '\\M-\\t'" do - Regexp.send(@method, "\M-\t").should == /#{"\x89"}/ - end - - it "accepts '\\M-\\r'" do - Regexp.send(@method, "\M-\r").should == /#{"\x8d"}/ - end - - it "accepts '\\M-\\f'" do - Regexp.send(@method, "\M-\f").should == /#{"\x8c"}/ - end - - it "accepts '\\M-\\v'" do - Regexp.send(@method, "\M-\v").should == /#{"\x8b"}/ - end - - it "accepts '\\M-\\a'" do - Regexp.send(@method, "\M-\a").should == /#{"\x87"}/ - end - - it "accepts '\\M-\\e'" do - Regexp.send(@method, "\M-\e").should == /#{"\x9b"}/ - end - - it "accepts '\\M-\\C-\\n'" do - Regexp.send(@method, "\M-\C-\n").should == /#{"\x8a"}/ - end - - it "accepts '\\M-\\C-\\t'" do - Regexp.send(@method, "\M-\C-\t").should == /#{"\x89"}/ - end - - it "accepts '\\M-\\C-\\r'" do - Regexp.send(@method, "\M-\C-\r").should == /#{"\x8d"}/ - end - - it "accepts '\\M-\\C-\\f'" do - Regexp.send(@method, "\M-\C-\f").should == /#{"\x8c"}/ - end - - it "accepts '\\M-\\C-\\v'" do - Regexp.send(@method, "\M-\C-\v").should == /#{"\x8b"}/ - end - - it "accepts '\\M-\\C-\\a'" do - Regexp.send(@method, "\M-\C-\a").should == /#{"\x87"}/ - end - - it "accepts '\\M-\\C-\\e'" do - Regexp.send(@method, "\M-\C-\e").should == /#{"\x9b"}/ - end end end