You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
# `set_temporary_name': the temporary name must not be a constant path to avoid confusion (ArgumentError)
154
161
155
-
# When the module with a temporary name is put into a constant,
156
-
# it receives a permanent name, which can't be changed anymore
157
-
C= dynamic_class
162
+
# When the module with a temporary name is put into a constant,
163
+
# it receives a permanent name, which can't be changed anymore
164
+
C= dynamic_class
158
165
159
-
# It affects all associated values (including modules)
166
+
# It affects all associated values (including modules)
160
167
161
-
dynamic_class #=> C
162
-
instance #=> #<C:0x0...>
163
-
instance.method(:foo) #=> #<Method: C#foo() ...>
164
-
dynamic_class::Nested#=> C::Nested
165
-
dynamic_class::OtherNested#=> C::OtherNested
168
+
dynamic_class #=> C
169
+
instance #=> #<C:0x0...>
170
+
instance.method(:foo) #=> #<Method: C#foo() ...>
171
+
dynamic_class::Nested#=> C::Nested
172
+
dynamic_class::OtherNested#=> C::OtherNested
166
173
167
-
dynamic_class.set_temporary_name("Can I have it back?")
168
-
# `set_temporary_name': can't change permanent name (RuntimeError)
174
+
dynamic_class.set_temporary_name("Can I have it back?")
175
+
# `set_temporary_name': can't change permanent name (RuntimeError)
169
176
170
-
# `nil` can be used to cleanup a temporary name:
171
-
other_class =Class.new
172
-
other_class.set_temporary_name("another one")
173
-
other_class #=> another one
174
-
other_class.set_temporary_name(nil)
175
-
other_class #=> #<Class:0x0...>
177
+
# `nil` can be used to cleanup a temporary name:
178
+
other_class =Class.new
179
+
other_class.set_temporary_name("another one")
180
+
other_class #=> another one
181
+
other_class.set_temporary_name(nil)
182
+
other_class #=> #<Class:0x0...>
176
183
```
177
-
***Notes:** Any phrase that used as a temporary name would be used verbatim; this might create very confusing `#inspect` results and error messages; so it is advised to use strings somehow implying that the name belong to a module. Imagine we wrap into classes with temporary names RSpec-style examples, and then there is a typo in such example:
184
+
***Notes:** Any phrase that used as a temporary name would be used verbatim; this might create very confusing `#inspect` results and error messages; so it is advised to use strings somehow implying that the name belong to a module. Imagine we wrap into classes with temporary names RSpec-style examples, and then there is a typo in the body of such example:
178
185
```ruby
179
186
it "works as a calculator"do
180
187
expec(2+2).to eq 4
@@ -200,6 +207,20 @@ A new "weak map" concept implementation. Unlike `ObjectSpace::WeakMap`, it compa
***Notes:** The class interface is significantly leaner than `WeakMap`'s, and doesn't provide any kind of iteration methods (which is very hard to implement and use correctly with weakly-referenced objects), so the new class is more like a black box with associations than a collection.
204
225
205
226
### `ObjectSpace::WeakMap#delete`
@@ -232,13 +253,14 @@ A new "weak map" concept implementation. Unlike `ObjectSpace::WeakMap`, it compa
232
253
233
254
### `Proc#dup` and `#clone` call `#initialize_dup` and `#initialize_copy`
234
255
235
-
***Reason:** A fix for an old inconsistency: `Object`'s `#dup` and `#clone` methods docs
256
+
***Reason:** A fix for an old inconsistency: `Object`'s `#dup` and `#clone` methods docs claimed that corresponding copying constructors would be called on object cloning/duplication, yet it was not true for `Proc`.
236
257
***Discussion:**[Feature #19362]
237
258
***Documentation:** — (Adheres to the behavior described for [Object#dup](https://docs.ruby-lang.org/en/master/Object.html#method-i-dup) and [#clone](https://docs.ruby-lang.org/en/master/Kernel.html#method-i-clone))
238
259
***Code:**
239
260
```ruby
240
-
# The examples would work the same with
261
+
# The examples would work the same way with
241
262
# #dup/#initialize_dup and #clone/#initialize_copy
263
+
242
264
classTaggedProc < Proc
243
265
attr_reader:tag
244
266
@@ -279,19 +301,21 @@ A new "weak map" concept implementation. Unlike `ObjectSpace::WeakMap`, it compa
279
301
280
302
### `Thread::Queue#freeze` and `SizedQueue#freeze` raise `TypeError`
281
303
282
-
***Reason:** The discussion was started with a bug report about `Queue` not respecting `#freeze` in any way (`#push` and `#pop` were still working after `#freeze` call). It was then decided that allowing to freeze a queue like any other collection (leaving it immutable) would have questionable semantics: as`Queue` is meant to be an inter-thread communication utility, freezing a queue while some thread waits for it would either leave this thread hanging, or would require `#freeze`'s functionality to extend for communication with dependent threads. Neither is a good option, so the behavior of the method was changed to communicate that queue freezing doesn't make sense.
304
+
***Reason:** The discussion was started with a bug report about `Queue` not respecting `#freeze` in any way (`#push` and `#pop` were still working after `#freeze` call). It was then decided that allowing to freeze a queue like any other collection (leaving it immutable) would have questionable semantics. As`Queue` is meant to be an inter-thread communication utility, freezing a queue while some thread waits for it would either leave this thread hanging, or would require `#freeze`'s functionality to extend for communication with dependent threads. Neither is a good option, so the behavior of the method was changed to communicate that queue freezing doesn't make sense.
283
305
***Discussion:**[Bug #17146]
284
306
***Documentation:**[Thread::Queue#freeze](https://docs.ruby-lang.org/en/master/Thread/Queue.html#method-i-freeze) and [Thread::SizedQueue#freeze](https://docs.ruby-lang.org/en/master/Thread/SizedQueue.html#method-i-freeze)
285
307
286
308
### `Range#reverse_each`
287
309
288
310
Specialized `Range#reverse_each` method is implemented.
289
311
290
-
***Reason:** Previously, `Range` didn't have a specialized `#reverse_each` method, so calling it would invoke a generic `Enumerable#reverse_each`. The latter works by converting the object to array, and then enumerating this array. In case of a `Range` this can be inefficient (producing large arrays) or impossible (when only upper bound of the range is defined)
312
+
***Reason:** Previously, `Range` didn't have a specialized `#reverse_each` method, so calling it invoked a generic `Enumerable#reverse_each`. The latter works by converting the object to array, and then enumerating this array. In case of a `Range` this can be inefficient (producing large arrays) or impossible (when only upper bound of the range is defined). It also went into infinite loop with endless ranges, trying to enumerate it all to convert into array, while the range can say beforehand that it would be impossible.
@@ -301,6 +325,8 @@ Specialized `Range#reverse_each` method is implemented.
301
325
# Ruby 3.2: can't iterate from NilClass (TypeError)
302
326
# Ruby 3.3: #=> [5, 4, 3]
303
327
328
+
# Explicit error for endless ranges:
329
+
304
330
(1...).reverse_each
305
331
# Ruby 3.2: hangs forever, trying to produce an array
306
332
# Ruby 3.3: `reverse_each': can't iterate from NilClass (TypeError)
@@ -397,31 +423,92 @@ Allows to trace when some exception was `rescue`'d in the code of interest.
397
423
```
398
424
***Notes:**
399
425
* The discussion was once [started](https://bugs.ruby-lang.org/issues/15973) from the proposal to make `lambda` change "lambiness" of a passed block, but it raises multiple issues (changing the block semantics mid-program is just one of them). In general, `lambda` as a _method_ is considered legacy, inferior to the `-> { }` lambda literal syntax, exactly due to problems like this: it looks like a regular method that receives a block, and therefore should be able accept _any_ block, but in fact it is "special" method. So in 3.0, there was a warning about `lambda(&proc_instance)`, and since 3.3, the warning finally turned into an error.
400
-
* There is exactly one occurrence in Ruby where block semantics _changes_ mid-flight:
401
426
402
427
### Deprecate subprocess creation with method dedicated to files
403
428
404
-
***Reason:**
429
+
***Reason:** Methods that are dedicated for opening/reading a file by name historically supported the special syntax of the argument: if it started with pipe character `|`, the subprocess was created and could've been used to communicate with an external command. The functionality is still explained in [Ruby 3.2 docs](https://docs.ruby-lang.org/en/3.2/Kernel.html#method-i-open). It, though, created a security vulnerability: even when the program's author didn't rely on that behavior, the malicious string could've been passed by the attacker instead of an innocent filename.
*[URI.open](https://docs.ruby-lang.org/en/master/URI.html#method-c-open) ([open-uri](https://github.com/ruby/open-uri) standard library)
415
439
***Code:**
440
+
```ruby
441
+
IO.read('| ls')
442
+
#=> contents of the current folder
443
+
444
+
Warning[:deprecated] =true# Or pass -w command-line option
445
+
IO.read('| ls')
446
+
# warning: Calling Kernel#open with a leading '|' is deprecated and will be removed in Ruby 4.0; use IO.popen instead
447
+
#=> contents of the current folder
448
+
```
416
449
***Notes:**
450
+
* The documentation for the corresponding methods was adjusted accordingly. Compare the documentation for `Kernel#open` from [3.2](https://docs.ruby-lang.org/en/3.2/Kernel.html#method-i-open) (explains and showcases the `|` trick) and [3.3](https://docs.ruby-lang.org/en/master/Kernel.html#method-i-open) (just mentions that there is a vulnerability to command injection attack).
451
+
* As advised by the warning, [IO.popen](https://docs.ruby-lang.org/en/master/IO.html#method-c-popen) is a specialized method when communicating with an external process is desired functionality:
452
+
```ruby
453
+
IO.popen('ls')
454
+
#=> contents of the current folder
455
+
```
456
+
*As the impact of the change might be big, note that target version for removal is set to **4.0**. To the best of my knowledge, there are no set date for major version yet.
417
457
418
458
### New `Warning` category: `:performance`
419
459
420
-
***Reason:**
460
+
Anew warning category was introduced for a code that is correct but is known to produces a performance problems. Onenew such warning was added for objects with too many "shape" variations.
***Code:**Here is an example of the new warning inplay:
465
+
```ruby
466
+
class C
467
+
def initialize(i)
468
+
instance_variable_set("@var_#{i}", i**2)
469
+
end
470
+
end
471
+
472
+
Warning[:performance] = true # or pass `-W:performance` command-line argument
473
+
474
+
(1..10).map { C.new(_1) }
475
+
# warning: Maximum shapes variations (8) reached by C, instance variables accesses will be slower.
476
+
```
477
+
The example is artificial, but it shows the principle:when we have more than 8 instances of the _same_class, but with different _list of instance variables_ (shape), we might have a performance problem. This means, for example, that a frequently-used class that has many methods with a memoization idiom (`@var ||= value` on the first access) would create the same problem, unless all of them would be initialized in the `initialize`, making all instances having the same shape:
478
+
```ruby
479
+
class C
480
+
# 9 different getters that create an instance varaible
481
+
# on the first access.
482
+
def var1 = @var1 ||= rand
483
+
def var2 = @var2 ||= rand
484
+
def var3 = @var3 ||= rand
485
+
def var4 = @var4 ||= rand
486
+
def var5 = @var5 ||= rand
487
+
def var6 = @var6 ||= rand
488
+
def var7 = @var7 ||= rand
489
+
def var8 = @var8 ||= rand
490
+
def var9 = @var9 ||= rand
491
+
end
492
+
493
+
Warning[:performance] = true
494
+
# Invoking different getters on different instances of the same class makes
495
+
# them have different set of instance variables.
496
+
(1..9).map { C.new.send("var#{_1}") }
497
+
# warning: Maximum shapes variations (8) reached by C, instance variables accesses will be slower.
# no warning. All objects have the same list of instance vars = the same shape
508
+
```
424
509
***Notes:**
510
+
*The warning category should be turned on explicitly by providing `-W:performance`CLI option or`Warning[:performance] = true` from the program.
511
+
***Additionalreading:** [Performance impact of the memoization idiom on modern Ruby](https://railsatscale.com/2023-10-24-memoization-pattern-and-object-shapes/) by Ruby core team member JeanBoussier.
425
512
426
513
### `Fiber#kill`
427
514
@@ -432,6 +519,7 @@ Terminates the Fiber by sending an exception inside it.
@@ -459,8 +547,6 @@ Terminates the Fiber by sending an exception inside it.
459
547
* **Code:**
460
548
* **Notes:**
461
549
462
-
added for checking if two ranges overlap. [[Feature #19839]]
463
-
464
550
### `Range#overlap?`
465
551
466
552
Checks for overlapping of two ranges.
@@ -500,6 +586,7 @@ In Ruby 3.3, it will just warn to prepare for a change.
500
586
***Code:** In the code below, where Ruby 3.3 currently produces a warning, Ruby 3.4 would treat `it` as an anonymous block argument; where Ruby 3.3 doesn't produce a warning, Ruby 3.4 would treat `it` as a local variable name or a method call (and would look for such names available in the scope).
501
587
```ruby
502
588
# The cases that are warned:
589
+
# -------------------------
503
590
# warning: `it` calls without arguments will refer to the first block param in Ruby 3.4; use it() or self.it
504
591
505
592
(1..3).map { it } # inside a block without explicit parameters
@@ -508,6 +595,7 @@ In Ruby 3.3, it will just warn to prepare for a change.
508
595
(1..3).map { it } # even if a method with name `it` exists in the scope
509
596
510
597
# The cases that are not warned:
598
+
# -----------------------------
511
599
512
600
it # not inside a block
513
601
(1..3).map { |x| it } # inside a block with named parameters
0 commit comments