/
2.6.md
730 lines (587 loc) · 35.8 KB
/
2.6.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
---
title: Ruby 2.6 changes
prev: 2.7
next: 2.5
description: Ruby 2.6 full and annotated changelog
---
# Ruby 2.6
* **Released at:** Dec 25, 2018 ([NEWS](https://github.com/ruby/ruby/blob/ruby_2_6/NEWS) file)
* **Status (as of <<date>>):** EOL, latest is 2.6.10
* **This document first published:** Dec 29, 2018
* **Last change to this document:** <<date>>
> **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.
## Highlights
* [Endless range](#endless-range-1)
* [`#then` "piping" method](#then-as-an-alias-for-yield_self)
* [Support for timezones in `Time`](#time-support-for-timezones)
* [`Proc` composition](#proc-composition)
* [Enumerator chaining](#enumerator-chaining)
* [`RubyVM::AbstractSyntaxTree`](#rubyvmabstractsyntaxtree)
## Language
### Endless range: `(1..)`
* **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)
* **Code:**
```ruby
# Usage
ary = %w[List of words]
ary[2..] # => ["words"]
case indicator
when 1...8 then # ...
when 8...15 then # ...
when 15.. then # ...
end
years.grep(2017...)
# Details of behavior:
(1..).end # => nil
(1..).to_a # RangeError (cannot convert endless range to an array)
(1..).each { } # hangs forever
(1..) == (1...) # => false
(1..).size # => Infinity
```
* **Follow-up:** "Beginless" range [was introduced](2.7.html#beginless-range) in 2.7.
### Non-ASCII constant names
Constant names may start with a non-ASCII capital letter.
* **Discussion:** [Feature #13770](https://bugs.ruby-lang.org/issues/13770)
* **Reason:** Why not?
### `else` in exception-handling context
In exception-handling context, `else` without any `rescue` is now a syntax error
* **Discussion:** [Feature #14606](https://bugs.ruby-lang.org/issues/14606)
* **Code:**
```ruby
# In Ruby 2.6 it would be SyntaxError: else without rescue is useless
[1,2,3].each do
p :foo
else
p :bar
end
```
The code above obviously contains some error (omitted `if` or something), but before Ruby 2.6 the interpreter would not complain, interpreting it as "begin → perform code that might raise → else (if nothing was raised) performing something", due to lesser-used Ruby feature of allowing `else` in an exception-handling construct.
### Refinements: improved visibility
Refinements are now compatible with `#public_send` and `#respond_to?`, and implicit `#to_proc`.
* **Reason:** This is part of the effort to make great yet neglected feature of refinements behave more naturally;
* **Discussions:** [Feature #14223](https://bugs.ruby-lang.org/issues/14223), [Feature #15326](https://bugs.ruby-lang.org/issues/15326), [Feature #15327](https://bugs.ruby-lang.org/issues/15327)
* **Code:**
```ruby
module StringExt
refine String do
def surround(before, after = before)
after + self + before
end
def to_proc
proc { |val| self % val }
end
end
end
using StringExt
'foo'.respond_to?(:surround) # => true in 2.6, false in 2.5
'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`](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](2.7.html#warningsdeprecations) in 2.7
## Core classes and modules
### `Kernel`
_Notice that methods defined in `Kernel` are typically available on any object, that's why these changes are important._
#### `#then` as an alias for `#yield_self`
* **Reason:** Since the introduction of `Kernel#yield_self` at Ruby 2.5, it was pointed out that the name chosen is too long for this basic method, and, unlike most of other core methods, says "how it is implemented" not the intention; after lots of discussion it was decided `#then` corresponds best to the method's goal.
* **Notice:** There is a controversy in the community about this alias, pointing out the fact that `#then` is a typical method name for promises.
* **Discussion:** [Feature #14594](https://bugs.ruby-lang.org/issues/14594)
* **Code:**
```ruby
[BASE_URL, path].join('/')
.then { |url| open(url).read }
.then { |body| JSON.parse(body, symbolyze_names: true) }
.dig(:data, :items)
.then { |items| File.write('response.yml', items.to_yaml) }
```
#### `<Numeric>()` methods have `exception:` argument
* **Reason:** As of Ruby 2.5, `Integer('x')` will raise `ArgumentError (invalid value for Integer(): x)`, but in a lot of cases a sane thing to desire is "convert it, if possible"
* **Discussion:** [Feature #12732](https://bugs.ruby-lang.org/issues/12732)
* **Affected methods:** `#Integer`, `#Float`, `#Rational`, `#Complex` and `#BigDecimal` (stdlib; previously known as `BigDecimal.new`)
* **Documentation:** [Kernel#Integer](https://ruby-doc.org/core-2.6/Kernel.html#method-i-Integer), [Kernel#Float](https://ruby-doc.org/core-2.6/Kernel.html#method-i-Float), [Kernel#Rational](https://ruby-doc.org/core-2.6/Kernel.html#method-i-Rational), [Kernel#Complex](https://ruby-doc.org/core-2.6/Kernel.html#method-i-Complex), [Kernel#BigDecimal](https://ruby-doc.org/stdlib-2.6/libdoc/bigdecimal/rdoc/Kernel.html#method-i-BigDecimal)
* **Code:**
```ruby
Integer('x') # => ArgumentError (invalid value for Integer(): "x")
Integer('x', exception: false) # => nil
```
#### `#system` has `exception:` argument
With `exception: true`, the method raises instead of returning `false` on non-0 exit code, or `nil` on command execution failure.
* **Discussion:** [Feature #14386](https://bugs.ruby-lang.org/issues/14386)
* **Documentation:** [Kernel#system](https://ruby-doc.org/core-2.6/Kernel.html#method-i-system) (unfortunately, seems documentation haven't been updated with new feature)
* **Code:**
```ruby
system('cat nonexistent.txt') # => false
system('ctat nonexistent.txt') # => nil
system('cat nonexistent.txt', exception: true) # RuntimeError (Command failed with exit 1: cat)
system('ctat nonexistent.txt', exception: true) # Errno::ENOENT (No such file or directory - ctat)
```
### `Module#method_defined?`: `inherit` argument
`Module#method_defined?` and similar methods accept an optional second argument. If it is `false`, only module's own methods would be returned.
* **Reason:** Other module introspection methods, like `#methods`, already have similar arguments. It may be important for meta-programming; like "when this module is included, it will redefine some host's methods, but only those that belong to host", or "test that all descendants of some abstract class redefine required methods".
* **Affected methods:** `#method_defined?`, `#private_method_defined?`, `#protected_method_defined?`, `#public_method_defined?`.
* **Discussion:** [Feature #14944](https://bugs.ruby-lang.org/issues/14944)
* **Documentation:** [Module#method_defined?](https://ruby-doc.org/core-2.6/Module.html#method-i-method_defined-3F), [Module#private_method_defined?](https://ruby-doc.org/core-2.6/Module.html#method-i-private_method_defined-3F), [Module#protected_method_defined?](https://ruby-doc.org/core-2.6/Module.html#method-i-protected_method_defined-3F), [Module#public_method_defined?](https://ruby-doc.org/core-2.6/Module.html#method-i-public_method_defined-3F)
* **Code:**
```ruby
Array.method_defined?(:chunk)
# => true
Array.method_defined?(:chunk, false)
# => false -- it is Enumerable's method
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
* **Reason:** When parsing a huge string, and each substring is used exactly once, it could be ineffective to first create the whole array of parts, and only then iterate through it; with block form, parts are just yielded one by one;
* **Discussion:** [Feature #4780](https://bugs.ruby-lang.org/issues/4780)
* **Documentation:** [String#split](https://ruby-doc.org/core-2.6/String.html#method-i-split)
* **Code:**
```ruby
"several\nlong\nlines".split("\n") { |part| puts part if part.start_with?('l') }
# prints:
# long
# lines
# => "several\nlong\nlines"
```
Note that in block form, the _original string_ will be returned, not the result of processing parts with block. To work with split results in a method-chaining style, one can utilize `Object#to_enum`:
```ruby
"several\nlong\nlines"
.to_enum(:split, "\n") # => Makes a enumerator, yielding each entry from split("\n")
.each_with_object(Hash.new(0)) { |ln, h| h[ln.length] += 1 }
# => {7=>1, 4=>1, 5=>1}
```
### `Time`: support for timezones
The concept of a "timezone object" is introduced for various `Time` methods. Ruby does not define any timezone classes by itself, but the API expected corresponds to that of [TZInfo::Timezone](https://www.rubydoc.info/gems/tzinfo/TZInfo/Timezone):
> A timezone argument must have `local_to_utc` and `utc_to_local` methods, and may have `name` and `abbr` methods.
* **Methods affected:**
* `.new` (timezone may be passed as a last argument, which previously accepted only raw UTC offsets)
* `.at` (new keyword `in:` argument)
* `#getlocal` (accepts timezone where previously only UTC offset was accepted)
* `#+`, `#-`, `#succ` (no new argument, but preserve timezone of the source)
* **Discussion:** [Feature #14850](https://bugs.ruby-lang.org/issues/14850)
* **Documentation:** [Time: Timezone argument](https://ruby-doc.org/core-2.6/Time.html#class-Time-label-Timezone+argument)
* **Reason:** Named timezones are more complicated than just "offset from UTC". Going over DST date, or between different years in country history, time could have the same timezone, but different UTC offset.
* **Code:**
```ruby
zone = TZInfo::Timezone.get('America/New_York')
time = Time.new(2018, 6, 1, 0, 0, 0, zone)
time.zone # => #<TZInfo::DataTimezone: America/New_York>
time.strftime('%H:%M %Z') # => "00:00 EDT"
time.utc_offset # => -14400 = -4 hours
time += 180 * 24*60*60 # + 180 days, summery->winter transition
time.utc_offset # => -18000, -5 hours -- daylight saving handled by timezone
```
* **Follow-up:** In Ruby 3.1, the new ways for handier constructing time with timezones [were introduced](3.1.html#in-parameter-for-constructing-time).
### `Proc` composition
`Proc` and `Method` classes now have `#>>` and `#<<` methods for functional composition.
* **Reason:** This was a long-anticipated feature for moving Ruby towards more functional code.
* **Discussion:** [Feature #6284](https://bugs.ruby-lang.org/issues/6284)
* **Code:**
```ruby
plus = ->(x, y) { x + y }
mul2 = ->(x) { x * 2 }
stringify = :to_s.to_proc
(plus >> mul2).call(5, 6)
# => 22
(mul2 >> stringify).call(5) # (5 * 2).to_s
# => "10"
(mul2 << stringify).call(5) # 5.to_s * 2
# => "55"
# Realistic examples:
# 1. Providing chain of functions instead of chaining map's:
URLS.map(&Faraday.method(:get) >> :body.to_proc >> JSON.method(:parse) >> ->(data) { data.dig('response', 'items')})
# 2. Storing chain of processings in constant:
RESPONSE_PROCESSOR = Faraday.method(:get) >>
:body.to_proc >>
JSON.method(:parse) >>
->(data) { data.dig('response', 'items')}
# ...later...
URLS.map(&RESPONSE_PROCESSOR)
# 3. Utilizing block-based DSLs (Sinatra-alike)
get '/my_endpoint', &parse_params >> perform_business_action >> render_response
```
* **Important notice**: Unlike any other places in Ruby (where objects are coerced into procs with `#to_proc` method), "what can be chained" is decided by existence of `#call` method; this means you CAN'T chain symbols (notice `:body.to_proc` above), but CAN chain some "command pattern" classes with `MyClass.call` API.
### `Array#union` and `Array#difference`
`Array#union` is like `|`, but also accepts multiple arguments; `Array#difference` is like `-`, but accepts multiple arguments.
* **Discussion:** [Feature #14097](https://bugs.ruby-lang.org/issues/14097)
* **Reason:** Multiple-argument methods are more effective, and better chainable than operator form
* **Documentation:** [Array#union](https://ruby-doc.org/core-2.6/Array.html#method-i-union), [Array#difference](https://ruby-doc.org/core-2.6/Array.html#method-i-difference)
* **Code:**
```ruby
[1, 2, 3].union([3, 4], [4, 5])
# => [1, 2, 3, 4, 5]
[1, 2, 3, 4].difference([3, 4], [1])
# => [2]
```
* **Notice:** There are also plans (discussed in the same ticket above) to introduce mutating `union!` form.
* **Follow-up:** Ruby 2.7 [added](2.7.html#arrayintersection) `#intersection` method.
### `Hash#merge` with multiple arguments
* **Discussion:** [Feature #15111](https://bugs.ruby-lang.org/issues/15111)
* **Methods affected:** `Hash#merge`, `Hash#merge!`, `Hash#update` (alias for `#merge!`)
* **Documentation:** [Hash#merge](https://ruby-doc.org/core-2.6/Hash.html#method-i-merge)
* **Code:**
```ruby
{a: 1, b: 2}.merge({b: 3, c: 4}, {c: 5, b: 6}) # => {a: 1, b: 6, c: 5}
{a: 1, b: 2}.merge({b: 3, c: 4}, {c: 5, b: 6}) { |key, oldval, newval| [oldval, newval] }
# => {a: 1, b: [[2, 3], 6], c: [4, 5]}
```
Note the last example: if the block is provided for conflict resolution, it is called repeatedly for **each pair** of values provided for the conflicting key (not `|key, *all_conflicting_values|` as one may expect).
### Enumerables
#### `#filter`/`#filter!`
`#select` is aliased as `#filter` (and `#select!` as `#filter!`, where applicable).
* **Reason:** It was argued for a long time that most of other languages name the concept "filter", so the new alias was added to lessen the confusion for newcomers.
* **Discussion:** [Feature #13784](https://bugs.ruby-lang.org/issues/13784)
* **Classes and modules affected:** `Enumerable`, `Enumerator`, `Enumerator::Lazy`, `Struct` (only `#filter`), `Array`, `Hash`, and `Set` of standard library (`#filter` and `#filter!`).
#### `#to_h` with a block
* **Reason:** It was noted that `.map { |...| [some_key, some_value] }.to_h` is a very common pattern and should be made DRY;
* **Discussion:** [Feature #15143](https://bugs.ruby-lang.org/issues/15143);
* **Classes and modules affected:** `Enumerable` and everything that includes it;
* **Documentation:** [Enumerable#to_h](https://ruby-doc.org/core-2.6/Enumerable.html#method-i-to_h), [Array#to_h](https://ruby-doc.org/core-2.6/Array.html#method-i-to_h), [ENV.to_h](https://ruby-doc.org/core-2.6/ENV.html#method-c-to_h), [Hash#to_h](https://ruby-doc.org/core-2.6/Hash.html#method-i-to_h), [Struct#to_h](https://ruby-doc.org/core-2.6/Struct.html#method-i-to_h)
* **Code:**
```ruby
{a: 1, b: 2, c: 3}.to_h { |k, v| [k.to_s, -v] } # => {'a' => -1, 'b' => -2, 'c' => -3}
File.readlines('test.txt').each_with_index.to_h { |l, i| [i, l] }
# => {0 => 'first line', 1 => 'second line', 2 => 'third line'}
```
#### `Enumerator::ArithmeticSequence`
`Range#step` and `Numeric#step` are now returning not an instance of `Enumerator`, but `Enumerator::ArithmeticSequence` instead.
* **Reason/Usage:** This feature was added by request of scientific Ruby community; it is useful for reusing results of `(from..to).step(s)` as a value object for idiomatic slicing of custom collections, see also `Range#%`.
* **Discussion:** [Feature #13904](https://bugs.ruby-lang.org/issues/13904)
* **Documentation:** [Enumerator::ArithmeticSequence](https://ruby-doc.org/core-2.6.0.preview2/Enumerator/ArithmeticSequence.html), [Range#step](https://ruby-doc.org/core-2.6/Range.html#method-i-step), [Numeric#step](https://ruby-doc.org/core-2.6/Numeric.html#method-i-step)
* **Code:**
```ruby
# Basic usage remains the same:
(1..10).step(2).to_a
# => [1, 3, 5, 7, 9]
enum = (1..10).step(2)
# => ((1..10).step(2))
enum.class
# => Enumerator::ArithmeticSequence
enum.class.ancestors
# => [Enumerator::ArithmeticSequence, Enumerator, Enumerable ...]
# So, it is just a specialized subclass of enumerator, adding this methods:
enum.begin # => 1
enum.end # => 10
enum.step # => 2
```
* **Follow-up:** In 3.0, `Array` slicing with `ArithmeticSequence` (from 1st to 10th, each 2nd element) [became possible](3.0.html#array-slicing-with-enumeratorarithmeticsequence).
#### `Enumerator` chaining
Several enumerators can now be chained into one with `Enumerator#+(other)` or `Enumerable#chain(list, of, enumerators)`. The result of the operation is `Enumerator::Chain` (specialized subclass of `Enumerator`).
* **Reason:** Cleaner expression of chaining enumeration for several sequences, especially for lazy enumerators
* **Discussion:** [Feature #15144](https://bugs.ruby-lang.org/issues/15144)
* **Documentation:** [Enumerator#+](https://ruby-doc.org/core-2.6/Enumerator.html#method-i-2B), [Enumerable#chain](https://ruby-doc.org/core-2.6/Enumerable.html#method-i-chain), [Enumerator::Chain](https://ruby-doc.org/core-2.6/Enumerator/Chain.html)
* **Code:**
```ruby
[1, 2, 3].chain
# => #<Enumerator::Chain: [[1, 2, 3]]>
[1, 2, 3].each + [4, 5, 6].each
# => #<Enumerator::Chain: [#<Enumerator: [1, 2, 3]:each>, #<Enumerator: [4, 5, 6]:each>]>
[1, 2, 3].chain([4, 5, 6])
# => #<Enumerator::Chain: [[1, 2, 3], [4, 5, 6]]>
# Realistic use-case:
# Take data from several sources, abstracted into enumerator, fetching it on demand
sources = URLS.lazy.map { |url| open(url).read }
.chain(LOCAL_FILES.lazy.map { |path| File.read(path) })
# ...then uniformely search several sources (lazy-loading them) for some value
sources.detect { |body| body.include?('Ruby 2.6') }
```
### `Range`
#### `Range#===` uses `#cover?` instead of `#include?`
* **Reason:** Previously, case equality operator used `#include?`, which underneath iterates through entire range (except for Numerics). With objects other than numbers it could be ineffective (creating thousands of objects), impossible (if there is no notion of "next object", but exists notion of order) or imprecise.
* **Discussion:** [Feature #14575](https://bugs.ruby-lang.org/issues/14575)
* **Code:**
```ruby
case DateTime.now
when Date.today..Date.today + 1
# this would've not been reached before Ruby 2.6
end
# this would raise "can't iterate from Gem::Version" before Ruby 2.6
case Gem::Version.new('2.4')
when Gem::Version.new('1.8.7')..Gem::Version.new('2.5')
end
gem 'ruby-ip'
require 'ip'
# this would perform ~0.5 sec before Ruby 2.6, iterating over 65536-elt sequence
case IP.new('192.168.10.4')
when IP.new('192.168.0.0')..IP.new('192.168.255.255')
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](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'
puts "yes"
else
puts "no"
end
```
#### `Range#cover?` accepts range argument
* **Discussion:** [Feature #14473](https://bugs.ruby-lang.org/issues/14473)
* **Code:**
```ruby
(1..5).cover?(2..3) # => true
```
#### `Range#%` alias
* **Reason/Usage:** It was proposed to have short syntax of complex array slicing, usable for math algorithms; see also `Enumerable::ArithmeticSequence`.
* **Discussion:** [Feature #14697](https://bugs.ruby-lang.org/issues/14697)
* **Code:**
```ruby
(5..20) % 2
# => ((5..20).%(2)) -- an instance of Enumerable::ArithmeticSequencee
some_fancy_collection[(5..20) % 2] # each second element in 5..20 range
```
Note that `()` around the range is mandatory here, because `5..20 % 2` will be parsed as `(5)..(20 % 2)`
* **Notice:** Ruby's `Array` doesn't support slicing with `Enumerable::ArithmeticSequencee` as of 2.6.
* **Follow-up:** In 3.0, `Array` slicing with `ArithmeticSequence` [became possible](3.0.html#array-slicing-with-enumeratorarithmeticsequence).
### Exceptions
#### New arguments: `receiver:` and `key:`
`NameError`, `NoMethodError` accept `:receiver` on creation; `KeyError` accepts `:receiver` and `:key`.
* **Reason:** Since Ruby 2.5, these exception classes were "introspectable": when you catch them, you can fetch the object that caused the problem and (in case of `KeyError`) problematic key; but there were no way to add that helpful data when raising an exception in your own code.
* **Discussion:** [Feature #14313](https://bugs.ruby-lang.org/issues/14313)
* **Documentation:** [NameError.new](https://ruby-doc.org/core-2.6/NameError.html#method-c-new), [NoMethodError.new](https://ruby-doc.org/core-2.6/NoMethodError.html#method-c-new) _(docs not updated)_, [KeyError.new](https://ruby-doc.org/core-2.6/KeyError.html#method-c-new)
* **Code:**
```ruby
class MyFancyCollection
def [](key)
# ...
raise KeyError.new("don't have this: #{key}", receiver: self, key: key)
# ...
end
end
```
* **Notice:** `<Exception>.new` syntax is the only way to pass new arguments, this would **not** work:
```ruby
raise KeyError, "don't have this: #{key}", receiver: self, key: key
```
* **Follow-up:** Ruby 2.7 [adds](2.7.html#frozenerror-receiver-argument) `receiver:` argument for `FrozenError`, too.
#### `Exception#full_message` options
`Exception#full_message` (which returns "exceptions how they are printed by Ruby", including message, class and backtrace) takes `:highlight` and `:order` options. This means client code can fine-tune which result it wants to achieve.
* **Reason:** Since introduction of new way for printing exceptions to STDERR (backtrace in reverse order and message is highlighted in bold), there were some improvements proposed to handle different contexts of exception printing.
* **Discussion:** [Bug #14324](https://bugs.ruby-lang.org/issues/14324)
* **Documentation:** [Exception#full_message](https://ruby-doc.org/core-2.6/Exception.html#method-i-full_message)
* **Code:**
```ruby
begin
{a: 1}.fetch(:b)
rescue => e
# highlight: true/false, order: :top/:bottom
p e.full_message(highlight: false, order: :top)
# "t.rb:2:in `fetch': key not found: :b (KeyError)\n\tfrom t.rb:2:in `<main>'\n"
p e.full_message(highlight: true, order: :bottom)
# "\e[1mTraceback\e[m (most recent call last):\n\t1: from t.rb:2:in `<main>'\nt.rb:2:in `fetch': \e[1mkey not found: :b (\e[1;4mKeyError\e[m\e[1m)\e[m\n"
p e.full_message
# Output could be either of two above, depending on whether STDERR is terminal
# or redirected with `ruby test.rb 2> err.log
end
```
* **Follow-up:** In Ruby 3.2, one more related method `#detailed_message` [was added](3.2.html#exceptiondetailed_message).
#### Exception output tweaking
* `Exception#cause` is now printed if STDERR is the terminal:
```ruby
begin
{a: 1}.fetch(:b)
rescue => e
raise 'something went wrong'
end
# Traceback (most recent call last):
# 1: from t.rb:2:in `<main>'
# t.rb:2:in `fetch': key not found: :b (KeyError)
# 1: from t.rb:1:in `<main>'
# t.rb:4:in `rescue in <main>': something went wrong (RuntimeError)
```
Notice the exception that led to `rescue`/`raise` block (`KeyError`) is printed too.
* **Discussion**: [Feature #8257](https://bugs.ruby-lang.org/issues/8257)
* Exception backtrace and error message are printed in reverse order when the exception is not caught and STDOUT is unchanged and a tty. [Feature #8661](https://bugs.ruby-lang.org/issues/8661)
* **Follow-up:** [Reverted completely](3.0.html#exception-output-order-is-changed----again) to pre-2.5 order in 3.0.
### Filesystem and IO
#### `Dir#each_child` and `Dir#children`
* **Discussion:** [Feature #13969](https://bugs.ruby-lang.org/issues/13969)
* **Documentation:** [Dir#each_child](https://ruby-doc.org/core-2.6/Dir.html#method-i-each_child), [Dir#children](https://ruby-doc.org/core-2.6/Dir.html#method-i-children)
* **Code:**
```ruby
Dir.new('.').children
# => ["_layouts", ".gitignore", "README.md", "_data", "_src", "_site", "_config.yml", "2.6.md", "js", "404.html", ".git", "images", "Gemfile", "css", "Gemfile.lock"]
```
#### IO open mode: `'x'`
When opening a new file for writing, `'wx'` can be specified to request the file does not exist before opening.
* **Discussion:** [Feature #11258](https://bugs.ruby-lang.org/issues/11258)
* **Documentation:** [IO: Open mode](https://ruby-doc.org/core-2.6/IO.html#method-c-new-label-IO+Open+Mode)
* **Code:**
```ruby
f1 = File.open('temp.txt', 'wx')
# => #<File:temp.txt>
f2 = File.open('temp.txt', 'wx') # it is already created by previous statement
# Errno::EEXIST (File exists @ rb_sysopen - temp.txt)
```
### Minor changes
* `Object#=~` is deprecated: [Feature #15231](https://bugs.ruby-lang.org/issues/15231) (but `NilClass#=~` is still allowed without deprecation notice).
* `Random.bytes`: [Feature #4938](https://bugs.ruby-lang.org/issues/4938) / [Random.bytes](https://ruby-doc.org/core-2.6/Random.html#method-c-bytes)
```ruby
Random.bytes(5)
# => "8\a\xB0\xD1V"
# ...will probably return different value for you, though :)
```
### Introspection
#### `Binding#source_location`
* **Reason:** Important usage for this new feature is proper reporting in block-based DSL usage: if something was inconsistent in the passed block, library code may use `block.binding.source_location` to report _where exactly_ problematic code came from.
* **Discussion:** [Feature #14230](https://bugs.ruby-lang.org/issues/14230)
* **Documentation:** [Binding#source_location](https://ruby-doc.org/core-2.6/Binding.html#method-i-source_location)
* **Code:**
```ruby
binding.source_location
# => ["(irb)", 114]
def my_dsl(&block)
puts "Evaluating block from #{block.binding.source_location.join(':')}"
end
my_dsl { ... } # Prints "Evaluating block from (irb):118"
```
#### `RubyVM.resolve_feature_path`
For string `name`, returns what path `require(name)` will load (without actually loading it).
* **Reason:** Static analysis of the program (finding the code it will use without really loading it); understanding where dependencies are coming from.
* **Discussion:** [Feature #15230](https://bugs.ruby-lang.org/issues/15230)
* **Documentation:** —
* **Code:**
```ruby
RubyVM.resolve_feature_path('net/http')
# => [:rb, "<...>/ruby-2.6.0/lib/ruby/2.6.0/net/http.rb"]
RubyVM.resolve_feature_path('syslog')
# => [:so, "<...>/ruby-2.6.0/lib/ruby/2.6.0/x86_64-linux/syslog.so"]
RubyVM.resolve_feature_path('garbage')
# LoadError (cannot load such file -- garbage)
require 'net/http'
RubyVM.resolve_feature_path('net/http')
# => [:rb, false] -- for already loaded libraries, the path would not be returned
# For gems:
RubyVM.resolve_feature_path('faraday')
# LoadError (cannot load such file -- faraday)
gem 'faraday' # without this, gems can not be deduced
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](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`
The new module provides an "official" Ruby parser, replacing stdlib Ripper (and third-party parsers). The module is considered experimental, and its API can change in the future versions.
* **Documentation:** [RubyVM::AbstractSyntaxTree](https://ruby-doc.org/core-2.6/RubyVM/AbstractSyntaxTree.html)
* **Code:**
```ruby
tree = RubyVM::AbstractSyntaxTree.parse('1+2')
# => #<RubyVM::AbstractSyntaxTree::Node(SCOPE(0) 1:0, 1:3): >
tree.type
# => :SCOPE
tree.children
# => [[], nil, #<RubyVM::AbstractSyntaxTree::Node(OPCALL(36) 1:0, 1:3): >]
tree.children[2].type
# => :OPCALL
tree.children[2].children
# => [#<RubyVM::AbstractSyntaxTree::Node(LIT(59) 1:0, 1:1): >, :+, #<RubyVM::AbstractSyntaxTree::Node(ARRAY(42) 1:2, 1:3): >]
tree.children[2].children[0].children
# => [1]
```
* **Note:** One may be excited about `RubyVM::AbstractSyntaxTree.of(proc)`, but it doesn't mean the "real" extraction of code from live `Proc`, rather a simple hack with trying to find proc's source file and parse it.
* **Follow-up:** In Ruby 3.2, new options were added for `parse`, allowing to:
* Perform [fault-tolerant parsing](3.2.html#error_tolerant-true-option-for-parsing) (parse syntactically wrong/incomplete code);
* [Preserve tokens](3.2.html#keep_tokens-true-option-for-parsing) from source code alongside nodes;
### `TracePoint` improvements
Ruby [TracePoint](https://ruby-doc.org/core-2.6/TracePoint.html) API allows to observe any events that are happening during program evaluation. In 2.6, there were several improvements to allow better control on event handling and their introspection.
#### `#parameters`
On `:b_call` and `:c_call` (block and method call events), `TracePoint` now provides `#parameters` method to fetch call params definitions.
* **Discussion:** [Feature #14694](https://bugs.ruby-lang.org/issues/14694)
* **Documentation:** [TracePoint#parameters](https://ruby-doc.org/core-2.6/TracePoint.html#method-i-parameters)
* **Code:**
```ruby
t = TracePoint.new(:b_call) { |tp| p [tp.event, tp.parameters] }
t.enable
[1, 2, 3].map { |x| x.to_s }
# [:b_call, [[:opt, :x]]]
# [:b_call, [[:opt, :x]]]
# [:b_call, [[:opt, :x]]]
```
* **Notice:** Format of return value is the same as for [Method#parameters](https://ruby-doc.org/core-2.6/Method.html#method-i-parameters)
#### `:script_compiled` event
Event fired when a new piece of Ruby code is loaded (through `eval`, `require` or `load`). `#instruction_sequence` on this event will return compiled [RubyVM::InstructionSequence](https://ruby-doc.org/core-2.6/RubyVM/InstructionSequence.html) object, and `#eval_script` will return the text of the script sent to `eval`.
* **Discussion:** [Feature #15287](https://bugs.ruby-lang.org/issues/15287)
* **Documentation:** [TracePoint: Events](https://ruby-doc.org/core-2.6/TracePoint.html#class-TracePoint-label-Events) (though new event seems to be omitted), [TracePoint#instruction_sequence](https://ruby-doc.org/core-2.6/TracePoint.html#method-i-instruction_sequence), [TracePoint#eval_script](https://ruby-doc.org/core-2.6/TracePoint.html#method-i-eval_script)
* **Code:**
```ruby
t = TracePoint.new(:script_compiled) { |tp| p [tp.event, tp.instruction_sequence, tp.eval_script] }
t.enable
eval('p 1')
# [:script_compiled, <RubyVM::InstructionSequence:<main>@(eval):1>, "p 1"]
require 'net/http'
# [:script_compiled, <RubyVM::InstructionSequence:<top (required)>@.../ruby-2.6.0/lib/ruby/2.6.0/net/http.rb:0>, nil]
# [:script_compiled, <RubyVM::InstructionSequence:<top (required)>@.../ruby-2.6.0/lib/ruby/2.6.0/net/protocol.rb:0>, nil]
# [:script_compiled, <RubyVM::InstructionSequence:<top (required)>@.../ruby-2.6.0/lib/ruby/2.6.0/socket.rb:0>, nil]
# .....
```
#### `#enable`: new params `target:` and `target_line:`
With new parameters provided, you can fine-tune for what methods or specific lines to catch events.
* **Reason:** Less trace garbage and performance footprint on tracing in complex codebase, allowing very specific tracepoints to be enabled even in production.
* **Discussion:** [Feature #15289](https://bugs.ruby-lang.org/issues/15289).
* **Documentation:** [TracePoint#enable](https://ruby-doc.org/core-2.6/TracePoint.html#method-i-enable) (not much, though...)
* **Code:**
```ruby
t = TracePoint.new(:line) { |tp| p tp }
def m1
p 1
end
def m2
p 2
end
t.enable(target: method(:m1))
m1
# prints #<TracePoint:line@test.rb:5 in `m1'>
m2
# prints nothing
```
* **Notice:** In absence of docs, we can at least point at [commit message](https://bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/66003):
> `code` should be consisted of `InstructionSequence` (iseq) (`RubyVM::InstructionSequence.of(code)` should not return nil). If code is a tree of iseq, `TracePoint` is enabled on all of iseqs in a tree.
* **Follow-ups:**
* In 2.7, [the docs](https://docs.ruby-lang.org/en/2.7.0/TracePoint.html#method-i-enable) have emerged, and a new parameter named `target_thread:` was introduced. It was missing from the official `NEWS`-file and therefore missing from this changelog _(which is a thing to be fixed!)_
* In 3.2, `target_thread:` began [defaulting to the current thread](3.2.html#tracepoint-for-block-default-to-trace-the-current-thread) with block form of `enable`.
## Standard library
* **Bundler is added to Standard Library:** [Feature #12733](https://bugs.ruby-lang.org/issues/12733).
* `Kernel#BigDecimal()` method now accepts `exception:` argument (see [above](#numeric-methods-have-exception-argument) for explanations)
* `URI` now can open `file://` URIs, `URI::File` class added.
* **Documentation:** [URI::File](https://ruby-doc.org/stdlib-2.6/libdoc/uri/rdoc/URI/File.html)
* **Discussion:** [Feature #14035](https://bugs.ruby-lang.org/issues/14035)
* `Net::HTTP`: `write_timeout` added
* **Documentation:** [Net::HTTP#write_timeout=](https://ruby-doc.org/stdlib-2.6/libdoc/net/http/rdoc/Net/HTTP.html#method-i-write_timeout-3D)
* **Discussion:** [Feature #13396](https://bugs.ruby-lang.org/issues/13396)
* `Net::HTTPServerError` renamed to `Net::HTTPClientException`
* **Discussion:** [Bug #14688](https://bugs.ruby-lang.org/issues/14688)
* `FileUtils.cp_lr` added
* **Documentation:** [FileUtils.cp_lr](https://ruby-doc.org/stdlib-2.6/libdoc/fileutils/rdoc/FileUtils.html#method-c-cp_lr)
* **Discussion:** [Feature #4189](https://bugs.ruby-lang.org/issues/4189)
### Large updated libraries
* **RubyGems** 3.0.1: [Changelog](https://github.com/rubygems/rubygems/blob/master/History.txt#L3)
* **RDoc**: _[Changelog](https://github.com/ruby/rdoc/blob/master/History.rdoc) is outdated._
* **CSV** 3.0.2: [Changelog](https://github.com/ruby/csv/blob/master/NEWS.md#302---2018-12-23).
* **REXML** 3.1.9: [Changelog](https://github.com/ruby/rexml/blob/master/NEWS.md#319---2018-12-20-version-3-1-9).
* **Matrix** 1.0.0: [Changelog](https://github.com/ruby/matrix/blob/master/CHANGELOG.md#v100--ruby-26).
* **RSS**: _No Changelog available_.
### 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.6:
* [e2mmap](https://github.com/ruby/e2mmap)
* [forwardable](https://github.com/ruby/forwardable)
* [irb](https://github.com/ruby/irb)
* [logger](https://github.com/ruby/logger)
* [matrix](https://github.com/ruby/matrix)
* [mutex_m](https://github.com/ruby/mutex_m)
* [ostruct](https://github.com/ruby/ostruct)
* [prime](https://github.com/ruby/prime)
* [rexml](https://github.com/ruby/rexml)
* [rss](https://github.com/ruby/rss)
* [shell](https://github.com/ruby/shell)
* [sync](https://github.com/ruby/sync)
* [thwait](https://github.com/ruby/thwait)
* [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;
* [34 (!) more libraries](3.0.html#libraries-promoted-to-default-gems) gemified in 3.0, and [3 more just dropped](3.0.html#libraries-promoted-to-default-gems) from the standard library (including webrick).