spec icon indicating copy to clipboard operation
spec copied to clipboard

Write specs for new Ruby 2.7 features and changes

Open eregon opened this issue 6 years ago • 16 comments

ruby/spec already contains some specs for 2.7, but we should aim to cover all new features and important changes. This will improve the test coverage of these features (and maybe discover a few bugs along the way), allow other Ruby implementations to implement the changes faster with more confidence and document clearly the new behavior.

The new specs should be within a version guard block:

ruby_version_is "2.7" do
  # New specs
end

NOTE: https://rubyreferences.github.io/rubychanges/2.7.html gives more details for many features and changes.

From https://github.com/ruby/ruby/blob/v2_7_0/NEWS:

NEWS for Ruby 2.7.0

This document is a list of user visible feature changes made between releases except for bug fixes.

Note that each entry is kept so brief that no reason behind or reference information is supplied with. For a full list of changes with all sufficient information, see the ChangeLog file or Redmine (e.g. https://bugs.ruby-lang.org/issues/$FEATURE_OR_BUG_NUMBER).

Changes since the 2.6.0 release

Language changes

Pattern matching

  • [x] Pattern matching is introduced as an experimental feature. Feature #14912
case [0, [1, 2, 3]]
in [a, [b, *c]]
  p a #=> 0
  p b #=> 1
  p c #=> [2, 3]
end

case {a: 0, b: 1}
in {a: 0, x: 1}
  :unreachable
in {a: 0, b: var}
  p var #=> 1
end

case -1
in 0 then :unreachable
in 1 then :unreachable
end #=> NoMatchingPatternError

json = <<END
{
  "name": "Alice",
  "age": 30,
  "children": [{ "name": "Bob", "age": 2 }]
}
END

JSON.parse(json, symbolize_names: true) in {name: "Alice", children: [{name: name, age: age}]}

p name #=> "Bob"
p age  #=> 2

JSON.parse(json, symbolize_names: true) in {name: "Alice", children: [{name: "Charlie", age: age}]}
#=> NoMatchingPatternError

See the following slides for more details:

  • https://speakerdeck.com/k_tsj/pattern-matching-new-feature-in-ruby-2-dot-7
  • Note that the slides are slightly obsolete.
  • [x] The warning against pattern matching can be suppressed with {-W:no-experimental option}[#label-Warning+option].

The spec of keyword arguments is changed towards 3.0

  • [x] Non-symbols are allowed as keyword argument keys if the method accepts arbitrary keywords. Feature #14183

    Non-Symbol keys in a keyword arguments hash were prohibited in 2.6.0, but are now allowed again. Bug #15658

def foo(**kw); p kw; end; foo("str" => 1) #=> {"str"=>1}
  • [x] **nil is allowed in method definitions to explicitly mark that the method accepts no keywords. Calling such a method with keywords will result in an ArgumentError. Feature #14183
def foo(h, **nil); end; foo(key: 1)       # ArgumentError
def foo(h, **nil); end; foo(**{key: 1})   # ArgumentError
def foo(h, **nil); end; foo("str" => 1)   # ArgumentError
def foo(h, **nil); end; foo({key: 1})     # OK
def foo(h, **nil); end; foo({"str" => 1}) # OK
  • [x] Passing an empty keyword splat to a method that does not accept keywords no longer passes an empty hash, unless the empty hash is necessary for a required parameter, in which case a warning will be emitted. Remove the double splat to continue passing a positional hash. Feature #14183
h = {}; def foo(*a) a end; foo(**h) # []
h = {}; def foo(a) a end; foo(**h)  # {} and warning
h = {}; def foo(*a) a end; foo(h)   # [{}]
h = {}; def foo(a) a end; foo(h)    # {}
  • [x] Above warnings can be suppressed also with {-W:no-deprecated option}[#label-Warning+option].

Numbered parameters

  • [x] Numbered parameters as default block parameters are introduced. Feature #4475

    [1, 2, 10].map { _1.to_s(16) }    #=> ["1", "2", "a"]
    [[1, 2], [3, 4]].map { _1 + _2 }  #=> [3, 7]
    

    You can still define a local variable named _1 and so on, and that is honored when present, but renders a warning.

    _1 = 0            #=> warning: `_1' is reserved for numbered parameter; consider another name
    [1].each { p _1 } # prints 0 instead of 1
    

proc/lambda without block is deprecated

  • [x] Proc.new and Kernel#proc with no block in a method called with a block is warned now.

    def foo
      proc
    end
    foo { puts "Hello" } #=> warning: Capturing the given block using Kernel#proc is deprecated; use `&block` instead
    

    This warning can be suppressed with {-W:no-deprecated option}[#label-Warning+option].

  • [x] Kernel#lambda with no block in a method called with a block raises an exception.

    def bar
      lambda
    end
    bar { puts "Hello" } #=> tried to create Proc object without a block (ArgumentError)
    

Other miscellaneous changes

  • [x] A beginless range is experimentally introduced. It might be useful in case, new call-sequence of the Comparable#clamp, constants and DSLs. Feature #14799

    ary[..3]  # identical to ary[0..3]
    
    case RUBY_VERSION
    when ..."2.4" then puts "EOL"
    # ...
    end
    
    age.clamp(..100)
    
    where(sales: ..100)
    
  • [x] Quoted here-document identifiers must end within the same line.

    <<"EOS
    " # This had been warned since 2.4; Now it raises a SyntaxError
    EOS
    
  • [x] The flip-flop syntax deprecation is reverted. Feature #5400

  • [x] Comment lines can be placed between fluent dot now.

    foo
      # .bar
      .baz # => foo.baz
    
  • [x] Calling a private method with a literal self as the receiver is now allowed. Feature #11297 Feature #16123

  • [x] Modifier rescue now operates the same for multiple assignment as single assignment. Bug #8279

    a, b = raise rescue [1, 2]
    # Previously parsed as: (a, b = raise) rescue [1, 2]
    # Now parsed as:         a, b = (raise rescue [1, 2])
    
  • [x] yield in singleton class syntax is warned and will be deprecated later. Feature #15575.

    def foo
      class << Object.new
        yield #=> warning: `yield' in class syntax will not be supported from Ruby 3.0. [Feature #15575](https://bugs.ruby-lang.org/issues/15575)
      end
    end
    foo { p :ok }
    

    This warning can be suppressed with {-W:no-deprecated option}[#label-Warning+option].

  • [x] Argument forwarding by (...) is introduced. Feature #16253

    def foo(...)
      bar(...)
    end
    

    All arguments to foo are forwarded to bar, including keyword and block arguments. Note that the parentheses are mandatory. bar ... is parsed as an endless range.

  • [x] Access and setting of $SAFE is now always warned. $SAFE will become a normal global variable in Ruby 3.0. Feature #16131

  • [x] Object#{taint,untaint,trust,untrust} and related functions in the C-API no longer have an effect (all objects are always considered untainted), and are now warned in verbose mode. This warning will be disabled even in non-verbose mode in Ruby 3.0, and the methods and C functions will be removed in Ruby 3.2. Feature #16131

  • [x] Refinements take place at Object#method and Module#instance_method. Feature #15373

Command line options

Warning option

The -W option has been extended with a following :, to manage categorized warnings. Feature #16345 Feature #16420

  • [x] To suppress deprecation warnings:

    $ ruby -e '$; = ""'
    -e:1: warning: `$;' is deprecated
    
    $ ruby -W:no-deprecated -e '$; = //'
    
  • [x] It works with the RUBYOPT environment variable:

    $ RUBYOPT=-W:no-deprecated ruby -e '$; = //'
    
  • [x] To suppress experimental feature warnings:

    $ ruby -e '0 in a'
    -e:1: warning: Pattern matching is experimental, and the behavior may change in future versions of Ruby!
    
    $ ruby -W:no-experimental -e '0 in a'
    
  • [x] To suppress both by using RUBYOPT, set space separated values:

    $ RUBYOPT='-W:no-deprecated -W:no-experimental' ruby -e '($; = "") in a'
    

See also Warning in {Core classes updates}[#label-Core+classes+updates+-28outstanding+ones+only-29].

Core classes updates (outstanding ones only)

Array

  • [x] Added Array#intersection. Feature #16155

  • [x] Added Array#minmax, with a faster implementation than Enumerable#minmax. Bug #15929

Comparable

  • [x] Comparable#clamp now accepts a Range argument. Feature #14784

      ```ruby
      -1.clamp(0..2) #=> 0
       1.clamp(0..2) #=> 1
       3.clamp(0..2) #=> 2
      # With beginless and endless ranges:
      -1.clamp(0..)  #=> 0
       3.clamp(..2)  #=> 2
      ```
    

Complex

  • [x] Added Complex#<=>. So 0 <=> 0i will not raise NoMethodError. Bug #15857

Dir

  • [x] Dir.glob and Dir.[] no longer allow NUL-separated glob pattern. Use Array instead. Feature #14643

Encoding

Enumerable

  • [x] Added Enumerable#filter_map. Feature #15323

      [1, 2, 3].filter_map {|x| x.odd? ? x.to_s : nil } #=> ["1", "3"]
    
  • [x] Added Enumerable#tally. Feature #11076

      ["A", "B", "C", "B", "A"].tally #=> {"A"=>2, "B"=>2, "C"=>1}
    

Enumerator

  • [x] Added Enumerator.produce to generate an Enumerator from any custom data transformation. Feature #14781

      require "date"
      dates = Enumerator.produce(Date.today, &:succ) #=> infinite sequence of dates
      dates.detect(&:tuesday?) #=> next Tuesday
    
  • [x] Added Enumerator::Lazy#eager that generates a non-lazy enumerator from a lazy enumerator. Feature #15901

      a = %w(foo bar baz)
      e = a.lazy.map {|x| x.upcase }.map {|x| x + "!" }.eager
      p e.class               #=> Enumerator
      p e.map {|x| x + "?" }  #=> ["FOO!?", "BAR!?", "BAZ!?"]
    
  • [x] Added Enumerator::Yielder#to_proc so that a Yielder object can be directly passed to another method as a block argument. Feature #15618

Fiber

  • [x] Added Fiber#raise that behaves like Fiber#resume but raises an exception on the resumed fiber. Feature #10344

File

  • [x] File.extname now returns a dot string for names ending with a dot on non-Windows platforms. Bug #15267

        File.extname("foo.") #=> "."
    

FrozenError

  • [x] Added FrozenError#receiver to return the frozen object on which modification was attempted. To set this object when raising FrozenError in Ruby code, FrozenError.new accepts a :receiver option. Feature #15751

GC

  • [x] (MRI-specific) Added GC.compact method for compacting the heap. This function compacts live objects in the heap so that fewer pages may be used, and the heap may be more CoW (copy-on-write) friendly. Feature #15626

    Details on the algorithm and caveats can be found here:
    https://bugs.ruby-lang.org/issues/15626
    

IO

  • [x] Added IO#set_encoding_by_bom to check the BOM and set the external encoding. Bug #15210

Integer

  • [x] Integer#[] now supports range operations. Feature #8842

       0b01001101[2, 4]  #=> 0b0011
       0b01001100[2..5]  #=> 0b0011
       0b01001100[2...6] #=> 0b0011
       #   ^^^^
    

Method

Module

  • [x] Added Module#const_source_location to retrieve the location where a constant is defined. Feature #10771

  • [x] Added Module#ruby2_keywords for marking a method as passing keyword arguments through a regular argument splat, useful when delegating all arguments to another method in a way that can be backwards compatible with older Ruby versions. Bug #16154

  • [x] Module#autoload? now takes an inherit optional argument, like Module#const_defined?. Feature #15777

  • [x] Module#name now always returns a frozen String. The returned String is always the same for a given Module. This change is experimental. Feature #16150

NilClass / TrueClass / FalseClass

  • [x] NilClass#to_s, TrueClass#to_s, and FalseClass#to_s now always return a frozen String. The returned String is always the same for each of these values. This change is experimental. Feature #16150

ObjectSpace::WeakMap

  • [x] ObjectSpace::WeakMap#[]= now accepts special objects as either key or values. Feature #16035

Proc

  • [x] Added Proc#ruby2_keywords for marking the proc as passing keyword arguments through a regular argument splat, useful when delegating all arguments to another method or proc in a way that can be backwards compatible with older Ruby versions. Feature #16404

Range

  • [x] Added Range#minmax, with a faster implementation than Enumerable#minmax. It returns a maximum that now corresponds to Range#max. Bug #15807

  • [x] Range#=== now uses Range#cover? for String arguments, too (in Ruby 2.6, it was changed from Range#include? for all types except strings). Bug #15449

String

  • [x] Update Unicode version and Emoji version from 11.0.0 to 12.0.0. Feature #15321

  • [x] Update Unicode version to 12.1.0, adding support for U+32FF SQUARE ERA NAME REIWA. Feature #15195

  • [x] Update Unicode Emoji version to 12.1. Feature #16272

Symbol

  • [x] Added Symbol#start_with? and Symbol#end_with? methods. Feature #16348

Time

UnboundMethod

  • [x] Added UnboundMethod#bind_call method. Feature #15955

    <code>umethod.bind_call(obj, ...)</code> is semantically equivalent
    to <code>umethod.bind(obj).call(...)</code>.  This idiom is used in
    some libraries to call a method that is overridden.  The added
    method does the same without allocation of an intermediate Method
    object.
    
        class Foo
          def add_1(x)
            x + 1
          end
        end
        class Bar < Foo
          def add_1(x) # override
            x + 2
          end
        end
    
        obj = Bar.new
        p obj.add_1(1) #=> 3
        p Foo.instance_method(:add_1).bind(obj).call(1) #=> 2
        p Foo.instance_method(:add_1).bind_call(obj, 1) #=> 2
    

Warning

$LOAD_PATH

Stdlib updates (outstanding ones only)

  • [x] Pathname.glob now delegates 3 arguments to Dir.glob to accept base keyword. Feature #14405

Compatibility issues (excluding feature bug fixes)

  • [x] The following libraries are no longer bundled gems. Install corresponding gems to use these features.
    • CMath (cmath gem)
    • Scanf (scanf gem)
    • Shell (shell gem)
    • Synchronizer (sync gem)
    • ThreadsWait (thwait gem)
    • E2MM (e2mmap gem)

Proc

Range

  • [x] #777 Range#minmax used to iterate on the range to determine the maximum. It now uses the same algorithm as Range#max. In rare cases (e.g. ranges of Floats or Strings), this may yield different results. Bug #15807

Stdlib compatibility issues (excluding feature bug fixes)

  • Promote stdlib to default gems
    • The following default gems were published on rubygems.org
      • benchmark
      • cgi
      • delegate
      • getoptlong
      • net-pop
      • net-smtp
      • open3
      • pstore
      • readline
      • readline-ext
      • singleton
    • The following default gems were only promoted at ruby-core, but not yet published on rubygems.org.
      • monitor
      • observer
      • timeout
      • tracer
      • uri
      • yaml
  • [x] The did_you_mean gem has been promoted up to a default gem from a bundled gem

pathname

  • [x] Kernel#Pathname when called with a Pathname argument now returns the argument instead of creating a new Pathname. This is more similar to other Kernel methods, but can break code that modifies the return value and expects the argument not to be modified.

C API updates

  • [ ] Many *_kw functions have been added for setting whether the final argument being passed should be treated as keywords. You may need to switch to these functions to avoid keyword argument separation warnings, and to ensure correct behavior in Ruby 3.

  • [ ] The : character in rb_scan_args format string is now treated as keyword arguments. Passing a positional hash instead of keyword arguments will emit a deprecation warning.

  • [x] C API declarations with ANYARGS are changed not to use ANYARGS. See https://github.com/ruby/ruby/pull/2404

eregon avatar Dec 30 '19 16:12 eregon

@andrykonchin I tried to keep the ones you checked after my last fix for links to issues. Should be fine but would be good if you can double-check.

eregon avatar Dec 30 '19 17:12 eregon

@eregon Checked 👍

andrykonchin avatar Dec 30 '19 19:12 andrykonchin

Note for contributors: please mention here which item/checkbox you'd like to add specs for, in order to avoid duplicated work.

eregon avatar Oct 06 '20 10:10 eregon

Hi @eregon

I believe you can mark this as completed

  • [ ] "Added Symbol#start_with? and Symbol#end_with? methods."

Symbol#end_with?: https://github.com/ruby/spec/commit/c51c102ceb1966839e8febca927771ab9d04829f

Symbol#start_with?: https://github.com/ruby/spec/commit/7377c2b0ad8bee9603646b5a7bc4db2176918c4a

lxxxvi avatar Oct 07 '20 09:10 lxxxvi

And this looks like completed too

The Proc#to_s format was changed. Feature #16101

https://github.com/ruby/spec/commit/d3c12442817a4f2582659d43eba0017e81d5159b

moofkit avatar Oct 07 '20 16:10 moofkit

@eregon

How could I cover this one?

  • [ ] Added Array#minmax, with a faster implementation than Enumerable#minmax. Bug #15929

As far as I understand there's already a Enumerable#minmax spec which uses Arrays... so it's already tested?

lxxxvi avatar Oct 08 '20 11:10 lxxxvi

@lxxxvi In that case we could just add a new file and check Array.should have_instance_method(:minmax), and a comment saying the behavior is tested in the core/enumerable/minmax_spec.rb.

I don't see Arrays being used (as receivers) in https://github.com/ruby/spec/blob/bc1942130ba064d7759f4e91e84ff6b126b2c721/core/enumerable/minmax_spec.rb though, so I think best is to copy that file and adapt to use Arrays. And check Array.should have_instance_method(:minmax) in 2.7+.

eregon avatar Oct 08 '20 15:10 eregon

The flip-flop syntax deprecation is reverted. Feature #5400

And this one completed: https://github.com/ruby/spec/commit/61092f3c9e3e66b27c780ae274fc7c855cfbbfc7 https://github.com/ruby/spec/commit/574b5c05fa17955abecfe0cd4cfe3e1866b2063d

moofkit avatar Oct 08 '20 18:10 moofkit

ObjectSpace::WeakMap#[]= now accepts special objects as either key or values. Feature #16035

It seems this case has already been covered in this commit https://github.com/ruby/spec/commit/47be917309aba997f3e353046a28296a41f95e46, can you please check?

kuei0221 avatar Oct 19 '20 08:10 kuei0221

@kuei0221 Indeed, I now checked the box.

eregon avatar Oct 19 '20 09:10 eregon

^ PR for

[ ] Object#{taint,untaint,trust,untrust} and related functions in the C-API no longer have an effect (all objects are always considered untainted), and are now warned in verbose mode. This warning will be disabled even in non-verbose mode in Ruby 3.0, and the methods and C functions will be removed in Ruby 3.2. Feature #16131

HeroProtagonist avatar Oct 20 '20 04:10 HeroProtagonist

61/70 done, we're almost there!

eregon avatar Oct 24 '20 13:10 eregon

@eregon Is it time for new Ruby3-related issue?

andrykonchin avatar Dec 30 '20 14:12 andrykonchin

I'll try to make one soon. It would be great to finish the last 5 items for this issue.

eregon avatar Dec 30 '20 15:12 eregon

Only 2 items left under C API updates now.

eregon avatar Jan 06 '21 19:01 eregon

Issue for Ruby 3.0 specs: https://github.com/ruby/spec/issues/823

eregon avatar Jan 06 '21 20:01 eregon