From 1867a7b26dba25acf330fa3f7ca75ce334369c39 Mon Sep 17 00:00:00 2001 From: zverok Date: Mon, 23 Dec 2019 22:51:11 +0200 Subject: [PATCH 1/6] 2.7 draft + migrate to Rakefile --- 2.4.md | 13 +- 2.5.md | 5 +- 2.6.md | 12 +- 2.7.md | 868 +++++++++++++++++++++++++++++++++++++ Rakefile | 28 ++ _data/book.yml | 497 ++++++++++++--------- _src/2.6.md | 7 +- _src/2.7.md | 868 +++++++++++++++++++++++++++++++++++++ _src/lib/file.rb | 19 + _src/lib/render.rb | 27 ++ _src/lib/toc.rb | 84 ++++ _src/lib/util.rb | 33 ++ _src/script/postprocess.rb | 34 -- _src/script/toc.rb | 97 ----- 14 files changed, 2254 insertions(+), 338 deletions(-) create mode 100644 2.7.md create mode 100644 Rakefile create mode 100644 _src/2.7.md create mode 100644 _src/lib/file.rb create mode 100644 _src/lib/render.rb create mode 100644 _src/lib/toc.rb create mode 100644 _src/lib/util.rb delete mode 100644 _src/script/postprocess.rb delete mode 100644 _src/script/toc.rb diff --git a/2.4.md b/2.4.md index b8958bf..41b7fc4 100644 --- a/2.4.md +++ b/2.4.md @@ -15,8 +15,9 @@ prev: 2.5 --> * **Released at:** Dec 25, 2016 ([NEWS](https://github.com/ruby/ruby/blob/trunk/doc/NEWS-2.4.0) file) -* **Status (as of Oct 14, 2019):** EOL soon, latest is 2.4.9 -* **Last change to this document:** Oct 14, 2019 +* **Status (as of Dec 07, 2019):** EOL soon, latest is 2.4.9 +* **This document first published:** Oct 14, 2019 +* **Last change to this document:** Dec 07, 2019 ## Highlights[](#highlights) @@ -189,7 +190,7 @@ New single-method module was introduced, meant to be overriden in order to contr Allows to receive unfrozen copy of a frozen object. -* **Reason:** Previously, there were no way to receive an unfrozen copy of the frozen object, including its singleton class: `.dup` returns unfrozen object, but doesn't copies the singleton class, while `.clone` copies both (singleton class & frozen state). +* **Reason:** Previously, there was no way to receive an unfrozen copy of the frozen object, including its singleton class: `.dup` returns unfrozen object, but doesn't copies the singleton class, while `.clone` copies both (singleton class & frozen state). * **Discussion:** Feature #12300 * **Documentation:** Object#clone * **Code:** @@ -282,7 +283,7 @@ Returns an array of digits of the number. #### `ndigits` optional argument for rounding methods[](#ndigits-optional-argument-for-rounding-methods) -Rounding methods of numerics (`ceil`/`floor` etc.) now accept an optional argument to specify how much digits to truncate to. If argument is positive, it means decimal digits, and if it is negative, means tens (the same behavior `#round` had since Ruby 1.9). +Rounding methods of numerics (`ceil`/`floor` etc.) now accept an optional argument to specify how many digits to truncate to. If argument is positive, it means decimal digits, and if it is negative, means tens (the same behavior `#round` had since Ruby 1.9). * **Discussion:** Feature #12245 * **Documentation:** Numeric#ceil, Numeric#floor, Numeric#truncate (in fact, `Integer` and `Float` classes are affected, because `Rational` had the same option long ago). @@ -482,7 +483,7 @@ New boolean methods for checking if some pattern matches some string/symbol. ('a'..'f').sum # TypeError: String can't be coerced into Integer ('a'..'f').sum('') # => "abcdef" ``` -* **Note:** Separate implementation of `Array#sum` is provided for effectiveness. Important thing to note is it doesn't rely on `Array#each` method: +* **Note:** Separate implementation of `Array#sum` is provided for effeciency. Important thing to note is it doesn't rely on `Array#each` method: ```ruby class MyAry < Array def each(&block) @@ -717,7 +718,7 @@ Returns an actual name of the method being called, even if aliased. ## Stdlib[](#stdlib) -* `Set`: #compare_by_identity and #compare_by_identity methods added, behaving the same way as (existing since 1.9) Hash#compare_by_identity: only elements being the same object (same `#object_id`) are considered same set element. Discussion: Feature #12210 +* `Set`: #compare_by_identity and #compare_by_identity? methods added, behaving the same way as (existing since 1.9) Hash#compare_by_identity: only elements being the same object (same `#object_id`) are considered same set element. Discussion: Feature #12210 * CSV::new: Add a `liberal_parsing` option, allowing to (try to) parse not-completely-valid CSV. Discussion: Feature #11839 * `Binding#irb` start a REPL session like `binding.pry`.Follow-up: since [Ruby 2.5](https://rubyreferences.github.io/rubychanges/2.5.html#ruby-development-and-introspection), `require 'irb'` is not necessary for the feature to work, it is done automatically. * Logger::new adds keyword arguments `level:`, `progname:`, `datetime_format:`, `formatter:`, `shift_period_suffix:`. The latter allows specifying suffix for filenames on log rotation. Discussions: Feature #12224 (keyword args), Feature #10772 (`shift_period_suffix`). diff --git a/2.5.md b/2.5.md index 622b9c0..42d4e1d 100644 --- a/2.5.md +++ b/2.5.md @@ -15,8 +15,9 @@ prev: 2.6 --> * **Released at:** Dec 25, 2017 ([NEWS](https://github.com/ruby/ruby/blob/trunk/doc/NEWS-2.5.0) file) -* **Status (as of Oct 13, 2019):** active, latest is 2.5.7 -* **Last change to this document:** Oct 13, 2019 +* **Status (as of Dec 07, 2019):** active, latest is 2.5.7 +* **This document first published:** Jun 6, 2019 +* **Last change to this document:** Dec 07, 2019 ## Highlights[](#highlights) diff --git a/2.6.md b/2.6.md index ecc5a21..0821ec4 100644 --- a/2.6.md +++ b/2.6.md @@ -7,8 +7,9 @@ next: 2.5 # Ruby 2.6 * **Released at:** Dec 25, 2018 ([NEWS](https://github.com/ruby/ruby/blob/ruby_2_6/NEWS) file) -* **Status (as of Oct 14, 2019):** 2.6.5 is current _stable_ -* **Last change to this document:** Oct 14, 2019 +* **Status (as of Dec 21, 2019):** 2.6.5 is current _stable_ +* **This document first published:** Dec 29, 2018 +* **Last change to this document:** Dec 21, 2019 > **Note:** As already explained in [Introduction](README.md), this site is dedicated to changes in the **language**, not the **implementation**, therefore the list below lacks mentions of lots of important optimization introduced in 2.6, including the whole JIT big thing. That's not because they are not important, just because this site's goals are different. @@ -28,7 +29,6 @@ next: 2.5 * **Discussion:** Feature #12912 * **Reason/Usage:** More convenient `Array` slicing, and idiomatic open-ended `case` conditions * **Documentation:** Range: Endless ranges -* **Related:** [startless ranges](https://bugs.ruby-lang.org/issues/14799) are being discussed currently. * **Code:** ```ruby # Usage @@ -50,6 +50,7 @@ next: 2.5 (1..) == (1...) # => false (1..).size # => Infinity ``` +* **Follow-up:** "Beginless" range [was introduced](TODO) in 2.7. ### Non-ASCII constant names[](#non-ascii-constant-names) @@ -103,6 +104,7 @@ Refinements are now compatible with `#public_send` and `#respond_to?`, and impli ### Misc[](#misc) * Infamous esoteric flip-flop syntax is deprecated finally: Feature #5400. + * **Follow-up:** Deprecation is [reverted](#TODO) in 2.7 ## Core classes and modules[](#core-classes-and-modules) @@ -268,6 +270,7 @@ The concept of a "timezone object" is introduced for various `Time` methods. Rub # => [2] ``` * **Notice:** There are also plans (discussed in the same ticket above) to introduce mutating `union!` form. +* **Follow-up:** Ruby 2.7 [added](TODO) `#intersection` method. ### `Hash#merge` with multiple arguments[](#hashmerge-with-multiple-arguments) @@ -383,7 +386,7 @@ Several enumerators can now be chained into one with `Enumerator#+(other)` or `E end ``` * **Notice:** For `String` ranges, behavior left unchanged, so example with versions above would NOT work with pure-`String` versions. -* **Follow-up:** `String` behavior was fixed in Ruby 2.7, so in 2.7 this code prints "yes": +* **Follow-up:** `String` behavior was [fixed](TODO) in Ruby 2.7, so in 2.7 this code prints "yes": ```ruby case '2.5' when '1.8.7'..'2.6' @@ -438,6 +441,7 @@ Several enumerators can now be chained into one with `Enumerator#+(other)` or `E ```ruby raise KeyError, "don't have this: #{key}", receiver: self, key: key ``` +* **Follow-up:** Ruby 2.7 [adds](TODO) `receiver:` argument for `FrozenError`, too. #### `Exception#full_message` options[](#exceptionfull_message-options) diff --git a/2.7.md b/2.7.md new file mode 100644 index 0000000..b1b7ee6 --- /dev/null +++ b/2.7.md @@ -0,0 +1,868 @@ +--- +title: Ruby 2.7 changes +prev: / +next: 2.6 +--- + +# Ruby 2.7 + +* **Released at:** Dec 25, 2019 ([NEWS](TODO) file) +* **Status (as of Dec 23, 2019):** TODO +* **This document first published:** TODO +* **Last change to this document:** Dec 23, 2019 + +## Highlights[](#highlights) + +Ruby 2.7 is a last big release before 3.0 + + +## Language[](#language) + +### Pattern matching[](#pattern-matching) + +### Keyword argument-related changes[](#keyword-argument-related-changes) + +Ruby 2.7 introduced a lot of changes towards more consistent keyword arguments processing. Fortunately, examples, justifications and relationships of those changes, as well as links to docs and discussions are published on [official site](https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/). + +Therefore here we'll just list the changes for the sake of completeness of this changelog: + +* Warnings (would be errors in 3.0) for implicit coversion of the last argument-hash to keyword arguments, and vice versa; +* `Module#ruby2_keywords` method to mark method as not warning when the last argument is used as keyword one (to provide backward- and forward-compatible way of defining "delegating all" methods); +* "Forward all arguments" syntax: `(...)` +* Non-`Symbol` keys are allowed in keyword arguments unpacking; +* `**nil` syntax in method definition to explicitly mark method that doesn't accept keywords (and can't be called with a hash without curly braces); +* empty hash splat doesn't pass empty hash as a positional argument. + +### Numbered block parameters[](#numbered-block-parameters) + +In block without explicitly specified parameters, variables `_1` through `_9` can be used to reference parameters. + +* **Reason:** It is one of the approaches to make short blocks DRY-er and easier to read. E.g. in `filenames.each { |f| File.read(f) }`, repetition of `f` and extra syntax needed for it can be considered an unnecessary verbosity, so `each { File.read(_1) }` could be now used instead. +* **Discussion:** _The feature was discussed for a long time, syntax and semantics was changed several times on the road to 2.7_ Feature #4475 (initial discussion, started 9 years ago and finished with accepting of `@0`-`@9`), Misc #15723 (change to `_0`-`_9`), Feature #16178 (dropping of `_0` and changing semantics of `_1`) +* **Documentation:** []() +* **Code:** + ```ruby + # Simplest usage: + [10, 20, 30].map { _1**2 } + # => [100, 400, 900] + + # Multiple block parameters can be accessed as subsequent numbers + [10, 20, 30].zip([40, 50, 60], [70, 80, 90]).map { _1 + _2 + _3 } + # => [120, 150, 180] + + # If only _1 is used for multi-argument block, it contains all arguments + [10, 20, 30].zip([40, 50, 60], [70, 80, 90]).map { _1.join(',') } + # => ["10,40,70", "20,50,80", "30,60,90"] + + # If the block has explicit parameters, numbered one is SyntaxError + [10, 20, 30].map { |x| _1**2 } + # SyntaxError ((irb):1: ordinary parameter is defined) + + # Outside the block, usage is warned: + _1 = 'test' + # warning: `_1' is reserved as numbered parameter + + # But after that, _1 references local variable: + [10].each { p _1 } + # prints "test" + + # Numbered parameters are reflected in Proc's parameters and arity + p = proc { _1 + _2 } + l = lambda { _1 + _2 } + p.parameters + # => [[:opt, :_1], [:opt, :_2]] + p.arity + # => 2 + l.parameters + # => [[:req, :_1], [:req, :_2]] + l.arity + # => 2 + + # Nested blocks with numbered parameters are not allowed: + %w[test me].each { _1.each_char { p _1 } } + # SyntaxError (numbered parameter is already used in outer block here) + # %w[test me].each { _1.each_char { p _1 } } + # ^~ + ``` + +### Beginless range[](#beginless-range) + +In addition to endless range in Ruby 2.6 `(start..)`, Ruby 2.7 introduces beginless one: `(..end)`. + +* **Reason:** Array slicing (initial justification for endless ranges) turned out to be not the only usage for semi-open ranges. Another ones, like `case`/`grep`, DSLs and constants and [`Comparable#clamp`](TODO), can gain from the symmetricity of "range without end"/"range without beginning". +* **Discussion:** Feature #14799 +* **Documentation:** doc/syntax/literals.rdoc#Ranges +* **Code:** + ```ruby + # Usage examples + %w[a b c][..1] # same as [0..1], not really useful + + case creation_time + when ...1.year.ago then 'ancient' + when 1.year.ago...1.month.ago then 'old' + when 1.month.ago...1.day.ago then 'recent' + when 1.day.ago... then 'new' + end + + users.map(&:age).any?(...18) + + # Properties: + r = ..10 + r.begin # => nil + r.count # TypeError (can't iterate from NilClass), same with any other Enumerable methods + r.size # => Infinity + ``` +* **Notes:** + * Slightly "a-grammatic" name ("beginless" instead of "beginningless") is due to the fact that it is a range literally lacking `begin` property. + * Unfortunately, parser can not always handle beginless range without the help of the parenthises: + ```ruby + (1..10).grep ..5 + # ArgumentError (wrong number of arguments (given 0, expected 1)) + # ...because it is in fact parsed as ((1..10).grep()..5), e.g. range from grep results to 5 + ``` + This may seem an esotheric problem, but it becomes less esotheric in DSLs. For example in RubySpec, one would like to write: + ```ruby + ruby_version ..'1.9' do + # some tests for old Ruby + end + ``` + ...but the only way is + ```ruby + ruby_version(..'1.9') do + # some tests for old Ruby + end + ``` + +### Syntax changes[](#syntax-changes) + +* Comments between `.foo` calls are now allowed: + ```ruby + (1..20).select(&:odd?) + # This was not possible in 2.6 + .map { |x| x**2 } + # => [1, 9, 25, 49, 81, 121, 169, 225, 289, 361] + ``` +* Quotes (if exist) in HERE-documents should be on the same line as document start: + ```ruby + <<"EOS + " # This had been warned since 2.4; Now it raises a SyntaxError + EOS + ``` +* Modifier `rescue` parsing change (multiple assignement now consisent with singular): + + ```ruby + a = raise rescue 1 + # => 1 + a # => 1 in Ruby 2.6 and 2.7 + + a, b = raise rescue [1, 2] + # => [1, 2] + + # 2.6 + a # => nil + b # => nil + # The statement parsed as: (a, b = raise) rescue [1, 2] + + # 2.7 + a # => 1 + b # => 2 + # The statement parsed as: a, b = (raise rescue [1, 2]) + ``` + +### Warnings/deprecations[](#warningsdeprecations) + +Some older or accidental features are deprecated on the road to 3.0 and currently produce warnings. + +* `yield` in singleton class syntax (was inconsistent with local variables accessibility). Discussion: Feature #15575. + ```ruby + def foo + x = 1 + class << Object.new + p x # ArgumentError (undefined local variable or method) -- enclosing scope NOT accessible + yield # calls block passed to foo, implying enclosing scope IS accessible + # In Ruby 2.7: warning: `yield' in class syntax will not be supported from Ruby 3.0. + end + end + foo { p :ok } + ``` +* `$;` and `$,` global variables (Perl ancestry: default `split` and `join` separators): + ```ruby + $; = '???' + # warning: non-nil $; will be deprecated + 'foo???bar'.split + # warning: $; is set to non-nil value + # => ["foo", "bar"] + + $, = '###' + # warning: non-nil $, will be deprecated + %w[foo bar].join + # warning: $, is set to non-nil value + "foo###bar" + ``` +* Contrastingly, the flip-flop syntax deprecation, [introduced](#TODO) in 2.6, **is reverted**. It turned out that for brevity in text-processing scrips (including one-liners run as `ruby -e "print "`) the feature, however esotheric it may seem, has a justified usage. + * **Discussion:** Feature #5400. + * **Documentation:** doc/syntax/control_expressions.rdoc#Flip-Flop + * **Code:** + ```ruby + # Imagine we are working with some data file where real data starts with "<<<"" line and end with ">>>>" + File.each_line('data.txt', chomp: true).filter_map { |ln| ln if ln == '<<<'..ln == '>>>' } + # => gets lines starting from <<<, and ending with >>>. + # The condition "flips on" when the first part is true, and then "flips off" when the second is true + + # Flip-flop-less version would be something like this (with the differenc it ignores last >>>): + lines.drop_while { |ln| ln != '<<<' }.take_while { |ln| ln != '>>>' } + ``` + +#### "Safe" and "taint" concepts are deprecated in general[](#safe-and-taint-concepts-are-deprecated-in-general) + +The concepts of marking objects as "tainted" (unsafe, came from the outside) and "untaint" them after the check, is for a long time ignored by most of the libraries. `$SAFE` constant, trying to limit "unsafe" calls when set to higher values, is considered a fundamentally flawed method and documented so since Ruby 2.0. At the same time, as the "official" security features, subtle bugs in implementation of `$SAFE` and tainting have caused lot of "vulnerability reports" and added maintenance burden. + +* **Discussion:** Feature #16131 + +### `self.`[](#selfprivate_method) + +Calling a private method with a literal `self` as the receiver is now allowed. + +* **Reason:** "`anything.method` is always disallowed for private methods" seemed like a simple and unambiguous rule, but produced some ugly edge cases (for example, `self.foo = something` for private `attr_accessor` _was_ allowed). It turned out "allow literal `self.`" makes things much clearer. +* **Discussion:** Feature #11297, Feature #16123 +* **Documentation:** []() +* **Code:** + ```ruby + class A + def update + self.a = 42 # works even in Ruby 2.6, because there was no other way to call private setter + self.a += 42 # "private method `a' called" in 2.6, works in 2.7 + + self + 42 # "private method `+' called" in 2.6, works in 2.7 + + x = self + x.a = 42 # "private method `a=' called" even in 2.7, not a literal self + end + + private + + attr_accessor :a + + def +(other) + puts "+ called" + end + end + ``` + +## Core classes and modules[](#core-classes-and-modules) + +### Better `Method#inspect`[](#better-methodinspect) + +`Method#inspect` now shows method's arguments and source location (if available). + +* **Reason:** As there are more code style approaches that operate `Method` objects, making them better identifiable and informative seemed necessary. +* **Discussion:** Feature #14145 +* **Documentation:** Method#inspect +* **Code:** + ```ruby + p CSV.method(:read) + # Ruby 2.6: + # => # + # Ruby 2.7: + # => #/lib/ruby/2.7.0/csv.rb:714> + + # For methods defined in C, path and param names aren't available, but at least generic signature is: + [].method(:at) + # => # + + # Convention: unknown param name is displayed as _, param has default value -- as ... + def m(a, b=nil, *c, d:, e: nil, **rest, &block) + end + + p method(:m) + #=> # + ``` +* **Notes:** + * while enhancing `Method#inspect`, `Proc`'s string representation also changed for consistency: now object id separated from location by ` ` (for easier copy-pasting). Discussion: Feature #16101 + ```ruby + p(proc {}) + # Ruby 2.6: + # => # + # Ruby 2.7: + # => # + ``` + * The same is related to `Thread`. Discussion: Feature #16412 + ```ruby + p(Thread.new {}) + # Ruby 2.6: + # => # + # Ruby 2.7: + # => # + ``` + +### `UnboundMethod#bind_call`[](#unboundmethodbind_call) + +Binds `UnboundMethod` to a receiver and calls it. Semantically equiavalent to `unbound_method.bind(receiver).call(arguments)`, but doesn't produce intermediate `Method` object (which `bind` does). + +* **Reason:** The technique of storing unbound methods and binding them later is used in some metaprogramming-heavy code to robustly use "original" implementation. For example: + ```ruby + MODULE_NAME = Module.instance_method(:name) + + class Customer + def self.name + "" + end + end + + Customer.name # => "" + MODULE_NAME.bind_call(Customer) # => "Customer" + ``` + In such cases (for example, code reloaders), overhead of producing new `Method` object on each `bind().call` is pretty significant, and `bind_call` allows to avoid it. +* **Discussion:** Feature #15955 +* **Documentation:** UnboundMethod#bind_call + +### `Module#const_source_location`[](#moduleconst_source_location) + +* **Discussion:** Feature #10771 +* **Documentation:** []() +* **Code:** +* **Follow-up:** +* **Notes:** + +### `Module#autoload?`: `inherit` argument[](#moduleautoload-inherit-argument) + +* **Reason:** More granular checking "if something is marked to be autoloaded directly or through ancestry chain" is necessary for advanced code reloaders (requested by author of the [zeitwerk](https://github.com/fxn/zeitwerk)). +* **Discussion:** Feature #15777 +* **Documentation:** Module#autoload? +* **Code:** + ```ruby + class Parent + autoload :Feature1, 'feature1.rb' + end + + module Mixin + autoload :Feature2, 'feature2.rb' + end + + class Child < Parent + include Mixin + end + + Child.autoload?(:Feature1) # => "feature1.rb" + Child.autoload?(:Feature1, false) # => nil + Child.autoload?(:Feature2) # => "feature2.rb" + Child.autoload?(:Feature2, false) # => nil + ``` + +### `Comparable#clamp` with `Range`[](#comparableclamp-with-range) + +`Comparable#clamp` now can accept `Range` as its only argument, including beginless and endless ranges. + +* **Reason:** First, ranges can be seen as more "natural" to specify _a range_ of acceptable values. Second, with introduction of beginless and endless ranges, `#clamp` now can be used for one-sided value limitation, too. +* **Discussion:** Feature #14784 +* **Documentation:** Comparable#clamp +* **Code:** + ```ruby + 123.clamp(0..100) # => 100 + -20.clamp(0..100) # => 0 + 15.clamp(0..100) # => 15 + + # With semi-open ranges: + 123.clamp(150..) # => 150 + 123.clamp(..120) # => 120 + + # Range with excluding end is not allowed + 123.clamp(0...150) # ArgumentError (cannot clamp with an exclusive range) + + # Old two-argument form still works: + 123.clamp(0, 150) + # => 123 + ``` + +### `Integer[]` with range[](#integer-with-range) + +Allows to get several bits at once. + +* **Discussion:** Feature #8842 +* **Documentation:** [Integer#[]](https://ruby-doc.org/core-2.7.0.preview3/Integer.html#method-i-5B-5D) +* **Code:** + ```ruby + # 4---0 + # v v + 0b10101001[0..4] # => 9 + 0b10101001[0..4].to_s(2) + # => "1001" + ``` + +### `Complex#<=>`[](#complex) + +`Complex#<=>(other)` now returns `nil` if the number has imaginary part, and behaves like `Numeric#<=>` if it does not. + +* **Reason:** Method `#<=>` was explicitly undefined in `Complex` to underline the fact that linear order of complex numbers can't be established, but it was inconsistent with most of the other objects implementations (which return `nil` for impossible/incompatible comparison instead of rasing) +* **Discussion:** [Feature #](https://bugs.ruby-lang.org/issues/) +* **Documentation:** Complex#<=> +* **Code:** + ```ruby + 1 + 2i <=> 1 # => nil + 1 + 2i <=> 1 + 2i # => nil, even if numbers are equal + 1 + 0i <=> 2 # => -1 + ``` + +### Strings, symbols and regexps[](#strings-symbols-and-regexps) + +* **Unicode version:** 12.1 +* **New encodigs:** [CESU-8](https://en.wikipedia.org/wiki/CESU-8) + +#### Core methods returning frozen strings[](#core-methods-returning-frozen-strings) + +Several core methods now return frozen, deduplicated `String` instead of generating it every time the string is requested. + +* **Reason:** Avoiding allocations of new strings for each `#to_s` of primitive objects can save dramatical amounts of memory. +* **Discussion:** Feature #16150 +* **Affected methods:** NilClass#to_s, TrueClass#to_s, FalseClass#to_s, Module#name +* **Code:** + ```ruby + # Ruby 2.6 + true.to_s.frozen? # => false + 3.times.map { true.to_s.object_id } + # => [47224710953060, 47224710953040, 47224710953000] -- every time new object + + # Ruby 2.7 + true.to_s.frozen? # => true + 3.times.map { true.to_s.object_id } + # => [180, 180, 180] -- frozen special string + ``` +* **Notes:** + * Change introduces incompatibility for the code looking like: + ```ruby + value = true + # ... + buffer = value.to_s + buffer << ' -- received' + # Ruby 2.6: "true -- received" + # Ruby 2.7: FrozenError (can't modify frozen String: "true") + ``` + * The same change was proposed for `Symbol#to_s` (and could've been a dramatical improvement in some kinds of code), but the change turned out to be too disruptive. + +#### `Symbol#start_with?` and `#end_with?`[](#symbolstart_with-and-end_with) + +* **Reason:** Symbol was once thought as "just immutable names" with any of "string-y" operations not making sense for them, but as `Symbol#match` was always present, and `Symbol#match?` [implemented in 2.4](TODO), it turns out that other content inspection methods are also useful. +* **Discussion:** Feature #16348 +* **Documentation:** Symbol#end_with?, Symbol#start_with? +* **Code:** + ```ruby + :table_name.end_with?('name', 'value') + # => true + :table_name.start_with?('table', 'index') + # => true + + # Somewhat confusingly, Symbol arguments are not supported + :table_name.end_with?(:name, 'value') + # TypeError (no implicit conversion of Symbol into String) + ``` + +### `Time`[](#time) + +#### `#floor` and `#ceil`[](#floor-and-ceil) + +Rounds Time's nanoseconds down or up to a specified number of digits (0 by default, e.g. round to whole seconds). + +* **Reason:** Rounding of nanoseconds important in a test code, when comparing `Time` instances from a different sources (stored in DB, passed through third-party libraries, etc.). Having better control on truncation comparing to `Time#round` (which existed since 1.9.2) +* **Discussion:** Feature #15653 (floor, Japanese), Feature #15772 (ceil) +* **Documentation:** []() +* **Code:** +* **Follow-up:** +* **Notes:** + +#### `#inspect` includes subseconds[](#inspect-includes-subseconds) + +* **Reason:** Losing subseconds in `#inspect` always made debugging and testing harder, producing test failures like "Expected: 2019-12-21 16:11:08 +0200, got 2019-12-21 16:11:08 +0200" (which are visually the same, but one of them, probably going through some serialization or DB storage, has different value for subseconds). +* **Discussion:** Feature #15958 +* **Documentation:** Time#inspect +* **Code:** + ```ruby + t = Time.now + p t + # Ruby 2.6: prints "2019-12-21 16:11:08 +0200" + # Ruby 2.7: prints "2019-12-21 16:11:08.189273595 +0200" + puts t + # prints "2019-12-21 16:11:08 +0200" + ``` + +### Enumerables and collections[](#enumerables-and-collections) + +#### `Enumerable#filter_map`[](#enumerablefilter_map) + +Transforms elements of enumerable with provided block, and drops falsy results, in one pass. + +* **Reason:** Filter suitable elements, then process them somehow is a common flow of sequence processing, yet with `filter { ... }.map { ... }` additional intermediate `Array` is produced, which is not always desirable. Also, some processing can indicate "can't be processed" by returning `false` or `nil`, which requires code like `map { ... }.compact` (to drop `nil`s) or `map { ... }.select(:itself)` (to drop all falsy values). +* **Discussion:** Feature #15323 +* **Documentation:** Enumerable#filter_map +* **Code:** + ```ruby + (1..10).filter_map { |i| i**2 if i.even? } # => [4, 16, 36, 64, 100] + + # imagine method constantize() returning false if string can't be converted to + # a proper constant name + constant_names = %w[foo 123 _ bar baz/test].filter_map { |str| constantize(str) } # => ['Foo', 'Bar'] + + # Without block, returns Enumerator: + %w[foo bar baz test].filter_map + .with_index { |str, i| str.capitalize if i.even? } + # => ["Foo", "Baz"] + ``` + +#### `Enumerable#tally`[](#enumerabletally) + +Counts unique objects in the enumerable and returns hash of `{object => count}`. + +* **Discussion:** Feature #11076 +* **Documentation:** Enumerable#tally +* **Code:** + ```ruby + %w[Ruby Python Ruby Perl Python Ruby].tally + # => {"Ruby"=>3, "Python"=>2, "Perl"=>1} + ``` +* **Notes:** + * `#tally` follows `#to_h` intuitions (and uses it underneath): objects are considered same if they have the same `#hash`; orders of keys corresponds to order of appearance in sequence; first object in sequence becomes the key + * Additional block argument, or, alternatively, additional method `tally_by(&block)` was proposed in the same ticket to allow code like + ```ruby + (1..10).tally_by(&:even?) # => {true => 5, false => 5} + ``` + ...but was not accepted yet. + +#### `Enumerator.produce`[](#enumeratorproduce) + +Produces infinite enumerator by calling provided block and passing its result to subsequent block call. + +* **Reason:** `.produce` allows to convert any `while`-alike or `loop`-alike loops into enumerators, making possible to work with them in a Ruby-idiomatic `Enumerable` style. +* **Discussion:** Feature #14781 +* **Documentation:** [Enumerator.produce]() +* **Code:** + ```ruby + require 'date' + # Before: while cycle to search next tuesday: + d = Date.today + while !d.tuesday + d += 1 + end + # After: enumerator: + Enumerator.produce(Date.today, &:succ) # => enumerator of infinitely increasing dates + .find(&:tuesday?) + + require 'strscan' + PATTERN = %r{\d+|[-/+*]} + scanner = StringScanner.new('7+38/6') + # Before: while cycle to implement simple lexer: + result = + result << scanner.scan(PATTERN) while !scanner.eos? + # After: achieving the same with enumerator (which can be passed to other methods to process): + Enumerator.produce { scanner.scan(PATTERN) }.slice_after { scanner.eos? }.first + # => ["7", "+", "38", "/", "6"] + + # Raising StopIteration allows to stop the iteration: + ancestors = Enumerator.produce(node) { |prev| prev.parent or raise StopIteration } + # => enumerator + enclosing_section = ancestors.find { |n| n.type == :section } # => :section node or nil + + # If the initial value is passed, it is an argument to first block call, and yielded as a first + # value of enumerator + Enumerator.produce(1) { |prev| p "PREVIOUS: #{prev}"; prev + 1 }.take(3) + # "PREVIOUS: 1" + # "PREVIOUS: 2" + # => [1, 2, 3] + + # If the initial value is not passed, first block call receives `nil`, and the block's first result + # is yielded as a first value of enumerator + Enumerator.produce { |prev| p "PREVIOUS: #{prev.inspect}"; (prev || 0) + 1 }.take(3) + # "PREVIOUS: nil" + # "PREVIOUS: 1" + # "PREVIOUS: 2" + # => [1, 2, 3] + ``` + +#### `Enumerator::Lazy#eager`[](#enumeratorlazyeager) + +Converts lazy enumerator back to eager one. + +* **Reason:** When working with large data sequences, lazy enumerators are useful tools to not produce intermediate array on each step. But some data consuming methods expect to receive enumeratos that would really return data from methods like `take_while` and not just add them to pipeline. +* **Discussion:** Feature #15901 +* **Documentation:** Enumerator::Lazy#eager +* **Code:** + ```ruby + # Imagine we read very large data file: + lines = + File.open('data.csv') + .each_line + .lazy + .map { |ln| ln.sub(/\#.+$/, '').strip } # remove "comments" + .reject(&:empty?) # drop empty lines + p lines + # => # + + # Now, we want to consume just "headers" from this CSV, and pass the rest of enumerator + # into the other methods. + + # This code: + headers = lines.take_while { |ln| ln.start_with?('$$$') } + # ...will just produce another lazy enumerator, with `take_while` chained to pipeline. + + # Now, this: + lines = lines.eager # makes the enumerator eager, but (unlike `#force`) doesn't consume it yet + p lines + # => #:each> + + # consumes only several first lines and returns array of headers + headers = lines.take_while { |ln| ln.start_with?('$$$') } + # => [array, of, header, lines] + + # now we can pass `lines` to methods that expect take_while/take and other similar methods to + # consume enumerator partially and return arrays + ``` + +#### `Enumerator::Yielder#to_proc`[](#enumeratoryielderto_proc) + +* **Reason:** When constructing the enumerator, value yielding is frequently delegated to other methods, which accept blocks. Before the change, it was inconvenient to delegate. +* **Discussion:** Feature #15618 +* **Documentation:** Enumerator::Yielder#to_proc +* **Code:** + ```ruby + # Construct a enumerator which will pass all lines from all files from some folder: + + # Before the change: + all_lines = Enumerator.new { |y| # y is Yielder object here + Dir.glob("*.rb") { |file| + File.open(file) { |f| f.each_line { |ln| y << ln } } + } + } + + # After the change: + all_lines = Enumerator.new { |y| # y is Yielder object here + Dir.glob("*.rb") { |file| + File.open(file) { |f| f.each_line(&y) } + } + } + ``` + +#### `Array#intersection`[](#arrayintersection) + +Like `Array#union` and `#difference`, [added](2.6.html#TODO) in 2.6 as a explicitly-named and accepting multiple arguments alternatives for `#|` and `#-`, the new method is alternative for `#&`. + +* **Discussion:** Feature #16155 +* **Documentation:** Array#intersection +* **Code:** + ```ruby + ['Ruby', 'Python', 'Perl'].intersection(['Ruby', 'Diamond', 'Perl'], ['Ruby', 'Nikole', 'Kate']) # => ["Ruby"] + + ['Ruby', 'Python', 'Perl'].intersection # => ["Ruby", "Python", "Perl"] + ``` + +### `Fiber#raise`[](#fiberraise) + +Raises exception inside the resumed Fiber. + +* **Reason:** Ability to raise an exception inside Fiber makes control passing abilities more feature-complete. +* **Discussion:** Feature #10344 +* **Documentation:** Fiber#raise +* **Code:** + ```ruby + f = Fiber.new { + Enumerator.produce { Fiber.yield } # Infinite yielding enumerator, breaks on StopIteration + .to_a.join(', ') + } + f.resume + f.resume 1 + f.resume 2 + f.resume 3 + f.raise StopIteration # => "1, 2, 3" + ``` +* **Notes:** `Fiber#raise` has the same call sequence as `Kernel#raise` and can be called without any arguments (`RuntimeError` is empty message is raised), with string (`RuntimeError` with provided message is raised), exception class and (optional) message, or instance of the exception. + +### `Range`[](#range) + +#### `Range#===` for `String`[](#range-for-string) + +In 2.6, `Range#===` was changed to use `#cover?` underneath, but not for `String`. This was fixed. + +* **Discussion:** Bug #15449 +* **Documentation:** Range#=== +* **Code:** + ```ruby + case '2.6.5' + when '2.4'..'2.7' + 'matches' + else + 'nope :(' + end + # => "nope :(" in 2.6, "matches" in 2.7 + ``` + +### Filesystem and IO[](#filesystem-and-io) + +#### `IO#set_encoding_by_bom`[](#ioset_encoding_by_bom) + +Auto-sets encoding to UTF-8 if byte-order mark is present in the stream. + +* **Discussion:** [Feature #]15210(https://bugs.ruby-lang.org/issues/15210) +* **Documentation:** IO#set_encoding_by_bom +* **Code:** + ```ruby + File.write("tmp/bom.txt", "\u{FEFF}мама") + ios = File.open("tmp/bom.txt", "rb") + ios.binmode? # => true + ios.external_encoding # => # + ios.set_encoding_by_bom # => # + ios.external_encoding # => # + ios.read # => "мама" + + File.write("tmp/nobom.txt", "мама") + ios = File.open("tmp/nobom.txt", "rb") + ios.set_encoding_by_bom # => nil + ios.external_encoding # => # + ios.read # => "\xD0\xBC\xD0\xB0\xD0\xBC\xD0\xB0" + + # The method raises in non-binary-mode streams, or if encoding already set: + File.open("tmp/bom.txt", "r").set_encoding_by_bom + # ArgumentError (ASCII incompatible encoding needs binmode) + File.open("tmp/bom.txt", "rb", encoding: 'Windows-1251').set_encoding_by_bom + # ArgumentError (encoding is set to Windows-1251 already) + ``` + +#### `Dir.glob` and `Dir.[]` not allow `\0`-separated patterns[](#dirglob-and-dir-not-allow-0-separated-patterns) + +* **Discussion:** Feature #14643 +* **Documentation:** Dir.glob +* **Code:** + ```ruby + Dir.glob("*.rb\0*.md") + # 2.6: + # warning: use glob patterns list instead of nul-separated patterns + # => ["2.5.md", "History.md", "README.md" ... + # 2.7 + # ArgumentError (nul-separated glob pattern is deprecated) + + # Proper alternative, works in 2.7 and earlier versions: + Dir.glob(["*.rb", "*.md"]) + # => ["2.5.md", "History.md", "README.md" ... + ``` + * File.extname now returns a dot string at a name ending with a dot. [Bug #15267] + +### Exceptions[](#exceptions) + +#### `FrozenError`: receiver argument[](#frozenerror-receiver-argument) + +In 2.6 several exception class constructrs [were enhanced](TODO) so the user code could create them providing context, now `FrozenError` also got this functionality. + +* **Discussion:** Feature #15751 +* **Documentation:** FrozenError#new +* **Code:** + ```ruby + class AlwaysFrozenHash < Hash + # ... + def update!(*) + raise FrozenError.new("I am frozen!", receiver: self) + end + end + ``` +* **Notice:** `.new` syntax is the only way to pass new arguments, this would **not** work: + ```ruby + raise FrozenError, "I am frozen!", receiver: self + ``` + +### Interpreter internals[](#interpreter-internals) + +* +RubyVM.resolve_feature_path+ moved to + $LOAD_PATH.resolve_feature_path. [Feature #15903] [Feature #15230] + +#### `GC.compact`[](#gccompact) + +* 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 friendly. [Feature #15626] + TODO https://bugs.ruby-lang.org/issues/16443 + +* ObjectSpace::WeakMap#[]= now accepts special objects as either key or + values. [Feature #16035] + +## Standard library[](#standard-library) + + * Date.jisx0301, Date#jisx0301, and Date.parse support the new Japanese + era. [Feature #15742] + * Object#DelegateClass accepts a block and module_evals it in the context + of the returned class, similar to Class.new and Struct.new. + +IRB:: + + * Introduce syntax highlight inspired by pry.gem to Binding#irb source lines, + REPL input, and inspect output of some core-class objects. + + * Introduce multiline mode by Reline. + + * Show documents when completion. + + * Enable auto indent and save/load history by default. +Reline:: + + * New stdlib that is compatible with readline stdlib by pure Ruby and also + has a multiline mode. + +* Net::HTTP Add ipaddr optional parameter to Net::HTTP#start to replace the address for + TCP/IP connection [Feature #5180] +* Add Net::FTP#features to check available features, and Net::FTP#option to + enable/disable each of them. [Feature #15964] +* Net::IMAP:: Add Server Name Indication (SNI) support. [Feature #15594] + +open-uri:: + * Warn open-uri's "open" method at Kernel. + Use URI.open instead. [Misc #15893] + * The default charset of text/* media type is UTF-8 instead of + ISO-8859-1. [Bug #15933] + +Pathname:: + * Delegates 3 arguments from Pathname.glob to Dir.glob to + accept base: keyword. + * 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. + +OptionParser:: + * Now show "Did you mean?" for unknown option. [Feature #16256] + + +### Standard library contents change[](#standard-library-contents-change) + +* 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) +profile.rb, Profiler__:: + * Removed from standard library. No one maintains it from Ruby 2.0.0. + +* Promote stdlib to default gems + * The following default gems was published at rubygems.org + * benchmark + * cgi + * delegate + * getoptlong + * net-pop + * net-smtp + * open3 + * pstore + * singleton + * The following default gems only promoted ruby-core, Not yet published at rubygems.org. + * monitor + * observer + * timeout + * tracer + * uri + * yaml + +### Large updated libraries[](#large-updated-libraries) + +large updated + * Upgrade to Bundler 2.1.0.pre.3 + * Upgrade to CSV 3.1.2 + * Merge Racc 1.4.15 from upstream repository and added cli of racc. + * Upgrade REXML to 3.2.3. + * Upgrade to RSS 0.2.8. + * Upgrade to RubyGems 3.1.0.pre3 + * Upgrade to StringScanner 1.0.3. diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..d6e7c8f --- /dev/null +++ b/Rakefile @@ -0,0 +1,28 @@ +require 'kramdown' +require 'yaml' +require 'ostruct' + +require_relative '_src/lib/util' +require_relative '_src/lib/file' +require_relative '_src/lib/toc' +require_relative '_src/lib/render' + +file '_data/book.yml' => FileList['_src/*.md'] do |t| + chapters = TOC.(t.prerequisites) + File.write(t.name, Util.deep_stringify_keys(chapters: chapters).to_yaml) +end + +rule /^\d+\.\d+\.md$/ => ->(s) { "_src/#{s}" } do |t| + from, to = t.prerequisites.first, t.name + + puts "Rendering #{from} => #{to}" + File.write(to, Render.(from)) +end + +desc 'Convert file contents from source to target (prettify)' +task contents: ('2.4'..'2.7').to_a.map(&'%s.md'.method(:%)) + +desc 'Render TOC for the changelog "book"' +task toc: '_data/book.yml' + +task default: %i[toc contents] \ No newline at end of file diff --git a/_data/book.yml b/_data/book.yml index 55ec1ed..046de6b 100644 --- a/_data/book.yml +++ b/_data/book.yml @@ -2,127 +2,116 @@ chapters: - title: Introduction path: "/" -- title: Ruby 2.6 - path: "/2.6.html" +- title: Ruby 2.4 + path: "/2.4.html" is_version: true - published_at: Dec 29, 2018 + published_at: Oct 14, 2019 description: |

Highlights:

    -
  • Endless range
  • -
  • #then “piping” method
  • -
  • Support for timezones in Time
  • -
  • Proc composition
  • -
  • Enumerator chaining
  • -
  • RubyVM::AbstractSyntaxTree
  • +
  • Toplevel return
  • +
  • Unification of Fixnum and Bignum into Integer
  • +
  • Full Unicode support for String case operations
  • +
  • Warning module for fine-grained warnings output control
  • +
  • Comparable#clamp
-

Read more »

+

Read more »

children: - title: Highlights - path: "/2.6.html#highlights" + path: "/2.4.html#highlights" - title: Language - path: "/2.6.html#language" + path: "/2.4.html#language" children: - - title: 'Endless range: (1..)' - path: "/2.6.html#endless-range-1" - - title: Non-ASCII constant names - path: "/2.6.html#non-ascii-constant-names" - - title: "else in exception-handling context" - path: "/2.6.html#else-in-exception-handling-context" - - title: 'Refinements: improved visibility' - path: "/2.6.html#refinements-improved-visibility" - - title: Misc - path: "/2.6.html#misc" - - title: Core classes and modules - path: "/2.6.html#core-classes-and-modules" + - title: Multiple assignment allowed in conditional expression + path: "/2.4.html#multiple-assignment-allowed-in-conditional-expression" + - title: Toplevel return + path: "/2.4.html#toplevel-return" + - title: Refinements improvements + path: "/2.4.html#refinements-improvements" children: - - title: "Kernel" - path: "/2.6.html#kernel" - children: - - title: "#then as an alias for #yield_self" - path: "/2.6.html#then-as-an-alias-for-yield_self" - - title: "<Numeric>() methods have exception: - argument" - path: "/2.6.html#numeric-methods-have-exception-argument" - - title: "#system has exception: argument" - path: "/2.6.html#system-has-exception-argument" - - title: "Module#method_defined?: inherit argument" - path: "/2.6.html#modulemethod_defined-inherit-argument" - - title: "String#split with block" - path: "/2.6.html#stringsplit-with-block" - - title: "Time: support for timezones" - path: "/2.6.html#time-support-for-timezones" - - title: "Proc composition" - path: "/2.6.html#proc-composition" - - title: "Array#union and Array#difference" - path: "/2.6.html#arrayunion-and-arraydifference" - - title: "Hash#merge with multiple arguments" - path: "/2.6.html#hashmerge-with-multiple-arguments" - - title: Enumerables - path: "/2.6.html#enumerables" + - title: Refinements are supported in Symbol#to_proc and send + path: "/2.4.html#refinements-are-supported-in-symbolto_proc-and-send" + - title: "refine can refine modules, too" + path: "/2.4.html#refine-can-refine-modules-too" + - title: "Module.used_modules" + path: "/2.4.html#moduleused_modules" + - title: Core + path: "/2.4.html#core" + children: + - title: "Warning module" + path: "/2.4.html#warning-module" + - title: "Object#clone(freeze: false)" + path: "/2.4.html#objectclonefreeze-false" + - title: "Comparable#clamp" + path: "/2.4.html#comparableclamp" + - title: Numerics + path: "/2.4.html#numerics" children: - - title: "#filter/#filter!" - path: "/2.6.html#filterfilter" - - title: "#to_h with a block" - path: "/2.6.html#to_h-with-a-block" - - title: "Enumerable::ArithmeticSequence" - path: "/2.6.html#enumerablearithmeticsequence" - - title: "Enumerator chaining" - path: "/2.6.html#enumerator-chaining" - - title: "Range" - path: "/2.6.html#range" + - title: "Fixnum and Bignum are unified into Integer" + path: "/2.4.html#fixnum-and-bignum-are-unified-into-integer" + - title: "Numeric#finite? and #infinite?" + path: "/2.4.html#numericfinite-and-infinite" + - title: "Integer#digits" + path: "/2.4.html#integerdigits" + - title: "ndigits optional argument for rounding methods" + path: "/2.4.html#ndigits-optional-argument-for-rounding-methods" + - title: "half: option for #round method" + path: "/2.4.html#half-option-for-round-method" + - title: Strings, symbols and regexps + path: "/2.4.html#strings-symbols-and-regexps" children: - - title: "Range#=== uses #cover? instead of #include?" - path: "/2.6.html#range-uses-cover-instead-of-include" - - title: "Range#cover? accepts range argument" - path: "/2.6.html#rangecover-accepts-range-argument" - - title: "Range#% alias" - path: "/2.6.html#range-alias" - - title: Exceptions - path: "/2.6.html#exceptions" + - title: Unicode case conversions + path: "/2.4.html#unicode-case-conversions" + - title: "String.new(capacity: size)" + path: "/2.4.html#stringnewcapacity-size" + - title: "#casecmp?" + path: "/2.4.html#casecmp" + - title: "String#concat and #prepend accept multiple + arguments" + path: "/2.4.html#stringconcat-and-prepend-accept-multiple-arguments" + - title: "String#unpack1" + path: "/2.4.html#stringunpack1" + - title: "#match? method" + path: "/2.4.html#match-method" + - title: "MatchData: better support for named captures" + path: "/2.4.html#matchdata-better-support-for-named-captures" + - title: Collections + path: "/2.4.html#collections" children: - - title: 'New arguments: receiver: and key:' - path: "/2.6.html#new-arguments-receiver-and-key" - - title: "Exception#full_message options" - path: "/2.6.html#exceptionfull_message-options" - - title: Exception output tweaking - path: "/2.6.html#exception-output-tweaking" + - title: "Enumerable#chunk without a block returns an Enumerator" + path: "/2.4.html#enumerablechunk-without-a-block-returns-an-enumerator" + - title: "#sum" + path: "/2.4.html#sum" + - title: "#uniq" + path: "/2.4.html#uniq" + - title: "Array#max and #min" + path: "/2.4.html#arraymax-and-min" + - title: "Array#concat takes multiple arguments" + path: "/2.4.html#arrayconcat-takes-multiple-arguments" + - title: "Array#pack(buffer:)" + path: "/2.4.html#arraypackbuffer" + - title: "Hash#compact and #compact!" + path: "/2.4.html#hashcompact-and-compact" + - title: "Hash#transform_values and #transform_values!" + path: "/2.4.html#hashtransform_values-and-transform_values" - title: Filesystem and IO - path: "/2.6.html#filesystem-and-io" - children: - - title: "Dir#each_child and Dir#children" - path: "/2.6.html#direach_child-and-dirchildren" - - title: 'IO open mode: ''x''' - path: "/2.6.html#io-open-mode-x" - - title: Minor changes - path: "/2.6.html#minor-changes" - - title: Introspection - path: "/2.6.html#introspection" - children: - - title: "Binding#source_location" - path: "/2.6.html#bindingsource_location" - - title: "RubyVM.resolve_feature_path" - path: "/2.6.html#rubyvmresolve_feature_path" - - title: "RubyVM::AbstractSyntaxTree" - path: "/2.6.html#rubyvmabstractsyntaxtree" - - title: "TracePoint improvements" - path: "/2.6.html#tracepoint-improvements" + path: "/2.4.html#filesystem-and-io" children: - - title: "#parameters" - path: "/2.6.html#parameters" - - title: ":script_compiled event" - path: "/2.6.html#script_compiled-event" - - title: "#enable: new params target: and target_line:" - path: "/2.6.html#enable-new-params-target-and-target_line" - - title: Standard library - path: "/2.6.html#standard-library" + - title: "chomp: option for string splitting" + path: "/2.4.html#chomp-option-for-string-splitting" + - title: "#empty? method for filesystem objects" + path: "/2.4.html#empty-method-for-filesystem-objects" + - title: "Thread#report_on_exception and Thread.report_on_exception" + path: "/2.4.html#threadreport_on_exception-and-threadreport_on_exception" + - title: "TracePoint#callee_id" + path: "/2.4.html#tracepointcallee_id" + - title: Stdlib + path: "/2.4.html#stdlib" children: - - title: Large updated libraries - path: "/2.6.html#large-updated-libraries" - - title: Libraries promoted to default gems - path: "/2.6.html#libraries-promoted-to-default-gems" + - title: Libraries promoted to default/bundled gems + path: "/2.4.html#libraries-promoted-to-defaultbundled-gems" - title: Ruby 2.5 path: "/2.5.html" is_version: true @@ -278,116 +267,238 @@ chapters: path: "/2.5.html#large-updated-libraries" - title: Libraries promoted to default gems path: "/2.5.html#libraries-promoted-to-default-gems" -- title: Ruby 2.4 - path: "/2.4.html" +- title: Ruby 2.6 + path: "/2.6.html" is_version: true - published_at: Oct 14, 2019 + published_at: Dec 29, 2018 description: |

Highlights:

    -
  • Toplevel return
  • -
  • Unification of Fixnum and Bignum into Integer
  • -
  • Full Unicode support for String case operations
  • -
  • Warning module for fine-grained warnings output control
  • -
  • Comparable#clamp
  • +
  • Endless range
  • +
  • #then “piping” method
  • +
  • Support for timezones in Time
  • +
  • Proc composition
  • +
  • Enumerator chaining
  • +
  • RubyVM::AbstractSyntaxTree
-

Read more »

+

Read more »

children: - title: Highlights - path: "/2.4.html#highlights" + path: "/2.6.html#highlights" - title: Language - path: "/2.4.html#language" + path: "/2.6.html#language" children: - - title: Multiple assignment allowed in conditional expression - path: "/2.4.html#multiple-assignment-allowed-in-conditional-expression" - - title: Toplevel return - path: "/2.4.html#toplevel-return" - - title: Refinements improvements - path: "/2.4.html#refinements-improvements" + - title: 'Endless range: (1..)' + path: "/2.6.html#endless-range-1" + - title: Non-ASCII constant names + path: "/2.6.html#non-ascii-constant-names" + - title: "else in exception-handling context" + path: "/2.6.html#else-in-exception-handling-context" + - title: 'Refinements: improved visibility' + path: "/2.6.html#refinements-improved-visibility" + - title: Misc + path: "/2.6.html#misc" + - title: Core classes and modules + path: "/2.6.html#core-classes-and-modules" children: - - title: Refinements are supported in Symbol#to_proc and send - path: "/2.4.html#refinements-are-supported-in-symbolto_proc-and-send" - - title: "refine can refine modules, too" - path: "/2.4.html#refine-can-refine-modules-too" - - title: "Module.used_modules" - path: "/2.4.html#moduleused_modules" - - title: Core - path: "/2.4.html#core" + - title: "Kernel" + path: "/2.6.html#kernel" + children: + - title: "#then as an alias for #yield_self" + path: "/2.6.html#then-as-an-alias-for-yield_self" + - title: "<Numeric>() methods have exception: + argument" + path: "/2.6.html#numeric-methods-have-exception-argument" + - title: "#system has exception: argument" + path: "/2.6.html#system-has-exception-argument" + - title: "Module#method_defined?: inherit argument" + path: "/2.6.html#modulemethod_defined-inherit-argument" + - title: "String#split with block" + path: "/2.6.html#stringsplit-with-block" + - title: "Time: support for timezones" + path: "/2.6.html#time-support-for-timezones" + - title: "Proc composition" + path: "/2.6.html#proc-composition" + - title: "Array#union and Array#difference" + path: "/2.6.html#arrayunion-and-arraydifference" + - title: "Hash#merge with multiple arguments" + path: "/2.6.html#hashmerge-with-multiple-arguments" + - title: Enumerables + path: "/2.6.html#enumerables" + children: + - title: "#filter/#filter!" + path: "/2.6.html#filterfilter" + - title: "#to_h with a block" + path: "/2.6.html#to_h-with-a-block" + - title: "Enumerable::ArithmeticSequence" + path: "/2.6.html#enumerablearithmeticsequence" + - title: "Enumerator chaining" + path: "/2.6.html#enumerator-chaining" + - title: "Range" + path: "/2.6.html#range" + children: + - title: "Range#=== uses #cover? instead of #include?" + path: "/2.6.html#range-uses-cover-instead-of-include" + - title: "Range#cover? accepts range argument" + path: "/2.6.html#rangecover-accepts-range-argument" + - title: "Range#% alias" + path: "/2.6.html#range-alias" + - title: Exceptions + path: "/2.6.html#exceptions" + children: + - title: 'New arguments: receiver: and key:' + path: "/2.6.html#new-arguments-receiver-and-key" + - title: "Exception#full_message options" + path: "/2.6.html#exceptionfull_message-options" + - title: Exception output tweaking + path: "/2.6.html#exception-output-tweaking" + - title: Filesystem and IO + path: "/2.6.html#filesystem-and-io" + children: + - title: "Dir#each_child and Dir#children" + path: "/2.6.html#direach_child-and-dirchildren" + - title: 'IO open mode: ''x''' + path: "/2.6.html#io-open-mode-x" + - title: Minor changes + path: "/2.6.html#minor-changes" + - title: Introspection + path: "/2.6.html#introspection" + children: + - title: "Binding#source_location" + path: "/2.6.html#bindingsource_location" + - title: "RubyVM.resolve_feature_path" + path: "/2.6.html#rubyvmresolve_feature_path" + - title: "RubyVM::AbstractSyntaxTree" + path: "/2.6.html#rubyvmabstractsyntaxtree" + - title: "TracePoint improvements" + path: "/2.6.html#tracepoint-improvements" + children: + - title: "#parameters" + path: "/2.6.html#parameters" + - title: ":script_compiled event" + path: "/2.6.html#script_compiled-event" + - title: "#enable: new params target: and target_line:" + path: "/2.6.html#enable-new-params-target-and-target_line" + - title: Standard library + path: "/2.6.html#standard-library" children: - - title: "Warning module" - path: "/2.4.html#warning-module" - - title: "Object#clone(freeze: false)" - path: "/2.4.html#objectclonefreeze-false" - - title: "Comparable#clamp" - path: "/2.4.html#comparableclamp" - - title: Numerics - path: "/2.4.html#numerics" + - title: Large updated libraries + path: "/2.6.html#large-updated-libraries" + - title: Libraries promoted to default gems + path: "/2.6.html#libraries-promoted-to-default-gems" +- title: Ruby 2.7 + path: "/2.7.html" + is_version: true + published_at: TODO + description: | +

Highlights:

+ +

Ruby 2.7 is a last big release before 3.0

+ +

Read more »

+ children: + - title: Highlights + path: "/2.7.html#highlights" + - title: Language + path: "/2.7.html#language" + children: + - title: Pattern matching + path: "/2.7.html#pattern-matching" + - title: Keyword argument-related changes + path: "/2.7.html#keyword-argument-related-changes" + - title: Numbered block parameters + path: "/2.7.html#numbered-block-parameters" + - title: Beginless range + path: "/2.7.html#beginless-range" + - title: Syntax changes + path: "/2.7.html#syntax-changes" + - title: Warnings/deprecations + path: "/2.7.html#warningsdeprecations" children: - - title: "Fixnum and Bignum are unified into Integer" - path: "/2.4.html#fixnum-and-bignum-are-unified-into-integer" - - title: "Numeric#finite? and #infinite?" - path: "/2.4.html#numericfinite-and-infinite" - - title: "Integer#digits" - path: "/2.4.html#integerdigits" - - title: "ndigits optional argument for rounding methods" - path: "/2.4.html#ndigits-optional-argument-for-rounding-methods" - - title: "half: option for #round method" - path: "/2.4.html#half-option-for-round-method" + - title: "“Safe” and “taint” concepts are deprecated in general" + path: "/2.7.html#safe-and-taint-concepts-are-deprecated-in-general" + - title: "self.<private_method>" + path: "/2.7.html#selfprivate_method" + - title: Core classes and modules + path: "/2.7.html#core-classes-and-modules" + children: + - title: Better Method#inspect + path: "/2.7.html#better-methodinspect" + - title: "UnboundMethod#bind_call" + path: "/2.7.html#unboundmethodbind_call" + - title: "Module#const_source_location" + path: "/2.7.html#moduleconst_source_location" + - title: "Module#autoload?: inherit argument" + path: "/2.7.html#moduleautoload-inherit-argument" + - title: "Comparable#clamp with Range" + path: "/2.7.html#comparableclamp-with-range" + - title: "Integer[] with range" + path: "/2.7.html#integer-with-range" + - title: "Complex#<=>" + path: "/2.7.html#complex" - title: Strings, symbols and regexps - path: "/2.4.html#strings-symbols-and-regexps" + path: "/2.7.html#strings-symbols-and-regexps" children: - - title: Unicode case conversions - path: "/2.4.html#unicode-case-conversions" - - title: "String.new(capacity: size)" - path: "/2.4.html#stringnewcapacity-size" - - title: "#casecmp?" - path: "/2.4.html#casecmp" - - title: "String#concat and #prepend accept multiple - arguments" - path: "/2.4.html#stringconcat-and-prepend-accept-multiple-arguments" - - title: "String#unpack1" - path: "/2.4.html#stringunpack1" - - title: "#match? method" - path: "/2.4.html#match-method" - - title: "MatchData: better support for named captures" - path: "/2.4.html#matchdata-better-support-for-named-captures" - - title: Collections - path: "/2.4.html#collections" + - title: Core methods returning frozen strings + path: "/2.7.html#core-methods-returning-frozen-strings" + - title: "Symbol#start_with? and #end_with?" + path: "/2.7.html#symbolstart_with-and-end_with" + - title: "Time" + path: "/2.7.html#time" children: - - title: "Enumerable#chunk without a block returns an Enumerator" - path: "/2.4.html#enumerablechunk-without-a-block-returns-an-enumerator" - - title: "#sum" - path: "/2.4.html#sum" - - title: "#uniq" - path: "/2.4.html#uniq" - - title: "Array#max and #min" - path: "/2.4.html#arraymax-and-min" - - title: "Array#concat takes multiple arguments" - path: "/2.4.html#arrayconcat-takes-multiple-arguments" - - title: "Array#pack(buffer:)" - path: "/2.4.html#arraypackbuffer" - - title: "Hash#compact and #compact!" - path: "/2.4.html#hashcompact-and-compact" - - title: "Hash#transform_values and #transform_values!" - path: "/2.4.html#hashtransform_values-and-transform_values" + - title: "#floor and #ceil" + path: "/2.7.html#floor-and-ceil" + - title: "#inspect includes subseconds" + path: "/2.7.html#inspect-includes-subseconds" + - title: Enumerables and collections + path: "/2.7.html#enumerables-and-collections" + children: + - title: "Enumerable#filter_map" + path: "/2.7.html#enumerablefilter_map" + - title: "Enumerable#tally" + path: "/2.7.html#enumerabletally" + - title: "Enumerator.produce" + path: "/2.7.html#enumeratorproduce" + - title: "Enumerator::Lazy#eager" + path: "/2.7.html#enumeratorlazyeager" + - title: "Enumerator::Yielder#to_proc" + path: "/2.7.html#enumeratoryielderto_proc" + - title: "Array#intersection" + path: "/2.7.html#arrayintersection" + - title: "Fiber#raise" + path: "/2.7.html#fiberraise" + - title: "Range" + path: "/2.7.html#range" + children: + - title: "Range#=== for String" + path: "/2.7.html#range-for-string" - title: Filesystem and IO - path: "/2.4.html#filesystem-and-io" + path: "/2.7.html#filesystem-and-io" children: - - title: "chomp: option for string splitting" - path: "/2.4.html#chomp-option-for-string-splitting" - - title: "#empty? method for filesystem objects" - path: "/2.4.html#empty-method-for-filesystem-objects" - - title: "Thread#report_on_exception and Thread.report_on_exception" - path: "/2.4.html#threadreport_on_exception-and-threadreport_on_exception" - - title: "TracePoint#callee_id" - path: "/2.4.html#tracepointcallee_id" - - title: Stdlib - path: "/2.4.html#stdlib" + - title: "IO#set_encoding_by_bom" + path: "/2.7.html#ioset_encoding_by_bom" + - title: "Dir.glob and Dir.[] not allow \\0-separated + patterns" + path: "/2.7.html#dirglob-and-dir-not-allow-0-separated-patterns" + - title: Exceptions + path: "/2.7.html#exceptions" + children: + - title: "FrozenError: receiver argument" + path: "/2.7.html#frozenerror-receiver-argument" + - title: Interpreter internals + path: "/2.7.html#interpreter-internals" + children: + - title: "GC.compact" + path: "/2.7.html#gccompact" + - title: Standard library + path: "/2.7.html#standard-library" children: - - title: Libraries promoted to default/bundled gems - path: "/2.4.html#libraries-promoted-to-defaultbundled-gems" + - title: Standard library contents change + path: "/2.7.html#standard-library-contents-change" + - title: Large updated libraries + path: "/2.7.html#large-updated-libraries" - title: History (of this site) path: "/History.html" - title: Contributing diff --git a/_src/2.6.md b/_src/2.6.md index 305f16b..cc09202 100644 --- a/_src/2.6.md +++ b/_src/2.6.md @@ -29,7 +29,6 @@ next: 2.5 * **Discussion:** [Feature #12912](https://bugs.ruby-lang.org/issues/12912) * **Reason/Usage:** More convenient `Array` slicing, and idiomatic open-ended `case` conditions * **Documentation:** [Range: Endless ranges](https://ruby-doc.org/core-2.6/Range.html#class-Range-label-Endless+Ranges) -* **Related:** [startless ranges](https://bugs.ruby-lang.org/issues/14799) are being discussed currently. * **Code:** ```ruby # Usage @@ -51,6 +50,7 @@ next: 2.5 (1..) == (1...) # => false (1..).size # => Infinity ``` +* **Follow-up:** "Beginless" range [was introduced](TODO) in 2.7. ### Non-ASCII constant names @@ -104,6 +104,7 @@ Refinements are now compatible with `#public_send` and `#respond_to?`, and impli ### Misc * Infamous esoteric flip-flop syntax is deprecated finally: [Feature #5400](https://bugs.ruby-lang.org/issues/5400). + * **Follow-up:** Deprecation is [reverted](#TODO) in 2.7 ## Core classes and modules @@ -269,6 +270,7 @@ The concept of a "timezone object" is introduced for various `Time` methods. Rub # => [2] ``` * **Notice:** There are also plans (discussed in the same ticket above) to introduce mutating `union!` form. +* **Follow-up:** Ruby 2.7 [added](TODO) `#intersection` method. ### `Hash#merge` with multiple arguments @@ -384,7 +386,7 @@ Several enumerators can now be chained into one with `Enumerator#+(other)` or `E end ``` * **Notice:** For `String` ranges, behavior left unchanged, so example with versions above would NOT work with pure-`String` versions. -* **Follow-up:** `String` behavior was fixed in Ruby 2.7, so in 2.7 this code prints "yes": +* **Follow-up:** `String` behavior was [fixed](TODO) in Ruby 2.7, so in 2.7 this code prints "yes": ```ruby case '2.5' when '1.8.7'..'2.6' @@ -439,6 +441,7 @@ Several enumerators can now be chained into one with `Enumerator#+(other)` or `E ```ruby raise KeyError, "don't have this: #{key}", receiver: self, key: key ``` +* **Follow-up:** Ruby 2.7 [adds](TODO) `receiver:` argument for `FrozenError`, too. #### `Exception#full_message` options diff --git a/_src/2.7.md b/_src/2.7.md new file mode 100644 index 0000000..dd941dd --- /dev/null +++ b/_src/2.7.md @@ -0,0 +1,868 @@ +--- +title: Ruby 2.7 changes +prev: / +next: 2.6 +--- + +# Ruby 2.7 + +* **Released at:** Dec 25, 2019 ([NEWS](TODO) file) +* **Status (as of <>):** TODO +* **This document first published:** TODO +* **Last change to this document:** <> + +## Highlights + +Ruby 2.7 is a last big release before 3.0 + + +## Language + +### Pattern matching + +### Keyword argument-related changes + +Ruby 2.7 introduced a lot of changes towards more consistent keyword arguments processing. Fortunately, examples, justifications and relationships of those changes, as well as links to docs and discussions are published on [official site](https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/). + +Therefore here we'll just list the changes for the sake of completeness of this changelog: + +* Warnings (would be errors in 3.0) for implicit coversion of the last argument-hash to keyword arguments, and vice versa; +* `Module#ruby2_keywords` method to mark method as not warning when the last argument is used as keyword one (to provide backward- and forward-compatible way of defining "delegating all" methods); +* "Forward all arguments" syntax: `(...)` +* Non-`Symbol` keys are allowed in keyword arguments unpacking; +* `**nil` syntax in method definition to explicitly mark method that doesn't accept keywords (and can't be called with a hash without curly braces); +* empty hash splat doesn't pass empty hash as a positional argument. + +### Numbered block parameters + +In block without explicitly specified parameters, variables `_1` through `_9` can be used to reference parameters. + +* **Reason:** It is one of the approaches to make short blocks DRY-er and easier to read. E.g. in `filenames.each { |f| File.read(f) }`, repetition of `f` and extra syntax needed for it can be considered an unnecessary verbosity, so `each { File.read(_1) }` could be now used instead. +* **Discussion:** _The feature was discussed for a long time, syntax and semantics was changed several times on the road to 2.7_ [Feature #4475](https://bugs.ruby-lang.org/issues/4475) (initial discussion, started 9 years ago and finished with accepting of `@0`-`@9`), [Misc #15723](https://bugs.ruby-lang.org/issues/15723) (change to `_0`-`_9`), [Feature #16178](https://bugs.ruby-lang.org/issues/16178) (dropping of `_0` and changing semantics of `_1`) +* **Documentation:** []() +* **Code:** + ```ruby + # Simplest usage: + [10, 20, 30].map { _1**2 } + # => [100, 400, 900] + + # Multiple block parameters can be accessed as subsequent numbers + [10, 20, 30].zip([40, 50, 60], [70, 80, 90]).map { _1 + _2 + _3 } + # => [120, 150, 180] + + # If only _1 is used for multi-argument block, it contains all arguments + [10, 20, 30].zip([40, 50, 60], [70, 80, 90]).map { _1.join(',') } + # => ["10,40,70", "20,50,80", "30,60,90"] + + # If the block has explicit parameters, numbered one is SyntaxError + [10, 20, 30].map { |x| _1**2 } + # SyntaxError ((irb):1: ordinary parameter is defined) + + # Outside the block, usage is warned: + _1 = 'test' + # warning: `_1' is reserved as numbered parameter + + # But after that, _1 references local variable: + [10].each { p _1 } + # prints "test" + + # Numbered parameters are reflected in Proc's parameters and arity + p = proc { _1 + _2 } + l = lambda { _1 + _2 } + p.parameters + # => [[:opt, :_1], [:opt, :_2]] + p.arity + # => 2 + l.parameters + # => [[:req, :_1], [:req, :_2]] + l.arity + # => 2 + + # Nested blocks with numbered parameters are not allowed: + %w[test me].each { _1.each_char { p _1 } } + # SyntaxError (numbered parameter is already used in outer block here) + # %w[test me].each { _1.each_char { p _1 } } + # ^~ + ``` + +### Beginless range + +In addition to endless range in Ruby 2.6 `(start..)`, Ruby 2.7 introduces beginless one: `(..end)`. + +* **Reason:** Array slicing (initial justification for endless ranges) turned out to be not the only usage for semi-open ranges. Another ones, like `case`/`grep`, DSLs and constants and [`Comparable#clamp`](TODO), can gain from the symmetricity of "range without end"/"range without beginning". +* **Discussion:** [Feature #14799](https://bugs.ruby-lang.org/issues/14799) +* **Documentation:** [doc/syntax/literals.rdoc#Ranges](https://ruby-doc.org/core-2.7.0.preview3/doc/syntax/literals_rdoc.html#label-Ranges) +* **Code:** + ```ruby + # Usage examples + %w[a b c][..1] # same as [0..1], not really useful + + case creation_time + when ...1.year.ago then 'ancient' + when 1.year.ago...1.month.ago then 'old' + when 1.month.ago...1.day.ago then 'recent' + when 1.day.ago... then 'new' + end + + users.map(&:age).any?(...18) + + # Properties: + r = ..10 + r.begin # => nil + r.count # TypeError (can't iterate from NilClass), same with any other Enumerable methods + r.size # => Infinity + ``` +* **Notes:** + * Slightly "a-grammatic" name ("beginless" instead of "beginningless") is due to the fact that it is a range literally lacking `begin` property. + * Unfortunately, parser can not always handle beginless range without the help of the parenthises: + ```ruby + (1..10).grep ..5 + # ArgumentError (wrong number of arguments (given 0, expected 1)) + # ...because it is in fact parsed as ((1..10).grep()..5), e.g. range from grep results to 5 + ``` + This may seem an esotheric problem, but it becomes less esotheric in DSLs. For example in RubySpec, one would like to write: + ```ruby + ruby_version ..'1.9' do + # some tests for old Ruby + end + ``` + ...but the only way is + ```ruby + ruby_version(..'1.9') do + # some tests for old Ruby + end + ``` + +### Syntax changes + +* Comments between `.foo` calls are now allowed: + ```ruby + (1..20).select(&:odd?) + # This was not possible in 2.6 + .map { |x| x**2 } + # => [1, 9, 25, 49, 81, 121, 169, 225, 289, 361] + ``` +* Quotes (if exist) in HERE-documents should be on the same line as document start: + ```ruby + <<"EOS + " # This had been warned since 2.4; Now it raises a SyntaxError + EOS + ``` +* Modifier `rescue` parsing change (multiple assignement now consisent with singular): + + ```ruby + a = raise rescue 1 + # => 1 + a # => 1 in Ruby 2.6 and 2.7 + + a, b = raise rescue [1, 2] + # => [1, 2] + + # 2.6 + a # => nil + b # => nil + # The statement parsed as: (a, b = raise) rescue [1, 2] + + # 2.7 + a # => 1 + b # => 2 + # The statement parsed as: a, b = (raise rescue [1, 2]) + ``` + +### Warnings/deprecations + +Some older or accidental features are deprecated on the road to 3.0 and currently produce warnings. + +* `yield` in singleton class syntax (was inconsistent with local variables accessibility). Discussion: [Feature #15575](https://bugs.ruby-lang.org/issues/15575). + ```ruby + def foo + x = 1 + class << Object.new + p x # ArgumentError (undefined local variable or method) -- enclosing scope NOT accessible + yield # calls block passed to foo, implying enclosing scope IS accessible + # In Ruby 2.7: warning: `yield' in class syntax will not be supported from Ruby 3.0. + end + end + foo { p :ok } + ``` +* `$;` and `$,` global variables (Perl ancestry: default `split` and `join` separators): + ```ruby + $; = '???' + # warning: non-nil $; will be deprecated + 'foo???bar'.split + # warning: $; is set to non-nil value + # => ["foo", "bar"] + + $, = '###' + # warning: non-nil $, will be deprecated + %w[foo bar].join + # warning: $, is set to non-nil value + "foo###bar" + ``` +* Contrastingly, the flip-flop syntax deprecation, [introduced](#TODO) in 2.6, **is reverted**. It turned out that for brevity in text-processing scrips (including one-liners run as `ruby -e "print "`) the feature, however esotheric it may seem, has a justified usage. + * **Discussion:** [Feature #5400](https://bugs.ruby-lang.org/issues/5400#note-23). + * **Documentation:** [doc/syntax/control_expressions.rdoc#Flip-Flop](https://ruby-doc.org/core-2.6.5/doc/syntax/control_expressions_rdoc.html#label-Flip-Flop) + * **Code:** + ```ruby + # Imagine we are working with some data file where real data starts with "<<<"" line and end with ">>>>" + File.each_line('data.txt', chomp: true).filter_map { |ln| ln if ln == '<<<'..ln == '>>>' } + # => gets lines starting from <<<, and ending with >>>. + # The condition "flips on" when the first part is true, and then "flips off" when the second is true + + # Flip-flop-less version would be something like this (with the differenc it ignores last >>>): + lines.drop_while { |ln| ln != '<<<' }.take_while { |ln| ln != '>>>' } + ``` + +#### "Safe" and "taint" concepts are deprecated in general + +The concepts of marking objects as ["tainted"](https://ruby-doc.org/core-2.6.5/Object.html#method-i-taint) (unsafe, came from the outside) and "untaint" them after the check, is for a long time ignored by most of the libraries. `$SAFE` constant, trying to limit "unsafe" calls when set to higher values, is considered a fundamentally flawed method and [documented so since Ruby 2.0](https://ruby-doc.org/core-2.0.0/doc/security_rdoc.html#label-24SAFE). At the same time, as the "official" security features, subtle bugs in implementation of `$SAFE` and tainting have caused lot of "vulnerability reports" and added maintenance burden. + +* **Discussion:** [Feature #16131](https://bugs.ruby-lang.org/issues/16131) + +### `self.` + +Calling a private method with a literal `self` as the receiver is now allowed. + +* **Reason:** "`anything.method` is always disallowed for private methods" seemed like a simple and unambiguous rule, but produced some ugly edge cases (for example, `self.foo = something` for private `attr_accessor` _was_ allowed). It turned out "allow literal `self.`" makes things much clearer. +* **Discussion:** [Feature #11297](https://bugs.ruby-lang.org/issues/11297), [Feature #16123](https://bugs.ruby-lang.org/issues/16123) +* **Documentation:** []() +* **Code:** + ```ruby + class A + def update + self.a = 42 # works even in Ruby 2.6, because there was no other way to call private setter + self.a += 42 # "private method `a' called" in 2.6, works in 2.7 + + self + 42 # "private method `+' called" in 2.6, works in 2.7 + + x = self + x.a = 42 # "private method `a=' called" even in 2.7, not a literal self + end + + private + + attr_accessor :a + + def +(other) + puts "+ called" + end + end + ``` + +## Core classes and modules + +### Better `Method#inspect` + +`Method#inspect` now shows method's arguments and source location (if available). + +* **Reason:** As there are more code style approaches that operate `Method` objects, making them better identifiable and informative seemed necessary. +* **Discussion:** [Feature #14145](https://bugs.ruby-lang.org/issues/14145) +* **Documentation:** [Method#inspect](https://ruby-doc.org/core-2.7.0.preview3/Method.html#method-i-inspect) +* **Code:** + ```ruby + p CSV.method(:read) + # Ruby 2.6: + # => # + # Ruby 2.7: + # => #/lib/ruby/2.7.0/csv.rb:714> + + # For methods defined in C, path and param names aren't available, but at least generic signature is: + [].method(:at) + # => # + + # Convention: unknown param name is displayed as _, param has default value -- as ... + def m(a, b=nil, *c, d:, e: nil, **rest, &block) + end + + p method(:m) + #=> # + ``` +* **Notes:** + * while enhancing `Method#inspect`, `Proc`'s string representation also changed for consistency: now object id separated from location by ` ` (for easier copy-pasting). Discussion: [Feature #16101](https://bugs.ruby-lang.org/issues/16101) + ```ruby + p(proc {}) + # Ruby 2.6: + # => # + # Ruby 2.7: + # => # + ``` + * The same is related to `Thread`. Discussion: [Feature #16412](https://bugs.ruby-lang.org/issues/16412) + ```ruby + p(Thread.new {}) + # Ruby 2.6: + # => # + # Ruby 2.7: + # => # + ``` + +### `UnboundMethod#bind_call` + +Binds `UnboundMethod` to a receiver and calls it. Semantically equiavalent to `unbound_method.bind(receiver).call(arguments)`, but doesn't produce intermediate `Method` object (which `bind` does). + +* **Reason:** The technique of storing unbound methods and binding them later is used in some metaprogramming-heavy code to robustly use "original" implementation. For example: + ```ruby + MODULE_NAME = Module.instance_method(:name) + + class Customer + def self.name + "" + end + end + + Customer.name # => "" + MODULE_NAME.bind_call(Customer) # => "Customer" + ``` + In such cases (for example, code reloaders), overhead of producing new `Method` object on each `bind().call` is pretty significant, and `bind_call` allows to avoid it. +* **Discussion:** [Feature #15955](https://bugs.ruby-lang.org/issues/15955) +* **Documentation:** [UnboundMethod#bind_call](https://ruby-doc.org/core-2.7.0.preview3/UnboundMethod.html#method-i-bind_call) + +### `Module#const_source_location` + +* **Discussion:** [Feature #10771](https://bugs.ruby-lang.org/issues/10771) +* **Documentation:** []() +* **Code:** +* **Follow-up:** +* **Notes:** + +### `Module#autoload?`: `inherit` argument + +* **Reason:** More granular checking "if something is marked to be autoloaded directly or through ancestry chain" is necessary for advanced code reloaders (requested by author of the [zeitwerk](https://github.com/fxn/zeitwerk)). +* **Discussion:** [Feature #15777](https://bugs.ruby-lang.org/issues/15777) +* **Documentation:** [Module#autoload?](https://ruby-doc.org/core-2.7.0.preview3/Module.html#method-i-autoload-3F) +* **Code:** + ```ruby + class Parent + autoload :Feature1, 'feature1.rb' + end + + module Mixin + autoload :Feature2, 'feature2.rb' + end + + class Child < Parent + include Mixin + end + + Child.autoload?(:Feature1) # => "feature1.rb" + Child.autoload?(:Feature1, false) # => nil + Child.autoload?(:Feature2) # => "feature2.rb" + Child.autoload?(:Feature2, false) # => nil + ``` + +### `Comparable#clamp` with `Range` + +`Comparable#clamp` now can accept `Range` as its only argument, including beginless and endless ranges. + +* **Reason:** First, ranges can be seen as more "natural" to specify _a range_ of acceptable values. Second, with introduction of beginless and endless ranges, `#clamp` now can be used for one-sided value limitation, too. +* **Discussion:** [Feature #14784](https://bugs.ruby-lang.org/issues/14784) +* **Documentation:** [Comparable#clamp](https://ruby-doc.org/core-2.7.0.preview3/Comparable.html#method-i-clamp) +* **Code:** + ```ruby + 123.clamp(0..100) # => 100 + -20.clamp(0..100) # => 0 + 15.clamp(0..100) # => 15 + + # With semi-open ranges: + 123.clamp(150..) # => 150 + 123.clamp(..120) # => 120 + + # Range with excluding end is not allowed + 123.clamp(0...150) # ArgumentError (cannot clamp with an exclusive range) + + # Old two-argument form still works: + 123.clamp(0, 150) + # => 123 + ``` + +### `Integer[]` with range + +Allows to get several bits at once. + +* **Discussion:** [Feature #8842](https://bugs.ruby-lang.org/issues/8842) +* **Documentation:** [Integer#[]](https://ruby-doc.org/core-2.7.0.preview3/Integer.html#method-i-5B-5D) +* **Code:** + ```ruby + # 4---0 + # v v + 0b10101001[0..4] # => 9 + 0b10101001[0..4].to_s(2) + # => "1001" + ``` + +### `Complex#<=>` + +`Complex#<=>(other)` now returns `nil` if the number has imaginary part, and behaves like `Numeric#<=>` if it does not. + +* **Reason:** Method `#<=>` was explicitly undefined in `Complex` to underline the fact that linear order of complex numbers can't be established, but it was inconsistent with most of the other objects implementations (which return `nil` for impossible/incompatible comparison instead of rasing) +* **Discussion:** [Feature #](https://bugs.ruby-lang.org/issues/) +* **Documentation:** [Complex#<=>](https://ruby-doc.org/core-2.7.0.preview3/Complex.html#method-i-3C-3D-3E) +* **Code:** + ```ruby + 1 + 2i <=> 1 # => nil + 1 + 2i <=> 1 + 2i # => nil, even if numbers are equal + 1 + 0i <=> 2 # => -1 + ``` + +### Strings, symbols and regexps + +* **Unicode version:** 12.1 +* **New encodigs:** [CESU-8](https://en.wikipedia.org/wiki/CESU-8) + +#### Core methods returning frozen strings + +Several core methods now return frozen, deduplicated `String` instead of generating it every time the string is requested. + +* **Reason:** Avoiding allocations of new strings for each `#to_s` of primitive objects can save dramatical amounts of memory. +* **Discussion:** [Feature #16150](https://bugs.ruby-lang.org/issues/16150) +* **Affected methods:** [NilClass#to_s](https://ruby-doc.org/core-2.7.0.preview3/NilClass.html#method-i-to_s), [TrueClass#to_s](https://ruby-doc.org/core-2.7.0.preview3/TrueClass.html#method-i-to_s), [FalseClass#to_s](https://ruby-doc.org/core-2.7.0.preview3/TrueClass.html#method-i-to_s), [Module#name](https://ruby-doc.org/core-2.7.0.preview3/Module.html#method-i-name) +* **Code:** + ```ruby + # Ruby 2.6 + true.to_s.frozen? # => false + 3.times.map { true.to_s.object_id } + # => [47224710953060, 47224710953040, 47224710953000] -- every time new object + + # Ruby 2.7 + true.to_s.frozen? # => true + 3.times.map { true.to_s.object_id } + # => [180, 180, 180] -- frozen special string + ``` +* **Notes:** + * Change introduces incompatibility for the code looking like: + ```ruby + value = true + # ... + buffer = value.to_s + buffer << ' -- received' + # Ruby 2.6: "true -- received" + # Ruby 2.7: FrozenError (can't modify frozen String: "true") + ``` + * The same change was proposed for `Symbol#to_s` (and could've been a dramatical improvement in some kinds of code), but the change turned out to be too disruptive. + +#### `Symbol#start_with?` and `#end_with?` + +* **Reason:** Symbol was once thought as "just immutable names" with any of "string-y" operations not making sense for them, but as `Symbol#match` was always present, and `Symbol#match?` [implemented in 2.4](TODO), it turns out that other content inspection methods are also useful. +* **Discussion:** [Feature #16348](https://bugs.ruby-lang.org/issues/16348) +* **Documentation:** [Symbol#end_with?](https://ruby-doc.org/core-2.7.0_rc1/Symbol.html#method-i-end_with-3F), [Symbol#start_with?](https://ruby-doc.org/core-2.7.0_rc1/Symbol.html#method-i-start_with-3F) +* **Code:** + ```ruby + :table_name.end_with?('name', 'value') + # => true + :table_name.start_with?('table', 'index') + # => true + + # Somewhat confusingly, Symbol arguments are not supported + :table_name.end_with?(:name, 'value') + # TypeError (no implicit conversion of Symbol into String) + ``` + +### `Time` + +#### `#floor` and `#ceil` + +Rounds Time's nanoseconds down or up to a specified number of digits (0 by default, e.g. round to whole seconds). + +* **Reason:** Rounding of nanoseconds important in a test code, when comparing `Time` instances from a different sources (stored in DB, passed through third-party libraries, etc.). Having better control on truncation comparing to `Time#round` (which existed since 1.9.2) +* **Discussion:** [Feature #15653](https://bugs.ruby-lang.org/issues/15653) (floor, Japanese), [Feature #15772](https://bugs.ruby-lang.org/issues/15772) (ceil) +* **Documentation:** []() +* **Code:** +* **Follow-up:** +* **Notes:** + +#### `#inspect` includes subseconds + +* **Reason:** Losing subseconds in `#inspect` always made debugging and testing harder, producing test failures like "Expected: 2019-12-21 16:11:08 +0200, got 2019-12-21 16:11:08 +0200" (which are visually the same, but one of them, probably going through some serialization or DB storage, has different value for subseconds). +* **Discussion:** [Feature #15958](https://bugs.ruby-lang.org/issues/15958) +* **Documentation:** [Time#inspect](https://ruby-doc.org/core-2.7.0.preview3/Time.html#method-i-inspect) +* **Code:** + ```ruby + t = Time.now + p t + # Ruby 2.6: prints "2019-12-21 16:11:08 +0200" + # Ruby 2.7: prints "2019-12-21 16:11:08.189273595 +0200" + puts t + # prints "2019-12-21 16:11:08 +0200" + ``` + +### Enumerables and collections + +#### `Enumerable#filter_map` + +Transforms elements of enumerable with provided block, and drops falsy results, in one pass. + +* **Reason:** Filter suitable elements, then process them somehow is a common flow of sequence processing, yet with `filter { ... }.map { ... }` additional intermediate `Array` is produced, which is not always desirable. Also, some processing can indicate "can't be processed" by returning `false` or `nil`, which requires code like `map { ... }.compact` (to drop `nil`s) or `map { ... }.select(:itself)` (to drop all falsy values). +* **Discussion:** [Feature #15323](https://bugs.ruby-lang.org/issues/15323) +* **Documentation:** [Enumerable#filter_map](https://ruby-doc.org/core-2.7.0.preview3/Enumerable.html#method-i-filter_map) +* **Code:** + ```ruby + (1..10).filter_map { |i| i**2 if i.even? } # => [4, 16, 36, 64, 100] + + # imagine method constantize() returning false if string can't be converted to + # a proper constant name + constant_names = %w[foo 123 _ bar baz/test].filter_map { |str| constantize(str) } # => ['Foo', 'Bar'] + + # Without block, returns Enumerator: + %w[foo bar baz test].filter_map + .with_index { |str, i| str.capitalize if i.even? } + # => ["Foo", "Baz"] + ``` + +#### `Enumerable#tally` + +Counts unique objects in the enumerable and returns hash of `{object => count}`. + +* **Discussion:** [Feature #11076](https://bugs.ruby-lang.org/issues/11076) +* **Documentation:** [Enumerable#tally](https://ruby-doc.org/core-2.7.0.preview3/Enumerable.html#method-i-tally) +* **Code:** + ```ruby + %w[Ruby Python Ruby Perl Python Ruby].tally + # => {"Ruby"=>3, "Python"=>2, "Perl"=>1} + ``` +* **Notes:** + * `#tally` follows `#to_h` intuitions (and uses it underneath): objects are considered same if they have the same `#hash`; orders of keys corresponds to order of appearance in sequence; first object in sequence becomes the key + * Additional block argument, or, alternatively, additional method `tally_by(&block)` was proposed in the same ticket to allow code like + ```ruby + (1..10).tally_by(&:even?) # => {true => 5, false => 5} + ``` + ...but was not accepted yet. + +#### `Enumerator.produce` + +Produces infinite enumerator by calling provided block and passing its result to subsequent block call. + +* **Reason:** `.produce` allows to convert any `while`-alike or `loop`-alike loops into enumerators, making possible to work with them in a Ruby-idiomatic `Enumerable` style. +* **Discussion:** [Feature #14781](https://bugs.ruby-lang.org/issues/14781) +* **Documentation:** [Enumerator.produce]() +* **Code:** + ```ruby + require 'date' + # Before: while cycle to search next tuesday: + d = Date.today + while !d.tuesday + d += 1 + end + # After: enumerator: + Enumerator.produce(Date.today, &:succ) # => enumerator of infinitely increasing dates + .find(&:tuesday?) + + require 'strscan' + PATTERN = %r{\d+|[-/+*]} + scanner = StringScanner.new('7+38/6') + # Before: while cycle to implement simple lexer: + result = + result << scanner.scan(PATTERN) while !scanner.eos? + # After: achieving the same with enumerator (which can be passed to other methods to process): + Enumerator.produce { scanner.scan(PATTERN) }.slice_after { scanner.eos? }.first + # => ["7", "+", "38", "/", "6"] + + # Raising StopIteration allows to stop the iteration: + ancestors = Enumerator.produce(node) { |prev| prev.parent or raise StopIteration } + # => enumerator + enclosing_section = ancestors.find { |n| n.type == :section } # => :section node or nil + + # If the initial value is passed, it is an argument to first block call, and yielded as a first + # value of enumerator + Enumerator.produce(1) { |prev| p "PREVIOUS: #{prev}"; prev + 1 }.take(3) + # "PREVIOUS: 1" + # "PREVIOUS: 2" + # => [1, 2, 3] + + # If the initial value is not passed, first block call receives `nil`, and the block's first result + # is yielded as a first value of enumerator + Enumerator.produce { |prev| p "PREVIOUS: #{prev.inspect}"; (prev || 0) + 1 }.take(3) + # "PREVIOUS: nil" + # "PREVIOUS: 1" + # "PREVIOUS: 2" + # => [1, 2, 3] + ``` + +#### `Enumerator::Lazy#eager` + +Converts lazy enumerator back to eager one. + +* **Reason:** When working with large data sequences, lazy enumerators are useful tools to not produce intermediate array on each step. But some data consuming methods expect to receive enumeratos that would really return data from methods like `take_while` and not just add them to pipeline. +* **Discussion:** [Feature #15901](https://bugs.ruby-lang.org/issues/15901) +* **Documentation:** [Enumerator::Lazy#eager](https://ruby-doc.org/core-2.7.0.preview3/Enumerator/Lazy.html#method-i-eager) +* **Code:** + ```ruby + # Imagine we read very large data file: + lines = + File.open('data.csv') + .each_line + .lazy + .map { |ln| ln.sub(/\#.+$/, '').strip } # remove "comments" + .reject(&:empty?) # drop empty lines + p lines + # => # + + # Now, we want to consume just "headers" from this CSV, and pass the rest of enumerator + # into the other methods. + + # This code: + headers = lines.take_while { |ln| ln.start_with?('$$$') } + # ...will just produce another lazy enumerator, with `take_while` chained to pipeline. + + # Now, this: + lines = lines.eager # makes the enumerator eager, but (unlike `#force`) doesn't consume it yet + p lines + # => #:each> + + # consumes only several first lines and returns array of headers + headers = lines.take_while { |ln| ln.start_with?('$$$') } + # => [array, of, header, lines] + + # now we can pass `lines` to methods that expect take_while/take and other similar methods to + # consume enumerator partially and return arrays + ``` + +#### `Enumerator::Yielder#to_proc` + +* **Reason:** When constructing the enumerator, value yielding is frequently delegated to other methods, which accept blocks. Before the change, it was inconvenient to delegate. +* **Discussion:** [Feature #15618](https://bugs.ruby-lang.org/issues/15618) +* **Documentation:** [Enumerator::Yielder#to_proc](https://ruby-doc.org/core-2.7.0.preview3/Enumerator/Yielder.html#method-i-to_proc) +* **Code:** + ```ruby + # Construct a enumerator which will pass all lines from all files from some folder: + + # Before the change: + all_lines = Enumerator.new { |y| # y is Yielder object here + Dir.glob("*.rb") { |file| + File.open(file) { |f| f.each_line { |ln| y << ln } } + } + } + + # After the change: + all_lines = Enumerator.new { |y| # y is Yielder object here + Dir.glob("*.rb") { |file| + File.open(file) { |f| f.each_line(&y) } + } + } + ``` + +#### `Array#intersection` + +Like `Array#union` and `#difference`, [added](2.6.html#TODO) in 2.6 as a explicitly-named and accepting multiple arguments alternatives for `#|` and `#-`, the new method is alternative for `#&`. + +* **Discussion:** [Feature #16155](https://bugs.ruby-lang.org/issues/16155) +* **Documentation:** [Array#intersection](https://ruby-doc.org/core-2.7.0.preview3/Array.html#method-i-intersection) +* **Code:** + ```ruby + ['Ruby', 'Python', 'Perl'].intersection(['Ruby', 'Diamond', 'Perl'], ['Ruby', 'Nikole', 'Kate']) # => ["Ruby"] + + ['Ruby', 'Python', 'Perl'].intersection # => ["Ruby", "Python", "Perl"] + ``` + +### `Fiber#raise` + +Raises exception inside the resumed Fiber. + +* **Reason:** Ability to raise an exception inside Fiber makes control passing abilities more feature-complete. +* **Discussion:** [Feature #10344](https://bugs.ruby-lang.org/issues/10344) +* **Documentation:** [Fiber#raise](https://ruby-doc.org/core-2.7.0.preview3/Fiber.html#method-i-raise) +* **Code:** + ```ruby + f = Fiber.new { + Enumerator.produce { Fiber.yield } # Infinite yielding enumerator, breaks on StopIteration + .to_a.join(', ') + } + f.resume + f.resume 1 + f.resume 2 + f.resume 3 + f.raise StopIteration # => "1, 2, 3" + ``` +* **Notes:** `Fiber#raise` has the same call sequence as `Kernel#raise` and can be called without any arguments (`RuntimeError` is empty message is raised), with string (`RuntimeError` with provided message is raised), exception class and (optional) message, or instance of the exception. + +### `Range` + +#### `Range#===` for `String` + +In 2.6, `Range#===` was changed to use `#cover?` underneath, but not for `String`. This was fixed. + +* **Discussion:** [Bug #15449](https://bugs.ruby-lang.org/issues/15449) +* **Documentation:** [Range#===](https://ruby-doc.org/core-2.7.0.preview3/Range.html#method-i-3D-3D-3D) +* **Code:** + ```ruby + case '2.6.5' + when '2.4'..'2.7' + 'matches' + else + 'nope :(' + end + # => "nope :(" in 2.6, "matches" in 2.7 + ``` + +### Filesystem and IO + +#### `IO#set_encoding_by_bom` + +Auto-sets encoding to UTF-8 if byte-order mark is present in the stream. + +* **Discussion:** [Feature #]15210(https://bugs.ruby-lang.org/issues/15210) +* **Documentation:** [IO#set_encoding_by_bom](https://ruby-doc.org/core-2.7.0.preview3/IO.html#method-i-set_encoding_by_bom) +* **Code:** + ```ruby + File.write("tmp/bom.txt", "\u{FEFF}мама") + ios = File.open("tmp/bom.txt", "rb") + ios.binmode? # => true + ios.external_encoding # => # + ios.set_encoding_by_bom # => # + ios.external_encoding # => # + ios.read # => "мама" + + File.write("tmp/nobom.txt", "мама") + ios = File.open("tmp/nobom.txt", "rb") + ios.set_encoding_by_bom # => nil + ios.external_encoding # => # + ios.read # => "\xD0\xBC\xD0\xB0\xD0\xBC\xD0\xB0" + + # The method raises in non-binary-mode streams, or if encoding already set: + File.open("tmp/bom.txt", "r").set_encoding_by_bom + # ArgumentError (ASCII incompatible encoding needs binmode) + File.open("tmp/bom.txt", "rb", encoding: 'Windows-1251').set_encoding_by_bom + # ArgumentError (encoding is set to Windows-1251 already) + ``` + +#### `Dir.glob` and `Dir.[]` not allow `\0`-separated patterns + +* **Discussion:** [Feature #14643](https://bugs.ruby-lang.org/issues/14643) +* **Documentation:** [Dir.glob](https://ruby-doc.org/core-2.7.0.preview3/Dir.html#method-c-glob) +* **Code:** + ```ruby + Dir.glob("*.rb\0*.md") + # 2.6: + # warning: use glob patterns list instead of nul-separated patterns + # => ["2.5.md", "History.md", "README.md" ... + # 2.7 + # ArgumentError (nul-separated glob pattern is deprecated) + + # Proper alternative, works in 2.7 and earlier versions: + Dir.glob(["*.rb", "*.md"]) + # => ["2.5.md", "History.md", "README.md" ... + ``` + * File.extname now returns a dot string at a name ending with a dot. [Bug #15267] + +### Exceptions + +#### `FrozenError`: receiver argument + +In 2.6 several exception class constructrs [were enhanced](TODO) so the user code could create them providing context, now `FrozenError` also got this functionality. + +* **Discussion:** [Feature #15751](https://bugs.ruby-lang.org/issues/15751) +* **Documentation:** [FrozenError#new](https://ruby-doc.org/core-2.7.0.preview3/FrozenError.html#method-c-new) +* **Code:** + ```ruby + class AlwaysFrozenHash < Hash + # ... + def update!(*) + raise FrozenError.new("I am frozen!", receiver: self) + end + end + ``` +* **Notice:** `.new` syntax is the only way to pass new arguments, this would **not** work: + ```ruby + raise FrozenError, "I am frozen!", receiver: self + ``` + +### Interpreter internals + +* +RubyVM.resolve_feature_path+ moved to + $LOAD_PATH.resolve_feature_path. [Feature #15903] [Feature #15230] + +#### `GC.compact` + +* 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 friendly. [Feature #15626] + TODO https://bugs.ruby-lang.org/issues/16443 + +* ObjectSpace::WeakMap#[]= now accepts special objects as either key or + values. [Feature #16035] + +## Standard library + + * Date.jisx0301, Date#jisx0301, and Date.parse support the new Japanese + era. [Feature #15742] + * Object#DelegateClass accepts a block and module_evals it in the context + of the returned class, similar to Class.new and Struct.new. + +IRB:: + + * Introduce syntax highlight inspired by pry.gem to Binding#irb source lines, + REPL input, and inspect output of some core-class objects. + + * Introduce multiline mode by Reline. + + * Show documents when completion. + + * Enable auto indent and save/load history by default. +Reline:: + + * New stdlib that is compatible with readline stdlib by pure Ruby and also + has a multiline mode. + +* Net::HTTP Add ipaddr optional parameter to Net::HTTP#start to replace the address for + TCP/IP connection [Feature #5180] +* Add Net::FTP#features to check available features, and Net::FTP#option to + enable/disable each of them. [Feature #15964] +* Net::IMAP:: Add Server Name Indication (SNI) support. [Feature #15594] + +open-uri:: + * Warn open-uri's "open" method at Kernel. + Use URI.open instead. [Misc #15893] + * The default charset of text/* media type is UTF-8 instead of + ISO-8859-1. [Bug #15933] + +Pathname:: + * Delegates 3 arguments from Pathname.glob to Dir.glob to + accept base: keyword. + * 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. + +OptionParser:: + * Now show "Did you mean?" for unknown option. [Feature #16256] + + +### Standard library contents change + +* 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) +profile.rb, Profiler__:: + * Removed from standard library. No one maintains it from Ruby 2.0.0. + +* Promote stdlib to default gems + * The following default gems was published at rubygems.org + * benchmark + * cgi + * delegate + * getoptlong + * net-pop + * net-smtp + * open3 + * pstore + * singleton + * The following default gems only promoted ruby-core, Not yet published at rubygems.org. + * monitor + * observer + * timeout + * tracer + * uri + * yaml + +### Large updated libraries + +large updated + * Upgrade to Bundler 2.1.0.pre.3 + * Upgrade to CSV 3.1.2 + * Merge Racc 1.4.15 from upstream repository and added cli of racc. + * Upgrade REXML to 3.2.3. + * Upgrade to RSS 0.2.8. + * Upgrade to RubyGems 3.1.0.pre3 + * Upgrade to StringScanner 1.0.3. diff --git a/_src/lib/file.rb b/_src/lib/file.rb new file mode 100644 index 0000000..f78153e --- /dev/null +++ b/_src/lib/file.rb @@ -0,0 +1,19 @@ +require 'memoist' + +class FileProcessor < Struct.new(:path) + extend Memoist + + def self.call(path) + new(path).call + end + + private + + memoize def version + path[%r{_src/(.+)\.md}, 1] + end + + memoize def text + File.read(path) + end +end \ No newline at end of file diff --git a/_src/lib/render.rb b/_src/lib/render.rb new file mode 100644 index 0000000..f23b335 --- /dev/null +++ b/_src/lib/render.rb @@ -0,0 +1,27 @@ +class Render < FileProcessor + TRACKER_LINK_RE = %r{\[(?Bug|Feature|Misc) \#(?\d+)\]\(https://bugs\.ruby-lang\.org/issues/(?\d+(\#.+)?)\)} + + def call + text + .gsub('<>', File.mtime(path).strftime('%b %d, %Y')) + .gsub(/\[(Bug|Feature|Misc) \#\d+\]\(.+?\)/, &method(:process_link)) + .gsub( + %r{\[([^\[\]]+ [^\[\]]+)\]\((https://ruby-doc\.org.+?)\)}, + '\\1' + ) + .gsub( + %r{\[([^\[\]]+)\]\((https://ruby-doc\.org.+?)\)}, + '\\1' + ) + .gsub(%r{^\#{2,} (.+)$}) { |header| header + "[](##{Util.id(header)})" } # attach nice clickable links to headers + end + + private + + def process_link(link) + m = link.match(TRACKER_LINK_RE) or fail("Wrong link: #{link}") + kind, num, num2 = m.values_at(:kind, :num, :num2) + num == num2.sub(/\#.+$/, '') or fail "Wrong link: #{link}" + %{#{kind} ##{num}} + end +end \ No newline at end of file diff --git a/_src/lib/toc.rb b/_src/lib/toc.rb new file mode 100644 index 0000000..920e406 --- /dev/null +++ b/_src/lib/toc.rb @@ -0,0 +1,84 @@ +module TOC + START_CHAPTERS = [ + {title: 'Introduction', path: '/'} + ] + FINAL_CHAPTERS = [ + {title: 'History (of this site)', path: '/History.html'}, + {title: 'Contributing', path: '/Contributing.html'} + ] + + RSS_DESCRIPTION = <<~DESC + **Highlights:** + + %s + + [Read more »](https://rubyreferences.github.io/rubychanges/%s.html) + DESC + + def self.call(pathes) + [ + *START_CHAPTERS, + *pathes.map(&Item.method(:call)).flatten(1), + *FINAL_CHAPTERS + ] + end + + class Item < FileProcessor + def call + doc.root.children + .select { |c| c.type == :header } + .map { |c| + OpenStruct.new( + level: c.options[:level], + text: c.options[:raw_text], + html: Util.inner_html(c) + ) + } + .then(&method(:nest_headers)) + .then { |nesting| + toc_entries(nesting, "/#{version}.html", is_version: true, **rss_fields) + } + end + + private + + memoize def doc + Kramdown::Document.new(text) + end + + def toc_entries(nodes, prefix, **extra) + nodes.map { |node| + { + title: node.html, + path: node.level == 1 ? prefix : "#{prefix}##{Util.id(node.text)}", + **(node.level == 1 ? extra : {}), + children: toc_entries(node.children, prefix) + } + .tap { |h| h.delete(:children) if h[:children].empty? } + } + end + + def nest_headers(headers, level = 1) + res = [] + while !headers.empty? && headers.first.level == level + cur = headers.shift + children = nest_headers(headers, level + 1) + cur.children = children + res << cur + end + res + end + + def rss_fields + pub = text[/\*\*This document first published:\*\* (.+)\n/, 1] or fail "Published at not found" + desc = text[/\#\# Highlights\n(.+?)\n\#\# /m, 1] or fail "Description not found" + desc = desc + .gsub(/\[(.+?)\]\(.+?\)/, '\1') # remove links + .then { |desc| RSS_DESCRIPTION % [desc, version] } + .then(&Kramdown::Document.method(:new)) + .to_html + + {published_at: pub, description: desc} + end + end +end \ No newline at end of file diff --git a/_src/lib/util.rb b/_src/lib/util.rb new file mode 100644 index 0000000..eb7abee --- /dev/null +++ b/_src/lib/util.rb @@ -0,0 +1,33 @@ +module Util + extend self + + HTML = Kramdown::Converter::Html + + def deep_stringify_keys(hash) + _stringify_keys_any(hash) + end + + # It is Kramdown::Base#basic_generate_id + def id(str) + str.gsub(/^[^a-zA-Z]+/, '') + .tr('^_a-zA-Z0-9 -', '') + .tr(' ', '-') + .downcase + end + + def inner_html(h) + h.options[:encoding] = 'UTF-8' + h.type = :root + HTML.convert(h).first + end + + private + + def _stringify_keys_any(v) + case v + when Hash then v.to_h { |k, v| [k.to_s, _stringify_keys_any(v)] } + when Array then v.map(&method(:_stringify_keys_any)) + else v + end + end +end diff --git a/_src/script/postprocess.rb b/_src/script/postprocess.rb deleted file mode 100644 index 2c25438..0000000 --- a/_src/script/postprocess.rb +++ /dev/null @@ -1,34 +0,0 @@ -# FIXME: Maybe it is reasonable to parse it with Kramdown and render back with -# Kramdown::Converter::Markdown, but for now, regexp-s seem to be enough, and -# proper "re-rendering" sometimes produces some pretty weird side effects. - -# It is Kramdown::Base#basic_generate_id -def id(str) - gen_id = str.gsub(/^[^a-zA-Z]+/, '') - gen_id.tr!('^_a-zA-Z0-9 -', '') - gen_id.tr!(' ', '-') - gen_id.downcase! - gen_id -end - -Dir['_src/*.md'].grep(/\d/).sort.each do |path| - ver = path.scan(%r{_src/(.+)\.md}).flatten.first - - srcpath = File.expand_path("../#{ver}.md", __dir__) - dstpath = File.expand_path("../../#{ver}.md", __dir__) - - File.read(srcpath) - .gsub('<>', File.mtime(srcpath).strftime('%b %d, %Y')) - .gsub(/\[(Bug|Feature|Misc) \#\d+\]\(.+?\)/) { |link| - m = link.match(%r{\[(?Bug|Feature|Misc) \#(?\d+)\]\(https://bugs\.ruby-lang\.org/issues/(?\d+)\)}) or fail("Wrong link: #{link}") - m[:num] == m[:num2] or fail "Wrong link: #{link}" - kind, num = m.values_at(:kind, :num) - %{#{kind} ##{num}} - } - .gsub(%r{\[([^\[\]]+ [^\[\]]+)\]\((https://ruby-doc\.org.+?)\)}, '\\1') - .gsub(%r{\[([^\[\]]+)\]\((https://ruby-doc\.org.+?)\)}, '\\1') - .gsub(%r{^\#{2,} (.+)$}) { |header| - header + "[](##{id(header)})" - } - .tap(&File.open(dstpath, 'w').method(:write)) -end \ No newline at end of file diff --git a/_src/script/toc.rb b/_src/script/toc.rb deleted file mode 100644 index 4ff842c..0000000 --- a/_src/script/toc.rb +++ /dev/null @@ -1,97 +0,0 @@ -require 'kramdown' -require 'yaml' -require 'ostruct' - -START_CHAPTERS = [ - {title: 'Introduction', path: '/'} -] -FINAL_CHAPTERS = [ - {title: 'History (of this site)', path: '/History.html'}, - {title: 'Contributing', path: '/Contributing.html'} -] - -class Hash - def deep_stringify_keys - _stringify_keys_any(self) - end - - def _stringify_keys_any(v) - case v - when Hash - v.map { |k, v| [k.to_s, _stringify_keys_any(v)] }.to_h - when Array - v.map(&method(:_stringify_keys_any)) - else - v - end - end -end - -chapters = START_CHAPTERS.dup - -HTML = Kramdown::Converter::Html - -def nest_headers(headers, level = 1) - res = [] - while !headers.empty? && headers.first.level == level - cur = headers.shift - children = nest_headers(headers, level + 1) - cur.children = children - res << cur - end - res -end - -# It is Kramdown::Base#basic_generate_id -def id(str) - gen_id = str.gsub(/^[^a-zA-Z]+/, '') - gen_id.tr!('^_a-zA-Z0-9 -', '') - gen_id.tr!(' ', '-') - gen_id.downcase! - gen_id -end - -def toc_entries(nodes, prefix, **extra) - nodes.map do |node| - { - title: node.html, - path: node.level == 1 ? prefix : "#{prefix}##{id(node.text)}", - **(node.level == 1 ? extra : {}), - children: toc_entries(node.children, prefix) - }.tap { |h| h.delete(:children) if h[:children].empty? } - end -end - -def inner_html(h) - h.options[:encoding] = 'UTF-8' - h.type = :root - HTML.convert(h).first -end - -Dir['_src/*.md'].grep(/\d/).sort.reverse.each do |path| - ver = path.scan(%r{_src/(.+)\.md}).flatten.first - - text = File.read(path) - doc = Kramdown::Document.new(text) - - headers = doc.root.children - .select { |c| c.type == :header } - .map { |c| - OpenStruct.new(level: c.options[:level], text: c.options[:raw_text], html: inner_html(c)) - } - - nesting = nest_headers(headers) - pub = text[/\*\*This document first published:\*\* (.+)\n/, 1] or fail "Published at not found" - desc = text[/\#\# Highlights\n(.+?)\n\#\# /m, 1] or fail "Description not found" - desc = desc - .gsub(/\[(.+?)\]\(.+?\)/, '\1') # remove links - .then { |highlights| "**Highlights:**\n\n" + highlights + "\n\n[Read more »](https://rubyreferences.github.io/rubychanges/#{ver}.html)" } - .then(&Kramdown::Document.method(:new)) - .to_html - - chapters.concat(toc_entries(nesting, "/#{ver}.html", is_version: true, published_at: pub, description: desc)) -end - -chapters.concat(FINAL_CHAPTERS) - -File.write('_data/book.yml', {chapters: chapters}.deep_stringify_keys.to_yaml) \ No newline at end of file From 93a41bca91fb35f689d4058a0729a665155ecc92 Mon Sep 17 00:00:00 2001 From: zverok Date: Fri, 27 Dec 2019 00:37:17 +0200 Subject: [PATCH 2/6] Most of the content is ready --- 2.6.md | 12 +- 2.7.md | 468 ++++++++++++++++++++++++++++++++++----------- Rakefile | 1 + _data/book.yml | 64 +++++-- _src/2.6.md | 8 +- _src/2.7.md | 464 ++++++++++++++++++++++++++++++++++---------- _src/lib/render.rb | 4 +- 7 files changed, 790 insertions(+), 231 deletions(-) diff --git a/2.6.md b/2.6.md index 0821ec4..5da0c11 100644 --- a/2.6.md +++ b/2.6.md @@ -1,15 +1,15 @@ --- title: Ruby 2.6 changes -prev: / +prev: 2.7 next: 2.5 --- # Ruby 2.6 * **Released at:** Dec 25, 2018 ([NEWS](https://github.com/ruby/ruby/blob/ruby_2_6/NEWS) file) -* **Status (as of Dec 21, 2019):** 2.6.5 is current _stable_ +* **Status (as of Dec 26, 2019):** 2.6.5 is current _stable_ * **This document first published:** Dec 29, 2018 -* **Last change to this document:** Dec 21, 2019 +* **Last change to this document:** Dec 26, 2019 > **Note:** As already explained in [Introduction](README.md), this site is dedicated to changes in the **language**, not the **implementation**, therefore the list below lacks mentions of lots of important optimization introduced in 2.6, including the whole JIT big thing. That's not because they are not important, just because this site's goals are different. @@ -100,6 +100,7 @@ Refinements are now compatible with `#public_send` and `#respond_to?`, and impli 'foo'.public_send(:surround, '|') # => "|foo|" in 2.6, NoMethodError in 2.5 (1..3).map(&'%02i') # => ["01", "02", "03"] in 2.6; wrong argument type String (expected Proc) in 2.5 ``` +* **Follow-up:** Ruby 2.7 also made refinements available in [`#method`](TODO) ### Misc[](#misc) @@ -567,6 +568,7 @@ For string `name`, returns what path `require(name)` will load (without actually RubyVM.resolve_feature_path('faraday') # => [:rb, "<...>/gems/faraday-0.15.4/lib/faraday.rb"] ``` +* **Follow-up:** In Ruby 2.7, `resolve_feature_path` was [moved](TODO) to a `$LOAD_PATH` singleton method, and its behavior for already loaded pathes was [fixed](TODO) to return path nevertheless. #### `RubyVM::AbstractSyntaxTree`[](#rubyvmabstractsyntaxtree) @@ -707,4 +709,6 @@ Libraries extracted in 2.6: * [shell](https://github.com/ruby/shell) * [sync](https://github.com/ruby/sync) * [thwait](https://github.com/ruby/thwait) -* [tracer](https://github.com/ruby/tracer) \ No newline at end of file +* [tracer](https://github.com/ruby/tracer) + +**Follow-up:** [16 more libraries](2.7.html#libraries-promoted-to-default-gems) gemified in 2.7, and [6 just dropped](2.7.html#libraries-excluded-from-the-standard-library) from the standard library. diff --git a/2.7.md b/2.7.md index b1b7ee6..21aa0a5 100644 --- a/2.7.md +++ b/2.7.md @@ -7,27 +7,51 @@ next: 2.6 # Ruby 2.7 * **Released at:** Dec 25, 2019 ([NEWS](TODO) file) -* **Status (as of Dec 23, 2019):** TODO +* **Status (as of Dec 27, 2019):** TODO * **This document first published:** TODO -* **Last change to this document:** Dec 23, 2019 +* **Last change to this document:** Dec 27, 2019 ## Highlights[](#highlights) -Ruby 2.7 is a last big release before 3.0 +Ruby 2.7 is a last major release before 3.0¹, so it introduces several important changes, larger in scale than previous releases (and also a bit lean on a "just nice to have" features side). Be prepared! +* Pattern matching +* "Real" keyword argument +* Numbered block parameters +* Beginless range +* `Enumerator.produce` +* `GC.compact` +* Large update of IRB +* Serious cleanup of the standard library + +¹There is a possibility 2.8 will also be released, but in any case, Christmas release of 2020 is promised to be Ruby 3.0. ## Language[](#language) ### Pattern matching[](#pattern-matching) -### Keyword argument-related changes[](#keyword-argument-related-changes) +Pattern matching is a completely new and experimental feature for structural value checking against patterns, and local variable binding. As it is new and huge, we'll not try to cover the feature here and just send the reader to the official documentation. Unfortunately, the one was not merged before the 2.7 release, so the best we can do currently is to link to **[documentation candidate](https://github.com/zverok/ruby/blob/pattern-matching-docs/doc/syntax/pattern_matching.rdoc)** _(disclaimer: it is written by author of the current changelog)_. Just a small example: + +```ruby +require 'open-uri' +require 'json' + +data = URI.open('https://api.github.com/repos/ruby/ruby/pulls').read + .then { |body| JSON.parse(body, symbolize_names: true) } -Ruby 2.7 introduced a lot of changes towards more consistent keyword arguments processing. Fortunately, examples, justifications and relationships of those changes, as well as links to docs and discussions are published on [official site](https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/). +data in [{user: {login:}, title:, created_at:}, *] # match array of hashes, with deep matching inside first hash + +[login, title, created_at] # matched values bound to local variables +# => ["zverok", "Add pattern matching documentation", "2019-12-25T18:42:03Z"] +``` + +### Keyword argument-related changes[](#keyword-argument-related-changes) -Therefore here we'll just list the changes for the sake of completeness of this changelog: +Ruby 2.7 introduced a lot of changes towards more consistent keyword arguments processing. Fortunately, the official Ruby site has a [full description of those changes](https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/), with examples, justifications and relationships of features with each other.
+Therefore here we'll just list the changes for the sake of completeness of this changelog. -* Warnings (would be errors in 3.0) for implicit coversion of the last argument-hash to keyword arguments, and vice versa; -* `Module#ruby2_keywords` method to mark method as not warning when the last argument is used as keyword one (to provide backward- and forward-compatible way of defining "delegating all" methods); +* Warnings (would be errors in 3.0) for implicit conversion of the last argument-hash to keyword arguments, and vice versa; +* `Module#ruby2_keywords` and `Proc#ruby2_keywords` methods to mark method and proc as not warning when the last argument is used as keyword one (to provide backward- and forward-compatible way of defining "delegating all" methods); * "Forward all arguments" syntax: `(...)` * Non-`Symbol` keys are allowed in keyword arguments unpacking; * `**nil` syntax in method definition to explicitly mark method that doesn't accept keywords (and can't be called with a hash without curly braces); @@ -38,8 +62,11 @@ Therefore here we'll just list the changes for the sake of completeness of this In block without explicitly specified parameters, variables `_1` through `_9` can be used to reference parameters. * **Reason:** It is one of the approaches to make short blocks DRY-er and easier to read. E.g. in `filenames.each { |f| File.read(f) }`, repetition of `f` and extra syntax needed for it can be considered an unnecessary verbosity, so `each { File.read(_1) }` could be now used instead. -* **Discussion:** _The feature was discussed for a long time, syntax and semantics was changed several times on the road to 2.7_ Feature #4475 (initial discussion, started 9 years ago and finished with accepting of `@0`-`@9`), Misc #15723 (change to `_0`-`_9`), Feature #16178 (dropping of `_0` and changing semantics of `_1`) -* **Documentation:** []() +* **Discussion:** _The feature was discussed for a long time, syntax and semantics was changed several times on the road to 2.7:_ + * Feature #4475 (initial discussion, started 9 years ago, and finished with accepting of `@0`—`@9`), + * Misc #15723 (change to `_0`—`_9`), + * Bug #16178 (dropping of `_0`, and changing semantics of `_1`) +* **Documentation:** [Proc#Numbered parameters](https://docs.rubocop.org/en/stable/cops_layout/) * **Code:** ```ruby # Simplest usage: @@ -87,7 +114,7 @@ In block without explicitly specified parameters, variables `_1` through `_9` ca ### Beginless range[](#beginless-range) -In addition to endless range in Ruby 2.6 `(start..)`, Ruby 2.7 introduces beginless one: `(..end)`. +In addition to [endless range in Ruby 2.6](TODO): `(start..)`, Ruby 2.7 introduces beginless one: `(..end)`. * **Reason:** Array slicing (initial justification for endless ranges) turned out to be not the only usage for semi-open ranges. Another ones, like `case`/`grep`, DSLs and constants and [`Comparable#clamp`](TODO), can gain from the symmetricity of "range without end"/"range without beginning". * **Discussion:** Feature #14799 @@ -133,7 +160,7 @@ In addition to endless range in Ruby 2.6 `(start..)`, Ruby 2.7 introduces beginl end ``` -### Syntax changes[](#syntax-changes) +### Other syntax changes[](#other-syntax-changes) * Comments between `.foo` calls are now allowed: ```ruby @@ -173,6 +200,8 @@ In addition to endless range in Ruby 2.6 `(start..)`, Ruby 2.7 introduces beginl Some older or accidental features are deprecated on the road to 3.0 and currently produce warnings. +> Note that Ruby 2.7 also [introduced a way](TODO) to turn off only some categories of warnings, for example, only deprecation ones. + * `yield` in singleton class syntax (was inconsistent with local variables accessibility). Discussion: Feature #15575. ```ruby def foo @@ -199,6 +228,15 @@ Some older or accidental features are deprecated on the road to 3.0 and currentl # warning: $, is set to non-nil value "foo###bar" ``` +* Implicit block capturing in `proc` and `lambda`: + ```ruby + def foo + proc.call # here the block passed to method is implicitly captured by proc + end + foo { puts "Hello" } + # warning: Capturing the given block using Kernel#proc is deprecated; use `&block` instead + # still prints "Hello" + ``` * Contrastingly, the flip-flop syntax deprecation, [introduced](#TODO) in 2.6, **is reverted**. It turned out that for brevity in text-processing scrips (including one-liners run as `ruby -e "print "`) the feature, however esotheric it may seem, has a justified usage. * **Discussion:** Feature #5400. * **Documentation:** doc/syntax/control_expressions.rdoc#Flip-Flop @@ -225,7 +263,7 @@ Calling a private method with a literal `self` as the receiver is now allowed. * **Reason:** "`anything.method` is always disallowed for private methods" seemed like a simple and unambiguous rule, but produced some ugly edge cases (for example, `self.foo = something` for private `attr_accessor` _was_ allowed). It turned out "allow literal `self.`" makes things much clearer. * **Discussion:** Feature #11297, Feature #16123 -* **Documentation:** []() +* **Documentation:** [doc/syntax/modules_and_classes.rdoc#Visibility](https://docs.ruby-lang.org/en/master/syntax/modules_and_classes_rdoc.html#label-Visibility) * **Code:** ```ruby class A @@ -249,6 +287,31 @@ Calling a private method with a literal `self` as the receiver is now allowed. end ``` +### Refinements in `#method`/`#instance_method`[](#refinements-in-methodinstance_method) + + +* **Discussion:** Feature #15373 +* **Code:** + ```ruby + module StringExt + refine String do + def wrap(what) + before, after = self.split('|', 2) + "#{before}#{what}#{after}" + end + end + end + + using StringExt + + '<<|>>'.method(:wrap) + # => #)#wrap(what) refinement_test.rb:3> + + %w[test me please].map(&'<<|>>'.method(:wrap)) + # 2.6: undefined method `wrap' for class `String' + # 2.7: => ["<>", "<>", "<>"] + ``` + ## Core classes and modules[](#core-classes-and-modules) ### Better `Method#inspect`[](#better-methodinspect) @@ -316,15 +379,51 @@ Binds `UnboundMethod` to a receiver and calls it. Semantically equiavalent to `u * **Discussion:** Feature #15955 * **Documentation:** UnboundMethod#bind_call -### `Module#const_source_location`[](#moduleconst_source_location) +### `Module`[](#module) + +#### `#const_source_location`[](#const_source_location) + +Returns the location of the _first_ definition of the specified constant. * **Discussion:** Feature #10771 -* **Documentation:** []() +* **Documentation:** Module#const_source_location * **Code:** -* **Follow-up:** -* **Notes:** + ```ruby + # Assuming test.rb: + class A + C1 = 1 + end + + module M + C2 = 2 + end + + class B < A + include M + C3 = 3 + end + + class A # continuation of A definition + end + + p B.const_source_location('C3') # => ["test.rb", 11] + p B.const_source_location('C2') # => ["test.rb", 6] + p B.const_source_location('C1') # => ["test.rb", 2] + p B.const_source_location('C4') # => nil -- constant is not defined + + p B.const_source_location('C2', false) # => nil -- don't lookup in ancestors + + p Object.const_source_location('B') # => ["test.rb", 9] + p Object.const_source_location('A') # => ["test.rb", 1] -- note it is first entry, not "continuation" + + p B.const_source_location('A') # => ["test.rb", 1] -- because Object is in ancestors + p M.const_source_location('A') # => ["test.rb", 1] -- Object is not ancestor, but additionally checked for modules + + p Object.const_source_location('A::C1') # => ["test.rb", 2] -- nesting is supported + p Object.const_source_location('String') # => [] -- constant is defined in C code + ``` -### `Module#autoload?`: `inherit` argument[](#moduleautoload-inherit-argument) +#### `#autoload?`: `inherit` argument[](#autoload-inherit-argument) * **Reason:** More granular checking "if something is marked to be autoloaded directly or through ancestry chain" is necessary for advanced code reloaders (requested by author of the [zeitwerk](https://github.com/fxn/zeitwerk)). * **Discussion:** Feature #15777 @@ -464,10 +563,16 @@ Rounds Time's nanoseconds down or up to a specified number of digits (0 by defau * **Reason:** Rounding of nanoseconds important in a test code, when comparing `Time` instances from a different sources (stored in DB, passed through third-party libraries, etc.). Having better control on truncation comparing to `Time#round` (which existed since 1.9.2) * **Discussion:** Feature #15653 (floor, Japanese), Feature #15772 (ceil) -* **Documentation:** []() +* **Documentation:** Time#floor, Time#ceil * **Code:** -* **Follow-up:** -* **Notes:** + ```ruby + t = Time.utc(2019, 12, 24, 5, 43, 25.8765432r) + t.floor # => 2019-12-24 05:43:25 UTC + t.floor(2) # => 2019-12-24 05:43:25.87 UTC + + t.ceil # => 2019-12-24 05:43:26 UTC + t.ceil(2) # => 2019-12-24 05:43:25.88 UTC + ``` #### `#inspect` includes subseconds[](#inspect-includes-subseconds) @@ -476,12 +581,22 @@ Rounds Time's nanoseconds down or up to a specified number of digits (0 by defau * **Documentation:** Time#inspect * **Code:** ```ruby - t = Time.now + t = Time.utc(2019, 12, 24, 5, 43, 25.8765432r) p t - # Ruby 2.6: prints "2019-12-21 16:11:08 +0200" - # Ruby 2.7: prints "2019-12-21 16:11:08.189273595 +0200" + # Ruby 2.6: prints "2019-12-24 05:43:25 UTC" + # Ruby 2.7: prints "2019-12-24 05:43:25.8765432 UTC" puts t - # prints "2019-12-21 16:11:08 +0200" + # always prints "2019-12-24 05:43:25 UTC" + + # Note that sometimes representation falls back to Rational fractions: + t2 = Time.utc(2019,12,31, 23,59,59) + 1.4 + p t2 + # => 2020-01-01 00:00:00 900719925474099/2251799813685248 UTC + # That's when subseconds can't be represented as 9-digit whole number: + (t.subsec * 10**9).to_f + # => 876543200.0 + (t2.subsec * 10**9).to_f + # => 399999999.99999994 ``` ### Enumerables and collections[](#enumerables-and-collections) @@ -652,6 +767,24 @@ Like `Array#union` and `#difference`, [added](2.6.html#TODO) in 2.6 as a explici ['Ruby', 'Python', 'Perl'].intersection # => ["Ruby", "Python", "Perl"] ``` +#### `ObjectSpace::WeakMap#[]=` now accepts non-GC-able objects[](#objectspaceweakmap-now-accepts-non-gc-able-objects) + +* **Reason:** `ObjectSpace::WeakMap` (the `Hash` variety that doesn't hold its contents from being garbage-collected) is mostly thought, as the name implies, as an "internal" thing. But turns out it can have some legitimate usages in a regular code, for example, implementing flexible caching (cache which "auto-cleans" on garbage collection). But before this change, keys for `WeekMap` wasn't allowed to be non-GC-able (for example, numbers and symbols), which prohibits some interesting usages. +* **Discussion:** Feature #16035 +* **Documentation:** [WeekMap#[]=](https://ruby-doc.org/core-2.7.0.preview3/ObjectSpace/WeakMap.html#method-i-5B-5D-3D) +* **Code:** + ```ruby + map = ObjectSpace::WeakMap.new + map[1] = Object.new + # Ruby 2.6: ArgumentError (cannot define finalizer for Integer) + # Ruby 2.7: Writes the map successfully + map[1] + # => # + GC.start + map[1] + # => nil -- value successfully collected, even if key was not GC-able + ``` + ### `Fiber#raise`[](#fiberraise) Raises exception inside the resumed Fiber. @@ -675,7 +808,7 @@ Raises exception inside the resumed Fiber. ### `Range`[](#range) -#### `Range#===` for `String`[](#range-for-string) +#### `#===` for `String`[](#for-string) In 2.6, `Range#===` was changed to use `#cover?` underneath, but not for `String`. This was fixed. @@ -692,13 +825,35 @@ In 2.6, `Range#===` was changed to use `#cover?` underneath, but not for `String # => "nope :(" in 2.6, "matches" in 2.7 ``` +#### `#minmax` implementation change[](#minmax-implementation-change) + +`Range#minmax` switched to returning `Range#end` instead of iterating through Range to get maximum value. + +* **Reason:** `Range#minmax` was previously implemented in `Enumerable`, giving some inconsistencies with separate `#min` and `#max` in edge cases like: + ```ruby + (1..).max #=> RangeError (cannot get the maximum of endless range) + (1..).minmax #=> Runs forever, trying to iterate while it is not exhausted + ("a".."aa").max #=> "aa" + ("a".."aa").minmax #=> ["a","z"] -- iteration through range goes till "aa", but then "aa" < "z", so "z" is maximum + ``` +* **Discussion:** Feature #15807 +* **Documentation:** Range#minmax +* **Code:** + ```ruby + (1..).minmax # => RangeError (cannot get the maximum of endless range) + (1..Float::INFINITY).minmax # => [1, Infinity] + ("a".."aa").minmax # => ["a", "aa"] + ``` + +* **Note:** As can be seen in String example, sometimes `#minmax` (as well as `#max`) can yield unexpected result (the value which is in fact _not_ the maximum of all Range contents). This is true for value types with ambiguous order definition ("zz" is _between_ "a" and "aa" in enumeration, yet still _larger_ than "aa"). + ### Filesystem and IO[](#filesystem-and-io) #### `IO#set_encoding_by_bom`[](#ioset_encoding_by_bom) Auto-sets encoding to UTF-8 if byte-order mark is present in the stream. -* **Discussion:** [Feature #]15210(https://bugs.ruby-lang.org/issues/15210) +* **Discussion:** Feature #15210 * **Documentation:** IO#set_encoding_by_bom * **Code:** ```ruby @@ -740,13 +895,25 @@ Auto-sets encoding to UTF-8 if byte-order mark is present in the stream. Dir.glob(["*.rb", "*.md"]) # => ["2.5.md", "History.md", "README.md" ... ``` - * File.extname now returns a dot string at a name ending with a dot. [Bug #15267] + +#### `File.extname` returns a `"."` string at a name ending with a dot.[](#fileextname-returns-a--string-at-a-name-ending-with-a-dot) + +* **Reason:** It is argued that `File.basename(str, '.*') + File.extname(str)` should always reconstruct the full name, but it was not the case for names like `image.` +* **Discussion:** Bug #15267 +* **Documentation:** [File.extname](https://bugs.ruby-lang.org/issues/15267) +* **Code:** + ```ruby + filename = "image." + [File.basename(filename, ".*"), File.extname(filename)] + # 2.6: => ["image", ""] -- the dot is lost + # 2.7: => ["image", "."] + ``` ### Exceptions[](#exceptions) #### `FrozenError`: receiver argument[](#frozenerror-receiver-argument) -In 2.6 several exception class constructrs [were enhanced](TODO) so the user code could create them providing context, now `FrozenError` also got this functionality. +In 2.6 several exception class constructors [were enhanced](TODO) so the user code could create them providing context, now `FrozenError` also got this functionality. * **Discussion:** Feature #15751 * **Documentation:** FrozenError#new @@ -766,103 +933,190 @@ In 2.6 several exception class constructrs [were enhanced](TODO) so the user cod ### Interpreter internals[](#interpreter-internals) -* +RubyVM.resolve_feature_path+ moved to - $LOAD_PATH.resolve_feature_path. [Feature #15903] [Feature #15230] +#### `$LOAD_PATH.resolve_feature_path`[](#load_pathresolve_feature_path) + +`resolve_feature_path` was [introduced](TODO) in Ruby 2.6 as `RubyVM` method, now it is singleton method of `$LOAD_PATH` global variable. + +* **Reason:** It was argued that `RubyVM` should contain code specific for particular Ruby implementation (e.g. it can be different between CRuby/JRuby/TruffleRuby/etc.), while `resolve_feature_path` is a generic feature that should behave consistently between Ruby implementations. +* **Discussion:** Feature #15903 +* **Documentation:** _As it turns, documenting global's singleton method is not easy. So it is just mentioned at_ [doc/globals.rdoc](https://docs.ruby-lang.org/en/master/globals_rdoc.html) +* **Code:** + ```ruby + $LOAD_PATH.resolve_feature_path('net/http') + # => [:rb, "/home/zverok/.rvm/rubies/ruby-head/lib/ruby/2.7.0/net/http.rb"] + ``` +* **Notes:** As method was just moved, for details of `resolve_feature_path` behavior, see [2.6 changelog's entry](TODO) (with the exception for what described in the next section) + +#### `resolve_feature_path` behavior for loaded features fixed[](#resolve_feature_path-behavior-for-loaded-features-fixed) + +In 2.6, `resolve_feature_path` returned `false` instead of the path for already loaded libraries. That was fixed. + +* **Discussion:** Feature #15230 +* **Documentation:** — _(see above)_ +* **Code:** + ```ruby + # Ruby 2.6: + RubyVM.resolve_feature_path('net/http') + # => [:rb, "<...>/lib/ruby/2.6.0/net/http.rb"] + require 'net/http' + RubyVM.resolve_feature_path('net/http') + # => [:rb, false] + + # Ruby 2.7: + $LOAD_PATH.resolve_feature_path('net/http') + # => [:rb, "<...>/lib/ruby/2.7.0/net/http.rb"] + require 'net/http' + $LOAD_PATH.resolve_feature_path('net/http') + # => [:rb, "<...>/lib/ruby/2.7.0/net/http.rb"] + ``` #### `GC.compact`[](#gccompact) -* 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 friendly. [Feature #15626] - TODO https://bugs.ruby-lang.org/issues/16443 +Ruby 2.7 ships with an improved GC, which allows to manually defragment memory. -* ObjectSpace::WeakMap#[]= now accepts special objects as either key or - values. [Feature #16035] +* **Reason:** After some time of application running, creating objects and garbage collecting them, the memory becomes "defragmented": there are a large holes of unused memory between actual living objects. The new methods meant to be called between, say, spanning of the new processes/workers, potentially making current process using less memory. +* **Discussion:** Feature #15626 +* **Documentation:** [GC.compact](https://docs.ruby-lang.org/en/master/GC.html#method-c-compact) +* **Note:** This changelog author's understanding of GC and compacting is far from perfect, so the explanations are sparse. Unfortunately, the new feature is not thoroughly documented yet, so the best guess for understanding the change is reading "discussion" link above. The PRs (to the changelog and/or to the Ruby's main documentation) are welcome. -## Standard library[](#standard-library) +#### `Warning::[]` and `::[]=`[](#warning-and-) - * Date.jisx0301, Date#jisx0301, and Date.parse support the new Japanese - era. [Feature #15742] - * Object#DelegateClass accepts a block and module_evals it in the context - of the returned class, similar to Class.new and Struct.new. +Allows to emit/suppress separate categories of warnings. -IRB:: +* **Reason:** 2.7 introduced a lot of new deprecations (especially around keyword arguments, there can easily be thousands), and one "really experimental" feature (pattern matching), which emits warning about its experimental status on every use. To make working with older code, or experimenting with new features, less tiresome, the ability to turn warnings on and off per category was introduced. +* **Discussion:** Feature #16345 (deprecated warnings), Feature #16420 (experimental warnings) +* **Documentation:** [Warning::[]](https://docs.ruby-lang.org/en/master/Warning.html#method-c-5B-5D), [Warning::[]=](https://docs.ruby-lang.org/en/master/Warning.html#method-c-5B-5D-3D) +* **Code:** + ```ruby + {a: 1} in {a:} + # warning: Pattern matching is experimental, and the behavior may change in future versions of Ruby! + Warning[:experimental] = false + {a: 1} in {a:} + # ...no warning issued... - * Introduce syntax highlight inspired by pry.gem to Binding#irb source lines, - REPL input, and inspect output of some core-class objects. + def old_method(hash, **kwargs) + end - * Introduce multiline mode by Reline. + old_method(foo: 'bar') + # warning: Passing the keyword argument as the last hash parameter is deprecated + Warning[:deprecated] = false + old_method(foo: 'bar') + # ...no warning... + + # The current settings can be inspected: + Warning[:deprecated] # => false + # ...and changed back: + Warning[:deprecated] = true + old_method(foo: 'bar') + # warning: Passing the keyword argument as the last hash parameter is deprecated + ``` +* **Notes:** + * The only existing categories currently are `:deprecated` (covers all deprecations) and `:experimental` (as of 2.7, covers only pattern matching) + * Note that turning of `:deprecated` warning will also mute the warning of features which was deprecated explicitly in your code, for example with [Module#deprecate_constant](https://docs.ruby-lang.org/en/master/Module.html#method-i-deprecate_constant) + ```ruby + class HTTP + NOT_FOUND = Exception.new + deprecate_constant :NOT_FOUND + end + HTTP::NOT_FOUND + # warning: constant HTTP::NOT_FOUND is deprecated + Warning[:deprecated] = false + HTTP::NOT_FOUND + # ...no warning issued... + ``` + * Another way to turn on and off separate categories of warnings is passing `-W:(no-)` flag to ruby interpreter, e.g. `-W:no-experimental` means "no warnings when using experimental features". + +## Standard library[](#standard-library) - * Show documents when completion. +* `Date` supports [new Japanese era](https://en.wikipedia.org/wiki/Reiwa) in parsing and rendering dates (generic [Date.parse](https://docs.ruby-lang.org/en/master/Date.html#method-c-parse) and [.jisx0301](https://docs.ruby-lang.org/en/master/Date.html#method-c-jisx0301)/[#jisx0301](https://docs.ruby-lang.org/en/master/Date.html#method-i-jisx0301)). Discussion: Feature #15742 +* [DelegateClass()](https://docs.ruby-lang.org/en/master/Object.html#method-i-DelegateClass) accepts a block to define delegates behavior on-the-fly. +* Pathname.glob passes third argument, if provided, to `Dir.glob`, allowing to specify `base:` for globbing. +* Pathname() method doesn't duplicates argument, if it was already a `Pathname` +* OptionParser now uses "Did you mean?" feature. Discussion: Feature #16256. + ```ruby + require 'optparse' - * Enable auto indent and save/load history by default. -Reline:: + OptionParser.new do |opts| + opts.on('-t', '--task NAME') + end.parse!(%w[--tsak build]) + # OptionParser::InvalidOption (invalid option: --tsak) + # Did you mean? task + ``` - * New stdlib that is compatible with readline stdlib by pure Ruby and also - has a multiline mode. +### Large IRB update[](#large-irb-update) -* Net::HTTP Add ipaddr optional parameter to Net::HTTP#start to replace the address for - TCP/IP connection [Feature #5180] -* Add Net::FTP#features to check available features, and Net::FTP#option to - enable/disable each of them. [Feature #15964] -* Net::IMAP:: Add Server Name Indication (SNI) support. [Feature #15594] +IRB, Ruby's default console, received the hugest update in years. Now it supports multiline editing, syntax highlighting of input and (some of) output, auto-indentation and other modern console behavior. Small demonstration screenshot: -open-uri:: - * Warn open-uri's "open" method at Kernel. - Use URI.open instead. [Misc #15893] - * The default charset of text/* media type is UTF-8 instead of - ISO-8859-1. [Bug #15933] +### Network and web[](#network-and-web) -Pathname:: - * Delegates 3 arguments from Pathname.glob to Dir.glob to - accept base: keyword. - * 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. +* Net::HTTP#start: new optional keyword parameter `ipaddr:`, and #ipaddr= setter allows to set the address for the connection manually. Discussion: Feature #5180. +* `open-uri` library: 2 versions after more safe alias [was added](TODO), using `Kernel#open` finally became deprecated, and URI.open the main library's interface. Discussion: Misc #15893 + ```ruby + require 'open-uri' + open('https://ruby-lang.org') + # warning: calling URI.open via Kernel#open is deprecated, call URI.open directly or use URI#open + URI.open('https://ruby-lang.org') + # => ok + ``` +* Net::FTP#features to check available features, and Net::FTP#option to enable/disable each of them. Discussion: Feature #15964. + ```ruby + ftp = Net::FTP.new('speedtest.tele2.net') # TELE2's open FTP for speed tests + ftp.features + # => ["EPRT", "EPSV", "MDTM", "PASV", "REST STREAM", "SIZE", "TVFS"] + ``` +* `Net::IMAP` now supports [Server Name Indication (SNI)](https://en.wikipedia.org/wiki/Server_Name_Indication) support. Discussion: Feature #15594 -OptionParser:: - * Now show "Did you mean?" for unknown option. [Feature #16256] +### Large updated libraries[](#large-updated-libraries) + * **Bundler** 2.1.2: [Changes](https://github.com/bundler/bundler/blob/v2.1.2/CHANGELOG.md#212-december-20-2019) + * **CSV** 3.1.2: [Changes](https://github.com/ruby/csv/blob/v3.1.2/NEWS.md#312---2019-10-12) + * **JSON** 2.3.0: [Changes](https://github.com/flori/json/blob/v2.3.0/CHANGES.md#2019-02-21-220) _(lacks 2.3.0)_ + * **Racc** 1.4.15: _(no changelog available)_ + * **REXML** 3.2.3: [Changes](https://github.com/ruby/rexml/blob/v3.2.3/NEWS.md#323---2019-10-12-version-3-2-3) + * **RSS** 0.2.8: [Changes](https://github.com/ruby/rss/blob/v0.2.8/NEWS.md) + * **RubyGems** 3.1.2: [Changes](https://github.com/rubygems/rubygems/blob/v3.1.2/History.txt#L4) + * **StringScanner** 1.0.3: [Changes](https://github.com/ruby/strscan/blob/v1.0.3/NEWS.md#103---2019-10-14) ### Standard library contents change[](#standard-library-contents-change) -* 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) -profile.rb, Profiler__:: - * Removed from standard library. No one maintains it from Ruby 2.0.0. - -* Promote stdlib to default gems - * The following default gems was published at rubygems.org - * benchmark - * cgi - * delegate - * getoptlong - * net-pop - * net-smtp - * open3 - * pstore - * singleton - * The following default gems only promoted ruby-core, Not yet published at rubygems.org. - * monitor - * observer - * timeout - * tracer - * uri - * yaml +#### New libraries[](#new-libraries) -### Large updated libraries[](#large-updated-libraries) +* [Reline](https://github.com/ruby/reline) is a newly introduced readline-compatible pure Ruby line editing library. It is behind the new IRB's magic. + +#### Libraries promoted to default gems[](#libraries-promoted-to-default-gems) + +> **[stdgems.org](https://stdgems.org/)** project has a nice explanations of default and bundled gems concepts, as well as a list of currently gemified libraries. + +"For the rest of us" this means libraries development extracted into separate GitHub repositories, and they are just packaged with main Ruby before release. It means you can do issue/PR to any of them independently, without going through more tough development process of the core Ruby. + +Libraries extracted in 2.7: + +* Published at rubygems.org + * [benchmark](https://github.com/ruby/benchmark) + * [cgi](https://github.com/ruby/cgi) + * [delegate](https://github.com/ruby/delegate) + * [getoptlong](https://github.com/ruby/getoptlong) + * [net-pop](https://github.com/ruby/net-pop) + * [net-smtp](https://github.com/ruby/net-smtp) + * [open3](https://github.com/ruby/open3) + * [pstore](https://github.com/ruby/pstore) + * [singleton](https://github.com/ruby/singleton) +* Extracted to default gems, but not published on rubygems.org yet: + * [monitor](https://github.com/ruby/monitor) + * [observer](https://github.com/ruby/observer) + * [timeout](https://github.com/ruby/timeout) + * [tracer](https://github.com/ruby/tracer) + * [uri](https://github.com/ruby/uri) + * [yaml](https://github.com/ruby/yaml) + +#### Libraries excluded from the standard library[](#libraries-excluded-from-the-standard-library) + +Unsupported and lesser used libraries removed from the standard library, and now can be installed as a separate gems. + +* [CMath](https://github.com/ruby/cmath) +* [Scanf](https://github.com/ruby/scanf) +* [Shell](https://github.com/ruby/shell) +* [Synchronizer](https://github.com/ruby/sync) +* [ThreadsWait](https://github.com/ruby/thwait) +* [profile.rb](https://github.com/ruby/profile) aka `Profiler__`: removed completely. No one maintains it since Ruby 2.0.0 (note it exists on GitHub and even has `.gemspec` there but is **not** published to rubygems.org) -large updated - * Upgrade to Bundler 2.1.0.pre.3 - * Upgrade to CSV 3.1.2 - * Merge Racc 1.4.15 from upstream repository and added cli of racc. - * Upgrade REXML to 3.2.3. - * Upgrade to RSS 0.2.8. - * Upgrade to RubyGems 3.1.0.pre3 - * Upgrade to StringScanner 1.0.3. diff --git a/Rakefile b/Rakefile index d6e7c8f..ddb8a7f 100644 --- a/Rakefile +++ b/Rakefile @@ -8,6 +8,7 @@ require_relative '_src/lib/toc' require_relative '_src/lib/render' file '_data/book.yml' => FileList['_src/*.md'] do |t| + puts "Rerendering TOC" chapters = TOC.(t.prerequisites) File.write(t.name, Util.deep_stringify_keys(chapters: chapters).to_yaml) end diff --git a/_data/book.yml b/_data/book.yml index 046de6b..3c5570c 100644 --- a/_data/book.yml +++ b/_data/book.yml @@ -395,7 +395,20 @@ chapters: description: |

Highlights:

-

Ruby 2.7 is a last big release before 3.0

+

Ruby 2.7 is a last major release before 3.0¹, so it introduces several important changes, larger in scale than previous releases (and also a bit lean on a “just nice to have” features side). Be prepared!

+ +
    +
  • Pattern matching
  • +
  • “Real” keyword argument
  • +
  • Numbered block parameters
  • +
  • Beginless range
  • +
  • Enumerator.produce
  • +
  • GC.compact
  • +
  • Large update of IRB
  • +
  • Serious cleanup of the standard library
  • +
+ +

¹There is a possibility 2.8 will also be released, but in any case, Christmas release of 2020 is promised to be Ruby 3.0.

Read more »

children: @@ -412,8 +425,8 @@ chapters: path: "/2.7.html#numbered-block-parameters" - title: Beginless range path: "/2.7.html#beginless-range" - - title: Syntax changes - path: "/2.7.html#syntax-changes" + - title: Other syntax changes + path: "/2.7.html#other-syntax-changes" - title: Warnings/deprecations path: "/2.7.html#warningsdeprecations" children: @@ -421,6 +434,8 @@ chapters: path: "/2.7.html#safe-and-taint-concepts-are-deprecated-in-general" - title: "self.<private_method>" path: "/2.7.html#selfprivate_method" + - title: Refinements in #method/#instance_method + path: "/2.7.html#refinements-in-methodinstance_method" - title: Core classes and modules path: "/2.7.html#core-classes-and-modules" children: @@ -428,10 +443,13 @@ chapters: path: "/2.7.html#better-methodinspect" - title: "UnboundMethod#bind_call" path: "/2.7.html#unboundmethodbind_call" - - title: "Module#const_source_location" - path: "/2.7.html#moduleconst_source_location" - - title: "Module#autoload?: inherit argument" - path: "/2.7.html#moduleautoload-inherit-argument" + - title: "Module" + path: "/2.7.html#module" + children: + - title: "#const_source_location" + path: "/2.7.html#const_source_location" + - title: "#autoload?: inherit argument" + path: "/2.7.html#autoload-inherit-argument" - title: "Comparable#clamp with Range" path: "/2.7.html#comparableclamp-with-range" - title: "Integer[] with range" @@ -467,13 +485,17 @@ chapters: path: "/2.7.html#enumeratoryielderto_proc" - title: "Array#intersection" path: "/2.7.html#arrayintersection" + - title: "ObjectSpace::WeakMap#[]= now accepts non-GC-able objects" + path: "/2.7.html#objectspaceweakmap-now-accepts-non-gc-able-objects" - title: "Fiber#raise" path: "/2.7.html#fiberraise" - title: "Range" path: "/2.7.html#range" children: - - title: "Range#=== for String" - path: "/2.7.html#range-for-string" + - title: "#=== for String" + path: "/2.7.html#for-string" + - title: "#minmax implementation change" + path: "/2.7.html#minmax-implementation-change" - title: Filesystem and IO path: "/2.7.html#filesystem-and-io" children: @@ -482,6 +504,9 @@ chapters: - title: "Dir.glob and Dir.[] not allow \\0-separated patterns" path: "/2.7.html#dirglob-and-dir-not-allow-0-separated-patterns" + - title: File.extname returns a "." string at a name + ending with a dot. + path: "/2.7.html#fileextname-returns-a--string-at-a-name-ending-with-a-dot" - title: Exceptions path: "/2.7.html#exceptions" children: @@ -490,15 +515,32 @@ chapters: - title: Interpreter internals path: "/2.7.html#interpreter-internals" children: + - title: "$LOAD_PATH.resolve_feature_path" + path: "/2.7.html#load_pathresolve_feature_path" + - title: "resolve_feature_path behavior for loaded features fixed" + path: "/2.7.html#resolve_feature_path-behavior-for-loaded-features-fixed" - title: "GC.compact" path: "/2.7.html#gccompact" + - title: "Warning::[] and ::[]=" + path: "/2.7.html#warning-and-" - title: Standard library path: "/2.7.html#standard-library" children: - - title: Standard library contents change - path: "/2.7.html#standard-library-contents-change" + - title: Large IRB update + path: "/2.7.html#large-irb-update" + - title: Network and web + path: "/2.7.html#network-and-web" - title: Large updated libraries path: "/2.7.html#large-updated-libraries" + - title: Standard library contents change + path: "/2.7.html#standard-library-contents-change" + children: + - title: New libraries + path: "/2.7.html#new-libraries" + - title: Libraries promoted to default gems + path: "/2.7.html#libraries-promoted-to-default-gems" + - title: Libraries excluded from the standard library + path: "/2.7.html#libraries-excluded-from-the-standard-library" - title: History (of this site) path: "/History.html" - title: Contributing diff --git a/_src/2.6.md b/_src/2.6.md index cc09202..9c006fe 100644 --- a/_src/2.6.md +++ b/_src/2.6.md @@ -1,6 +1,6 @@ --- title: Ruby 2.6 changes -prev: / +prev: 2.7 next: 2.5 --- @@ -100,6 +100,7 @@ Refinements are now compatible with `#public_send` and `#respond_to?`, and impli 'foo'.public_send(:surround, '|') # => "|foo|" in 2.6, NoMethodError in 2.5 (1..3).map(&'%02i') # => ["01", "02", "03"] in 2.6; wrong argument type String (expected Proc) in 2.5 ``` +* **Follow-up:** Ruby 2.7 also made refinements available in [`#method`](TODO) ### Misc @@ -567,6 +568,7 @@ For string `name`, returns what path `require(name)` will load (without actually RubyVM.resolve_feature_path('faraday') # => [:rb, "<...>/gems/faraday-0.15.4/lib/faraday.rb"] ``` +* **Follow-up:** In Ruby 2.7, `resolve_feature_path` was [moved](TODO) to a `$LOAD_PATH` singleton method, and its behavior for already loaded pathes was [fixed](TODO) to return path nevertheless. #### `RubyVM::AbstractSyntaxTree` @@ -707,4 +709,6 @@ Libraries extracted in 2.6: * [shell](https://github.com/ruby/shell) * [sync](https://github.com/ruby/sync) * [thwait](https://github.com/ruby/thwait) -* [tracer](https://github.com/ruby/tracer) \ No newline at end of file +* [tracer](https://github.com/ruby/tracer) + +**Follow-up:** [16 more libraries](2.7.html#libraries-promoted-to-default-gems) gemified in 2.7, and [6 just dropped](2.7.html#libraries-excluded-from-the-standard-library) from the standard library. diff --git a/_src/2.7.md b/_src/2.7.md index dd941dd..68436c4 100644 --- a/_src/2.7.md +++ b/_src/2.7.md @@ -13,21 +13,45 @@ next: 2.6 ## Highlights -Ruby 2.7 is a last big release before 3.0 +Ruby 2.7 is a last major release before 3.0¹, so it introduces several important changes, larger in scale than previous releases (and also a bit lean on a "just nice to have" features side). Be prepared! +* Pattern matching +* "Real" keyword argument +* Numbered block parameters +* Beginless range +* `Enumerator.produce` +* `GC.compact` +* Large update of IRB +* Serious cleanup of the standard library + +¹There is a possibility 2.8 will also be released, but in any case, Christmas release of 2020 is promised to be Ruby 3.0. ## Language ### Pattern matching -### Keyword argument-related changes +Pattern matching is a completely new and experimental feature for structural value checking against patterns, and local variable binding. As it is new and huge, we'll not try to cover the feature here and just send the reader to the official documentation. Unfortunately, the one was not merged before the 2.7 release, so the best we can do currently is to link to **[documentation candidate](https://github.com/zverok/ruby/blob/pattern-matching-docs/doc/syntax/pattern_matching.rdoc)** _(disclaimer: it is written by author of the current changelog)_. Just a small example: + +```ruby +require 'open-uri' +require 'json' + +data = URI.open('https://api.github.com/repos/ruby/ruby/pulls').read + .then { |body| JSON.parse(body, symbolize_names: true) } -Ruby 2.7 introduced a lot of changes towards more consistent keyword arguments processing. Fortunately, examples, justifications and relationships of those changes, as well as links to docs and discussions are published on [official site](https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/). +data in [{user: {login:}, title:, created_at:}, *] # match array of hashes, with deep matching inside first hash + +[login, title, created_at] # matched values bound to local variables +# => ["zverok", "Add pattern matching documentation", "2019-12-25T18:42:03Z"] +``` + +### Keyword argument-related changes -Therefore here we'll just list the changes for the sake of completeness of this changelog: +Ruby 2.7 introduced a lot of changes towards more consistent keyword arguments processing. Fortunately, the official Ruby site has a [full description of those changes](https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/), with examples, justifications and relationships of features with each other.
+Therefore here we'll just list the changes for the sake of completeness of this changelog. -* Warnings (would be errors in 3.0) for implicit coversion of the last argument-hash to keyword arguments, and vice versa; -* `Module#ruby2_keywords` method to mark method as not warning when the last argument is used as keyword one (to provide backward- and forward-compatible way of defining "delegating all" methods); +* Warnings (would be errors in 3.0) for implicit conversion of the last argument-hash to keyword arguments, and vice versa; +* `Module#ruby2_keywords` and `Proc#ruby2_keywords` methods to mark method and proc as not warning when the last argument is used as keyword one (to provide backward- and forward-compatible way of defining "delegating all" methods); * "Forward all arguments" syntax: `(...)` * Non-`Symbol` keys are allowed in keyword arguments unpacking; * `**nil` syntax in method definition to explicitly mark method that doesn't accept keywords (and can't be called with a hash without curly braces); @@ -38,8 +62,11 @@ Therefore here we'll just list the changes for the sake of completeness of this In block without explicitly specified parameters, variables `_1` through `_9` can be used to reference parameters. * **Reason:** It is one of the approaches to make short blocks DRY-er and easier to read. E.g. in `filenames.each { |f| File.read(f) }`, repetition of `f` and extra syntax needed for it can be considered an unnecessary verbosity, so `each { File.read(_1) }` could be now used instead. -* **Discussion:** _The feature was discussed for a long time, syntax and semantics was changed several times on the road to 2.7_ [Feature #4475](https://bugs.ruby-lang.org/issues/4475) (initial discussion, started 9 years ago and finished with accepting of `@0`-`@9`), [Misc #15723](https://bugs.ruby-lang.org/issues/15723) (change to `_0`-`_9`), [Feature #16178](https://bugs.ruby-lang.org/issues/16178) (dropping of `_0` and changing semantics of `_1`) -* **Documentation:** []() +* **Discussion:** _The feature was discussed for a long time, syntax and semantics was changed several times on the road to 2.7:_ + * [Feature #4475](https://bugs.ruby-lang.org/issues/4475) (initial discussion, started 9 years ago, and finished with accepting of `@0`—`@9`), + * [Misc #15723](https://bugs.ruby-lang.org/issues/15723) (change to `_0`—`_9`), + * [Bug #16178](https://bugs.ruby-lang.org/issues/16178) (dropping of `_0`, and changing semantics of `_1`) +* **Documentation:** [Proc#Numbered parameters](https://docs.rubocop.org/en/stable/cops_layout/) * **Code:** ```ruby # Simplest usage: @@ -87,7 +114,7 @@ In block without explicitly specified parameters, variables `_1` through `_9` ca ### Beginless range -In addition to endless range in Ruby 2.6 `(start..)`, Ruby 2.7 introduces beginless one: `(..end)`. +In addition to [endless range in Ruby 2.6](TODO): `(start..)`, Ruby 2.7 introduces beginless one: `(..end)`. * **Reason:** Array slicing (initial justification for endless ranges) turned out to be not the only usage for semi-open ranges. Another ones, like `case`/`grep`, DSLs and constants and [`Comparable#clamp`](TODO), can gain from the symmetricity of "range without end"/"range without beginning". * **Discussion:** [Feature #14799](https://bugs.ruby-lang.org/issues/14799) @@ -133,7 +160,7 @@ In addition to endless range in Ruby 2.6 `(start..)`, Ruby 2.7 introduces beginl end ``` -### Syntax changes +### Other syntax changes * Comments between `.foo` calls are now allowed: ```ruby @@ -173,6 +200,8 @@ In addition to endless range in Ruby 2.6 `(start..)`, Ruby 2.7 introduces beginl Some older or accidental features are deprecated on the road to 3.0 and currently produce warnings. +> Note that Ruby 2.7 also [introduced a way](TODO) to turn off only some categories of warnings, for example, only deprecation ones. + * `yield` in singleton class syntax (was inconsistent with local variables accessibility). Discussion: [Feature #15575](https://bugs.ruby-lang.org/issues/15575). ```ruby def foo @@ -199,6 +228,15 @@ Some older or accidental features are deprecated on the road to 3.0 and currentl # warning: $, is set to non-nil value "foo###bar" ``` +* Implicit block capturing in `proc` and `lambda`: + ```ruby + def foo + proc.call # here the block passed to method is implicitly captured by proc + end + foo { puts "Hello" } + # warning: Capturing the given block using Kernel#proc is deprecated; use `&block` instead + # still prints "Hello" + ``` * Contrastingly, the flip-flop syntax deprecation, [introduced](#TODO) in 2.6, **is reverted**. It turned out that for brevity in text-processing scrips (including one-liners run as `ruby -e "print "`) the feature, however esotheric it may seem, has a justified usage. * **Discussion:** [Feature #5400](https://bugs.ruby-lang.org/issues/5400#note-23). * **Documentation:** [doc/syntax/control_expressions.rdoc#Flip-Flop](https://ruby-doc.org/core-2.6.5/doc/syntax/control_expressions_rdoc.html#label-Flip-Flop) @@ -225,7 +263,7 @@ Calling a private method with a literal `self` as the receiver is now allowed. * **Reason:** "`anything.method` is always disallowed for private methods" seemed like a simple and unambiguous rule, but produced some ugly edge cases (for example, `self.foo = something` for private `attr_accessor` _was_ allowed). It turned out "allow literal `self.`" makes things much clearer. * **Discussion:** [Feature #11297](https://bugs.ruby-lang.org/issues/11297), [Feature #16123](https://bugs.ruby-lang.org/issues/16123) -* **Documentation:** []() +* **Documentation:** [doc/syntax/modules_and_classes.rdoc#Visibility](https://docs.ruby-lang.org/en/master/syntax/modules_and_classes_rdoc.html#label-Visibility) * **Code:** ```ruby class A @@ -249,6 +287,31 @@ Calling a private method with a literal `self` as the receiver is now allowed. end ``` +### Refinements in `#method`/`#instance_method` + + +* **Discussion:** [Feature #15373](https://bugs.ruby-lang.org/issues/15373) +* **Code:** + ```ruby + module StringExt + refine String do + def wrap(what) + before, after = self.split('|', 2) + "#{before}#{what}#{after}" + end + end + end + + using StringExt + + '<<|>>'.method(:wrap) + # => #)#wrap(what) refinement_test.rb:3> + + %w[test me please].map(&'<<|>>'.method(:wrap)) + # 2.6: undefined method `wrap' for class `String' + # 2.7: => ["<>", "<>", "<>"] + ``` + ## Core classes and modules ### Better `Method#inspect` @@ -316,15 +379,51 @@ Binds `UnboundMethod` to a receiver and calls it. Semantically equiavalent to `u * **Discussion:** [Feature #15955](https://bugs.ruby-lang.org/issues/15955) * **Documentation:** [UnboundMethod#bind_call](https://ruby-doc.org/core-2.7.0.preview3/UnboundMethod.html#method-i-bind_call) -### `Module#const_source_location` +### `Module` + +#### `#const_source_location` + +Returns the location of the _first_ definition of the specified constant. * **Discussion:** [Feature #10771](https://bugs.ruby-lang.org/issues/10771) -* **Documentation:** []() +* **Documentation:** [Module#const_source_location](https://ruby-doc.org/core-2.7.0.preview3/Module.html#method-i-const_source_location) * **Code:** -* **Follow-up:** -* **Notes:** + ```ruby + # Assuming test.rb: + class A + C1 = 1 + end + + module M + C2 = 2 + end + + class B < A + include M + C3 = 3 + end + + class A # continuation of A definition + end + + p B.const_source_location('C3') # => ["test.rb", 11] + p B.const_source_location('C2') # => ["test.rb", 6] + p B.const_source_location('C1') # => ["test.rb", 2] + p B.const_source_location('C4') # => nil -- constant is not defined + + p B.const_source_location('C2', false) # => nil -- don't lookup in ancestors + + p Object.const_source_location('B') # => ["test.rb", 9] + p Object.const_source_location('A') # => ["test.rb", 1] -- note it is first entry, not "continuation" + + p B.const_source_location('A') # => ["test.rb", 1] -- because Object is in ancestors + p M.const_source_location('A') # => ["test.rb", 1] -- Object is not ancestor, but additionally checked for modules + + p Object.const_source_location('A::C1') # => ["test.rb", 2] -- nesting is supported + p Object.const_source_location('String') # => [] -- constant is defined in C code + ``` -### `Module#autoload?`: `inherit` argument +#### `#autoload?`: `inherit` argument * **Reason:** More granular checking "if something is marked to be autoloaded directly or through ancestry chain" is necessary for advanced code reloaders (requested by author of the [zeitwerk](https://github.com/fxn/zeitwerk)). * **Discussion:** [Feature #15777](https://bugs.ruby-lang.org/issues/15777) @@ -464,10 +563,16 @@ Rounds Time's nanoseconds down or up to a specified number of digits (0 by defau * **Reason:** Rounding of nanoseconds important in a test code, when comparing `Time` instances from a different sources (stored in DB, passed through third-party libraries, etc.). Having better control on truncation comparing to `Time#round` (which existed since 1.9.2) * **Discussion:** [Feature #15653](https://bugs.ruby-lang.org/issues/15653) (floor, Japanese), [Feature #15772](https://bugs.ruby-lang.org/issues/15772) (ceil) -* **Documentation:** []() +* **Documentation:** [Time#floor](https://ruby-doc.org/core-2.7.0.preview3/Time.html#method-i-floor), [Time#ceil](https://ruby-doc.org/core-2.7.0.preview3/Time.html#method-i-ceil) * **Code:** -* **Follow-up:** -* **Notes:** + ```ruby + t = Time.utc(2019, 12, 24, 5, 43, 25.8765432r) + t.floor # => 2019-12-24 05:43:25 UTC + t.floor(2) # => 2019-12-24 05:43:25.87 UTC + + t.ceil # => 2019-12-24 05:43:26 UTC + t.ceil(2) # => 2019-12-24 05:43:25.88 UTC + ``` #### `#inspect` includes subseconds @@ -476,12 +581,22 @@ Rounds Time's nanoseconds down or up to a specified number of digits (0 by defau * **Documentation:** [Time#inspect](https://ruby-doc.org/core-2.7.0.preview3/Time.html#method-i-inspect) * **Code:** ```ruby - t = Time.now + t = Time.utc(2019, 12, 24, 5, 43, 25.8765432r) p t - # Ruby 2.6: prints "2019-12-21 16:11:08 +0200" - # Ruby 2.7: prints "2019-12-21 16:11:08.189273595 +0200" + # Ruby 2.6: prints "2019-12-24 05:43:25 UTC" + # Ruby 2.7: prints "2019-12-24 05:43:25.8765432 UTC" puts t - # prints "2019-12-21 16:11:08 +0200" + # always prints "2019-12-24 05:43:25 UTC" + + # Note that sometimes representation falls back to Rational fractions: + t2 = Time.utc(2019,12,31, 23,59,59) + 1.4 + p t2 + # => 2020-01-01 00:00:00 900719925474099/2251799813685248 UTC + # That's when subseconds can't be represented as 9-digit whole number: + (t.subsec * 10**9).to_f + # => 876543200.0 + (t2.subsec * 10**9).to_f + # => 399999999.99999994 ``` ### Enumerables and collections @@ -652,6 +767,24 @@ Like `Array#union` and `#difference`, [added](2.6.html#TODO) in 2.6 as a explici ['Ruby', 'Python', 'Perl'].intersection # => ["Ruby", "Python", "Perl"] ``` +#### `ObjectSpace::WeakMap#[]=` now accepts non-GC-able objects + +* **Reason:** `ObjectSpace::WeakMap` (the `Hash` variety that doesn't hold its contents from being garbage-collected) is mostly thought, as the name implies, as an "internal" thing. But turns out it can have some legitimate usages in a regular code, for example, implementing flexible caching (cache which "auto-cleans" on garbage collection). But before this change, keys for `WeekMap` wasn't allowed to be non-GC-able (for example, numbers and symbols), which prohibits some interesting usages. +* **Discussion:** [Feature #16035](https://bugs.ruby-lang.org/issues/16035) +* **Documentation:** [WeekMap#[]=](https://ruby-doc.org/core-2.7.0.preview3/ObjectSpace/WeakMap.html#method-i-5B-5D-3D) +* **Code:** + ```ruby + map = ObjectSpace::WeakMap.new + map[1] = Object.new + # Ruby 2.6: ArgumentError (cannot define finalizer for Integer) + # Ruby 2.7: Writes the map successfully + map[1] + # => # + GC.start + map[1] + # => nil -- value successfully collected, even if key was not GC-able + ``` + ### `Fiber#raise` Raises exception inside the resumed Fiber. @@ -675,7 +808,7 @@ Raises exception inside the resumed Fiber. ### `Range` -#### `Range#===` for `String` +#### `#===` for `String` In 2.6, `Range#===` was changed to use `#cover?` underneath, but not for `String`. This was fixed. @@ -692,13 +825,35 @@ In 2.6, `Range#===` was changed to use `#cover?` underneath, but not for `String # => "nope :(" in 2.6, "matches" in 2.7 ``` +#### `#minmax` implementation change + +`Range#minmax` switched to returning `Range#end` instead of iterating through Range to get maximum value. + +* **Reason:** `Range#minmax` was previously implemented in `Enumerable`, giving some inconsistencies with separate `#min` and `#max` in edge cases like: + ```ruby + (1..).max #=> RangeError (cannot get the maximum of endless range) + (1..).minmax #=> Runs forever, trying to iterate while it is not exhausted + ("a".."aa").max #=> "aa" + ("a".."aa").minmax #=> ["a","z"] -- iteration through range goes till "aa", but then "aa" < "z", so "z" is maximum + ``` +* **Discussion:** [Feature #15807](https://bugs.ruby-lang.org/issues/15807) +* **Documentation:** [Range#minmax](https://ruby-doc.org/core-2.7.0.preview3/Range.html#method-i-minmax) +* **Code:** + ```ruby + (1..).minmax # => RangeError (cannot get the maximum of endless range) + (1..Float::INFINITY).minmax # => [1, Infinity] + ("a".."aa").minmax # => ["a", "aa"] + ``` + +* **Note:** As can be seen in String example, sometimes `#minmax` (as well as `#max`) can yield unexpected result (the value which is in fact _not_ the maximum of all Range contents). This is true for value types with ambiguous order definition ("zz" is _between_ "a" and "aa" in enumeration, yet still _larger_ than "aa"). + ### Filesystem and IO #### `IO#set_encoding_by_bom` Auto-sets encoding to UTF-8 if byte-order mark is present in the stream. -* **Discussion:** [Feature #]15210(https://bugs.ruby-lang.org/issues/15210) +* **Discussion:** [Feature #15210](https://bugs.ruby-lang.org/issues/15210) * **Documentation:** [IO#set_encoding_by_bom](https://ruby-doc.org/core-2.7.0.preview3/IO.html#method-i-set_encoding_by_bom) * **Code:** ```ruby @@ -740,13 +895,25 @@ Auto-sets encoding to UTF-8 if byte-order mark is present in the stream. Dir.glob(["*.rb", "*.md"]) # => ["2.5.md", "History.md", "README.md" ... ``` - * File.extname now returns a dot string at a name ending with a dot. [Bug #15267] + +#### `File.extname` returns a `"."` string at a name ending with a dot. + +* **Reason:** It is argued that `File.basename(str, '.*') + File.extname(str)` should always reconstruct the full name, but it was not the case for names like `image.` +* **Discussion:** [Bug #15267](https://bugs.ruby-lang.org/issues/15267) +* **Documentation:** [File.extname](https://bugs.ruby-lang.org/issues/15267) +* **Code:** + ```ruby + filename = "image." + [File.basename(filename, ".*"), File.extname(filename)] + # 2.6: => ["image", ""] -- the dot is lost + # 2.7: => ["image", "."] + ``` ### Exceptions #### `FrozenError`: receiver argument -In 2.6 several exception class constructrs [were enhanced](TODO) so the user code could create them providing context, now `FrozenError` also got this functionality. +In 2.6 several exception class constructors [were enhanced](TODO) so the user code could create them providing context, now `FrozenError` also got this functionality. * **Discussion:** [Feature #15751](https://bugs.ruby-lang.org/issues/15751) * **Documentation:** [FrozenError#new](https://ruby-doc.org/core-2.7.0.preview3/FrozenError.html#method-c-new) @@ -766,103 +933,190 @@ In 2.6 several exception class constructrs [were enhanced](TODO) so the user cod ### Interpreter internals -* +RubyVM.resolve_feature_path+ moved to - $LOAD_PATH.resolve_feature_path. [Feature #15903] [Feature #15230] +#### `$LOAD_PATH.resolve_feature_path` + +`resolve_feature_path` was [introduced](TODO) in Ruby 2.6 as `RubyVM` method, now it is singleton method of `$LOAD_PATH` global variable. + +* **Reason:** It was argued that `RubyVM` should contain code specific for particular Ruby implementation (e.g. it can be different between CRuby/JRuby/TruffleRuby/etc.), while `resolve_feature_path` is a generic feature that should behave consistently between Ruby implementations. +* **Discussion:** [Feature #15903](https://bugs.ruby-lang.org/issues/15903) +* **Documentation:** _As it turns, documenting global's singleton method is not easy. So it is just mentioned at_ [doc/globals.rdoc](https://docs.ruby-lang.org/en/master/globals_rdoc.html) +* **Code:** + ```ruby + $LOAD_PATH.resolve_feature_path('net/http') + # => [:rb, "/home/zverok/.rvm/rubies/ruby-head/lib/ruby/2.7.0/net/http.rb"] + ``` +* **Notes:** As method was just moved, for details of `resolve_feature_path` behavior, see [2.6 changelog's entry](TODO) (with the exception for what described in the next section) + +#### `resolve_feature_path` behavior for loaded features fixed + +In 2.6, `resolve_feature_path` returned `false` instead of the path for already loaded libraries. That was fixed. + +* **Discussion:** [Feature #15230](https://bugs.ruby-lang.org/issues/15230) +* **Documentation:** — _(see above)_ +* **Code:** + ```ruby + # Ruby 2.6: + RubyVM.resolve_feature_path('net/http') + # => [:rb, "<...>/lib/ruby/2.6.0/net/http.rb"] + require 'net/http' + RubyVM.resolve_feature_path('net/http') + # => [:rb, false] + + # Ruby 2.7: + $LOAD_PATH.resolve_feature_path('net/http') + # => [:rb, "<...>/lib/ruby/2.7.0/net/http.rb"] + require 'net/http' + $LOAD_PATH.resolve_feature_path('net/http') + # => [:rb, "<...>/lib/ruby/2.7.0/net/http.rb"] + ``` #### `GC.compact` -* 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 friendly. [Feature #15626] - TODO https://bugs.ruby-lang.org/issues/16443 +Ruby 2.7 ships with an improved GC, which allows to manually defragment memory. -* ObjectSpace::WeakMap#[]= now accepts special objects as either key or - values. [Feature #16035] +* **Reason:** After some time of application running, creating objects and garbage collecting them, the memory becomes "defragmented": there are a large holes of unused memory between actual living objects. The new methods meant to be called between, say, spanning of the new processes/workers, potentially making current process using less memory. +* **Discussion:** [Feature #15626](https://bugs.ruby-lang.org/issues/15626) +* **Documentation:** [GC.compact](https://docs.ruby-lang.org/en/master/GC.html#method-c-compact) +* **Note:** This changelog author's understanding of GC and compacting is far from perfect, so the explanations are sparse. Unfortunately, the new feature is not thoroughly documented yet, so the best guess for understanding the change is reading "discussion" link above. The PRs (to the changelog and/or to the Ruby's main documentation) are welcome. -## Standard library +#### `Warning::[]` and `::[]=` - * Date.jisx0301, Date#jisx0301, and Date.parse support the new Japanese - era. [Feature #15742] - * Object#DelegateClass accepts a block and module_evals it in the context - of the returned class, similar to Class.new and Struct.new. +Allows to emit/suppress separate categories of warnings. -IRB:: +* **Reason:** 2.7 introduced a lot of new deprecations (especially around keyword arguments, there can easily be thousands), and one "really experimental" feature (pattern matching), which emits warning about its experimental status on every use. To make working with older code, or experimenting with new features, less tiresome, the ability to turn warnings on and off per category was introduced. +* **Discussion:** [Feature #16345](https://bugs.ruby-lang.org/issues/16345) (deprecated warnings), [Feature #16420](https://bugs.ruby-lang.org/issues/16420) (experimental warnings) +* **Documentation:** [Warning::[]](https://docs.ruby-lang.org/en/master/Warning.html#method-c-5B-5D), [Warning::[]=](https://docs.ruby-lang.org/en/master/Warning.html#method-c-5B-5D-3D) +* **Code:** + ```ruby + {a: 1} in {a:} + # warning: Pattern matching is experimental, and the behavior may change in future versions of Ruby! + Warning[:experimental] = false + {a: 1} in {a:} + # ...no warning issued... - * Introduce syntax highlight inspired by pry.gem to Binding#irb source lines, - REPL input, and inspect output of some core-class objects. + def old_method(hash, **kwargs) + end - * Introduce multiline mode by Reline. + old_method(foo: 'bar') + # warning: Passing the keyword argument as the last hash parameter is deprecated + Warning[:deprecated] = false + old_method(foo: 'bar') + # ...no warning... + + # The current settings can be inspected: + Warning[:deprecated] # => false + # ...and changed back: + Warning[:deprecated] = true + old_method(foo: 'bar') + # warning: Passing the keyword argument as the last hash parameter is deprecated + ``` +* **Notes:** + * The only existing categories currently are `:deprecated` (covers all deprecations) and `:experimental` (as of 2.7, covers only pattern matching) + * Note that turning of `:deprecated` warning will also mute the warning of features which was deprecated explicitly in your code, for example with [Module#deprecate_constant](https://docs.ruby-lang.org/en/master/Module.html#method-i-deprecate_constant) + ```ruby + class HTTP + NOT_FOUND = Exception.new + deprecate_constant :NOT_FOUND + end + HTTP::NOT_FOUND + # warning: constant HTTP::NOT_FOUND is deprecated + Warning[:deprecated] = false + HTTP::NOT_FOUND + # ...no warning issued... + ``` + * Another way to turn on and off separate categories of warnings is passing `-W:(no-)` flag to ruby interpreter, e.g. `-W:no-experimental` means "no warnings when using experimental features". + +## Standard library - * Show documents when completion. +* `Date` supports [new Japanese era](https://en.wikipedia.org/wiki/Reiwa) in parsing and rendering dates (generic [Date.parse](https://docs.ruby-lang.org/en/master/Date.html#method-c-parse) and [.jisx0301](https://docs.ruby-lang.org/en/master/Date.html#method-c-jisx0301)/[#jisx0301](https://docs.ruby-lang.org/en/master/Date.html#method-i-jisx0301)). Discussion: [Feature #15742](https://bugs.ruby-lang.org/issues/15742) +* [DelegateClass()](https://docs.ruby-lang.org/en/master/Object.html#method-i-DelegateClass) accepts a block to define delegates behavior on-the-fly. +* [Pathname.glob](https://ruby-doc.org/stdlib-2.7.0_rc1/libdoc/pathname/rdoc/Pathname.html#method-c-glob) passes third argument, if provided, to `Dir.glob`, allowing to specify `base:` for globbing. +* [Pathname()](https://ruby-doc.org/stdlib-2.7.0_rc1/libdoc/pathname/rdoc/Kernel.html#method-i-Pathname) method doesn't duplicates argument, if it was already a `Pathname` +* [OptionParser](https://ruby-doc.org/stdlib-2.7.0_rc1/libdoc/optparse/rdoc/OptionParser.html) now uses "Did you mean?" feature. Discussion: [Feature #16256](https://bugs.ruby-lang.org/issues/16256). + ```ruby + require 'optparse' - * Enable auto indent and save/load history by default. -Reline:: + OptionParser.new do |opts| + opts.on('-t', '--task NAME') + end.parse!(%w[--tsak build]) + # OptionParser::InvalidOption (invalid option: --tsak) + # Did you mean? task + ``` - * New stdlib that is compatible with readline stdlib by pure Ruby and also - has a multiline mode. +### Large IRB update -* Net::HTTP Add ipaddr optional parameter to Net::HTTP#start to replace the address for - TCP/IP connection [Feature #5180] -* Add Net::FTP#features to check available features, and Net::FTP#option to - enable/disable each of them. [Feature #15964] -* Net::IMAP:: Add Server Name Indication (SNI) support. [Feature #15594] +IRB, Ruby's default console, received the hugest update in years. Now it supports multiline editing, syntax highlighting of input and (some of) output, auto-indentation and other modern console behavior. Small demonstration screenshot: -open-uri:: - * Warn open-uri's "open" method at Kernel. - Use URI.open instead. [Misc #15893] - * The default charset of text/* media type is UTF-8 instead of - ISO-8859-1. [Bug #15933] +### Network and web -Pathname:: - * Delegates 3 arguments from Pathname.glob to Dir.glob to - accept base: keyword. - * 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. +* [Net::HTTP#start](https://ruby-doc.org/stdlib-2.7.0_rc1/libdoc/net/http/rdoc/Net/HTTP.html#method-c-start): new optional keyword parameter `ipaddr:`, and [#ipaddr=](https://ruby-doc.org/stdlib-2.7.0_rc1/libdoc/net/http/rdoc/Net/HTTP.html#method-i-ipaddr) setter allows to set the address for the connection manually. Discussion: [Feature #5180](https://bugs.ruby-lang.org/issues/5180). +* `open-uri` library: 2 versions after more safe alias [was added](TODO), using `Kernel#open` finally became deprecated, and [URI.open](https://ruby-doc.org/stdlib-2.7.0_rc1/libdoc/open-uri/rdoc/URI.html#method-c-open) the main library's interface. Discussion: [Misc #15893](https://bugs.ruby-lang.org/issues/15893) + ```ruby + require 'open-uri' + open('https://ruby-lang.org') + # warning: calling URI.open via Kernel#open is deprecated, call URI.open directly or use URI#open + URI.open('https://ruby-lang.org') + # => ok + ``` +* [Net::FTP#features](https://ruby-doc.org/stdlib-2.7.0_rc1/libdoc/net/ftp/rdoc/Net/FTP.html#method-i-features) to check available features, and [Net::FTP#option](https://ruby-doc.org/stdlib-2.7.0_rc1/libdoc/net/ftp/rdoc/Net/FTP.html#method-i-option) to enable/disable each of them. Discussion: [Feature #15964](https://bugs.ruby-lang.org/issues/15964). + ```ruby + ftp = Net::FTP.new('speedtest.tele2.net') # TELE2's open FTP for speed tests + ftp.features + # => ["EPRT", "EPSV", "MDTM", "PASV", "REST STREAM", "SIZE", "TVFS"] + ``` +* `Net::IMAP` now supports [Server Name Indication (SNI)](https://en.wikipedia.org/wiki/Server_Name_Indication) support. Discussion: [Feature #15594](https://bugs.ruby-lang.org/issues/15594) -OptionParser:: - * Now show "Did you mean?" for unknown option. [Feature #16256] +### Large updated libraries + * **Bundler** 2.1.2: [Changes](https://github.com/bundler/bundler/blob/v2.1.2/CHANGELOG.md#212-december-20-2019) + * **CSV** 3.1.2: [Changes](https://github.com/ruby/csv/blob/v3.1.2/NEWS.md#312---2019-10-12) + * **JSON** 2.3.0: [Changes](https://github.com/flori/json/blob/v2.3.0/CHANGES.md#2019-02-21-220) _(lacks 2.3.0)_ + * **Racc** 1.4.15: _(no changelog available)_ + * **REXML** 3.2.3: [Changes](https://github.com/ruby/rexml/blob/v3.2.3/NEWS.md#323---2019-10-12-version-3-2-3) + * **RSS** 0.2.8: [Changes](https://github.com/ruby/rss/blob/v0.2.8/NEWS.md) + * **RubyGems** 3.1.2: [Changes](https://github.com/rubygems/rubygems/blob/v3.1.2/History.txt#L4) + * **StringScanner** 1.0.3: [Changes](https://github.com/ruby/strscan/blob/v1.0.3/NEWS.md#103---2019-10-14) ### Standard library contents change -* 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) -profile.rb, Profiler__:: - * Removed from standard library. No one maintains it from Ruby 2.0.0. - -* Promote stdlib to default gems - * The following default gems was published at rubygems.org - * benchmark - * cgi - * delegate - * getoptlong - * net-pop - * net-smtp - * open3 - * pstore - * singleton - * The following default gems only promoted ruby-core, Not yet published at rubygems.org. - * monitor - * observer - * timeout - * tracer - * uri - * yaml +#### New libraries -### Large updated libraries +* [Reline](https://github.com/ruby/reline) is a newly introduced readline-compatible pure Ruby line editing library. It is behind the new IRB's magic. + +#### Libraries promoted to default gems + +> **[stdgems.org](https://stdgems.org/)** project has a nice explanations of default and bundled gems concepts, as well as a list of currently gemified libraries. + +"For the rest of us" this means libraries development extracted into separate GitHub repositories, and they are just packaged with main Ruby before release. It means you can do issue/PR to any of them independently, without going through more tough development process of the core Ruby. + +Libraries extracted in 2.7: + +* Published at rubygems.org + * [benchmark](https://github.com/ruby/benchmark) + * [cgi](https://github.com/ruby/cgi) + * [delegate](https://github.com/ruby/delegate) + * [getoptlong](https://github.com/ruby/getoptlong) + * [net-pop](https://github.com/ruby/net-pop) + * [net-smtp](https://github.com/ruby/net-smtp) + * [open3](https://github.com/ruby/open3) + * [pstore](https://github.com/ruby/pstore) + * [singleton](https://github.com/ruby/singleton) +* Extracted to default gems, but not published on rubygems.org yet: + * [monitor](https://github.com/ruby/monitor) + * [observer](https://github.com/ruby/observer) + * [timeout](https://github.com/ruby/timeout) + * [tracer](https://github.com/ruby/tracer) + * [uri](https://github.com/ruby/uri) + * [yaml](https://github.com/ruby/yaml) + +#### Libraries excluded from the standard library + +Unsupported and lesser used libraries removed from the standard library, and now can be installed as a separate gems. + +* [CMath](https://github.com/ruby/cmath) +* [Scanf](https://github.com/ruby/scanf) +* [Shell](https://github.com/ruby/shell) +* [Synchronizer](https://github.com/ruby/sync) +* [ThreadsWait](https://github.com/ruby/thwait) +* [profile.rb](https://github.com/ruby/profile) aka `Profiler__`: removed completely. No one maintains it since Ruby 2.0.0 (note it exists on GitHub and even has `.gemspec` there but is **not** published to rubygems.org) -large updated - * Upgrade to Bundler 2.1.0.pre.3 - * Upgrade to CSV 3.1.2 - * Merge Racc 1.4.15 from upstream repository and added cli of racc. - * Upgrade REXML to 3.2.3. - * Upgrade to RSS 0.2.8. - * Upgrade to RubyGems 3.1.0.pre3 - * Upgrade to StringScanner 1.0.3. diff --git a/_src/lib/render.rb b/_src/lib/render.rb index f23b335..fb76d6a 100644 --- a/_src/lib/render.rb +++ b/_src/lib/render.rb @@ -5,11 +5,11 @@ def call text .gsub('<>', File.mtime(path).strftime('%b %d, %Y')) .gsub(/\[(Bug|Feature|Misc) \#\d+\]\(.+?\)/, &method(:process_link)) - .gsub( + .gsub( # links to official docs to just nicer links (with icon) %r{\[([^\[\]]+ [^\[\]]+)\]\((https://ruby-doc\.org.+?)\)}, '\\1' ) - .gsub( + .gsub( # links without spaces are typically class/method names, so wrapped in %r{\[([^\[\]]+)\]\((https://ruby-doc\.org.+?)\)}, '\\1' ) From afe380210518ade980b8ee0a18d228f454925e82 Mon Sep 17 00:00:00 2001 From: zverok Date: Fri, 27 Dec 2019 13:05:52 +0200 Subject: [PATCH 3/6] Prepare to release! --- 2.4.md | 32 +- 2.5.md | 6 +- 2.6.md | 19 +- 2.7.md | 116 +++---- History.md | 6 + _data/book.yml | 736 ++++++++++++++++++++++----------------------- _src/2.4.md | 28 +- _src/2.5.md | 2 +- _src/2.6.md | 15 +- _src/2.7.md | 128 ++++---- _src/lib/render.rb | 5 +- _src/lib/toc.rb | 10 +- 12 files changed, 563 insertions(+), 540 deletions(-) diff --git a/2.4.md b/2.4.md index 41b7fc4..190f82d 100644 --- a/2.4.md +++ b/2.4.md @@ -15,9 +15,9 @@ prev: 2.5 --> * **Released at:** Dec 25, 2016 ([NEWS](https://github.com/ruby/ruby/blob/trunk/doc/NEWS-2.4.0) file) -* **Status (as of Dec 07, 2019):** EOL soon, latest is 2.4.9 +* **Status (as of Dec 27, 2019):** EOL soon, latest is 2.4.9 * **This document first published:** Oct 14, 2019 -* **Last change to this document:** Dec 07, 2019 +* **Last change to this document:** Dec 27, 2019 ## Highlights[](#highlights) @@ -172,19 +172,21 @@ New single-method module was introduced, meant to be overriden in order to contr # .warn called with: ": warning: already initialized constant X\n" # .warn called with: ": warning: previous definition of X was here\n" ``` -* **Follow-up:** Surprisingly as at may be, `Kernel#warn` haven't been changed to call `Warning.warn` in 2.4, but it was fixed in 2.5: - ```ruby - def Warning.warn(msg) - puts ".warn called with: #{msg.inspect}" - end +* **Follow-ups:** + * Surprisingly as at may be, `Kernel#warn` haven't been changed to call `Warning.warn` in 2.4, but it was [fixed in 2.5](2.5.html#warn-call-warningwarn): + ```ruby + def Warning.warn(msg) + puts ".warn called with: #{msg.inspect}" + end - warn 'foo', 'bar' - # Ruby 2.4 prints: - # foo - # bar - # Ruby 2.5 prints: - # .warn called with: "foo\nbar\n" - ``` + warn 'foo', 'bar' + # Ruby 2.4 prints: + # foo + # bar + # Ruby 2.5 prints: + # .warn called with: "foo\nbar\n" + ``` + * In Ruby 2.7, new methods [were added](2.7.html#warning-and-) to `Warning` module allowing control over per-category warning suppression ### `Object#clone(freeze: false)`[](#objectclonefreeze-false) @@ -226,7 +228,7 @@ Method to limit any comparable value to `min`—`max` range -123.clamp(0, 20) # => 0 18.clamp(0, 20) # => 18 ``` -* **Follow-up:** For Ruby 2.7, `clamp` with a range was [accepted](https://bugs.ruby-lang.org/issues/14784), which, especially when combined with endless (2.6) and beginless (2.7) ranges, will allow to use more powerful and idiomatic code: +* **Follow-up:** In Ruby 2.7, `clamp` also [allows](2.7.html#comparableclamp-with-range) passing range argument, which, especially when combined with [endless](2.6.html#endless-range-1) (2.6) and [beginless](2.7.html#beginless-range) (2.7) ranges, allows to use more powerful and idiomatic code: ```ruby 123.clamp(0..20) # => 20 123.clamp(..20) # => 20 diff --git a/2.5.md b/2.5.md index 42d4e1d..2c244bb 100644 --- a/2.5.md +++ b/2.5.md @@ -15,9 +15,9 @@ prev: 2.6 --> * **Released at:** Dec 25, 2017 ([NEWS](https://github.com/ruby/ruby/blob/trunk/doc/NEWS-2.5.0) file) -* **Status (as of Dec 07, 2019):** active, latest is 2.5.7 +* **Status (as of Dec 27, 2019):** active, latest is 2.5.7 * **This document first published:** Jun 6, 2019 -* **Last change to this document:** Dec 07, 2019 +* **Last change to this document:** Dec 27, 2019 ## Highlights[](#highlights) @@ -757,7 +757,7 @@ Much akin to `Hash#fetch`, allows to fetch thread-local variable or provide defa ### Network and Web[](#network-and-web) -* `open-uri`: `URI.open` method defined as an alias to open-uri's `Kernel#open`. open-uri's `Kernel#open` will be deprecated in future. Follow-up: Deprecation of `Kernel#open` and docs for URI.open were added in Ruby 2.7. +* `open-uri`: `URI.open` method defined as an alias to open-uri's `Kernel#open`. open-uri's `Kernel#open` will be deprecated in future. Follow-up: Deprecation of `Kernel#open` and docs for URI.open [were added](2.7.html#network-and-web) in Ruby 2.7. #### `Net::HTTP`[](#nethttp) diff --git a/2.6.md b/2.6.md index 5da0c11..7ca3b6d 100644 --- a/2.6.md +++ b/2.6.md @@ -7,9 +7,9 @@ next: 2.5 # Ruby 2.6 * **Released at:** Dec 25, 2018 ([NEWS](https://github.com/ruby/ruby/blob/ruby_2_6/NEWS) file) -* **Status (as of Dec 26, 2019):** 2.6.5 is current _stable_ +* **Status (as of Dec 27, 2019):** 2.6.5 is current _stable_ * **This document first published:** Dec 29, 2018 -* **Last change to this document:** Dec 26, 2019 +* **Last change to this document:** Dec 27, 2019 > **Note:** As already explained in [Introduction](README.md), this site is dedicated to changes in the **language**, not the **implementation**, therefore the list below lacks mentions of lots of important optimization introduced in 2.6, including the whole JIT big thing. That's not because they are not important, just because this site's goals are different. @@ -50,7 +50,7 @@ next: 2.5 (1..) == (1...) # => false (1..).size # => Infinity ``` -* **Follow-up:** "Beginless" range [was introduced](TODO) in 2.7. +* **Follow-up:** "Beginless" range [was introduced](2.7.html#beginless-range) in 2.7. ### Non-ASCII constant names[](#non-ascii-constant-names) @@ -100,12 +100,12 @@ Refinements are now compatible with `#public_send` and `#respond_to?`, and impli 'foo'.public_send(:surround, '|') # => "|foo|" in 2.6, NoMethodError in 2.5 (1..3).map(&'%02i') # => ["01", "02", "03"] in 2.6; wrong argument type String (expected Proc) in 2.5 ``` -* **Follow-up:** Ruby 2.7 also made refinements available in [`#method`](TODO) +* **Follow-up:** Ruby 2.7 also made refinements available in [`#method`](2.7.html#refinements-in-methodinstance_method) ### Misc[](#misc) * Infamous esoteric flip-flop syntax is deprecated finally: Feature #5400. - * **Follow-up:** Deprecation is [reverted](#TODO) in 2.7 + * **Follow-up:** Deprecation is [reverted](2.7.html#warningsdeprecations) in 2.7 ## Core classes and modules[](#core-classes-and-modules) @@ -172,6 +172,7 @@ With `exception: true`, the method raises instead of returning `false` on non-0 Array.method_defined?(:to_h, false) # => true -- despite inheriting from Enumerable, Array redefines it for performance ``` +* **Follow-up:** In 2.7, `inherit` argument was also [added to `autoload?`](2.7.html#autoload-inherit-argument) ### `String#split` with block[](#stringsplit-with-block) @@ -271,7 +272,7 @@ The concept of a "timezone object" is introduced for various `Time` methods. Rub # => [2] ``` * **Notice:** There are also plans (discussed in the same ticket above) to introduce mutating `union!` form. -* **Follow-up:** Ruby 2.7 [added](TODO) `#intersection` method. +* **Follow-up:** Ruby 2.7 [added](2.7.html#arrayintersection) `#intersection` method. ### `Hash#merge` with multiple arguments[](#hashmerge-with-multiple-arguments) @@ -387,7 +388,7 @@ Several enumerators can now be chained into one with `Enumerator#+(other)` or `E end ``` * **Notice:** For `String` ranges, behavior left unchanged, so example with versions above would NOT work with pure-`String` versions. -* **Follow-up:** `String` behavior was [fixed](TODO) in Ruby 2.7, so in 2.7 this code prints "yes": +* **Follow-up:** `String` behavior was [fixed](2.7.html#for-string) in Ruby 2.7, so in 2.7 this code prints "yes": ```ruby case '2.5' when '1.8.7'..'2.6' @@ -442,7 +443,7 @@ Several enumerators can now be chained into one with `Enumerator#+(other)` or `E ```ruby raise KeyError, "don't have this: #{key}", receiver: self, key: key ``` -* **Follow-up:** Ruby 2.7 [adds](TODO) `receiver:` argument for `FrozenError`, too. +* **Follow-up:** Ruby 2.7 [adds](2.7.html#frozenerror-receiver-argument) `receiver:` argument for `FrozenError`, too. #### `Exception#full_message` options[](#exceptionfull_message-options) @@ -568,7 +569,7 @@ For string `name`, returns what path `require(name)` will load (without actually RubyVM.resolve_feature_path('faraday') # => [:rb, "<...>/gems/faraday-0.15.4/lib/faraday.rb"] ``` -* **Follow-up:** In Ruby 2.7, `resolve_feature_path` was [moved](TODO) to a `$LOAD_PATH` singleton method, and its behavior for already loaded pathes was [fixed](TODO) to return path nevertheless. +* **Follow-up:** In Ruby 2.7, `resolve_feature_path` was [moved](2.7.html#load_pathresolve_feature_path) to a `$LOAD_PATH` singleton method, and its behavior for already loaded pathes was [fixed](2.7.html#resolve_feature_path-behavior-for-loaded-features-fixed) to return path nevertheless. #### `RubyVM::AbstractSyntaxTree`[](#rubyvmabstractsyntaxtree) diff --git a/2.7.md b/2.7.md index 21aa0a5..5bff964 100644 --- a/2.7.md +++ b/2.7.md @@ -6,23 +6,25 @@ next: 2.6 # Ruby 2.7 -* **Released at:** Dec 25, 2019 ([NEWS](TODO) file) -* **Status (as of Dec 27, 2019):** TODO -* **This document first published:** TODO +* **Released at:** Dec 25, 2019 ([NEWS](https://github.com/ruby/ruby/blob/ruby_2_7/NEWS) file) +* **Status (as of Dec 27, 2019):** 2.7.0 is current _stable_ +* **This document first published:** Dec 27, 2019 * **Last change to this document:** Dec 27, 2019 + + ## Highlights[](#highlights) Ruby 2.7 is a last major release before 3.0¹, so it introduces several important changes, larger in scale than previous releases (and also a bit lean on a "just nice to have" features side). Be prepared! -* Pattern matching -* "Real" keyword argument -* Numbered block parameters -* Beginless range -* `Enumerator.produce` -* `GC.compact` -* Large update of IRB -* Serious cleanup of the standard library +* [Pattern matching](#pattern-matching) +* ["Real" keyword argument](#pattern-matching) +* [Numbered block parameters](#numbered-block-parameters) +* [Beginless range](#beginless-range) +* [`Enumerator.produce`](#enumeratorproduce) +* [`GC.compact`](#gccompact) +* [Large update of IRB](#large-irb-update) +* [Serious cleanup of the standard library](#standard-library-contents-change) ¹There is a possibility 2.8 will also be released, but in any case, Christmas release of 2020 is promised to be Ruby 3.0. @@ -114,11 +116,11 @@ In block without explicitly specified parameters, variables `_1` through `_9` ca ### Beginless range[](#beginless-range) -In addition to [endless range in Ruby 2.6](TODO): `(start..)`, Ruby 2.7 introduces beginless one: `(..end)`. +In addition to [endless range in Ruby 2.6](2.6.html#endless-range-1): `(start..)`, Ruby 2.7 introduces beginless one: `(..end)`. -* **Reason:** Array slicing (initial justification for endless ranges) turned out to be not the only usage for semi-open ranges. Another ones, like `case`/`grep`, DSLs and constants and [`Comparable#clamp`](TODO), can gain from the symmetricity of "range without end"/"range without beginning". +* **Reason:** Array slicing (initial justification for endless ranges) turned out to be not the only usage for semi-open ranges. Another ones, like `case`/`grep`, DSLs and constants and [`Comparable#clamp`](#comparableclamp-with-range), can gain from the symmetry of "range without end"/"range without beginning". * **Discussion:** Feature #14799 -* **Documentation:** doc/syntax/literals.rdoc#Ranges +* **Documentation:** doc/syntax/literals.rdoc#Ranges * **Code:** ```ruby # Usage examples @@ -200,7 +202,7 @@ In addition to [endless range in Ruby 2.6](TODO): `(start..)`, Ruby 2.7 introduc Some older or accidental features are deprecated on the road to 3.0 and currently produce warnings. -> Note that Ruby 2.7 also [introduced a way](TODO) to turn off only some categories of warnings, for example, only deprecation ones. +> Note that Ruby 2.7 also [introduced a way](#warning-and-) to turn off only some categories of warnings, for example, only deprecation ones. * `yield` in singleton class syntax (was inconsistent with local variables accessibility). Discussion: Feature #15575. ```ruby @@ -237,7 +239,7 @@ Some older or accidental features are deprecated on the road to 3.0 and currentl # warning: Capturing the given block using Kernel#proc is deprecated; use `&block` instead # still prints "Hello" ``` -* Contrastingly, the flip-flop syntax deprecation, [introduced](#TODO) in 2.6, **is reverted**. It turned out that for brevity in text-processing scrips (including one-liners run as `ruby -e "print "`) the feature, however esotheric it may seem, has a justified usage. +* Contrastingly, the flip-flop syntax deprecation, [introduced](2.6.html#misc) in 2.6, **is reverted**. It turned out that for brevity in text-processing scrips (including one-liners run as `ruby -e "print "`) the feature, however esotheric it may seem, has a justified usage. * **Discussion:** Feature #5400. * **Documentation:** doc/syntax/control_expressions.rdoc#Flip-Flop * **Code:** @@ -263,7 +265,7 @@ Calling a private method with a literal `self` as the receiver is now allowed. * **Reason:** "`anything.method` is always disallowed for private methods" seemed like a simple and unambiguous rule, but produced some ugly edge cases (for example, `self.foo = something` for private `attr_accessor` _was_ allowed). It turned out "allow literal `self.`" makes things much clearer. * **Discussion:** Feature #11297, Feature #16123 -* **Documentation:** [doc/syntax/modules_and_classes.rdoc#Visibility](https://docs.ruby-lang.org/en/master/syntax/modules_and_classes_rdoc.html#label-Visibility) +* **Documentation:** doc/syntax/modules_and_classes.rdoc#Visibility * **Code:** ```ruby class A @@ -320,7 +322,7 @@ Calling a private method with a literal `self` as the receiver is now allowed. * **Reason:** As there are more code style approaches that operate `Method` objects, making them better identifiable and informative seemed necessary. * **Discussion:** Feature #14145 -* **Documentation:** Method#inspect +* **Documentation:** Method#inspect * **Code:** ```ruby p CSV.method(:read) @@ -377,7 +379,7 @@ Binds `UnboundMethod` to a receiver and calls it. Semantically equiavalent to `u ``` In such cases (for example, code reloaders), overhead of producing new `Method` object on each `bind().call` is pretty significant, and `bind_call` allows to avoid it. * **Discussion:** Feature #15955 -* **Documentation:** UnboundMethod#bind_call +* **Documentation:** UnboundMethod#bind_call ### `Module`[](#module) @@ -386,7 +388,7 @@ Binds `UnboundMethod` to a receiver and calls it. Semantically equiavalent to `u Returns the location of the _first_ definition of the specified constant. * **Discussion:** Feature #10771 -* **Documentation:** Module#const_source_location +* **Documentation:** Module#const_source_location * **Code:** ```ruby # Assuming test.rb: @@ -427,7 +429,7 @@ Returns the location of the _first_ definition of the specified constant. * **Reason:** More granular checking "if something is marked to be autoloaded directly or through ancestry chain" is necessary for advanced code reloaders (requested by author of the [zeitwerk](https://github.com/fxn/zeitwerk)). * **Discussion:** Feature #15777 -* **Documentation:** Module#autoload? +* **Documentation:** Module#autoload? * **Code:** ```ruby class Parent @@ -454,7 +456,7 @@ Returns the location of the _first_ definition of the specified constant. * **Reason:** First, ranges can be seen as more "natural" to specify _a range_ of acceptable values. Second, with introduction of beginless and endless ranges, `#clamp` now can be used for one-sided value limitation, too. * **Discussion:** Feature #14784 -* **Documentation:** Comparable#clamp +* **Documentation:** Comparable#clamp * **Code:** ```ruby 123.clamp(0..100) # => 100 @@ -478,7 +480,7 @@ Returns the location of the _first_ definition of the specified constant. Allows to get several bits at once. * **Discussion:** Feature #8842 -* **Documentation:** [Integer#[]](https://ruby-doc.org/core-2.7.0.preview3/Integer.html#method-i-5B-5D) +* **Documentation:** [Integer#[]](https://ruby-doc.org/core-2.7.0/Integer.html#method-i-5B-5D) * **Code:** ```ruby # 4---0 @@ -494,7 +496,7 @@ Allows to get several bits at once. * **Reason:** Method `#<=>` was explicitly undefined in `Complex` to underline the fact that linear order of complex numbers can't be established, but it was inconsistent with most of the other objects implementations (which return `nil` for impossible/incompatible comparison instead of rasing) * **Discussion:** [Feature #](https://bugs.ruby-lang.org/issues/) -* **Documentation:** Complex#<=> +* **Documentation:** Complex#<=> * **Code:** ```ruby 1 + 2i <=> 1 # => nil @@ -513,7 +515,7 @@ Several core methods now return frozen, deduplicated `String` instead of generat * **Reason:** Avoiding allocations of new strings for each `#to_s` of primitive objects can save dramatical amounts of memory. * **Discussion:** Feature #16150 -* **Affected methods:** NilClass#to_s, TrueClass#to_s, FalseClass#to_s, Module#name +* **Affected methods:** NilClass#to_s, TrueClass#to_s, FalseClass#to_s, Module#name * **Code:** ```ruby # Ruby 2.6 @@ -540,9 +542,9 @@ Several core methods now return frozen, deduplicated `String` instead of generat #### `Symbol#start_with?` and `#end_with?`[](#symbolstart_with-and-end_with) -* **Reason:** Symbol was once thought as "just immutable names" with any of "string-y" operations not making sense for them, but as `Symbol#match` was always present, and `Symbol#match?` [implemented in 2.4](TODO), it turns out that other content inspection methods are also useful. +* **Reason:** Symbol was once thought as "just immutable names" with any of "string-y" operations not making sense for them, but as `Symbol#match` was always present, and `Symbol#match?` [implemented in 2.4](2.4.html#match-method), it turns out that other content inspection methods are also useful. * **Discussion:** Feature #16348 -* **Documentation:** Symbol#end_with?, Symbol#start_with? +* **Documentation:** Symbol#end_with?, Symbol#start_with? * **Code:** ```ruby :table_name.end_with?('name', 'value') @@ -563,7 +565,7 @@ Rounds Time's nanoseconds down or up to a specified number of digits (0 by defau * **Reason:** Rounding of nanoseconds important in a test code, when comparing `Time` instances from a different sources (stored in DB, passed through third-party libraries, etc.). Having better control on truncation comparing to `Time#round` (which existed since 1.9.2) * **Discussion:** Feature #15653 (floor, Japanese), Feature #15772 (ceil) -* **Documentation:** Time#floor, Time#ceil +* **Documentation:** Time#floor, Time#ceil * **Code:** ```ruby t = Time.utc(2019, 12, 24, 5, 43, 25.8765432r) @@ -578,7 +580,7 @@ Rounds Time's nanoseconds down or up to a specified number of digits (0 by defau * **Reason:** Losing subseconds in `#inspect` always made debugging and testing harder, producing test failures like "Expected: 2019-12-21 16:11:08 +0200, got 2019-12-21 16:11:08 +0200" (which are visually the same, but one of them, probably going through some serialization or DB storage, has different value for subseconds). * **Discussion:** Feature #15958 -* **Documentation:** Time#inspect +* **Documentation:** Time#inspect * **Code:** ```ruby t = Time.utc(2019, 12, 24, 5, 43, 25.8765432r) @@ -607,7 +609,7 @@ Transforms elements of enumerable with provided block, and drops falsy results, * **Reason:** Filter suitable elements, then process them somehow is a common flow of sequence processing, yet with `filter { ... }.map { ... }` additional intermediate `Array` is produced, which is not always desirable. Also, some processing can indicate "can't be processed" by returning `false` or `nil`, which requires code like `map { ... }.compact` (to drop `nil`s) or `map { ... }.select(:itself)` (to drop all falsy values). * **Discussion:** Feature #15323 -* **Documentation:** Enumerable#filter_map +* **Documentation:** Enumerable#filter_map * **Code:** ```ruby (1..10).filter_map { |i| i**2 if i.even? } # => [4, 16, 36, 64, 100] @@ -627,7 +629,7 @@ Transforms elements of enumerable with provided block, and drops falsy results, Counts unique objects in the enumerable and returns hash of `{object => count}`. * **Discussion:** Feature #11076 -* **Documentation:** Enumerable#tally +* **Documentation:** Enumerable#tally * **Code:** ```ruby %w[Ruby Python Ruby Perl Python Ruby].tally @@ -697,7 +699,7 @@ Converts lazy enumerator back to eager one. * **Reason:** When working with large data sequences, lazy enumerators are useful tools to not produce intermediate array on each step. But some data consuming methods expect to receive enumeratos that would really return data from methods like `take_while` and not just add them to pipeline. * **Discussion:** Feature #15901 -* **Documentation:** Enumerator::Lazy#eager +* **Documentation:** Enumerator::Lazy#eager * **Code:** ```ruby # Imagine we read very large data file: @@ -734,7 +736,7 @@ Converts lazy enumerator back to eager one. * **Reason:** When constructing the enumerator, value yielding is frequently delegated to other methods, which accept blocks. Before the change, it was inconvenient to delegate. * **Discussion:** Feature #15618 -* **Documentation:** Enumerator::Yielder#to_proc +* **Documentation:** Enumerator::Yielder#to_proc * **Code:** ```ruby # Construct a enumerator which will pass all lines from all files from some folder: @@ -756,10 +758,10 @@ Converts lazy enumerator back to eager one. #### `Array#intersection`[](#arrayintersection) -Like `Array#union` and `#difference`, [added](2.6.html#TODO) in 2.6 as a explicitly-named and accepting multiple arguments alternatives for `#|` and `#-`, the new method is alternative for `#&`. +Like `Array#union` and `#difference`, [added](2.6.html#arrayunion-and-arraydifference) in 2.6 as a explicitly-named and accepting multiple arguments alternatives for `#|` and `#-`, the new method is alternative for `#&`. * **Discussion:** Feature #16155 -* **Documentation:** Array#intersection +* **Documentation:** Array#intersection * **Code:** ```ruby ['Ruby', 'Python', 'Perl'].intersection(['Ruby', 'Diamond', 'Perl'], ['Ruby', 'Nikole', 'Kate']) # => ["Ruby"] @@ -771,7 +773,7 @@ Like `Array#union` and `#difference`, [added](2.6.html#TODO) in 2.6 as a explici * **Reason:** `ObjectSpace::WeakMap` (the `Hash` variety that doesn't hold its contents from being garbage-collected) is mostly thought, as the name implies, as an "internal" thing. But turns out it can have some legitimate usages in a regular code, for example, implementing flexible caching (cache which "auto-cleans" on garbage collection). But before this change, keys for `WeekMap` wasn't allowed to be non-GC-able (for example, numbers and symbols), which prohibits some interesting usages. * **Discussion:** Feature #16035 -* **Documentation:** [WeekMap#[]=](https://ruby-doc.org/core-2.7.0.preview3/ObjectSpace/WeakMap.html#method-i-5B-5D-3D) +* **Documentation:** [WeekMap#[]=](https://ruby-doc.org/core-2.7.0/ObjectSpace/WeakMap.html#method-i-5B-5D-3D) * **Code:** ```ruby map = ObjectSpace::WeakMap.new @@ -791,7 +793,7 @@ Raises exception inside the resumed Fiber. * **Reason:** Ability to raise an exception inside Fiber makes control passing abilities more feature-complete. * **Discussion:** Feature #10344 -* **Documentation:** Fiber#raise +* **Documentation:** Fiber#raise * **Code:** ```ruby f = Fiber.new { @@ -813,7 +815,7 @@ Raises exception inside the resumed Fiber. In 2.6, `Range#===` was changed to use `#cover?` underneath, but not for `String`. This was fixed. * **Discussion:** Bug #15449 -* **Documentation:** Range#=== +* **Documentation:** Range#=== * **Code:** ```ruby case '2.6.5' @@ -837,7 +839,7 @@ In 2.6, `Range#===` was changed to use `#cover?` underneath, but not for `String ("a".."aa").minmax #=> ["a","z"] -- iteration through range goes till "aa", but then "aa" < "z", so "z" is maximum ``` * **Discussion:** Feature #15807 -* **Documentation:** Range#minmax +* **Documentation:** Range#minmax * **Code:** ```ruby (1..).minmax # => RangeError (cannot get the maximum of endless range) @@ -854,7 +856,7 @@ In 2.6, `Range#===` was changed to use `#cover?` underneath, but not for `String Auto-sets encoding to UTF-8 if byte-order mark is present in the stream. * **Discussion:** Feature #15210 -* **Documentation:** IO#set_encoding_by_bom +* **Documentation:** IO#set_encoding_by_bom * **Code:** ```ruby File.write("tmp/bom.txt", "\u{FEFF}мама") @@ -881,7 +883,7 @@ Auto-sets encoding to UTF-8 if byte-order mark is present in the stream. #### `Dir.glob` and `Dir.[]` not allow `\0`-separated patterns[](#dirglob-and-dir-not-allow-0-separated-patterns) * **Discussion:** Feature #14643 -* **Documentation:** Dir.glob +* **Documentation:** Dir.glob * **Code:** ```ruby Dir.glob("*.rb\0*.md") @@ -913,10 +915,10 @@ Auto-sets encoding to UTF-8 if byte-order mark is present in the stream. #### `FrozenError`: receiver argument[](#frozenerror-receiver-argument) -In 2.6 several exception class constructors [were enhanced](TODO) so the user code could create them providing context, now `FrozenError` also got this functionality. +In 2.6 several exception class constructors [were enhanced](2.6.html#new-arguments-receiver-and-key) so the user code could create them providing context, now `FrozenError` also got this functionality. * **Discussion:** Feature #15751 -* **Documentation:** FrozenError#new +* **Documentation:** FrozenError#new * **Code:** ```ruby class AlwaysFrozenHash < Hash @@ -935,17 +937,17 @@ In 2.6 several exception class constructors [were enhanced](TODO) so the user co #### `$LOAD_PATH.resolve_feature_path`[](#load_pathresolve_feature_path) -`resolve_feature_path` was [introduced](TODO) in Ruby 2.6 as `RubyVM` method, now it is singleton method of `$LOAD_PATH` global variable. +`resolve_feature_path` was [introduced](2.6.html#rubyvmresolve_feature_path) in Ruby 2.6 as `RubyVM` method, now it is singleton method of `$LOAD_PATH` global variable. * **Reason:** It was argued that `RubyVM` should contain code specific for particular Ruby implementation (e.g. it can be different between CRuby/JRuby/TruffleRuby/etc.), while `resolve_feature_path` is a generic feature that should behave consistently between Ruby implementations. * **Discussion:** Feature #15903 -* **Documentation:** _As it turns, documenting global's singleton method is not easy. So it is just mentioned at_ [doc/globals.rdoc](https://docs.ruby-lang.org/en/master/globals_rdoc.html) +* **Documentation:** _As it turns, documenting global's singleton method is not easy. So it is just mentioned at_ doc/globals.rdoc * **Code:** ```ruby $LOAD_PATH.resolve_feature_path('net/http') # => [:rb, "/home/zverok/.rvm/rubies/ruby-head/lib/ruby/2.7.0/net/http.rb"] ``` -* **Notes:** As method was just moved, for details of `resolve_feature_path` behavior, see [2.6 changelog's entry](TODO) (with the exception for what described in the next section) +* **Notes:** As method was just moved, for details of `resolve_feature_path` behavior, see [2.6 changelog's entry](2.6.html#rubyvmresolve_feature_path) (with the exception for what described in the next section) #### `resolve_feature_path` behavior for loaded features fixed[](#resolve_feature_path-behavior-for-loaded-features-fixed) @@ -976,7 +978,7 @@ Ruby 2.7 ships with an improved GC, which allows to manually defragment memory. * **Reason:** After some time of application running, creating objects and garbage collecting them, the memory becomes "defragmented": there are a large holes of unused memory between actual living objects. The new methods meant to be called between, say, spanning of the new processes/workers, potentially making current process using less memory. * **Discussion:** Feature #15626 -* **Documentation:** [GC.compact](https://docs.ruby-lang.org/en/master/GC.html#method-c-compact) +* **Documentation:** GC.compact * **Note:** This changelog author's understanding of GC and compacting is far from perfect, so the explanations are sparse. Unfortunately, the new feature is not thoroughly documented yet, so the best guess for understanding the change is reading "discussion" link above. The PRs (to the changelog and/or to the Ruby's main documentation) are welcome. #### `Warning::[]` and `::[]=`[](#warning-and-) @@ -985,7 +987,7 @@ Allows to emit/suppress separate categories of warnings. * **Reason:** 2.7 introduced a lot of new deprecations (especially around keyword arguments, there can easily be thousands), and one "really experimental" feature (pattern matching), which emits warning about its experimental status on every use. To make working with older code, or experimenting with new features, less tiresome, the ability to turn warnings on and off per category was introduced. * **Discussion:** Feature #16345 (deprecated warnings), Feature #16420 (experimental warnings) -* **Documentation:** [Warning::[]](https://docs.ruby-lang.org/en/master/Warning.html#method-c-5B-5D), [Warning::[]=](https://docs.ruby-lang.org/en/master/Warning.html#method-c-5B-5D-3D) +* **Documentation:** [Warning::[]](https://ruby-doc.org/core-2.7.0/Warning.html#method-c-5B-5D), [Warning::[]=](https://ruby-doc.org/core-2.7.0/Warning.html#method-c-5B-5D-3D) * **Code:** ```ruby {a: 1} in {a:} @@ -1012,7 +1014,7 @@ Allows to emit/suppress separate categories of warnings. ``` * **Notes:** * The only existing categories currently are `:deprecated` (covers all deprecations) and `:experimental` (as of 2.7, covers only pattern matching) - * Note that turning of `:deprecated` warning will also mute the warning of features which was deprecated explicitly in your code, for example with [Module#deprecate_constant](https://docs.ruby-lang.org/en/master/Module.html#method-i-deprecate_constant) + * Note that turning of `:deprecated` warning will also mute the warning of features which was deprecated explicitly in your code, for example with Module#deprecate_constant ```ruby class HTTP NOT_FOUND = Exception.new @@ -1028,11 +1030,11 @@ Allows to emit/suppress separate categories of warnings. ## Standard library[](#standard-library) -* `Date` supports [new Japanese era](https://en.wikipedia.org/wiki/Reiwa) in parsing and rendering dates (generic [Date.parse](https://docs.ruby-lang.org/en/master/Date.html#method-c-parse) and [.jisx0301](https://docs.ruby-lang.org/en/master/Date.html#method-c-jisx0301)/[#jisx0301](https://docs.ruby-lang.org/en/master/Date.html#method-i-jisx0301)). Discussion: Feature #15742 -* [DelegateClass()](https://docs.ruby-lang.org/en/master/Object.html#method-i-DelegateClass) accepts a block to define delegates behavior on-the-fly. -* Pathname.glob passes third argument, if provided, to `Dir.glob`, allowing to specify `base:` for globbing. -* Pathname() method doesn't duplicates argument, if it was already a `Pathname` -* OptionParser now uses "Did you mean?" feature. Discussion: Feature #16256. +* `Date` supports [new Japanese era](https://en.wikipedia.org/wiki/Reiwa) in parsing and rendering dates (generic Date.parse and .jisx0301/#jisx0301). Discussion: Feature #15742 +* DelegateClass() accepts a block to define delegates behavior on-the-fly. +* Pathname.glob passes third argument, if provided, to `Dir.glob`, allowing to specify `base:` for globbing. +* Pathname() method doesn't duplicates argument, if it was already a `Pathname` +* OptionParser now uses "Did you mean?" feature. Discussion: Feature #16256. ```ruby require 'optparse' @@ -1049,8 +1051,8 @@ IRB, Ruby's default console, received the hugest update in years. Now it support ### Network and web[](#network-and-web) -* Net::HTTP#start: new optional keyword parameter `ipaddr:`, and #ipaddr= setter allows to set the address for the connection manually. Discussion: Feature #5180. -* `open-uri` library: 2 versions after more safe alias [was added](TODO), using `Kernel#open` finally became deprecated, and URI.open the main library's interface. Discussion: Misc #15893 +* Net::HTTP#start: new optional keyword parameter `ipaddr:`, and #ipaddr= setter allows to set the address for the connection manually. Discussion: Feature #5180. +* `open-uri` library: 2 versions after more safe alias [was added](2.5.html#network-and-web), using `Kernel#open` finally became deprecated, and URI.open the main library's interface. Discussion: Misc #15893 ```ruby require 'open-uri' open('https://ruby-lang.org') @@ -1058,7 +1060,7 @@ IRB, Ruby's default console, received the hugest update in years. Now it support URI.open('https://ruby-lang.org') # => ok ``` -* Net::FTP#features to check available features, and Net::FTP#option to enable/disable each of them. Discussion: Feature #15964. +* Net::FTP#features to check available features, and Net::FTP#option to enable/disable each of them. Discussion: Feature #15964. ```ruby ftp = Net::FTP.new('speedtest.tele2.net') # TELE2's open FTP for speed tests ftp.features diff --git a/History.md b/History.md index b867337..e4d526b 100644 --- a/History.md +++ b/History.md @@ -2,6 +2,12 @@ (Only outstanding content changes listed, the texts and code are constantly updated and fixed thanks to awesome contributors.) +## 2019-12-27 + +* 2.7 changelog added! +* In 2.7 changelog, links to docs _sometimes_ switched to "official" docs.ruby-lang.org website. Most of the time, we are linking to ruby-doc.org, which, while being unofficial, has two advantages: a) it looks a bit nicer and b) it doesn't mix what is in core and what came from standard libraries. Unfortunately, as of release date of 2.7, ruby-doc.org's rendering is unable to properly handle new core methods implemented in `.rb` files (compare GC docs on [official site](https://docs.ruby-lang.org/en/master/GC.html), and on [ruby-doc.org](https://ruby-doc.org/core-2.7.0/GC.html)) + + ## 2019-10-14 * 2.4 changelog added; diff --git a/_data/book.yml b/_data/book.yml index 3c5570c..9d3fe1b 100644 --- a/_data/book.yml +++ b/_data/book.yml @@ -2,150 +2,314 @@ chapters: - title: Introduction path: "/" -- title: Ruby 2.4 - path: "/2.4.html" +- title: Ruby 2.7 + path: "/2.7.html" is_version: true - published_at: Oct 14, 2019 + published_at: '2019-12-27' description: |

Highlights:

+

Ruby 2.7 is a last major release before 3.0¹, so it introduces several important changes, larger in scale than previous releases (and also a bit lean on a “just nice to have” features side). Be prepared!

+
    -
  • Toplevel return
  • -
  • Unification of Fixnum and Bignum into Integer
  • -
  • Full Unicode support for String case operations
  • -
  • Warning module for fine-grained warnings output control
  • -
  • Comparable#clamp
  • +
  • Pattern matching
  • +
  • “Real” keyword argument
  • +
  • Numbered block parameters
  • +
  • Beginless range
  • +
  • Enumerator.produce
  • +
  • GC.compact
  • +
  • Large update of IRB
  • +
  • Serious cleanup of the standard library
-

Read more »

+

¹There is a possibility 2.8 will also be released, but in any case, Christmas release of 2020 is promised to be Ruby 3.0.

+ +

Read more »

children: - title: Highlights - path: "/2.4.html#highlights" + path: "/2.7.html#highlights" - title: Language - path: "/2.4.html#language" - children: - - title: Multiple assignment allowed in conditional expression - path: "/2.4.html#multiple-assignment-allowed-in-conditional-expression" - - title: Toplevel return - path: "/2.4.html#toplevel-return" - - title: Refinements improvements - path: "/2.4.html#refinements-improvements" + path: "/2.7.html#language" children: - - title: Refinements are supported in Symbol#to_proc and send - path: "/2.4.html#refinements-are-supported-in-symbolto_proc-and-send" - - title: "refine can refine modules, too" - path: "/2.4.html#refine-can-refine-modules-too" - - title: "Module.used_modules" - path: "/2.4.html#moduleused_modules" - - title: Core - path: "/2.4.html#core" + - title: Pattern matching + path: "/2.7.html#pattern-matching" + - title: Keyword argument-related changes + path: "/2.7.html#keyword-argument-related-changes" + - title: Numbered block parameters + path: "/2.7.html#numbered-block-parameters" + - title: Beginless range + path: "/2.7.html#beginless-range" + - title: Other syntax changes + path: "/2.7.html#other-syntax-changes" + - title: Warnings/deprecations + path: "/2.7.html#warningsdeprecations" + children: + - title: "“Safe” and “taint” concepts are deprecated in general" + path: "/2.7.html#safe-and-taint-concepts-are-deprecated-in-general" + - title: "self.<private_method>" + path: "/2.7.html#selfprivate_method" + - title: Refinements in #method/#instance_method + path: "/2.7.html#refinements-in-methodinstance_method" + - title: Core classes and modules + path: "/2.7.html#core-classes-and-modules" children: - - title: "Warning module" - path: "/2.4.html#warning-module" - - title: "Object#clone(freeze: false)" - path: "/2.4.html#objectclonefreeze-false" - - title: "Comparable#clamp" - path: "/2.4.html#comparableclamp" - - title: Numerics - path: "/2.4.html#numerics" + - title: Better Method#inspect + path: "/2.7.html#better-methodinspect" + - title: "UnboundMethod#bind_call" + path: "/2.7.html#unboundmethodbind_call" + - title: "Module" + path: "/2.7.html#module" children: - - title: "Fixnum and Bignum are unified into Integer" - path: "/2.4.html#fixnum-and-bignum-are-unified-into-integer" - - title: "Numeric#finite? and #infinite?" - path: "/2.4.html#numericfinite-and-infinite" - - title: "Integer#digits" - path: "/2.4.html#integerdigits" - - title: "ndigits optional argument for rounding methods" - path: "/2.4.html#ndigits-optional-argument-for-rounding-methods" - - title: "half: option for #round method" - path: "/2.4.html#half-option-for-round-method" + - title: "#const_source_location" + path: "/2.7.html#const_source_location" + - title: "#autoload?: inherit argument" + path: "/2.7.html#autoload-inherit-argument" + - title: "Comparable#clamp with Range" + path: "/2.7.html#comparableclamp-with-range" + - title: "Integer[] with range" + path: "/2.7.html#integer-with-range" + - title: "Complex#<=>" + path: "/2.7.html#complex" - title: Strings, symbols and regexps - path: "/2.4.html#strings-symbols-and-regexps" + path: "/2.7.html#strings-symbols-and-regexps" children: - - title: Unicode case conversions - path: "/2.4.html#unicode-case-conversions" - - title: "String.new(capacity: size)" - path: "/2.4.html#stringnewcapacity-size" - - title: "#casecmp?" - path: "/2.4.html#casecmp" - - title: "String#concat and #prepend accept multiple - arguments" - path: "/2.4.html#stringconcat-and-prepend-accept-multiple-arguments" - - title: "String#unpack1" - path: "/2.4.html#stringunpack1" - - title: "#match? method" - path: "/2.4.html#match-method" - - title: "MatchData: better support for named captures" - path: "/2.4.html#matchdata-better-support-for-named-captures" - - title: Collections - path: "/2.4.html#collections" + - title: Core methods returning frozen strings + path: "/2.7.html#core-methods-returning-frozen-strings" + - title: "Symbol#start_with? and #end_with?" + path: "/2.7.html#symbolstart_with-and-end_with" + - title: "Time" + path: "/2.7.html#time" children: - - title: "Enumerable#chunk without a block returns an Enumerator" - path: "/2.4.html#enumerablechunk-without-a-block-returns-an-enumerator" - - title: "#sum" - path: "/2.4.html#sum" - - title: "#uniq" - path: "/2.4.html#uniq" - - title: "Array#max and #min" - path: "/2.4.html#arraymax-and-min" - - title: "Array#concat takes multiple arguments" - path: "/2.4.html#arrayconcat-takes-multiple-arguments" - - title: "Array#pack(buffer:)" - path: "/2.4.html#arraypackbuffer" - - title: "Hash#compact and #compact!" - path: "/2.4.html#hashcompact-and-compact" - - title: "Hash#transform_values and #transform_values!" - path: "/2.4.html#hashtransform_values-and-transform_values" + - title: "#floor and #ceil" + path: "/2.7.html#floor-and-ceil" + - title: "#inspect includes subseconds" + path: "/2.7.html#inspect-includes-subseconds" + - title: Enumerables and collections + path: "/2.7.html#enumerables-and-collections" + children: + - title: "Enumerable#filter_map" + path: "/2.7.html#enumerablefilter_map" + - title: "Enumerable#tally" + path: "/2.7.html#enumerabletally" + - title: "Enumerator.produce" + path: "/2.7.html#enumeratorproduce" + - title: "Enumerator::Lazy#eager" + path: "/2.7.html#enumeratorlazyeager" + - title: "Enumerator::Yielder#to_proc" + path: "/2.7.html#enumeratoryielderto_proc" + - title: "Array#intersection" + path: "/2.7.html#arrayintersection" + - title: "ObjectSpace::WeakMap#[]= now accepts non-GC-able objects" + path: "/2.7.html#objectspaceweakmap-now-accepts-non-gc-able-objects" + - title: "Fiber#raise" + path: "/2.7.html#fiberraise" + - title: "Range" + path: "/2.7.html#range" + children: + - title: "#=== for String" + path: "/2.7.html#for-string" + - title: "#minmax implementation change" + path: "/2.7.html#minmax-implementation-change" - title: Filesystem and IO - path: "/2.4.html#filesystem-and-io" + path: "/2.7.html#filesystem-and-io" children: - - title: "chomp: option for string splitting" - path: "/2.4.html#chomp-option-for-string-splitting" - - title: "#empty? method for filesystem objects" - path: "/2.4.html#empty-method-for-filesystem-objects" - - title: "Thread#report_on_exception and Thread.report_on_exception" - path: "/2.4.html#threadreport_on_exception-and-threadreport_on_exception" - - title: "TracePoint#callee_id" - path: "/2.4.html#tracepointcallee_id" - - title: Stdlib - path: "/2.4.html#stdlib" + - title: "IO#set_encoding_by_bom" + path: "/2.7.html#ioset_encoding_by_bom" + - title: "Dir.glob and Dir.[] not allow \\0-separated + patterns" + path: "/2.7.html#dirglob-and-dir-not-allow-0-separated-patterns" + - title: File.extname returns a "." string at a name + ending with a dot. + path: "/2.7.html#fileextname-returns-a--string-at-a-name-ending-with-a-dot" + - title: Exceptions + path: "/2.7.html#exceptions" + children: + - title: "FrozenError: receiver argument" + path: "/2.7.html#frozenerror-receiver-argument" + - title: Interpreter internals + path: "/2.7.html#interpreter-internals" + children: + - title: "$LOAD_PATH.resolve_feature_path" + path: "/2.7.html#load_pathresolve_feature_path" + - title: "resolve_feature_path behavior for loaded features fixed" + path: "/2.7.html#resolve_feature_path-behavior-for-loaded-features-fixed" + - title: "GC.compact" + path: "/2.7.html#gccompact" + - title: "Warning::[] and ::[]=" + path: "/2.7.html#warning-and-" + - title: Standard library + path: "/2.7.html#standard-library" children: - - title: Libraries promoted to default/bundled gems - path: "/2.4.html#libraries-promoted-to-defaultbundled-gems" -- title: Ruby 2.5 - path: "/2.5.html" + - title: Large IRB update + path: "/2.7.html#large-irb-update" + - title: Network and web + path: "/2.7.html#network-and-web" + - title: Large updated libraries + path: "/2.7.html#large-updated-libraries" + - title: Standard library contents change + path: "/2.7.html#standard-library-contents-change" + children: + - title: New libraries + path: "/2.7.html#new-libraries" + - title: Libraries promoted to default gems + path: "/2.7.html#libraries-promoted-to-default-gems" + - title: Libraries excluded from the standard library + path: "/2.7.html#libraries-excluded-from-the-standard-library" +- title: Ruby 2.6 + path: "/2.6.html" is_version: true - published_at: Jun 6, 2019 + published_at: '2018-12-29' description: |

Highlights:

    -
  • rescue/ensure inside blocks
  • -
  • #yield_self
  • -
  • Struct with keyword initialization
  • -
  • Exceptions output changed
  • +
  • Endless range
  • +
  • #then “piping” method
  • +
  • Support for timezones in Time
  • +
  • Proc composition
  • +
  • Enumerator chaining
  • +
  • RubyVM::AbstractSyntaxTree
-

Read more »

+

Read more »

children: - title: Highlights - path: "/2.5.html#highlights" + path: "/2.6.html#highlights" - title: Language - path: "/2.5.html#language" - children: - - title: Top-level constant look-up is removed. - path: "/2.5.html#top-level-constant-look-up-is-removed" - - title: "rescue/else/ensure are allowed - inside blocks" - path: "/2.5.html#rescueelseensure-are-allowed-inside-blocks" - - title: Refinements work in string interpolations - path: "/2.5.html#refinements-work-in-string-interpolations" - - title: Core classes and modules - path: "/2.5.html#core-classes-and-modules" + path: "/2.6.html#language" children: - - title: "Kernel" - path: "/2.5.html#kernel" - children: + - title: 'Endless range: (1..)' + path: "/2.6.html#endless-range-1" + - title: Non-ASCII constant names + path: "/2.6.html#non-ascii-constant-names" + - title: "else in exception-handling context" + path: "/2.6.html#else-in-exception-handling-context" + - title: 'Refinements: improved visibility' + path: "/2.6.html#refinements-improved-visibility" + - title: Misc + path: "/2.6.html#misc" + - title: Core classes and modules + path: "/2.6.html#core-classes-and-modules" + children: + - title: "Kernel" + path: "/2.6.html#kernel" + children: + - title: "#then as an alias for #yield_self" + path: "/2.6.html#then-as-an-alias-for-yield_self" + - title: "<Numeric>() methods have exception: + argument" + path: "/2.6.html#numeric-methods-have-exception-argument" + - title: "#system has exception: argument" + path: "/2.6.html#system-has-exception-argument" + - title: "Module#method_defined?: inherit argument" + path: "/2.6.html#modulemethod_defined-inherit-argument" + - title: "String#split with block" + path: "/2.6.html#stringsplit-with-block" + - title: "Time: support for timezones" + path: "/2.6.html#time-support-for-timezones" + - title: "Proc composition" + path: "/2.6.html#proc-composition" + - title: "Array#union and Array#difference" + path: "/2.6.html#arrayunion-and-arraydifference" + - title: "Hash#merge with multiple arguments" + path: "/2.6.html#hashmerge-with-multiple-arguments" + - title: Enumerables + path: "/2.6.html#enumerables" + children: + - title: "#filter/#filter!" + path: "/2.6.html#filterfilter" + - title: "#to_h with a block" + path: "/2.6.html#to_h-with-a-block" + - title: "Enumerable::ArithmeticSequence" + path: "/2.6.html#enumerablearithmeticsequence" + - title: "Enumerator chaining" + path: "/2.6.html#enumerator-chaining" + - title: "Range" + path: "/2.6.html#range" + children: + - title: "Range#=== uses #cover? instead of #include?" + path: "/2.6.html#range-uses-cover-instead-of-include" + - title: "Range#cover? accepts range argument" + path: "/2.6.html#rangecover-accepts-range-argument" + - title: "Range#% alias" + path: "/2.6.html#range-alias" + - title: Exceptions + path: "/2.6.html#exceptions" + children: + - title: 'New arguments: receiver: and key:' + path: "/2.6.html#new-arguments-receiver-and-key" + - title: "Exception#full_message options" + path: "/2.6.html#exceptionfull_message-options" + - title: Exception output tweaking + path: "/2.6.html#exception-output-tweaking" + - title: Filesystem and IO + path: "/2.6.html#filesystem-and-io" + children: + - title: "Dir#each_child and Dir#children" + path: "/2.6.html#direach_child-and-dirchildren" + - title: 'IO open mode: ''x''' + path: "/2.6.html#io-open-mode-x" + - title: Minor changes + path: "/2.6.html#minor-changes" + - title: Introspection + path: "/2.6.html#introspection" + children: + - title: "Binding#source_location" + path: "/2.6.html#bindingsource_location" + - title: "RubyVM.resolve_feature_path" + path: "/2.6.html#rubyvmresolve_feature_path" + - title: "RubyVM::AbstractSyntaxTree" + path: "/2.6.html#rubyvmabstractsyntaxtree" + - title: "TracePoint improvements" + path: "/2.6.html#tracepoint-improvements" + children: + - title: "#parameters" + path: "/2.6.html#parameters" + - title: ":script_compiled event" + path: "/2.6.html#script_compiled-event" + - title: "#enable: new params target: and target_line:" + path: "/2.6.html#enable-new-params-target-and-target_line" + - title: Standard library + path: "/2.6.html#standard-library" + children: + - title: Large updated libraries + path: "/2.6.html#large-updated-libraries" + - title: Libraries promoted to default gems + path: "/2.6.html#libraries-promoted-to-default-gems" +- title: Ruby 2.5 + path: "/2.5.html" + is_version: true + published_at: '2019-06-06' + description: | +

Highlights:

+ +
    +
  • rescue/ensure inside blocks
  • +
  • #yield_self
  • +
  • Struct with keyword initialization
  • +
  • Exceptions output changed
  • +
+ +

Read more »

+ children: + - title: Highlights + path: "/2.5.html#highlights" + - title: Language + path: "/2.5.html#language" + children: + - title: Top-level constant look-up is removed. + path: "/2.5.html#top-level-constant-look-up-is-removed" + - title: "rescue/else/ensure are allowed + inside blocks" + path: "/2.5.html#rescueelseensure-are-allowed-inside-blocks" + - title: Refinements work in string interpolations + path: "/2.5.html#refinements-work-in-string-interpolations" + - title: Core classes and modules + path: "/2.5.html#core-classes-and-modules" + children: + - title: "Kernel" + path: "/2.5.html#kernel" + children: - title: "#yield_self" path: "/2.5.html#yield_self" - title: "#pp" @@ -267,280 +431,116 @@ chapters: path: "/2.5.html#large-updated-libraries" - title: Libraries promoted to default gems path: "/2.5.html#libraries-promoted-to-default-gems" -- title: Ruby 2.6 - path: "/2.6.html" +- title: Ruby 2.4 + path: "/2.4.html" is_version: true - published_at: Dec 29, 2018 + published_at: '2019-10-14' description: |

Highlights:

    -
  • Endless range
  • -
  • #then “piping” method
  • -
  • Support for timezones in Time
  • -
  • Proc composition
  • -
  • Enumerator chaining
  • -
  • RubyVM::AbstractSyntaxTree
  • +
  • Toplevel return
  • +
  • Unification of Fixnum and Bignum into Integer
  • +
  • Full Unicode support for String case operations
  • +
  • Warning module for fine-grained warnings output control
  • +
  • Comparable#clamp
-

Read more »

+

Read more »

children: - title: Highlights - path: "/2.6.html#highlights" + path: "/2.4.html#highlights" - title: Language - path: "/2.6.html#language" - children: - - title: 'Endless range: (1..)' - path: "/2.6.html#endless-range-1" - - title: Non-ASCII constant names - path: "/2.6.html#non-ascii-constant-names" - - title: "else in exception-handling context" - path: "/2.6.html#else-in-exception-handling-context" - - title: 'Refinements: improved visibility' - path: "/2.6.html#refinements-improved-visibility" - - title: Misc - path: "/2.6.html#misc" - - title: Core classes and modules - path: "/2.6.html#core-classes-and-modules" - children: - - title: "Kernel" - path: "/2.6.html#kernel" - children: - - title: "#then as an alias for #yield_self" - path: "/2.6.html#then-as-an-alias-for-yield_self" - - title: "<Numeric>() methods have exception: - argument" - path: "/2.6.html#numeric-methods-have-exception-argument" - - title: "#system has exception: argument" - path: "/2.6.html#system-has-exception-argument" - - title: "Module#method_defined?: inherit argument" - path: "/2.6.html#modulemethod_defined-inherit-argument" - - title: "String#split with block" - path: "/2.6.html#stringsplit-with-block" - - title: "Time: support for timezones" - path: "/2.6.html#time-support-for-timezones" - - title: "Proc composition" - path: "/2.6.html#proc-composition" - - title: "Array#union and Array#difference" - path: "/2.6.html#arrayunion-and-arraydifference" - - title: "Hash#merge with multiple arguments" - path: "/2.6.html#hashmerge-with-multiple-arguments" - - title: Enumerables - path: "/2.6.html#enumerables" - children: - - title: "#filter/#filter!" - path: "/2.6.html#filterfilter" - - title: "#to_h with a block" - path: "/2.6.html#to_h-with-a-block" - - title: "Enumerable::ArithmeticSequence" - path: "/2.6.html#enumerablearithmeticsequence" - - title: "Enumerator chaining" - path: "/2.6.html#enumerator-chaining" - - title: "Range" - path: "/2.6.html#range" - children: - - title: "Range#=== uses #cover? instead of #include?" - path: "/2.6.html#range-uses-cover-instead-of-include" - - title: "Range#cover? accepts range argument" - path: "/2.6.html#rangecover-accepts-range-argument" - - title: "Range#% alias" - path: "/2.6.html#range-alias" - - title: Exceptions - path: "/2.6.html#exceptions" - children: - - title: 'New arguments: receiver: and key:' - path: "/2.6.html#new-arguments-receiver-and-key" - - title: "Exception#full_message options" - path: "/2.6.html#exceptionfull_message-options" - - title: Exception output tweaking - path: "/2.6.html#exception-output-tweaking" - - title: Filesystem and IO - path: "/2.6.html#filesystem-and-io" - children: - - title: "Dir#each_child and Dir#children" - path: "/2.6.html#direach_child-and-dirchildren" - - title: 'IO open mode: ''x''' - path: "/2.6.html#io-open-mode-x" - - title: Minor changes - path: "/2.6.html#minor-changes" - - title: Introspection - path: "/2.6.html#introspection" - children: - - title: "Binding#source_location" - path: "/2.6.html#bindingsource_location" - - title: "RubyVM.resolve_feature_path" - path: "/2.6.html#rubyvmresolve_feature_path" - - title: "RubyVM::AbstractSyntaxTree" - path: "/2.6.html#rubyvmabstractsyntaxtree" - - title: "TracePoint improvements" - path: "/2.6.html#tracepoint-improvements" - children: - - title: "#parameters" - path: "/2.6.html#parameters" - - title: ":script_compiled event" - path: "/2.6.html#script_compiled-event" - - title: "#enable: new params target: and target_line:" - path: "/2.6.html#enable-new-params-target-and-target_line" - - title: Standard library - path: "/2.6.html#standard-library" + path: "/2.4.html#language" children: - - title: Large updated libraries - path: "/2.6.html#large-updated-libraries" - - title: Libraries promoted to default gems - path: "/2.6.html#libraries-promoted-to-default-gems" -- title: Ruby 2.7 - path: "/2.7.html" - is_version: true - published_at: TODO - description: | -

Highlights:

- -

Ruby 2.7 is a last major release before 3.0¹, so it introduces several important changes, larger in scale than previous releases (and also a bit lean on a “just nice to have” features side). Be prepared!

- -
    -
  • Pattern matching
  • -
  • “Real” keyword argument
  • -
  • Numbered block parameters
  • -
  • Beginless range
  • -
  • Enumerator.produce
  • -
  • GC.compact
  • -
  • Large update of IRB
  • -
  • Serious cleanup of the standard library
  • -
- -

¹There is a possibility 2.8 will also be released, but in any case, Christmas release of 2020 is promised to be Ruby 3.0.

- -

Read more »

- children: - - title: Highlights - path: "/2.7.html#highlights" - - title: Language - path: "/2.7.html#language" + - title: Multiple assignment allowed in conditional expression + path: "/2.4.html#multiple-assignment-allowed-in-conditional-expression" + - title: Toplevel return + path: "/2.4.html#toplevel-return" + - title: Refinements improvements + path: "/2.4.html#refinements-improvements" children: - - title: Pattern matching - path: "/2.7.html#pattern-matching" - - title: Keyword argument-related changes - path: "/2.7.html#keyword-argument-related-changes" - - title: Numbered block parameters - path: "/2.7.html#numbered-block-parameters" - - title: Beginless range - path: "/2.7.html#beginless-range" - - title: Other syntax changes - path: "/2.7.html#other-syntax-changes" - - title: Warnings/deprecations - path: "/2.7.html#warningsdeprecations" - children: - - title: "“Safe” and “taint” concepts are deprecated in general" - path: "/2.7.html#safe-and-taint-concepts-are-deprecated-in-general" - - title: "self.<private_method>" - path: "/2.7.html#selfprivate_method" - - title: Refinements in #method/#instance_method - path: "/2.7.html#refinements-in-methodinstance_method" - - title: Core classes and modules - path: "/2.7.html#core-classes-and-modules" + - title: Refinements are supported in Symbol#to_proc and send + path: "/2.4.html#refinements-are-supported-in-symbolto_proc-and-send" + - title: "refine can refine modules, too" + path: "/2.4.html#refine-can-refine-modules-too" + - title: "Module.used_modules" + path: "/2.4.html#moduleused_modules" + - title: Core + path: "/2.4.html#core" children: - - title: Better Method#inspect - path: "/2.7.html#better-methodinspect" - - title: "UnboundMethod#bind_call" - path: "/2.7.html#unboundmethodbind_call" - - title: "Module" - path: "/2.7.html#module" + - title: "Warning module" + path: "/2.4.html#warning-module" + - title: "Object#clone(freeze: false)" + path: "/2.4.html#objectclonefreeze-false" + - title: "Comparable#clamp" + path: "/2.4.html#comparableclamp" + - title: Numerics + path: "/2.4.html#numerics" children: - - title: "#const_source_location" - path: "/2.7.html#const_source_location" - - title: "#autoload?: inherit argument" - path: "/2.7.html#autoload-inherit-argument" - - title: "Comparable#clamp with Range" - path: "/2.7.html#comparableclamp-with-range" - - title: "Integer[] with range" - path: "/2.7.html#integer-with-range" - - title: "Complex#<=>" - path: "/2.7.html#complex" + - title: "Fixnum and Bignum are unified into Integer" + path: "/2.4.html#fixnum-and-bignum-are-unified-into-integer" + - title: "Numeric#finite? and #infinite?" + path: "/2.4.html#numericfinite-and-infinite" + - title: "Integer#digits" + path: "/2.4.html#integerdigits" + - title: "ndigits optional argument for rounding methods" + path: "/2.4.html#ndigits-optional-argument-for-rounding-methods" + - title: "half: option for #round method" + path: "/2.4.html#half-option-for-round-method" - title: Strings, symbols and regexps - path: "/2.7.html#strings-symbols-and-regexps" - children: - - title: Core methods returning frozen strings - path: "/2.7.html#core-methods-returning-frozen-strings" - - title: "Symbol#start_with? and #end_with?" - path: "/2.7.html#symbolstart_with-and-end_with" - - title: "Time" - path: "/2.7.html#time" - children: - - title: "#floor and #ceil" - path: "/2.7.html#floor-and-ceil" - - title: "#inspect includes subseconds" - path: "/2.7.html#inspect-includes-subseconds" - - title: Enumerables and collections - path: "/2.7.html#enumerables-and-collections" + path: "/2.4.html#strings-symbols-and-regexps" children: - - title: "Enumerable#filter_map" - path: "/2.7.html#enumerablefilter_map" - - title: "Enumerable#tally" - path: "/2.7.html#enumerabletally" - - title: "Enumerator.produce" - path: "/2.7.html#enumeratorproduce" - - title: "Enumerator::Lazy#eager" - path: "/2.7.html#enumeratorlazyeager" - - title: "Enumerator::Yielder#to_proc" - path: "/2.7.html#enumeratoryielderto_proc" - - title: "Array#intersection" - path: "/2.7.html#arrayintersection" - - title: "ObjectSpace::WeakMap#[]= now accepts non-GC-able objects" - path: "/2.7.html#objectspaceweakmap-now-accepts-non-gc-able-objects" - - title: "Fiber#raise" - path: "/2.7.html#fiberraise" - - title: "Range" - path: "/2.7.html#range" + - title: Unicode case conversions + path: "/2.4.html#unicode-case-conversions" + - title: "String.new(capacity: size)" + path: "/2.4.html#stringnewcapacity-size" + - title: "#casecmp?" + path: "/2.4.html#casecmp" + - title: "String#concat and #prepend accept multiple + arguments" + path: "/2.4.html#stringconcat-and-prepend-accept-multiple-arguments" + - title: "String#unpack1" + path: "/2.4.html#stringunpack1" + - title: "#match? method" + path: "/2.4.html#match-method" + - title: "MatchData: better support for named captures" + path: "/2.4.html#matchdata-better-support-for-named-captures" + - title: Collections + path: "/2.4.html#collections" children: - - title: "#=== for String" - path: "/2.7.html#for-string" - - title: "#minmax implementation change" - path: "/2.7.html#minmax-implementation-change" + - title: "Enumerable#chunk without a block returns an Enumerator" + path: "/2.4.html#enumerablechunk-without-a-block-returns-an-enumerator" + - title: "#sum" + path: "/2.4.html#sum" + - title: "#uniq" + path: "/2.4.html#uniq" + - title: "Array#max and #min" + path: "/2.4.html#arraymax-and-min" + - title: "Array#concat takes multiple arguments" + path: "/2.4.html#arrayconcat-takes-multiple-arguments" + - title: "Array#pack(buffer:)" + path: "/2.4.html#arraypackbuffer" + - title: "Hash#compact and #compact!" + path: "/2.4.html#hashcompact-and-compact" + - title: "Hash#transform_values and #transform_values!" + path: "/2.4.html#hashtransform_values-and-transform_values" - title: Filesystem and IO - path: "/2.7.html#filesystem-and-io" - children: - - title: "IO#set_encoding_by_bom" - path: "/2.7.html#ioset_encoding_by_bom" - - title: "Dir.glob and Dir.[] not allow \\0-separated - patterns" - path: "/2.7.html#dirglob-and-dir-not-allow-0-separated-patterns" - - title: File.extname returns a "." string at a name - ending with a dot. - path: "/2.7.html#fileextname-returns-a--string-at-a-name-ending-with-a-dot" - - title: Exceptions - path: "/2.7.html#exceptions" - children: - - title: "FrozenError: receiver argument" - path: "/2.7.html#frozenerror-receiver-argument" - - title: Interpreter internals - path: "/2.7.html#interpreter-internals" + path: "/2.4.html#filesystem-and-io" children: - - title: "$LOAD_PATH.resolve_feature_path" - path: "/2.7.html#load_pathresolve_feature_path" - - title: "resolve_feature_path behavior for loaded features fixed" - path: "/2.7.html#resolve_feature_path-behavior-for-loaded-features-fixed" - - title: "GC.compact" - path: "/2.7.html#gccompact" - - title: "Warning::[] and ::[]=" - path: "/2.7.html#warning-and-" - - title: Standard library - path: "/2.7.html#standard-library" + - title: "chomp: option for string splitting" + path: "/2.4.html#chomp-option-for-string-splitting" + - title: "#empty? method for filesystem objects" + path: "/2.4.html#empty-method-for-filesystem-objects" + - title: "Thread#report_on_exception and Thread.report_on_exception" + path: "/2.4.html#threadreport_on_exception-and-threadreport_on_exception" + - title: "TracePoint#callee_id" + path: "/2.4.html#tracepointcallee_id" + - title: Stdlib + path: "/2.4.html#stdlib" children: - - title: Large IRB update - path: "/2.7.html#large-irb-update" - - title: Network and web - path: "/2.7.html#network-and-web" - - title: Large updated libraries - path: "/2.7.html#large-updated-libraries" - - title: Standard library contents change - path: "/2.7.html#standard-library-contents-change" - children: - - title: New libraries - path: "/2.7.html#new-libraries" - - title: Libraries promoted to default gems - path: "/2.7.html#libraries-promoted-to-default-gems" - - title: Libraries excluded from the standard library - path: "/2.7.html#libraries-excluded-from-the-standard-library" + - title: Libraries promoted to default/bundled gems + path: "/2.4.html#libraries-promoted-to-defaultbundled-gems" - title: History (of this site) path: "/History.html" - title: Contributing diff --git a/_src/2.4.md b/_src/2.4.md index a3be061..c884e94 100644 --- a/_src/2.4.md +++ b/_src/2.4.md @@ -172,19 +172,21 @@ New single-method module was introduced, meant to be overriden in order to contr # .warn called with: ": warning: already initialized constant X\n" # .warn called with: ": warning: previous definition of X was here\n" ``` -* **Follow-up:** Surprisingly as at may be, `Kernel#warn` haven't been changed to call `Warning.warn` in 2.4, but it was fixed in 2.5: - ```ruby - def Warning.warn(msg) - puts ".warn called with: #{msg.inspect}" - end +* **Follow-ups:** + * Surprisingly as at may be, `Kernel#warn` haven't been changed to call `Warning.warn` in 2.4, but it was [fixed in 2.5](2.5.html#warn-call-warningwarn): + ```ruby + def Warning.warn(msg) + puts ".warn called with: #{msg.inspect}" + end - warn 'foo', 'bar' - # Ruby 2.4 prints: - # foo - # bar - # Ruby 2.5 prints: - # .warn called with: "foo\nbar\n" - ``` + warn 'foo', 'bar' + # Ruby 2.4 prints: + # foo + # bar + # Ruby 2.5 prints: + # .warn called with: "foo\nbar\n" + ``` + * In Ruby 2.7, new methods [were added](2.7.html#warning-and-) to `Warning` module allowing control over per-category warning suppression ### `Object#clone(freeze: false)` @@ -226,7 +228,7 @@ Method to limit any comparable value to `min`—`max` range -123.clamp(0, 20) # => 0 18.clamp(0, 20) # => 18 ``` -* **Follow-up:** For Ruby 2.7, `clamp` with a range was [accepted](https://bugs.ruby-lang.org/issues/14784), which, especially when combined with endless (2.6) and beginless (2.7) ranges, will allow to use more powerful and idiomatic code: +* **Follow-up:** In Ruby 2.7, `clamp` also [allows](2.7.html#comparableclamp-with-range) passing range argument, which, especially when combined with [endless](2.6.html#endless-range-1) (2.6) and [beginless](2.7.html#beginless-range) (2.7) ranges, allows to use more powerful and idiomatic code: ```ruby 123.clamp(0..20) # => 20 123.clamp(..20) # => 20 diff --git a/_src/2.5.md b/_src/2.5.md index 2ccc42c..52b2588 100644 --- a/_src/2.5.md +++ b/_src/2.5.md @@ -757,7 +757,7 @@ Much akin to `Hash#fetch`, allows to fetch thread-local variable or provide defa ### Network and Web -* `open-uri`: `URI.open` method defined as an alias to open-uri's `Kernel#open`. open-uri's `Kernel#open` will be deprecated in future. Follow-up: Deprecation of `Kernel#open` and docs for [URI.open](https://ruby-doc.org/stdlib-2.7.0.preview1/libdoc/open-uri/rdoc/URI.html#method-c-open) were added in Ruby 2.7. +* `open-uri`: `URI.open` method defined as an alias to open-uri's `Kernel#open`. open-uri's `Kernel#open` will be deprecated in future. Follow-up: Deprecation of `Kernel#open` and docs for [URI.open](https://ruby-doc.org/stdlib-2.7.0/libdoc/open-uri/rdoc/URI.html#method-c-open) [were added](2.7.html#network-and-web) in Ruby 2.7. #### `Net::HTTP` diff --git a/_src/2.6.md b/_src/2.6.md index 9c006fe..776a8c1 100644 --- a/_src/2.6.md +++ b/_src/2.6.md @@ -50,7 +50,7 @@ next: 2.5 (1..) == (1...) # => false (1..).size # => Infinity ``` -* **Follow-up:** "Beginless" range [was introduced](TODO) in 2.7. +* **Follow-up:** "Beginless" range [was introduced](2.7.html#beginless-range) in 2.7. ### Non-ASCII constant names @@ -100,12 +100,12 @@ Refinements are now compatible with `#public_send` and `#respond_to?`, and impli 'foo'.public_send(:surround, '|') # => "|foo|" in 2.6, NoMethodError in 2.5 (1..3).map(&'%02i') # => ["01", "02", "03"] in 2.6; wrong argument type String (expected Proc) in 2.5 ``` -* **Follow-up:** Ruby 2.7 also made refinements available in [`#method`](TODO) +* **Follow-up:** Ruby 2.7 also made refinements available in [`#method`](2.7.html#refinements-in-methodinstance_method) ### Misc * Infamous esoteric flip-flop syntax is deprecated finally: [Feature #5400](https://bugs.ruby-lang.org/issues/5400). - * **Follow-up:** Deprecation is [reverted](#TODO) in 2.7 + * **Follow-up:** Deprecation is [reverted](2.7.html#warningsdeprecations) in 2.7 ## Core classes and modules @@ -172,6 +172,7 @@ With `exception: true`, the method raises instead of returning `false` on non-0 Array.method_defined?(:to_h, false) # => true -- despite inheriting from Enumerable, Array redefines it for performance ``` +* **Follow-up:** In 2.7, `inherit` argument was also [added to `autoload?`](2.7.html#autoload-inherit-argument) ### `String#split` with block @@ -271,7 +272,7 @@ The concept of a "timezone object" is introduced for various `Time` methods. Rub # => [2] ``` * **Notice:** There are also plans (discussed in the same ticket above) to introduce mutating `union!` form. -* **Follow-up:** Ruby 2.7 [added](TODO) `#intersection` method. +* **Follow-up:** Ruby 2.7 [added](2.7.html#arrayintersection) `#intersection` method. ### `Hash#merge` with multiple arguments @@ -387,7 +388,7 @@ Several enumerators can now be chained into one with `Enumerator#+(other)` or `E end ``` * **Notice:** For `String` ranges, behavior left unchanged, so example with versions above would NOT work with pure-`String` versions. -* **Follow-up:** `String` behavior was [fixed](TODO) in Ruby 2.7, so in 2.7 this code prints "yes": +* **Follow-up:** `String` behavior was [fixed](2.7.html#for-string) in Ruby 2.7, so in 2.7 this code prints "yes": ```ruby case '2.5' when '1.8.7'..'2.6' @@ -442,7 +443,7 @@ Several enumerators can now be chained into one with `Enumerator#+(other)` or `E ```ruby raise KeyError, "don't have this: #{key}", receiver: self, key: key ``` -* **Follow-up:** Ruby 2.7 [adds](TODO) `receiver:` argument for `FrozenError`, too. +* **Follow-up:** Ruby 2.7 [adds](2.7.html#frozenerror-receiver-argument) `receiver:` argument for `FrozenError`, too. #### `Exception#full_message` options @@ -568,7 +569,7 @@ For string `name`, returns what path `require(name)` will load (without actually RubyVM.resolve_feature_path('faraday') # => [:rb, "<...>/gems/faraday-0.15.4/lib/faraday.rb"] ``` -* **Follow-up:** In Ruby 2.7, `resolve_feature_path` was [moved](TODO) to a `$LOAD_PATH` singleton method, and its behavior for already loaded pathes was [fixed](TODO) to return path nevertheless. +* **Follow-up:** In Ruby 2.7, `resolve_feature_path` was [moved](2.7.html#load_pathresolve_feature_path) to a `$LOAD_PATH` singleton method, and its behavior for already loaded pathes was [fixed](2.7.html#resolve_feature_path-behavior-for-loaded-features-fixed) to return path nevertheless. #### `RubyVM::AbstractSyntaxTree` diff --git a/_src/2.7.md b/_src/2.7.md index 68436c4..55489cc 100644 --- a/_src/2.7.md +++ b/_src/2.7.md @@ -6,23 +6,25 @@ next: 2.6 # Ruby 2.7 -* **Released at:** Dec 25, 2019 ([NEWS](TODO) file) -* **Status (as of <>):** TODO -* **This document first published:** TODO +* **Released at:** Dec 25, 2019 ([NEWS](https://github.com/ruby/ruby/blob/ruby_2_7/NEWS) file) +* **Status (as of <>):** 2.7.0 is current _stable_ +* **This document first published:** Dec 27, 2019 * **Last change to this document:** <> + + ## Highlights Ruby 2.7 is a last major release before 3.0¹, so it introduces several important changes, larger in scale than previous releases (and also a bit lean on a "just nice to have" features side). Be prepared! -* Pattern matching -* "Real" keyword argument -* Numbered block parameters -* Beginless range -* `Enumerator.produce` -* `GC.compact` -* Large update of IRB -* Serious cleanup of the standard library +* [Pattern matching](#pattern-matching) +* ["Real" keyword argument](#pattern-matching) +* [Numbered block parameters](#numbered-block-parameters) +* [Beginless range](#beginless-range) +* [`Enumerator.produce`](#enumeratorproduce) +* [`GC.compact`](#gccompact) +* [Large update of IRB](#large-irb-update) +* [Serious cleanup of the standard library](#standard-library-contents-change) ¹There is a possibility 2.8 will also be released, but in any case, Christmas release of 2020 is promised to be Ruby 3.0. @@ -114,11 +116,11 @@ In block without explicitly specified parameters, variables `_1` through `_9` ca ### Beginless range -In addition to [endless range in Ruby 2.6](TODO): `(start..)`, Ruby 2.7 introduces beginless one: `(..end)`. +In addition to [endless range in Ruby 2.6](2.6.html#endless-range-1): `(start..)`, Ruby 2.7 introduces beginless one: `(..end)`. -* **Reason:** Array slicing (initial justification for endless ranges) turned out to be not the only usage for semi-open ranges. Another ones, like `case`/`grep`, DSLs and constants and [`Comparable#clamp`](TODO), can gain from the symmetricity of "range without end"/"range without beginning". +* **Reason:** Array slicing (initial justification for endless ranges) turned out to be not the only usage for semi-open ranges. Another ones, like `case`/`grep`, DSLs and constants and [`Comparable#clamp`](#comparableclamp-with-range), can gain from the symmetry of "range without end"/"range without beginning". * **Discussion:** [Feature #14799](https://bugs.ruby-lang.org/issues/14799) -* **Documentation:** [doc/syntax/literals.rdoc#Ranges](https://ruby-doc.org/core-2.7.0.preview3/doc/syntax/literals_rdoc.html#label-Ranges) +* **Documentation:** [doc/syntax/literals.rdoc#Ranges](https://ruby-doc.org/core-2.7.0/doc/syntax/literals_rdoc.html#label-Ranges) * **Code:** ```ruby # Usage examples @@ -140,14 +142,14 @@ In addition to [endless range in Ruby 2.6](TODO): `(start..)`, Ruby 2.7 introduc r.size # => Infinity ``` * **Notes:** - * Slightly "a-grammatic" name ("beginless" instead of "beginningless") is due to the fact that it is a range literally lacking `begin` property. - * Unfortunately, parser can not always handle beginless range without the help of the parenthises: + * Slightly "a-grammatical" name ("beginless" instead of "beginningless") is due to the fact that it is a range literally lacking `begin` property. + * Unfortunately, parser can not always handle beginless range without the help of the parentheses: ```ruby (1..10).grep ..5 # ArgumentError (wrong number of arguments (given 0, expected 1)) # ...because it is in fact parsed as ((1..10).grep()..5), e.g. range from grep results to 5 ``` - This may seem an esotheric problem, but it becomes less esotheric in DSLs. For example in RubySpec, one would like to write: + This may seem an esoteric problem, but it becomes less esoteric in DSLs. For example in RubySpec, one would like to write: ```ruby ruby_version ..'1.9' do # some tests for old Ruby @@ -175,7 +177,7 @@ In addition to [endless range in Ruby 2.6](TODO): `(start..)`, Ruby 2.7 introduc " # This had been warned since 2.4; Now it raises a SyntaxError EOS ``` -* Modifier `rescue` parsing change (multiple assignement now consisent with singular): +* Modifier `rescue` parsing change (multiple assignment now consistent with singular): ```ruby a = raise rescue 1 @@ -200,7 +202,7 @@ In addition to [endless range in Ruby 2.6](TODO): `(start..)`, Ruby 2.7 introduc Some older or accidental features are deprecated on the road to 3.0 and currently produce warnings. -> Note that Ruby 2.7 also [introduced a way](TODO) to turn off only some categories of warnings, for example, only deprecation ones. +> Note that Ruby 2.7 also [introduced a way](#warning-and-) to turn off only some categories of warnings, for example, only deprecation ones. * `yield` in singleton class syntax (was inconsistent with local variables accessibility). Discussion: [Feature #15575](https://bugs.ruby-lang.org/issues/15575). ```ruby @@ -237,7 +239,7 @@ Some older or accidental features are deprecated on the road to 3.0 and currentl # warning: Capturing the given block using Kernel#proc is deprecated; use `&block` instead # still prints "Hello" ``` -* Contrastingly, the flip-flop syntax deprecation, [introduced](#TODO) in 2.6, **is reverted**. It turned out that for brevity in text-processing scrips (including one-liners run as `ruby -e "print "`) the feature, however esotheric it may seem, has a justified usage. +* Contrastingly, the flip-flop syntax deprecation, [introduced](2.6.html#misc) in 2.6, **is reverted**. It turned out that for brevity in text-processing scrips (including one-liners run as `ruby -e "print "`) the feature, however esoteric it may seem, has a justified usage. * **Discussion:** [Feature #5400](https://bugs.ruby-lang.org/issues/5400#note-23). * **Documentation:** [doc/syntax/control_expressions.rdoc#Flip-Flop](https://ruby-doc.org/core-2.6.5/doc/syntax/control_expressions_rdoc.html#label-Flip-Flop) * **Code:** @@ -263,7 +265,7 @@ Calling a private method with a literal `self` as the receiver is now allowed. * **Reason:** "`anything.method` is always disallowed for private methods" seemed like a simple and unambiguous rule, but produced some ugly edge cases (for example, `self.foo = something` for private `attr_accessor` _was_ allowed). It turned out "allow literal `self.`" makes things much clearer. * **Discussion:** [Feature #11297](https://bugs.ruby-lang.org/issues/11297), [Feature #16123](https://bugs.ruby-lang.org/issues/16123) -* **Documentation:** [doc/syntax/modules_and_classes.rdoc#Visibility](https://docs.ruby-lang.org/en/master/syntax/modules_and_classes_rdoc.html#label-Visibility) +* **Documentation:** [doc/syntax/modules_and_classes.rdoc#Visibility](https://ruby-doc.org/core-2.7.0/doc/syntax/modules_and_classes_rdoc.html#label-Visibility) * **Code:** ```ruby class A @@ -320,7 +322,7 @@ Calling a private method with a literal `self` as the receiver is now allowed. * **Reason:** As there are more code style approaches that operate `Method` objects, making them better identifiable and informative seemed necessary. * **Discussion:** [Feature #14145](https://bugs.ruby-lang.org/issues/14145) -* **Documentation:** [Method#inspect](https://ruby-doc.org/core-2.7.0.preview3/Method.html#method-i-inspect) +* **Documentation:** [Method#inspect](https://ruby-doc.org/core-2.7.0/Method.html#method-i-inspect) * **Code:** ```ruby p CSV.method(:read) @@ -360,7 +362,7 @@ Calling a private method with a literal `self` as the receiver is now allowed. ### `UnboundMethod#bind_call` -Binds `UnboundMethod` to a receiver and calls it. Semantically equiavalent to `unbound_method.bind(receiver).call(arguments)`, but doesn't produce intermediate `Method` object (which `bind` does). +Binds `UnboundMethod` to a receiver and calls it. Semantically equivalent to `unbound_method.bind(receiver).call(arguments)`, but doesn't produce intermediate `Method` object (which `bind` does). * **Reason:** The technique of storing unbound methods and binding them later is used in some metaprogramming-heavy code to robustly use "original" implementation. For example: ```ruby @@ -377,7 +379,7 @@ Binds `UnboundMethod` to a receiver and calls it. Semantically equiavalent to `u ``` In such cases (for example, code reloaders), overhead of producing new `Method` object on each `bind().call` is pretty significant, and `bind_call` allows to avoid it. * **Discussion:** [Feature #15955](https://bugs.ruby-lang.org/issues/15955) -* **Documentation:** [UnboundMethod#bind_call](https://ruby-doc.org/core-2.7.0.preview3/UnboundMethod.html#method-i-bind_call) +* **Documentation:** [UnboundMethod#bind_call](https://ruby-doc.org/core-2.7.0/UnboundMethod.html#method-i-bind_call) ### `Module` @@ -386,7 +388,7 @@ Binds `UnboundMethod` to a receiver and calls it. Semantically equiavalent to `u Returns the location of the _first_ definition of the specified constant. * **Discussion:** [Feature #10771](https://bugs.ruby-lang.org/issues/10771) -* **Documentation:** [Module#const_source_location](https://ruby-doc.org/core-2.7.0.preview3/Module.html#method-i-const_source_location) +* **Documentation:** [Module#const_source_location](https://ruby-doc.org/core-2.7.0/Module.html#method-i-const_source_location) * **Code:** ```ruby # Assuming test.rb: @@ -427,7 +429,7 @@ Returns the location of the _first_ definition of the specified constant. * **Reason:** More granular checking "if something is marked to be autoloaded directly or through ancestry chain" is necessary for advanced code reloaders (requested by author of the [zeitwerk](https://github.com/fxn/zeitwerk)). * **Discussion:** [Feature #15777](https://bugs.ruby-lang.org/issues/15777) -* **Documentation:** [Module#autoload?](https://ruby-doc.org/core-2.7.0.preview3/Module.html#method-i-autoload-3F) +* **Documentation:** [Module#autoload?](https://ruby-doc.org/core-2.7.0/Module.html#method-i-autoload-3F) * **Code:** ```ruby class Parent @@ -454,7 +456,7 @@ Returns the location of the _first_ definition of the specified constant. * **Reason:** First, ranges can be seen as more "natural" to specify _a range_ of acceptable values. Second, with introduction of beginless and endless ranges, `#clamp` now can be used for one-sided value limitation, too. * **Discussion:** [Feature #14784](https://bugs.ruby-lang.org/issues/14784) -* **Documentation:** [Comparable#clamp](https://ruby-doc.org/core-2.7.0.preview3/Comparable.html#method-i-clamp) +* **Documentation:** [Comparable#clamp](https://ruby-doc.org/core-2.7.0/Comparable.html#method-i-clamp) * **Code:** ```ruby 123.clamp(0..100) # => 100 @@ -478,7 +480,7 @@ Returns the location of the _first_ definition of the specified constant. Allows to get several bits at once. * **Discussion:** [Feature #8842](https://bugs.ruby-lang.org/issues/8842) -* **Documentation:** [Integer#[]](https://ruby-doc.org/core-2.7.0.preview3/Integer.html#method-i-5B-5D) +* **Documentation:** [Integer#[]](https://ruby-doc.org/core-2.7.0/Integer.html#method-i-5B-5D) * **Code:** ```ruby # 4---0 @@ -492,9 +494,9 @@ Allows to get several bits at once. `Complex#<=>(other)` now returns `nil` if the number has imaginary part, and behaves like `Numeric#<=>` if it does not. -* **Reason:** Method `#<=>` was explicitly undefined in `Complex` to underline the fact that linear order of complex numbers can't be established, but it was inconsistent with most of the other objects implementations (which return `nil` for impossible/incompatible comparison instead of rasing) +* **Reason:** Method `#<=>` was explicitly undefined in `Complex` to underline the fact that linear order of complex numbers can't be established, but it was inconsistent with most of the other objects implementations (which return `nil` for impossible/incompatible comparison instead of raising) * **Discussion:** [Feature #](https://bugs.ruby-lang.org/issues/) -* **Documentation:** [Complex#<=>](https://ruby-doc.org/core-2.7.0.preview3/Complex.html#method-i-3C-3D-3E) +* **Documentation:** [Complex#<=>](https://ruby-doc.org/core-2.7.0/Complex.html#method-i-3C-3D-3E) * **Code:** ```ruby 1 + 2i <=> 1 # => nil @@ -511,9 +513,9 @@ Allows to get several bits at once. Several core methods now return frozen, deduplicated `String` instead of generating it every time the string is requested. -* **Reason:** Avoiding allocations of new strings for each `#to_s` of primitive objects can save dramatical amounts of memory. +* **Reason:** Avoiding allocations of new strings for each `#to_s` of primitive objects can save dramatic amounts of memory. * **Discussion:** [Feature #16150](https://bugs.ruby-lang.org/issues/16150) -* **Affected methods:** [NilClass#to_s](https://ruby-doc.org/core-2.7.0.preview3/NilClass.html#method-i-to_s), [TrueClass#to_s](https://ruby-doc.org/core-2.7.0.preview3/TrueClass.html#method-i-to_s), [FalseClass#to_s](https://ruby-doc.org/core-2.7.0.preview3/TrueClass.html#method-i-to_s), [Module#name](https://ruby-doc.org/core-2.7.0.preview3/Module.html#method-i-name) +* **Affected methods:** [NilClass#to_s](https://ruby-doc.org/core-2.7.0/NilClass.html#method-i-to_s), [TrueClass#to_s](https://ruby-doc.org/core-2.7.0/TrueClass.html#method-i-to_s), [FalseClass#to_s](https://ruby-doc.org/core-2.7.0/TrueClass.html#method-i-to_s), [Module#name](https://ruby-doc.org/core-2.7.0/Module.html#method-i-name) * **Code:** ```ruby # Ruby 2.6 @@ -536,13 +538,13 @@ Several core methods now return frozen, deduplicated `String` instead of generat # Ruby 2.6: "true -- received" # Ruby 2.7: FrozenError (can't modify frozen String: "true") ``` - * The same change was proposed for `Symbol#to_s` (and could've been a dramatical improvement in some kinds of code), but the change turned out to be too disruptive. + * The same change was proposed for `Symbol#to_s` (and could've been a dramatic improvement in some kinds of code), but the change turned out to be too disruptive. #### `Symbol#start_with?` and `#end_with?` -* **Reason:** Symbol was once thought as "just immutable names" with any of "string-y" operations not making sense for them, but as `Symbol#match` was always present, and `Symbol#match?` [implemented in 2.4](TODO), it turns out that other content inspection methods are also useful. +* **Reason:** Symbol was once thought as "just immutable names" with any of "string-y" operations not making sense for them, but as `Symbol#match` was always present, and `Symbol#match?` [implemented in 2.4](2.4.html#match-method), it turns out that other content inspection methods are also useful. * **Discussion:** [Feature #16348](https://bugs.ruby-lang.org/issues/16348) -* **Documentation:** [Symbol#end_with?](https://ruby-doc.org/core-2.7.0_rc1/Symbol.html#method-i-end_with-3F), [Symbol#start_with?](https://ruby-doc.org/core-2.7.0_rc1/Symbol.html#method-i-start_with-3F) +* **Documentation:** [Symbol#end_with?](https://ruby-doc.org/core-2.7.0/Symbol.html#method-i-end_with-3F), [Symbol#start_with?](https://ruby-doc.org/core-2.7.0/Symbol.html#method-i-start_with-3F) * **Code:** ```ruby :table_name.end_with?('name', 'value') @@ -563,7 +565,7 @@ Rounds Time's nanoseconds down or up to a specified number of digits (0 by defau * **Reason:** Rounding of nanoseconds important in a test code, when comparing `Time` instances from a different sources (stored in DB, passed through third-party libraries, etc.). Having better control on truncation comparing to `Time#round` (which existed since 1.9.2) * **Discussion:** [Feature #15653](https://bugs.ruby-lang.org/issues/15653) (floor, Japanese), [Feature #15772](https://bugs.ruby-lang.org/issues/15772) (ceil) -* **Documentation:** [Time#floor](https://ruby-doc.org/core-2.7.0.preview3/Time.html#method-i-floor), [Time#ceil](https://ruby-doc.org/core-2.7.0.preview3/Time.html#method-i-ceil) +* **Documentation:** [Time#floor](https://ruby-doc.org/core-2.7.0/Time.html#method-i-floor), [Time#ceil](https://ruby-doc.org/core-2.7.0/Time.html#method-i-ceil) * **Code:** ```ruby t = Time.utc(2019, 12, 24, 5, 43, 25.8765432r) @@ -578,7 +580,7 @@ Rounds Time's nanoseconds down or up to a specified number of digits (0 by defau * **Reason:** Losing subseconds in `#inspect` always made debugging and testing harder, producing test failures like "Expected: 2019-12-21 16:11:08 +0200, got 2019-12-21 16:11:08 +0200" (which are visually the same, but one of them, probably going through some serialization or DB storage, has different value for subseconds). * **Discussion:** [Feature #15958](https://bugs.ruby-lang.org/issues/15958) -* **Documentation:** [Time#inspect](https://ruby-doc.org/core-2.7.0.preview3/Time.html#method-i-inspect) +* **Documentation:** [Time#inspect](https://ruby-doc.org/core-2.7.0/Time.html#method-i-inspect) * **Code:** ```ruby t = Time.utc(2019, 12, 24, 5, 43, 25.8765432r) @@ -607,7 +609,7 @@ Transforms elements of enumerable with provided block, and drops falsy results, * **Reason:** Filter suitable elements, then process them somehow is a common flow of sequence processing, yet with `filter { ... }.map { ... }` additional intermediate `Array` is produced, which is not always desirable. Also, some processing can indicate "can't be processed" by returning `false` or `nil`, which requires code like `map { ... }.compact` (to drop `nil`s) or `map { ... }.select(:itself)` (to drop all falsy values). * **Discussion:** [Feature #15323](https://bugs.ruby-lang.org/issues/15323) -* **Documentation:** [Enumerable#filter_map](https://ruby-doc.org/core-2.7.0.preview3/Enumerable.html#method-i-filter_map) +* **Documentation:** [Enumerable#filter_map](https://ruby-doc.org/core-2.7.0/Enumerable.html#method-i-filter_map) * **Code:** ```ruby (1..10).filter_map { |i| i**2 if i.even? } # => [4, 16, 36, 64, 100] @@ -627,7 +629,7 @@ Transforms elements of enumerable with provided block, and drops falsy results, Counts unique objects in the enumerable and returns hash of `{object => count}`. * **Discussion:** [Feature #11076](https://bugs.ruby-lang.org/issues/11076) -* **Documentation:** [Enumerable#tally](https://ruby-doc.org/core-2.7.0.preview3/Enumerable.html#method-i-tally) +* **Documentation:** [Enumerable#tally](https://ruby-doc.org/core-2.7.0/Enumerable.html#method-i-tally) * **Code:** ```ruby %w[Ruby Python Ruby Perl Python Ruby].tally @@ -697,7 +699,7 @@ Converts lazy enumerator back to eager one. * **Reason:** When working with large data sequences, lazy enumerators are useful tools to not produce intermediate array on each step. But some data consuming methods expect to receive enumeratos that would really return data from methods like `take_while` and not just add them to pipeline. * **Discussion:** [Feature #15901](https://bugs.ruby-lang.org/issues/15901) -* **Documentation:** [Enumerator::Lazy#eager](https://ruby-doc.org/core-2.7.0.preview3/Enumerator/Lazy.html#method-i-eager) +* **Documentation:** [Enumerator::Lazy#eager](https://ruby-doc.org/core-2.7.0/Enumerator/Lazy.html#method-i-eager) * **Code:** ```ruby # Imagine we read very large data file: @@ -734,7 +736,7 @@ Converts lazy enumerator back to eager one. * **Reason:** When constructing the enumerator, value yielding is frequently delegated to other methods, which accept blocks. Before the change, it was inconvenient to delegate. * **Discussion:** [Feature #15618](https://bugs.ruby-lang.org/issues/15618) -* **Documentation:** [Enumerator::Yielder#to_proc](https://ruby-doc.org/core-2.7.0.preview3/Enumerator/Yielder.html#method-i-to_proc) +* **Documentation:** [Enumerator::Yielder#to_proc](https://ruby-doc.org/core-2.7.0/Enumerator/Yielder.html#method-i-to_proc) * **Code:** ```ruby # Construct a enumerator which will pass all lines from all files from some folder: @@ -756,10 +758,10 @@ Converts lazy enumerator back to eager one. #### `Array#intersection` -Like `Array#union` and `#difference`, [added](2.6.html#TODO) in 2.6 as a explicitly-named and accepting multiple arguments alternatives for `#|` and `#-`, the new method is alternative for `#&`. +Like `Array#union` and `#difference`, [added](2.6.html#arrayunion-and-arraydifference) in 2.6 as a explicitly-named and accepting multiple arguments alternatives for `#|` and `#-`, the new method is alternative for `#&`. * **Discussion:** [Feature #16155](https://bugs.ruby-lang.org/issues/16155) -* **Documentation:** [Array#intersection](https://ruby-doc.org/core-2.7.0.preview3/Array.html#method-i-intersection) +* **Documentation:** [Array#intersection](https://ruby-doc.org/core-2.7.0/Array.html#method-i-intersection) * **Code:** ```ruby ['Ruby', 'Python', 'Perl'].intersection(['Ruby', 'Diamond', 'Perl'], ['Ruby', 'Nikole', 'Kate']) # => ["Ruby"] @@ -771,7 +773,7 @@ Like `Array#union` and `#difference`, [added](2.6.html#TODO) in 2.6 as a explici * **Reason:** `ObjectSpace::WeakMap` (the `Hash` variety that doesn't hold its contents from being garbage-collected) is mostly thought, as the name implies, as an "internal" thing. But turns out it can have some legitimate usages in a regular code, for example, implementing flexible caching (cache which "auto-cleans" on garbage collection). But before this change, keys for `WeekMap` wasn't allowed to be non-GC-able (for example, numbers and symbols), which prohibits some interesting usages. * **Discussion:** [Feature #16035](https://bugs.ruby-lang.org/issues/16035) -* **Documentation:** [WeekMap#[]=](https://ruby-doc.org/core-2.7.0.preview3/ObjectSpace/WeakMap.html#method-i-5B-5D-3D) +* **Documentation:** [WeekMap#[]=](https://ruby-doc.org/core-2.7.0/ObjectSpace/WeakMap.html#method-i-5B-5D-3D) * **Code:** ```ruby map = ObjectSpace::WeakMap.new @@ -791,7 +793,7 @@ Raises exception inside the resumed Fiber. * **Reason:** Ability to raise an exception inside Fiber makes control passing abilities more feature-complete. * **Discussion:** [Feature #10344](https://bugs.ruby-lang.org/issues/10344) -* **Documentation:** [Fiber#raise](https://ruby-doc.org/core-2.7.0.preview3/Fiber.html#method-i-raise) +* **Documentation:** [Fiber#raise](https://ruby-doc.org/core-2.7.0/Fiber.html#method-i-raise) * **Code:** ```ruby f = Fiber.new { @@ -813,7 +815,7 @@ Raises exception inside the resumed Fiber. In 2.6, `Range#===` was changed to use `#cover?` underneath, but not for `String`. This was fixed. * **Discussion:** [Bug #15449](https://bugs.ruby-lang.org/issues/15449) -* **Documentation:** [Range#===](https://ruby-doc.org/core-2.7.0.preview3/Range.html#method-i-3D-3D-3D) +* **Documentation:** [Range#===](https://ruby-doc.org/core-2.7.0/Range.html#method-i-3D-3D-3D) * **Code:** ```ruby case '2.6.5' @@ -837,7 +839,7 @@ In 2.6, `Range#===` was changed to use `#cover?` underneath, but not for `String ("a".."aa").minmax #=> ["a","z"] -- iteration through range goes till "aa", but then "aa" < "z", so "z" is maximum ``` * **Discussion:** [Feature #15807](https://bugs.ruby-lang.org/issues/15807) -* **Documentation:** [Range#minmax](https://ruby-doc.org/core-2.7.0.preview3/Range.html#method-i-minmax) +* **Documentation:** [Range#minmax](https://ruby-doc.org/core-2.7.0/Range.html#method-i-minmax) * **Code:** ```ruby (1..).minmax # => RangeError (cannot get the maximum of endless range) @@ -854,7 +856,7 @@ In 2.6, `Range#===` was changed to use `#cover?` underneath, but not for `String Auto-sets encoding to UTF-8 if byte-order mark is present in the stream. * **Discussion:** [Feature #15210](https://bugs.ruby-lang.org/issues/15210) -* **Documentation:** [IO#set_encoding_by_bom](https://ruby-doc.org/core-2.7.0.preview3/IO.html#method-i-set_encoding_by_bom) +* **Documentation:** [IO#set_encoding_by_bom](https://ruby-doc.org/core-2.7.0/IO.html#method-i-set_encoding_by_bom) * **Code:** ```ruby File.write("tmp/bom.txt", "\u{FEFF}мама") @@ -881,7 +883,7 @@ Auto-sets encoding to UTF-8 if byte-order mark is present in the stream. #### `Dir.glob` and `Dir.[]` not allow `\0`-separated patterns * **Discussion:** [Feature #14643](https://bugs.ruby-lang.org/issues/14643) -* **Documentation:** [Dir.glob](https://ruby-doc.org/core-2.7.0.preview3/Dir.html#method-c-glob) +* **Documentation:** [Dir.glob](https://ruby-doc.org/core-2.7.0/Dir.html#method-c-glob) * **Code:** ```ruby Dir.glob("*.rb\0*.md") @@ -913,10 +915,10 @@ Auto-sets encoding to UTF-8 if byte-order mark is present in the stream. #### `FrozenError`: receiver argument -In 2.6 several exception class constructors [were enhanced](TODO) so the user code could create them providing context, now `FrozenError` also got this functionality. +In 2.6 several exception class constructors [were enhanced](2.6.html#new-arguments-receiver-and-key) so the user code could create them providing context, now `FrozenError` also got this functionality. * **Discussion:** [Feature #15751](https://bugs.ruby-lang.org/issues/15751) -* **Documentation:** [FrozenError#new](https://ruby-doc.org/core-2.7.0.preview3/FrozenError.html#method-c-new) +* **Documentation:** [FrozenError#new](https://ruby-doc.org/core-2.7.0/FrozenError.html#method-c-new) * **Code:** ```ruby class AlwaysFrozenHash < Hash @@ -935,17 +937,17 @@ In 2.6 several exception class constructors [were enhanced](TODO) so the user co #### `$LOAD_PATH.resolve_feature_path` -`resolve_feature_path` was [introduced](TODO) in Ruby 2.6 as `RubyVM` method, now it is singleton method of `$LOAD_PATH` global variable. +`resolve_feature_path` was [introduced](2.6.html#rubyvmresolve_feature_path) in Ruby 2.6 as `RubyVM` method, now it is singleton method of `$LOAD_PATH` global variable. * **Reason:** It was argued that `RubyVM` should contain code specific for particular Ruby implementation (e.g. it can be different between CRuby/JRuby/TruffleRuby/etc.), while `resolve_feature_path` is a generic feature that should behave consistently between Ruby implementations. * **Discussion:** [Feature #15903](https://bugs.ruby-lang.org/issues/15903) -* **Documentation:** _As it turns, documenting global's singleton method is not easy. So it is just mentioned at_ [doc/globals.rdoc](https://docs.ruby-lang.org/en/master/globals_rdoc.html) +* **Documentation:** _As it turns, documenting global's singleton method is not easy. So it is just mentioned at_ [doc/globals.rdoc](https://ruby-doc.org/core-2.7.0/doc/globals_rdoc.html) * **Code:** ```ruby $LOAD_PATH.resolve_feature_path('net/http') # => [:rb, "/home/zverok/.rvm/rubies/ruby-head/lib/ruby/2.7.0/net/http.rb"] ``` -* **Notes:** As method was just moved, for details of `resolve_feature_path` behavior, see [2.6 changelog's entry](TODO) (with the exception for what described in the next section) +* **Notes:** As method was just moved, for details of `resolve_feature_path` behavior, see [2.6 changelog's entry](2.6.html#rubyvmresolve_feature_path) (with the exception for what described in the next section) #### `resolve_feature_path` behavior for loaded features fixed @@ -985,7 +987,7 @@ Allows to emit/suppress separate categories of warnings. * **Reason:** 2.7 introduced a lot of new deprecations (especially around keyword arguments, there can easily be thousands), and one "really experimental" feature (pattern matching), which emits warning about its experimental status on every use. To make working with older code, or experimenting with new features, less tiresome, the ability to turn warnings on and off per category was introduced. * **Discussion:** [Feature #16345](https://bugs.ruby-lang.org/issues/16345) (deprecated warnings), [Feature #16420](https://bugs.ruby-lang.org/issues/16420) (experimental warnings) -* **Documentation:** [Warning::[]](https://docs.ruby-lang.org/en/master/Warning.html#method-c-5B-5D), [Warning::[]=](https://docs.ruby-lang.org/en/master/Warning.html#method-c-5B-5D-3D) +* **Documentation:** [Warning::[]](https://ruby-doc.org/core-2.7.0/Warning.html#method-c-5B-5D), [Warning::[]=](https://ruby-doc.org/core-2.7.0/Warning.html#method-c-5B-5D-3D) * **Code:** ```ruby {a: 1} in {a:} @@ -1012,7 +1014,7 @@ Allows to emit/suppress separate categories of warnings. ``` * **Notes:** * The only existing categories currently are `:deprecated` (covers all deprecations) and `:experimental` (as of 2.7, covers only pattern matching) - * Note that turning of `:deprecated` warning will also mute the warning of features which was deprecated explicitly in your code, for example with [Module#deprecate_constant](https://docs.ruby-lang.org/en/master/Module.html#method-i-deprecate_constant) + * Note that turning of `:deprecated` warning will also mute the warning of features which was deprecated explicitly in your code, for example with [Module#deprecate_constant](https://ruby-doc.org/core-2.7.0/Module.html#method-i-deprecate_constant) ```ruby class HTTP NOT_FOUND = Exception.new @@ -1028,11 +1030,11 @@ Allows to emit/suppress separate categories of warnings. ## Standard library -* `Date` supports [new Japanese era](https://en.wikipedia.org/wiki/Reiwa) in parsing and rendering dates (generic [Date.parse](https://docs.ruby-lang.org/en/master/Date.html#method-c-parse) and [.jisx0301](https://docs.ruby-lang.org/en/master/Date.html#method-c-jisx0301)/[#jisx0301](https://docs.ruby-lang.org/en/master/Date.html#method-i-jisx0301)). Discussion: [Feature #15742](https://bugs.ruby-lang.org/issues/15742) +* `Date` supports [new Japanese era](https://en.wikipedia.org/wiki/Reiwa) in parsing and rendering dates (generic [Date.parse](https://ruby-doc.org/stdlib-2.7.0/libdoc/date/rdoc/Date.html#method-c-parse) and [.jisx0301](https://ruby-doc.org/stdlib-2.7.0/libdoc/date/rdoc/Date.html#method-c-jisx0301)/[#jisx0301](https://ruby-doc.org/stdlib-2.7.0/libdoc/date/rdoc/Date.html#method-i-jisx0301)). Discussion: [Feature #15742](https://bugs.ruby-lang.org/issues/15742) * [DelegateClass()](https://docs.ruby-lang.org/en/master/Object.html#method-i-DelegateClass) accepts a block to define delegates behavior on-the-fly. -* [Pathname.glob](https://ruby-doc.org/stdlib-2.7.0_rc1/libdoc/pathname/rdoc/Pathname.html#method-c-glob) passes third argument, if provided, to `Dir.glob`, allowing to specify `base:` for globbing. -* [Pathname()](https://ruby-doc.org/stdlib-2.7.0_rc1/libdoc/pathname/rdoc/Kernel.html#method-i-Pathname) method doesn't duplicates argument, if it was already a `Pathname` -* [OptionParser](https://ruby-doc.org/stdlib-2.7.0_rc1/libdoc/optparse/rdoc/OptionParser.html) now uses "Did you mean?" feature. Discussion: [Feature #16256](https://bugs.ruby-lang.org/issues/16256). +* [Pathname.glob](https://ruby-doc.org/stdlib-2.7.0/libdoc/pathname/rdoc/Pathname.html#method-c-glob) passes third argument, if provided, to `Dir.glob`, allowing to specify `base:` for globbing. +* [Pathname()](https://ruby-doc.org/stdlib-2.7.0/libdoc/pathname/rdoc/Kernel.html#method-i-Pathname) method doesn't duplicates argument, if it was already a `Pathname` +* [OptionParser](https://ruby-doc.org/stdlib-2.7.0/libdoc/optparse/rdoc/OptionParser.html) now uses "Did you mean?" feature. Discussion: [Feature #16256](https://bugs.ruby-lang.org/issues/16256). ```ruby require 'optparse' @@ -1049,8 +1051,8 @@ IRB, Ruby's default console, received the hugest update in years. Now it support ### Network and web -* [Net::HTTP#start](https://ruby-doc.org/stdlib-2.7.0_rc1/libdoc/net/http/rdoc/Net/HTTP.html#method-c-start): new optional keyword parameter `ipaddr:`, and [#ipaddr=](https://ruby-doc.org/stdlib-2.7.0_rc1/libdoc/net/http/rdoc/Net/HTTP.html#method-i-ipaddr) setter allows to set the address for the connection manually. Discussion: [Feature #5180](https://bugs.ruby-lang.org/issues/5180). -* `open-uri` library: 2 versions after more safe alias [was added](TODO), using `Kernel#open` finally became deprecated, and [URI.open](https://ruby-doc.org/stdlib-2.7.0_rc1/libdoc/open-uri/rdoc/URI.html#method-c-open) the main library's interface. Discussion: [Misc #15893](https://bugs.ruby-lang.org/issues/15893) +* [Net::HTTP#start](https://ruby-doc.org/stdlib-2.7.0/libdoc/net/http/rdoc/Net/HTTP.html#method-c-start): new optional keyword parameter `ipaddr:`, and [#ipaddr=](https://ruby-doc.org/stdlib-2.7.0/libdoc/net/http/rdoc/Net/HTTP.html#method-i-ipaddr) setter allows to set the address for the connection manually. Discussion: [Feature #5180](https://bugs.ruby-lang.org/issues/5180). +* `open-uri` library: 2 versions after more safe alias [was added](2.5.html#network-and-web), using `Kernel#open` finally became deprecated, and [URI.open](https://ruby-doc.org/stdlib-2.7.0/libdoc/open-uri/rdoc/URI.html#method-c-open) the main library's interface. Discussion: [Misc #15893](https://bugs.ruby-lang.org/issues/15893) ```ruby require 'open-uri' open('https://ruby-lang.org') @@ -1058,7 +1060,7 @@ IRB, Ruby's default console, received the hugest update in years. Now it support URI.open('https://ruby-lang.org') # => ok ``` -* [Net::FTP#features](https://ruby-doc.org/stdlib-2.7.0_rc1/libdoc/net/ftp/rdoc/Net/FTP.html#method-i-features) to check available features, and [Net::FTP#option](https://ruby-doc.org/stdlib-2.7.0_rc1/libdoc/net/ftp/rdoc/Net/FTP.html#method-i-option) to enable/disable each of them. Discussion: [Feature #15964](https://bugs.ruby-lang.org/issues/15964). +* [Net::FTP#features](https://ruby-doc.org/stdlib-2.7.0/libdoc/net/ftp/rdoc/Net/FTP.html#method-i-features) to check available features, and [Net::FTP#option](https://ruby-doc.org/stdlib-2.7.0/libdoc/net/ftp/rdoc/Net/FTP.html#method-i-option) to enable/disable each of them. Discussion: [Feature #15964](https://bugs.ruby-lang.org/issues/15964). ```ruby ftp = Net::FTP.new('speedtest.tele2.net') # TELE2's open FTP for speed tests ftp.features diff --git a/_src/lib/render.rb b/_src/lib/render.rb index fb76d6a..040a099 100644 --- a/_src/lib/render.rb +++ b/_src/lib/render.rb @@ -1,16 +1,17 @@ class Render < FileProcessor TRACKER_LINK_RE = %r{\[(?Bug|Feature|Misc) \#(?\d+)\]\(https://bugs\.ruby-lang\.org/issues/(?\d+(\#.+)?)\)} + DOC_URLS = '(?:https://ruby-doc\.org|https://docs.ruby-lang.org)' def call text .gsub('<>', File.mtime(path).strftime('%b %d, %Y')) .gsub(/\[(Bug|Feature|Misc) \#\d+\]\(.+?\)/, &method(:process_link)) .gsub( # links to official docs to just nicer links (with icon) - %r{\[([^\[\]]+ [^\[\]]+)\]\((https://ruby-doc\.org.+?)\)}, + %r{\[([^\[\]]+ [^\[\]]+)\]\((#{DOC_URLS}.+?)\)}, '\\1' ) .gsub( # links without spaces are typically class/method names, so wrapped in - %r{\[([^\[\]]+)\]\((https://ruby-doc\.org.+?)\)}, + %r{\[([^\[\]]+)\]\((#{DOC_URLS}.+?)\)}, '\\1' ) .gsub(%r{^\#{2,} (.+)$}) { |header| header + "[](##{Util.id(header)})" } # attach nice clickable links to headers diff --git a/_src/lib/toc.rb b/_src/lib/toc.rb index 920e406..bc5ad88 100644 --- a/_src/lib/toc.rb +++ b/_src/lib/toc.rb @@ -1,3 +1,5 @@ +require 'date' + module TOC START_CHAPTERS = [ {title: 'Introduction', path: '/'} @@ -18,7 +20,8 @@ module TOC def self.call(pathes) [ *START_CHAPTERS, - *pathes.map(&Item.method(:call)).flatten(1), + # Newest version is always on top + *pathes.sort.reverse.map(&Item.method(:call)).flatten(1), *FINAL_CHAPTERS ] end @@ -78,7 +81,10 @@ def rss_fields .then(&Kramdown::Document.method(:new)) .to_html - {published_at: pub, description: desc} + { + published_at: Date.parse(pub).strftime('%Y-%m-%d'), + description: desc + } end end end \ No newline at end of file From 3a7344f44732bca9ab2e063a17e7eaa5f419d644 Mon Sep 17 00:00:00 2001 From: zverok Date: Fri, 27 Dec 2019 13:12:29 +0200 Subject: [PATCH 4/6] +IRB screenshot --- 2.7.md | 22 ++++++++++++---------- _src/2.7.md | 4 +++- images/irb_2.7.png | Bin 0 -> 58219 bytes 3 files changed, 15 insertions(+), 11 deletions(-) create mode 100644 images/irb_2.7.png diff --git a/2.7.md b/2.7.md index 5bff964..70b184d 100644 --- a/2.7.md +++ b/2.7.md @@ -142,14 +142,14 @@ In addition to [endless range in Ruby 2.6](2.6.html#endless-range-1): `(start..) r.size # => Infinity ``` * **Notes:** - * Slightly "a-grammatic" name ("beginless" instead of "beginningless") is due to the fact that it is a range literally lacking `begin` property. - * Unfortunately, parser can not always handle beginless range without the help of the parenthises: + * Slightly "a-grammatical" name ("beginless" instead of "beginningless") is due to the fact that it is a range literally lacking `begin` property. + * Unfortunately, parser can not always handle beginless range without the help of the parentheses: ```ruby (1..10).grep ..5 # ArgumentError (wrong number of arguments (given 0, expected 1)) # ...because it is in fact parsed as ((1..10).grep()..5), e.g. range from grep results to 5 ``` - This may seem an esotheric problem, but it becomes less esotheric in DSLs. For example in RubySpec, one would like to write: + This may seem an esoteric problem, but it becomes less esoteric in DSLs. For example in RubySpec, one would like to write: ```ruby ruby_version ..'1.9' do # some tests for old Ruby @@ -177,7 +177,7 @@ In addition to [endless range in Ruby 2.6](2.6.html#endless-range-1): `(start..) " # This had been warned since 2.4; Now it raises a SyntaxError EOS ``` -* Modifier `rescue` parsing change (multiple assignement now consisent with singular): +* Modifier `rescue` parsing change (multiple assignment now consistent with singular): ```ruby a = raise rescue 1 @@ -239,7 +239,7 @@ Some older or accidental features are deprecated on the road to 3.0 and currentl # warning: Capturing the given block using Kernel#proc is deprecated; use `&block` instead # still prints "Hello" ``` -* Contrastingly, the flip-flop syntax deprecation, [introduced](2.6.html#misc) in 2.6, **is reverted**. It turned out that for brevity in text-processing scrips (including one-liners run as `ruby -e "print "`) the feature, however esotheric it may seem, has a justified usage. +* Contrastingly, the flip-flop syntax deprecation, [introduced](2.6.html#misc) in 2.6, **is reverted**. It turned out that for brevity in text-processing scrips (including one-liners run as `ruby -e "print "`) the feature, however esoteric it may seem, has a justified usage. * **Discussion:** Feature #5400. * **Documentation:** doc/syntax/control_expressions.rdoc#Flip-Flop * **Code:** @@ -362,7 +362,7 @@ Calling a private method with a literal `self` as the receiver is now allowed. ### `UnboundMethod#bind_call`[](#unboundmethodbind_call) -Binds `UnboundMethod` to a receiver and calls it. Semantically equiavalent to `unbound_method.bind(receiver).call(arguments)`, but doesn't produce intermediate `Method` object (which `bind` does). +Binds `UnboundMethod` to a receiver and calls it. Semantically equivalent to `unbound_method.bind(receiver).call(arguments)`, but doesn't produce intermediate `Method` object (which `bind` does). * **Reason:** The technique of storing unbound methods and binding them later is used in some metaprogramming-heavy code to robustly use "original" implementation. For example: ```ruby @@ -494,7 +494,7 @@ Allows to get several bits at once. `Complex#<=>(other)` now returns `nil` if the number has imaginary part, and behaves like `Numeric#<=>` if it does not. -* **Reason:** Method `#<=>` was explicitly undefined in `Complex` to underline the fact that linear order of complex numbers can't be established, but it was inconsistent with most of the other objects implementations (which return `nil` for impossible/incompatible comparison instead of rasing) +* **Reason:** Method `#<=>` was explicitly undefined in `Complex` to underline the fact that linear order of complex numbers can't be established, but it was inconsistent with most of the other objects implementations (which return `nil` for impossible/incompatible comparison instead of raising) * **Discussion:** [Feature #](https://bugs.ruby-lang.org/issues/) * **Documentation:** Complex#<=> * **Code:** @@ -513,7 +513,7 @@ Allows to get several bits at once. Several core methods now return frozen, deduplicated `String` instead of generating it every time the string is requested. -* **Reason:** Avoiding allocations of new strings for each `#to_s` of primitive objects can save dramatical amounts of memory. +* **Reason:** Avoiding allocations of new strings for each `#to_s` of primitive objects can save dramatic amounts of memory. * **Discussion:** Feature #16150 * **Affected methods:** NilClass#to_s, TrueClass#to_s, FalseClass#to_s, Module#name * **Code:** @@ -538,7 +538,7 @@ Several core methods now return frozen, deduplicated `String` instead of generat # Ruby 2.6: "true -- received" # Ruby 2.7: FrozenError (can't modify frozen String: "true") ``` - * The same change was proposed for `Symbol#to_s` (and could've been a dramatical improvement in some kinds of code), but the change turned out to be too disruptive. + * The same change was proposed for `Symbol#to_s` (and could've been a dramatic improvement in some kinds of code), but the change turned out to be too disruptive. #### `Symbol#start_with?` and `#end_with?`[](#symbolstart_with-and-end_with) @@ -697,7 +697,7 @@ Produces infinite enumerator by calling provided block and passing its result to Converts lazy enumerator back to eager one. -* **Reason:** When working with large data sequences, lazy enumerators are useful tools to not produce intermediate array on each step. But some data consuming methods expect to receive enumeratos that would really return data from methods like `take_while` and not just add them to pipeline. +* **Reason:** When working with large data sequences, lazy enumerators are useful tools to not produce intermediate array on each step. But some data consuming methods expect to receive enumerators that would really return data from methods like `take_while` and not just add them to pipeline. * **Discussion:** Feature #15901 * **Documentation:** Enumerator::Lazy#eager * **Code:** @@ -1049,6 +1049,8 @@ Allows to emit/suppress separate categories of warnings. IRB, Ruby's default console, received the hugest update in years. Now it supports multiline editing, syntax highlighting of input and (some of) output, auto-indentation and other modern console behavior. Small demonstration screenshot: +![](images/irb_2.7.png) + ### Network and web[](#network-and-web) * Net::HTTP#start: new optional keyword parameter `ipaddr:`, and #ipaddr= setter allows to set the address for the connection manually. Discussion: Feature #5180. diff --git a/_src/2.7.md b/_src/2.7.md index 55489cc..43c5dca 100644 --- a/_src/2.7.md +++ b/_src/2.7.md @@ -697,7 +697,7 @@ Produces infinite enumerator by calling provided block and passing its result to Converts lazy enumerator back to eager one. -* **Reason:** When working with large data sequences, lazy enumerators are useful tools to not produce intermediate array on each step. But some data consuming methods expect to receive enumeratos that would really return data from methods like `take_while` and not just add them to pipeline. +* **Reason:** When working with large data sequences, lazy enumerators are useful tools to not produce intermediate array on each step. But some data consuming methods expect to receive enumerators that would really return data from methods like `take_while` and not just add them to pipeline. * **Discussion:** [Feature #15901](https://bugs.ruby-lang.org/issues/15901) * **Documentation:** [Enumerator::Lazy#eager](https://ruby-doc.org/core-2.7.0/Enumerator/Lazy.html#method-i-eager) * **Code:** @@ -1049,6 +1049,8 @@ Allows to emit/suppress separate categories of warnings. IRB, Ruby's default console, received the hugest update in years. Now it supports multiline editing, syntax highlighting of input and (some of) output, auto-indentation and other modern console behavior. Small demonstration screenshot: +![](images/irb_2.7.png) + ### Network and web * [Net::HTTP#start](https://ruby-doc.org/stdlib-2.7.0/libdoc/net/http/rdoc/Net/HTTP.html#method-c-start): new optional keyword parameter `ipaddr:`, and [#ipaddr=](https://ruby-doc.org/stdlib-2.7.0/libdoc/net/http/rdoc/Net/HTTP.html#method-i-ipaddr) setter allows to set the address for the connection manually. Discussion: [Feature #5180](https://bugs.ruby-lang.org/issues/5180). diff --git a/images/irb_2.7.png b/images/irb_2.7.png new file mode 100644 index 0000000000000000000000000000000000000000..38e906418509aba0eac0f80fcafd86f262fcbcf6 GIT binary patch literal 58219 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%0Ar>)75P&rsW|-G*&WCY(}5mVOVT1PDY`}gVkdi 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 AMF0Q* literal 0 HcmV?d00001 From b10206d27f947073c57fe8b86ec8e0c8ae2d6095 Mon Sep 17 00:00:00 2001 From: zverok Date: Fri, 27 Dec 2019 13:15:29 +0200 Subject: [PATCH 5/6] Final changes --- History.md | 2 +- README.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index e4d526b..e925e32 100644 --- a/History.md +++ b/History.md @@ -6,7 +6,7 @@ * 2.7 changelog added! * In 2.7 changelog, links to docs _sometimes_ switched to "official" docs.ruby-lang.org website. Most of the time, we are linking to ruby-doc.org, which, while being unofficial, has two advantages: a) it looks a bit nicer and b) it doesn't mix what is in core and what came from standard libraries. Unfortunately, as of release date of 2.7, ruby-doc.org's rendering is unable to properly handle new core methods implemented in `.rb` files (compare GC docs on [official site](https://docs.ruby-lang.org/en/master/GC.html), and on [ruby-doc.org](https://ruby-doc.org/core-2.7.0/GC.html)) - +* [Atom feed](feed.xml) added to the site, allowing to put it in your favorite aggregator _(yes, I am aware it is almost 2020 and nobody does this anymore)_. ## 2019-10-14 diff --git a/README.md b/README.md index aab441f..70b9df5 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ Some things to know about the content: * I want to _eventually_ cover Ruby versions down to 1.8, or maybe even earlier, but it is currently work-in-progress, with the first priority of the recent release of **[Ruby 2.6](2.6.html)**, and then going down version by version in my free time. * **UPD 2019-06-06:** **[Ruby 2.5](2.5.html)** is now covered too. Despite being 1.5 years old news, I believe it is still important to cover it in the same manner as the recent version was. * **UPD 2019-10-14:** **[Ruby 2.4](2.4.html)** changelog added, and some other content changed. See [History](/History.html) for detail. +* **UPD 2019-12-27:** Newly released **[Ruby 2.7](2.7.html)** changelog added. _The source of the site can be found at [GitHub](https://github.com/rubyreferences/rubychanges). See also [Contributing](/Contributing.html) section._ From 3fe5b8ceba305a389725e4396e76dfbcfeadeb65 Mon Sep 17 00:00:00 2001 From: zverok Date: Fri, 27 Dec 2019 13:18:45 +0200 Subject: [PATCH 6/6] Update contributing --- Contributing.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Contributing.md b/Contributing.md index 4205e23..bfa270d 100644 --- a/Contributing.md +++ b/Contributing.md @@ -8,11 +8,13 @@ The repo gladly accepts contributions! Things you need to know, as of now: -* The source is in `_src/.md` (2.6 only); -* To "compile" the final Jekyll site, you need to run first `_src/script/toc.rb` (create TOC in `_data/book.yml`), and then `_src/script/postprocess.rb` (creates `./2.6.md` with a bit more sophisticated rendering). +* The source is in `_src/.md`; +* To "compile" the final Jekyll site, you need to run `rake` in the main folder, or separate tasks (visible with `rake -T`): + * `rake toc` (create TOC in `_data/book.yml`) + * `rake render` (postprocesses `./_src/.md`→`./.md` adding some nicer formatting not available in pure Markdown); +* Now you can run `jekyll serve` to preview the site locally. The main things to do, currently: * Proofread existing content; -* Add previous versions of Ruby; -* Make rendering scripts less ad-hoc. \ No newline at end of file +* Add previous versions of Ruby. \ No newline at end of file