Ruby Issue Tracking System: Issueshttps://bugs.ruby-lang.org/https://bugs.ruby-lang.org/favicon.ico?17113305112023-12-23T21:15:27ZRuby Issue Tracking System
Redmine Ruby master - Bug #20082 (Open): Killing fibers across threads: unexpected exceptionhttps://bugs.ruby-lang.org/issues/200822023-12-23T21:15:27Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>For providing the example in a changelog, I tried to imitate killing fibers belonging to other threads.<br>
Documentation <a href="https://docs.ruby-lang.org/en/master/Fiber.html#method-i-kill" class="external">claims</a></p>
<blockquote>
<p>Raises FiberError if called on a fiber belonging to another thread.</p>
</blockquote>
<p>So, I created this artificial example to check how it works:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">fibers</span> <span class="o">=</span> <span class="p">[]</span>
<span class="no">Thread</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span>
<span class="n">f</span> <span class="o">=</span> <span class="no">Fiber</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="p">).</span><span class="nf">each</span> <span class="p">{</span> <span class="nb">sleep</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span>
<span class="n">fibers</span> <span class="o"><<</span> <span class="n">f</span>
<span class="n">f</span><span class="p">.</span><span class="nf">resume</span>
<span class="p">}</span>
<span class="nb">sleep</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span> <span class="c1"># to make sure the thread has started</span>
<span class="n">fibers</span><span class="p">.</span><span class="nf">last</span><span class="p">.</span><span class="nf">kill</span>
</code></pre>
<p>The example indeed fails with <code>FiberError</code>, but the error message is confusing:</p>
<pre><code>in `kill': attempt to resume a resumed fiber (double resume) (FiberError)
</code></pre>
<p>Is this an expected message for such case? Or am I misunderstanding something?</p> Ruby master - Bug #20059 (Closed): TracePoint#enable can be called multiple times, increasing the...https://bugs.ruby-lang.org/issues/200592023-12-11T21:18:29Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>I just stumbled upon this accidentally (created a tracepoint with <code>TracePoint.trace</code>, and then <code>enable</code>d it).</p>
<p>My initial example</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">trace</span> <span class="o">=</span> <span class="no">TracePoint</span><span class="p">.</span><span class="nf">trace</span><span class="p">(</span><span class="ss">:raise</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">tp</span><span class="o">|</span>
<span class="nb">puts</span> <span class="s2">"Exception raised: </span><span class="si">#{</span><span class="n">tp</span><span class="p">.</span><span class="nf">raised_exception</span><span class="p">.</span><span class="nf">inspect</span><span class="si">}</span><span class="s2"> at </span><span class="si">#{</span><span class="n">tp</span><span class="p">.</span><span class="nf">path</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="n">tp</span><span class="p">.</span><span class="nf">lineno</span><span class="si">}</span><span class="s2">"</span>
<span class="k">end</span>
<span class="n">trace</span><span class="p">.</span><span class="nf">enable</span> <span class="c1"># shouldn't do this, it was already enabled!</span>
<span class="k">raise</span> <span class="s2">"foo"</span>
</code></pre>
<p>This prints</p>
<pre><code>Exception raised: #<RuntimeError: foo> at test.rb:5
Exception raised: #<RuntimeError: foo> at test.rb:5
</code></pre>
<p>Twice.<br>
If I'll remove the "unnecessary" <code>trace.enable</code>, it prints it once.</p>
<p>So the theory is "multiple enables = multiple invokations of a tracepoint"...</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">trace</span> <span class="o">=</span> <span class="no">TracePoint</span><span class="p">.</span><span class="nf">trace</span><span class="p">(</span><span class="ss">:raise</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">tp</span><span class="o">|</span>
<span class="nb">puts</span> <span class="s2">"Exception raised: </span><span class="si">#{</span><span class="n">tp</span><span class="p">.</span><span class="nf">raised_exception</span><span class="p">.</span><span class="nf">inspect</span><span class="si">}</span><span class="s2"> at </span><span class="si">#{</span><span class="n">tp</span><span class="p">.</span><span class="nf">path</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="n">tp</span><span class="p">.</span><span class="nf">lineno</span><span class="si">}</span><span class="s2">"</span>
<span class="k">end</span>
<span class="mi">5</span><span class="p">.</span><span class="nf">times</span> <span class="p">{</span> <span class="n">trace</span><span class="p">.</span><span class="nf">enable</span> <span class="p">}</span>
<span class="k">raise</span> <span class="s2">"foo"</span>
</code></pre>
<p>...and indeed, this code prints the "Exception raised:" message 6 times.</p>
<p>I don't see anything about this behavior in the <a href="https://docs.ruby-lang.org/en/master/TracePoint.html#method-i-enable" class="external">method's docs</a> or class docs, and can't think of a plausible explanation. But it was this way since Ruby 2.0, and either it <em>should</em> be this way, or it bothers nobody :)</p>
<p>I wonder:</p>
<ul>
<li>is there a reasonable explanation for this?</li>
<li>if so, shouldn't it be documented somewhere?..</li>
</ul>
<p>Or is it some part of a pattern of how the <code>TracePoint</code> works that I am missing here?..</p> Ruby master - Bug #20056 (Closed): Dir#chdir inconsistency with Dir.chdirhttps://bugs.ruby-lang.org/issues/200562023-12-10T18:16:11Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>I am not sure it is important; I just wanted to understand if this is intentional or accidental.</p>
<ol>
<li>There is no block form for <code>Dir#chdir</code>, unlike <code>Dir.chdir</code> (the form that will return to the previous directory when the block is finished)</li>
<li>
<code>Dir.chdir</code> returns <code>0</code>, while <code>Dir#chdir</code> returns <code>nil</code> (both seem to be not representing any particular internal value, just a hardcoded return value).</li>
</ol> Ruby master - Bug #19392 (Closed): Endless method and parsing prioritieshttps://bugs.ruby-lang.org/issues/193922023-01-30T15:04:18Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p><strong>Initial description</strong></p>
<p><a href="https://twitter.com/lucianghinda/status/1617783952353406977" class="external">Discovered</a> by Lucian Ghinda:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">def</span> <span class="nf">test</span> <span class="o">=</span> <span class="nb">puts</span><span class="p">(</span><span class="s2">"foo"</span><span class="p">)</span> <span class="ow">and</span> <span class="nb">puts</span><span class="p">(</span><span class="s2">"bar"</span><span class="p">)</span>
<span class="c1"># prints "bar" immediately</span>
<span class="nb">test</span>
<span class="c1"># prints "foo"</span>
</code></pre>
<p>It seems that it is a parser error, right?..</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">RubyVM</span><span class="o">::</span><span class="no">AbstractSyntaxTree</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s1">'def test = puts("foo") and puts("bar")'</span><span class="p">)</span>
<span class="c1"># => </span>
<span class="c1"># (SCOPE@1:0-1:38 </span>
<span class="c1"># tbl: [] </span>
<span class="c1"># args: nil </span>
<span class="c1"># body: </span>
<span class="c1"># (AND@1:0-1:38 </span>
<span class="c1"># (DEFN@1:0-1:22 </span>
<span class="c1"># mid: :test </span>
<span class="c1"># body: </span>
<span class="c1"># (SCOPE@1:0-1:22 </span>
<span class="c1"># tbl: [] </span>
<span class="c1"># args: </span>
<span class="c1"># (ARGS@1:0-1:8 pre_num: 0 pre_init: nil opt: nil first_post: nil post_num: 0 post_init: nil rest: nil kw: nil kwrest: nil block: nil)</span>
<span class="c1"># body: (FCALL@1:11-1:22 :puts (LIST@1:16-1:21 (STR@1:16-1:21 "foo") nil))))</span>
<span class="c1"># (FCALL@1:27-1:38 :puts (LIST@1:32-1:37 (STR@1:32-1:37 "bar") nil)))) </span>
</code></pre>
<p>E.g. it is parsed as</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="p">(</span><span class="k">def</span> <span class="nf">test</span> <span class="o">=</span> <span class="nb">puts</span><span class="p">(</span><span class="s2">"foo"</span><span class="p">))</span> <span class="ow">and</span> <span class="p">(</span><span class="nb">puts</span><span class="p">(</span><span class="s2">"bar"</span><span class="p">))</span>
</code></pre>
<p>...which is hardly intentional or have any practical use. The rightly parsed code in this case <em>can</em> have practical use, like</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">def</span> <span class="nf">write</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="o">=</span> <span class="no">File</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="vi">@filename</span><span class="p">,</span> <span class="n">data</span><span class="p">)</span> <span class="o">==</span> <span class="n">data</span><span class="p">.</span><span class="nf">size</span> <span class="ow">or</span> <span class="k">raise</span> <span class="s2">"Something went wrong"</span>
</code></pre>
<p><strong>Additional cases of what seems to be the same problem</strong></p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">def</span> <span class="nf">save</span> <span class="o">=</span> <span class="no">File</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="nb">name</span><span class="p">,</span> <span class="nb">self</span><span class="p">.</span><span class="nf">to_yaml</span><span class="p">)</span> <span class="k">unless</span> <span class="n">invalid?</span>
<span class="c1"># Parsed as:</span>
<span class="p">(</span><span class="k">def</span> <span class="nf">save</span> <span class="o">=</span> <span class="no">File</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="nb">name</span><span class="p">,</span> <span class="nb">self</span><span class="p">.</span><span class="nf">to_yaml</span><span class="p">))</span> <span class="k">unless</span> <span class="n">invalid?</span>
</code></pre>
<p>...which makes it very hard for the users to diagnose the real reason, see <a class="issue tracker-1 status-5 priority-4 priority-default closed" title="Bug: Can’t call an instance method inside an Endless method definition (Closed)" href="https://bugs.ruby-lang.org/issues/19731">#19731</a></p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span> <span class="o">=</span> <span class="vi">@a</span><span class="p">,</span> <span class="n">b</span> <span class="o">=</span> <span class="n">a</span><span class="p">,</span> <span class="n">b</span>
<span class="c1"># syntax error, unexpected ',', expecting end-of-input (SyntaxError) </span>
<span class="c1"># def initialize(a, b) = @a, b = a, b </span>
<span class="c1"># ^ </span>
<span class="c1"># Again, parsed as</span>
<span class="p">(</span><span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span> <span class="o">=</span> <span class="vi">@a</span><span class="p">),</span> <span class="n">b</span> <span class="o">=</span> <span class="n">a</span><span class="p">,</span> <span class="n">b</span>
</code></pre>
<p>While this one is at least diagnosed early, in pathological cases, it might lead to very subtle bugs:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="kp">private</span> <span class="k">def</span> <span class="nf">start</span> <span class="o">=</span> <span class="vi">@operation</span><span class="p">,</span> <span class="vi">@conversion</span> <span class="o">=</span> <span class="ss">:print</span><span class="p">,</span> <span class="ss">:to_s</span>
</code></pre>
<p>This code doesn't throw a syntax error, but its effect is very far from expected. Again, it is parsed as</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="kp">private</span><span class="p">(</span> <span class="p">(</span><span class="k">def</span> <span class="nf">start</span> <span class="o">=</span> <span class="vi">@operation</span><span class="p">),</span> <span class="vi">@conversion</span> <span class="o">=</span> <span class="ss">:print</span><span class="p">,</span> <span class="ss">:to_s</span> <span class="p">)</span>
</code></pre>
<p>...and ends up in:</p>
<ul>
<li>defining a private method <code>start</code>
</li>
<li>making private methods <code>:print</code> and <code>:to_s</code>
</li>
</ul> Ruby master - Feature #19370 (Closed): Anonymous parameters for blocks?https://bugs.ruby-lang.org/issues/193702023-01-23T10:23:25Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>Just to clarify: are anonymous parameters delegation is planned to support in blocks?</p>
<p>It would be a nice addition, if it is possible to implement:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># data in form [request method, URL, params]:</span>
<span class="p">[</span>
<span class="p">[</span><span class="ss">:get</span><span class="p">,</span> <span class="s1">'https://google.com'</span><span class="p">,</span> <span class="p">{</span><span class="ss">q: </span><span class="s1">'Ruby'</span><span class="p">},</span> <span class="p">{</span><span class="s1">'User-Argent'</span><span class="p">:</span> <span class="s1">'Google-Chrome'</span><span class="p">}],</span>
<span class="p">[</span><span class="ss">:post</span><span class="p">,</span> <span class="s1">'https://gist.github.com'</span><span class="p">,</span> <span class="s1">'body'</span><span class="p">],</span>
<span class="c1"># ...</span>
<span class="p">].</span><span class="nf">each</span> <span class="p">{</span> <span class="o">|</span><span class="nb">method</span><span class="p">,</span> <span class="o">*|</span> <span class="n">request</span><span class="p">(</span><span class="nb">method</span><span class="p">.</span><span class="nf">to_s</span><span class="p">.</span><span class="nf">upcase</span><span class="p">,</span> <span class="o">*</span><span class="p">)</span> <span class="p">}</span>
</code></pre>
<p>...and at the very least, consistent with what the method definition can have.</p>
<p>If they are NOT planned to be implemented, I believe that at least error messages should be made much clearer, because currently, this would happen while running the code above:</p>
<blockquote>
<p>no anonymous rest parameter (SyntaxError)</p>
</blockquote>
<p>I understand the reason (the <code>request</code> clause doesn't "see" anonymous parameter of the <strong>block</strong>, and claims that current <strong>method</strong> doesn't have them), but it looks honestly confusing and inconsistent.</p> Ruby master - Feature #19366 (Open): Rename/alias Refinedment#refined_class => #refined_modulehttps://bugs.ruby-lang.org/issues/193662023-01-22T14:29:31Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>In <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Module#defined_refinements (Closed)" href="https://bugs.ruby-lang.org/issues/12737">#12737</a>, <code>Refinement#refined_class</code> is introduced.</p>
<p>As "module" is more generic concept than "class", the name misleadingly implies that either this method doesn't returns refined <em>modules</em>, or modules can't be refined. This is obviously not true and trivially disproved:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">module</span> <span class="nn">Refs</span>
<span class="n">refine</span> <span class="no">Enumerable</span> <span class="k">do</span>
<span class="k">def</span> <span class="nf">foo</span> <span class="o">=</span> <span class="nb">puts</span> <span class="s1">'foo'</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="no">Refs</span><span class="p">.</span><span class="nf">refinements</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">refined_class</span>
<span class="c1">#=> Enumerable. Which is, well, not a class.</span>
<span class="c1"># The refinement is usable, so it is not a mute concept:</span>
<span class="n">using</span> <span class="no">Refs</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">].</span><span class="nf">foo</span> <span class="c1"># prints "foo" successfully</span>
</code></pre>
<p>I believe we refer to "modules" when some feature applies to modules and classes.</p>
<p>Unless there is some deeper consideration for the current naming (I don't see justification in <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Module#defined_refinements (Closed)" href="https://bugs.ruby-lang.org/issues/12737">#12737</a>, but I might miss something), the method should be renamed or aliased.</p>
<p>PS: Sorry for a huge amount of "nitpicky" issues after the version release. Typically, those concerns emerge while I am working on the <a href="https://rubyreferences.github.io/rubychanges/" class="external">changelog</a>, and I am usually trying to start the work at least a month before the release. Unfortunately, due to you-know-what, I was unable to do it in time this year.</p>
<p>After the 3.2 changelog, I plan to switch to the "master changelog" approach (following the development with the "current unreleased changes" document) this year. Unless I'll be unable to due to reasons not in my control.</p> Ruby master - Feature #19362 (Closed): #dup on Proc doesn't call initialize_duphttps://bugs.ruby-lang.org/issues/193622023-01-21T17:04:01Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>In <a class="issue tracker-1 status-5 priority-4 priority-default closed" title="Bug: Calling dup on a subclass of Proc returns a Proc and not the subclass (Closed)" href="https://bugs.ruby-lang.org/issues/17545">#17545</a>, <code>#dup</code> had changed to create an instance of the subclass.<br>
It, though, doesn't invoke <code>initialize_dup</code> of the subclass, unlike other standard classes.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">MyAry</span> <span class="o"><</span> <span class="no">Array</span>
<span class="k">def</span> <span class="nf">initialize_dup</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
<span class="nb">p</span><span class="p">(</span><span class="nb">self</span><span class="p">.</span><span class="nf">class</span><span class="p">,</span> <span class="o">...</span><span class="p">)</span>
<span class="k">super</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">class</span> <span class="nc">MyString</span> <span class="o"><</span> <span class="no">String</span>
<span class="k">def</span> <span class="nf">initialize_dup</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
<span class="nb">p</span><span class="p">(</span><span class="nb">self</span><span class="p">.</span><span class="nf">class</span><span class="p">,</span> <span class="o">...</span><span class="p">)</span>
<span class="k">super</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">class</span> <span class="nc">MyProc</span> <span class="o"><</span> <span class="no">Proc</span>
<span class="k">def</span> <span class="nf">initialize_dup</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
<span class="nb">p</span><span class="p">(</span><span class="nb">self</span><span class="p">.</span><span class="nf">class</span><span class="p">,</span> <span class="o">...</span><span class="p">)</span>
<span class="k">super</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="no">MyString</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="s1">'test'</span><span class="p">).</span><span class="nf">dup</span> <span class="c1"># prints MyString, "test"</span>
<span class="no">MyAry</span><span class="p">.</span><span class="nf">new</span><span class="p">([</span><span class="s1">'test'</span><span class="p">]).</span><span class="nf">dup</span> <span class="c1"># prints MyAry, ["test"]</span>
<span class="no">MyProc</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="s1">'test'</span> <span class="p">}.</span><span class="nf">dup</span> <span class="c1"># doesn't print anything</span>
</code></pre>
<p>This makes the change in <a class="issue tracker-1 status-5 priority-4 priority-default closed" title="Bug: Calling dup on a subclass of Proc returns a Proc and not the subclass (Closed)" href="https://bugs.ruby-lang.org/issues/17545">#17545</a> useless: while inheriting from core classes is indeed marginal, one of author's intention might be carrying additional information with the Proc instance, and bypassing <code>#initialize_dup</code> makes it impossible to maintain this information.</p>
<p>It seems that actually <code>#initialize_dup</code> is also invoked on the core classes themselves, but ignored on <code>Proc</code>.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">Array</span>
<span class="k">def</span> <span class="nf">initialize_dup</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
<span class="nb">p</span><span class="p">(</span><span class="nb">self</span><span class="p">.</span><span class="nf">class</span><span class="p">,</span> <span class="o">...</span><span class="p">)</span>
<span class="k">super</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">class</span> <span class="nc">String</span>
<span class="k">def</span> <span class="nf">initialize_dup</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
<span class="nb">p</span><span class="p">(</span><span class="nb">self</span><span class="p">.</span><span class="nf">class</span><span class="p">,</span> <span class="o">...</span><span class="p">)</span>
<span class="k">super</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">class</span> <span class="nc">Proc</span>
<span class="k">def</span> <span class="nf">initialize_dup</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
<span class="nb">p</span><span class="p">(</span><span class="nb">self</span><span class="p">.</span><span class="nf">class</span><span class="p">,</span> <span class="o">...</span><span class="p">)</span>
<span class="k">super</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="s1">'test'</span><span class="p">.</span><span class="nf">dup</span> <span class="c1"># prints String, "test"</span>
<span class="p">[</span><span class="s1">'test'</span><span class="p">].</span><span class="nf">dup</span> <span class="c1"># prints Array, ["test"]</span>
<span class="no">Proc</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="s1">'test'</span> <span class="p">}.</span><span class="nf">dup</span> <span class="c1"># doesn't print anything</span>
</code></pre>
<p>Which is an even more marginal problem but still an inconsistency.</p> Ruby master - Feature #19344 (Open): Regexp.new: stricter handling of second argumenthttps://bugs.ruby-lang.org/issues/193442023-01-15T17:10:53Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>Since Ruby 3.2 (<a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Support passing Regexp options as String to Regexp.new (Closed)" href="https://bugs.ruby-lang.org/issues/18788">#18788</a>), the second argument to <code>Regexp.new</code> can be:</p>
<ol>
<li>Integer: then it is treated as a combination of <code>Regexp::<constant></code> flags</li>
<li>String: then it is treated as a combination of string flags</li>
<li>
<code>nil</code> or <code>false</code>: then it is ignored</li>
<li>
<strong>any other truthy value</strong>: then it is treated as an "ignore case" option.</li>
</ol>
<p>The fourth one is confusing, especially since the introduction of the flexibility of flags: one might erroneously assume or forget the protocol, and the naive check will strengthen the assumption:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># maybe it accepts the array of string options?..</span>
<span class="no">Regexp</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="s1">'foo'</span><span class="p">,</span> <span class="sx">%w[i]</span><span class="p">)</span> <span class="c1">#=> /foo/i -- oh, seems it does</span>
<span class="c1"># would the symbol work?..</span>
<span class="no">Regexp</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="s1">'foo'</span><span class="p">,</span> <span class="ss">:i</span><span class="p">)</span> <span class="c1">#=> /foo/i -- awesome, it works!</span>
</code></pre>
<p>I propose to change (4) to only handle literal <code>true</code> value, and raise <code>TypeError</code> on any unsupported type.</p>
<p>On compatibility: I believe that whenever the usage of boolean to distinguish "ignore case/respect case" is deliberate (like <a href="https://github.com/rubygems/rubygems/blob/master/lib/rubygems/optparse/lib/optparse.rb#L442" class="external">in rubygems</a>), the code already passes true/false, not any random value. Otherwise, the change in Ruby 3.2 might also have broken it (what if it previously passed strings, meaning them to be "truthy values"?)</p>
<p>PS: BTW, the documentation for (4) was completely lost in Ruby 3.2, due to <a href="https://github.com/ruby/ruby/pull/5815" class="external">this PR</a>. cc <a class="user active user-mention" href="https://bugs.ruby-lang.org/users/1604">@jeremyevans0 (Jeremy Evans)</a>, <a class="user active user-mention" href="https://bugs.ruby-lang.org/users/52355">@burdettelamar (Burdette Lamar)</a></p> Ruby master - Feature #19324 (Open): Enumerator.product => Enumerable#producthttps://bugs.ruby-lang.org/issues/193242023-01-08T15:06:51Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>I know it might be too late after introducing a feature and releasing a version, but I find <code>Enumerator.product</code> quite confusing, and can't find any justification in <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Enumerator.product: Cartesian product of enumerables (Closed)" href="https://bugs.ruby-lang.org/issues/18685">#18685</a>.</p>
<p><strong>Problem 1: It is <code>Array#product</code> but <code>Enumerator.product</code></strong></p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">].</span><span class="nf">product</span><span class="p">([</span><span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">])</span>
<span class="c1"># => [[1, 4], [1, 5], [2, 4], [2, 5]]</span>
<span class="c1"># Usually, when we add methods to Enumerable/Enumerator which</span>
<span class="c1"># already array had before, it is symmetric, say...</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="kp">nil</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">].</span><span class="nf">compact</span> <span class="c1">#=> [1, 2, 3]</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="kp">nil</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">].</span><span class="nf">lazy</span><span class="p">.</span><span class="nf">compact</span><span class="p">.</span><span class="nf">first</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="c1">#=> [1, 2]</span>
<span class="c1"># But not in this case:</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">].</span><span class="nf">lazy</span><span class="p">.</span><span class="nf">product</span><span class="p">([</span><span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">]).</span><span class="nf">first</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="c1"># undefined method `product' for #<Enumerator::Lazy: [1, 2]> (NoMethodError)</span>
<span class="c1"># Because you "just" need to change it to:</span>
<span class="no">Enumerator</span><span class="p">.</span><span class="nf">product</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">].</span><span class="nf">lazy</span><span class="p">,</span> <span class="p">[</span><span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">]).</span><span class="nf">first</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="c1"># => [[1, 4], [1, 5]]</span>
</code></pre>
<p>No other method was "promoted" from Array this way</p>
<p>And in general, I believe core methods tend to belong to the first object in the expression and not be free module methods, Elixir style.</p>
<p><strong>Problem 2: It is one letter different from <code>Enumerator.produce</code></strong></p>
<p>I understand I might be biased here (as a person who proposed <code>produce</code>), and that method is not as popular (yet?) as I hoped, but still, two methods that do completely different things and differ by one letter, both being somewhat vague verbs (so it is easy to confuse them unless you did a lot of math and "product" is firmly set for set product in your head).</p>
<p>I believe that EITHER of two problems would be concerning enough, but the combination of them seems to be a strong enough argument to make the change?.. (Maybe with graceful deprecation of module method in one next version, but, considering the Ruby 3.2 is just released, maybe vice versa, fix the problem in the next minor release?..)</p> Ruby master - Misc #19304 (Open): Kernel vs Object documentationhttps://bugs.ruby-lang.org/issues/193042023-01-03T14:00:26Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>The restating of the problems raised in <a class="issue tracker-2 status-6 priority-4 priority-default closed" title="Feature: Move public methods from Kernel to Object (Rejected)" href="https://bugs.ruby-lang.org/issues/19300">#19300</a>, from scratch.</p>
<p>I believe it is rather a topic of <strong>community significance</strong> and would be happy if it is possible to <strong>discuss on dev-meeting</strong>, even if the outcome would probably "let's change RDoc in this or that way".</p>
<p>So, the problem statement:</p>
<ol>
<li>
<code>Kernel</code> module defines two "types" of methods: private ones, that pretend to be global (like <code>puts</code>), but actually available from inside every object; and public ones (like <code>#class</code> or <code>#frozen?</code>) that are present in every object including <code>Kernel</code>.</li>
<li>Since the beginning of times, docs provided an illusion that the second type of the methods belongs to <code>Object</code> class, which is, in fact, devoid of its own definitions, just includes <code>Kernel</code>. This was handled by special RDoc hack (which, basically, forcefully <a href="https://github.com/ruby/rdoc/blob/017bb9fa496ee0e0959facba694a053008d1ecb0/lib/rdoc/parser/c.rb#L477" class="external">reattached definition</a> of public methods if they are defined in <code>Kernel</code>, to <code>Object</code>)</li>
<li>The RDoc hack was working in C code only, so after introduction of <code>kernel.rb</code> the illusion started to crumble: methods like <code>#tap</code>, <code>#then</code>, <code>#class</code> <a href="https://docs.ruby-lang.org/en/3.2/Kernel.html" class="external">are now documented</a> as belonging to <code>Kernel</code> (breaking the tradition of public methods being documented in <code>Object</code>)</li>
<li>This is all inconsistent and confusing, and I believe, adds to friction both for newcomers and curious investigators of the language!</li>
</ol>
<p>Additionally, I believe:</p>
<ol>
<li>That the distinction of "two kinds of methods" is useful (saying from a practical experience with explaining Ruby while mentoring, writing articles, and discussing with colleagues)</li>
<li>But, considering everything, I agree with what <a class="user active user-mention" href="https://bugs.ruby-lang.org/users/772">@Eregon (Benoit Daloze)</a> and <a class="user active user-mention" href="https://bugs.ruby-lang.org/users/4">@nobu (Nobuyoshi Nakada)</a> say in <a class="issue tracker-2 status-6 priority-4 priority-default closed" title="Feature: Move public methods from Kernel to Object (Rejected)" href="https://bugs.ruby-lang.org/issues/19300">#19300</a>: pretending that methods belong not to the module they really belong to is not the optimal way to document things!</li>
</ol>
<p>So, the options I see (save for "do nothing and let things sort themselves somehow"):</p>
<ol>
<li>Change Ruby's implementation so public methods would really belong to <code>Object</code>. Actually, I don't believe anybody would agree to experiment on this scale, so just listing it here for completeness.</li>
<li>Just remove the RDoc hack; all methods would belong to <code>Kernel</code>, and maybe some introductory free-form text will instruct that they are different. <strong>Pro:</strong> Easy to do. <strong>Contra:</strong> The <code>Kernel</code> docs would be super-confusing.</li>
<li>Continue to maintain the hack in RDoc and extend it to <code>kernel.rb</code> (so methods like <code>#clone</code> and <code>#frozen?</code> would be still in <code>Object</code>). <strong>Pro:</strong> Relatively easy to do, will maintain structure many people used to. <strong>Contra:</strong> This is still a lie, methods belong to <code>Kernel</code>, not <code>Object</code>.</li>
<li>Adjust RDoc to a) be able to separately list <strong>public</strong> and <strong>private</strong> methods in two different lists, and add intro for <code>Kernel</code> explaining how to treat those. <strong>Pro:</strong> Probably the best correspondence to reality? <strong>Contra:</strong> Not sure how easy it is to change RDoc this way; and other renderers (like ruby-doc.com and rubyapi.org) would need to adjust themselves.</li>
</ol>
<p>So far, (4) looks the most reasonable (if (1) is 100% impossible, that is!)</p> Ruby master - Feature #19294 (Open): Enumerator.product works incorrectly with consuming enumeratorshttps://bugs.ruby-lang.org/issues/192942023-01-01T17:56:15Zzverok (Victor Shepelev)zverok.offline@gmail.com
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">s</span> <span class="o">=</span> <span class="no">StringIO</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="s1">'abc'</span><span class="p">)</span>
<span class="no">Enumerator</span><span class="p">.</span><span class="nf">product</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">],</span> <span class="n">s</span><span class="p">.</span><span class="nf">each_char</span><span class="p">).</span><span class="nf">to_a</span>
<span class="c1"># Expected: => [[1, "a"], [1, "b"], [1, "c"], [2, "a"], [2, "b"], [2, "c"], [3, "a"], [3, "b"], [3, "c"]]</span>
<span class="c1"># Actual: => [[1, "a"], [1, "b"], [1, "c"]]</span>
</code></pre>
<p>The implementation consumes the non-first enumerator to produce the first combination.</p>
<p>Somewhat related to the dilemma of consuming and non-consuming enumerators (<a class="issue tracker-2 status-1 priority-4 priority-default" title="Feature: Proposal: make a concept of "consuming enumerator" explicit (Open)" href="https://bugs.ruby-lang.org/issues/19061">#19061</a>).</p>
<p>PS: I noticed I don't understand why it is <code>Enumerator.product</code> and not <code>Enumerable#product</code>, but probably it is too late to raise the questions :(</p> Ruby master - Bug #19252 (Closed): IO::Buffer.pread last argument is always ignored?https://bugs.ruby-lang.org/issues/192522022-12-22T22:17:00Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>I am trying to document new <code>IO::Buffer#pread</code> and <code>#pwrite</code> methods.</p>
<p>According to what I can guess from code and tests, this code:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">IO</span><span class="o">::</span><span class="no">Buffer</span><span class="p">.</span><span class="nf">for</span><span class="p">(</span><span class="s1">'teststring'</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">buffer</span><span class="o">|</span>
<span class="n">buffer</span><span class="p">.</span><span class="nf">pread</span><span class="p">(</span><span class="no">File</span><span class="p">.</span><span class="nf">open</span><span class="p">(</span><span class="s1">'/dev/urandom'</span><span class="p">,</span> <span class="s1">'rb'</span><span class="p">),</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
<span class="nb">p</span> <span class="n">buffer</span><span class="p">.</span><span class="nf">get_string</span>
<span class="k">end</span>
</code></pre>
<p>...is expected to read <strong>2</strong> chars from <code>/dev/urandom</code>, starting from <strong>0</strong>th char, and write it to buffer starting from its <strong>3</strong>rd byte.<br>
Actual output:</p>
<pre><code>"F\x94ststring"
</code></pre>
<p>E.g. two first chars of the buffer are replaced.</p>
<p>Am I missing something, or is it a legitimate bug?..</p>
<p>UPD: Seems the same for <code>pwrite</code>:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">IO</span><span class="o">::</span><span class="no">Buffer</span><span class="p">.</span><span class="nf">for</span><span class="p">(</span><span class="s1">'1234567'</span><span class="p">).</span><span class="nf">pwrite</span><span class="p">(</span><span class="no">File</span><span class="p">.</span><span class="nf">open</span><span class="p">(</span><span class="s1">'tmp/buf.txt'</span><span class="p">,</span> <span class="s1">'wb'</span><span class="p">),</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span> <span class="c1"># expected to write "45" to file</span>
<span class="sb">`cat tmp/buf.txt`</span>
<span class="c1"># => "12"</span>
</code></pre> Ruby master - Misc #19247 (Closed): Ruby 3.2 documentation problems trackerhttps://bugs.ruby-lang.org/issues/192472022-12-20T18:57:26Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>Bugs and problems:</p>
<ul>
<li>
<a href="https://docs.ruby-lang.org/en/master/Fiber.html" class="external">Fiber</a>:
<ul>
<li>Formatting glitches for <code>::[]</code>, <code>::[]=</code>, <code>::storage</code>, <code>::storage=</code> (no references to companion methods)</li>
<li>Also there are some rendering glitches in Fiber, due to RDoc update? (Source didn't change)</li>
<li>After renaming SchedulerInterface to Scheduler, reference to it from Fiber are lost</li>
</ul>
</li>
<li>
<a href="https://docs.ruby-lang.org/en/master/Fiber/Scheduler.html" class="external">FiberScheduler</a> has no docs for new method <code>#io_select</code>
</li>
<li>
<a href="https://docs.ruby-lang.org/en/master/SyntaxError.html" class="external">SyntaxError</a>: new attribute <code>#path</code> wrongly rendered</li>
<li>
<a href="https://docs.ruby-lang.org/en/master/RubyVM/AbstractSyntaxTree.html" class="external">RubyVM::AbstractSyntaxTree</a>: <code>error_tolerant:</code> and <code>keep_tokens:</code> options aren't documented or mentioned in the docs</li>
<li>
<a href="https://docs.ruby-lang.org/en/master/Set.html" class="external">Set</a>: docs still start with <code>require 'set'</code> :)</li>
<li>
<a href="https://docs.ruby-lang.org/en/master/Struct.html" class="external">Struct</a>: no mention of the changed requirement about keyword initialization.</li>
<li>
<a href="https://docs.ruby-lang.org/en/master/Time.html#method-i-deconstruct_keys" class="external">Time#deconstruct_keys</a> instead of listing keys refers to "the same as returned by to_h", which we decided not to implement, my bad!</li>
<li>
<a href="https://docs.ruby-lang.org/en/master/UnboundMethod.html#method-i-3D-3D" class="external">UnboundMethod#==</a> says that methods are equal if are bound to the same object :) (shares docs with <code>Method</code>?..)</li>
<li>
<a href="https://docs.ruby-lang.org/en/master/GC.html#method-c-latest_gc_info" class="external">GC::latest_gc_info</a> rendering glitch (old probably). It also lacks any docs about the structure of the hash returned.</li>
<li>
<a href="https://docs.ruby-lang.org/en/master/ObjectSpace.html#method-i-dump_all" class="external">ObjectSpace.dump_all</a> and several companion methods are weirdly documented as instance methods (RDoc glitch, doubtfully easy to fix)</li>
</ul>
<p>Things that could be improved:</p>
<ul>
<li>
<a href="https://docs.ruby-lang.org/en/master/Module.html#method-i-undefined_instance_methods" class="external">Module#undefined_instance_methods</a> is not really helpful :)</li>
<li>
<a href="https://docs.ruby-lang.org/en/master/Refinement.html#method-i-refined_class" class="external">Refinement#refined_class</a> can use better explanation/example?</li>
<li>
<a href="https://docs.ruby-lang.org/en/master/Thread.html#method-c-each_caller_location" class="external">Thread::each_caller_location</a> can use an example? (Also wrongly referred as instance method in <a href="https://docs.ruby-lang.org/en/master/NEWS_md.html#label-Core+classes+updates" class="external">NEWS</a>)</li>
</ul>
<p>I'll try to work on those ASAP.</p>
<p>UPD 2022-12-21:</p>
<ul>
<li>
<a href="https://docs.ruby-lang.org/en/master/IO.html#class-IO-label-Open+Options" class="external">IO#Open Options</a> don't mention <code>path:</code>.</li>
</ul> Ruby master - Feature #19071 (Closed): Add Time#deconstruct, #deconstruct_keys, and #to_hhttps://bugs.ruby-lang.org/issues/190712022-10-19T19:21:04Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>I believe that <code>Time</code> being suitable for pattern-matching is a reasonable feature with many possible usages, which will increase usability of <code>Time</code> and would be a good show case for pattern matching.</p>
<p><strong>Implementation decisions</strong></p>
<p><code>Time#deconstruct</code>:</p>
<ul>
<li>returns time components in order <code>[year, month, mday, hour, min, sec, subsec]</code>
</li>
<li>I believe the highest-to-lowest order is the only reasonable/guessable, and there is no point to put into the array <em>all</em> of the time information available (e.g. zone, wday, yday)</li>
<li>I am not sure (and open to discussion) about <code>subsec</code>. It seems to me the most basic sub-second unit of Time, but I might be wrong; also, it might be not that useful for array deconstruction.</li>
</ul>
<p>Possible usage:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">case</span> <span class="n">tm</span>
<span class="k">in</span> <span class="p">[</span><span class="o">...</span><span class="mi">2022</span><span class="p">,</span> <span class="o">*</span><span class="p">]</span>
<span class="nb">puts</span> <span class="s2">"previous year"</span>
<span class="k">in</span> <span class="p">[</span><span class="mi">2022</span><span class="p">,</span> <span class="mi">1</span><span class="o">..</span><span class="mi">6</span><span class="p">,</span> <span class="o">*</span><span class="p">]</span>
<span class="nb">puts</span> <span class="s2">"Q1-2"</span>
<span class="k">in</span> <span class="p">[</span><span class="mi">2022</span><span class="p">,</span> <span class="mi">7</span><span class="o">..</span><span class="mi">9</span><span class="p">,</span> <span class="o">*</span><span class="p">]</span>
<span class="nb">puts</span> <span class="s2">"Q3"</span>
<span class="k">in</span> <span class="p">[</span><span class="mi">2022</span><span class="p">,</span> <span class="n">month</span><span class="p">,</span> <span class="n">day</span><span class="p">,</span> <span class="o">*</span><span class="p">]</span>
<span class="nb">puts</span> <span class="s2">"Current quarter, </span><span class="si">#{</span><span class="n">day</span><span class="si">}</span><span class="s2">/</span><span class="si">#{</span><span class="n">month</span><span class="si">}</span><span class="s2">"</span>
<span class="k">end</span>
</code></pre>
<p><code>Time#deconstruct_keys</code>:</p>
<ul>
<li>chosen keys: <code>[:year, :month, :day, :yday, :wday, :hour, :min, :sec, :subsec, :dst, :zone]</code>
</li>
<li>I am open to discussing whether we should include other subsecond units (or any whatsoever)</li>
<li>It might be useful (but too loose interface) to support <code>mon</code> as a synonym for <code>month</code>?.. But might be confusing if somebody will unpack the <code>**rest</code>
</li>
<li>
<code>day</code>, not <code>mday</code>, seems most reasonable</li>
</ul>
<p>Possible usages:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">case</span> <span class="n">t</span>
<span class="k">in</span> <span class="ss">year: </span><span class="o">...</span><span class="mi">2022</span>
<span class="nb">puts</span> <span class="s2">"too old"</span>
<span class="k">in</span> <span class="ss">month: </span><span class="o">..</span><span class="mi">9</span>
<span class="nb">puts</span> <span class="s2">"quarter 1-3"</span>
<span class="k">in</span> <span class="ss">wday: </span><span class="mi">1</span><span class="o">..</span><span class="mi">5</span><span class="p">,</span> <span class="ss">month:
</span><span class="nb">puts</span> <span class="s2">"working day in month </span><span class="si">#{</span><span class="n">month</span><span class="si">}</span><span class="s2">"</span>
<span class="k">end</span>
<span class="k">if</span> <span class="n">t</span> <span class="k">in</span> <span class="no">Time</span><span class="p">(</span><span class="ss">wday: </span><span class="mi">3</span><span class="p">,</span> <span class="ss">day: </span><span class="o">..</span><span class="mi">7</span><span class="p">)</span>
<span class="nb">puts</span> <span class="s2">"first Wednesday of the month"</span>
<span class="k">end</span>
</code></pre>
<p><code>Time#to_h</code>:</p>
<ul>
<li>added on a "why not" basis :) As we already have "convert to hash" in the form of <code>deconstruct_keys(nil)</code>, having a canonic form seems harmles. Open for discussion.</li>
<li>keys are the same as for <code>deconstruct_keys</code>
</li>
</ul>
<p>Pull request: <a href="https://github.com/ruby/ruby/pull/6594" class="external">https://github.com/ruby/ruby/pull/6594</a></p> Ruby master - Feature #19061 (Open): Proposal: make a concept of "consuming enumerator" explicithttps://bugs.ruby-lang.org/issues/190612022-10-15T15:00:10Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p><strong>The problem</strong></p>
<p>Let's imagine this synthetic data:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">lines</span> <span class="o">=</span> <span class="p">[</span>
<span class="s2">"--EMAIL--"</span><span class="p">,</span>
<span class="s2">"From: zverok.offline@gmail.com"</span><span class="p">,</span>
<span class="s2">"To; bugs@ruby-lang.org"</span><span class="p">,</span>
<span class="s2">"Subject: Consuming Enumerators"</span><span class="p">,</span>
<span class="s2">""</span><span class="p">,</span>
<span class="s2">"Here, I am presenting the following proposal."</span><span class="p">,</span>
<span class="s2">"Let's talk about consuming enumerators..."</span>
<span class="p">]</span>
</code></pre>
<p>The logic of parsing it is more or less clear:</p>
<ul>
<li>skip the first line</li>
<li>take lines until meet empty, to read the header</li>
<li>take the rest of the lines to read the body</li>
</ul>
<p>It can be easily translated into Ruby code, almost literally:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">def</span> <span class="nf">parse</span><span class="p">(</span><span class="n">enumerator</span><span class="p">)</span>
<span class="nb">puts</span> <span class="s2">"Testing: </span><span class="si">#{</span><span class="n">enumerator</span><span class="p">.</span><span class="nf">inspect</span><span class="si">}</span><span class="s2">"</span>
<span class="n">enumerator</span><span class="p">.</span><span class="nf">next</span>
<span class="nb">p</span> <span class="n">enumerator</span><span class="p">.</span><span class="nf">take_while</span> <span class="p">{</span> <span class="o">!</span><span class="n">_1</span><span class="p">.</span><span class="nf">empty?</span> <span class="p">}</span>
<span class="nb">p</span> <span class="n">enumerator</span><span class="p">.</span><span class="nf">to_a</span>
<span class="k">end</span>
</code></pre>
<p>Now, let's try this code with two different enumerators on those lines:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="nb">require</span> <span class="s1">'stringio'</span>
<span class="n">enumerator1</span> <span class="o">=</span> <span class="n">lines</span><span class="p">.</span><span class="nf">each</span>
<span class="n">enumerator2</span> <span class="o">=</span> <span class="no">StringIO</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">lines</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="p">)).</span><span class="nf">each_line</span><span class="p">(</span><span class="ss">chomp: </span><span class="kp">true</span><span class="p">)</span>
<span class="nb">puts</span> <span class="s2">"Array#each"</span>
<span class="n">parse</span><span class="p">(</span><span class="n">enumerator1</span><span class="p">)</span>
<span class="nb">puts</span>
<span class="nb">puts</span> <span class="s2">"StringIO#each_line"</span>
<span class="n">parse</span><span class="p">(</span><span class="n">enumerator2</span><span class="p">)</span>
</code></pre>
<p>Output (as you probably already guessed):</p>
<pre><code>Array#each
Testing: #<Enumerator: [...]:each>
["--EMAIL--", "From: zverok.offline@gmail.com", "To; bugs@ruby-lang.org", "Subject: Consuming Enumerators"]
["--EMAIL--", "From: zverok.offline@gmail.com", "To; bugs@ruby-lang.org", "Subject: Consuming Enumerators", "", "Here, I am presenting the following proposal.", "Let's talk about consuming enumerators..."]
StringIO#each_line
Testing: #<Enumerator: #<StringIO:0x00005581018c50a0>:each_line(chomp: true)>
["From: zverok.offline@gmail.com", "To; bugs@ruby-lang.org", "Subject: Consuming Enumerators"]
["Here, I am presenting the following proposal.", "Let's talk about consuming enumerators..."]
</code></pre>
<p>Only the second enumerator behaves the way we wanted it to.<br>
Things to notice here:</p>
<ol>
<li>Both enumerators are of the same class, "just enumerator," but they behave differently: one of them is <strong>consuming</strong> data on each iteration method, the other does not; but there is no programmatic way to tell whether some enumerator instance is consuming</li>
<li>There is no easy way to <strong>make a non-consuming enumerator behave in a consuming way</strong>, to open a possibility of a sequence of processing "skip this, take that, take the rest"</li>
</ol>
<p><strong>Concrete proposal</strong></p>
<ol>
<li>Introduce an <code>Enumerator#consuming?</code> method that will allow telling one of the other (and make core enumerators like <code>#each_line</code> properly report they are consuming).</li>
<li>Introduce <code>consuming: true</code> parameter for <code>Enumerator.new</code> so it would be easy for user's code to specify the flag</li>
<li>Introduce <code>Enumerator#consuming</code> method to produce a consuming enumerator from a non-consuming one:</li>
</ol>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># reference implementation is trivial:</span>
<span class="k">class</span> <span class="nc">Enumerator</span>
<span class="k">def</span> <span class="nf">consuming</span>
<span class="n">source</span> <span class="o">=</span> <span class="nb">self</span>
<span class="no">Enumerator</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="o">|</span><span class="n">y</span><span class="o">|</span> <span class="kp">loop</span> <span class="p">{</span> <span class="n">y</span> <span class="o"><<</span> <span class="n">source</span><span class="p">.</span><span class="nf">next</span> <span class="p">}</span> <span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">enumerator3</span> <span class="o">=</span> <span class="n">lines</span><span class="p">.</span><span class="nf">each</span><span class="p">.</span><span class="nf">consuming</span>
<span class="n">parse</span><span class="p">(</span><span class="n">enumerator3</span><span class="p">)</span>
</code></pre>
<p>Output:</p>
<pre><code>["From: zverok.offline@gmail.com", "To; bugs@ruby-lang.org", "Subject: Consuming Enumerators"]
["Here, I am presenting the following proposal.", "Let's talk about consuming enumerators..."]
</code></pre> Ruby master - Misc #19054 (Closed): `else` in exception-handling context vs early returnhttps://bugs.ruby-lang.org/issues/190542022-10-14T13:59:23Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p><code>else</code> in exception-handling context is rarely used (at least in codebase I saw), so we encountered it just recently:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">def</span> <span class="nf">foo</span>
<span class="nb">puts</span> <span class="s2">"body"</span>
<span class="k">return</span>
<span class="k">rescue</span> <span class="o">=></span> <span class="n">e</span>
<span class="nb">p</span> <span class="n">e</span>
<span class="k">else</span>
<span class="nb">puts</span> <span class="s2">"else"</span>
<span class="k">ensure</span>
<span class="nb">puts</span> <span class="s2">"ensure"</span>
<span class="k">end</span>
<span class="n">foo</span> <span class="c1"># prints "body", then "ensure"</span>
<span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nf">each</span> <span class="k">do</span>
<span class="nb">puts</span> <span class="s2">"body"</span>
<span class="k">next</span>
<span class="k">rescue</span> <span class="o">=></span> <span class="n">e</span>
<span class="nb">p</span> <span class="n">e</span>
<span class="k">else</span>
<span class="nb">puts</span> <span class="s2">"else"</span>
<span class="k">ensure</span>
<span class="nb">puts</span> <span class="s2">"ensure"</span>
<span class="k">end</span>
<span class="c1"># also prints "body" then "ensure"</span>
</code></pre>
<p>E.g. <code>else</code> is ignored in both cases. Intuitively, I would expect that if no exception is raised in block, <code>else</code> is performed always—like <code>ensure</code> is performed always, exception or not, early return or not.</p>
<p>I found only a very old discussion of this behavior in <a class="issue tracker-1 status-5 priority-4 priority-default closed" title="Bug: Calling return within begin still executes else (Closed)" href="https://bugs.ruby-lang.org/issues/4473">#4473</a> (it was broken accidentally on the road to 1.9.2, but then fixed back), but it doesn't explain the reason for it.</p>
<p>Can somebody provide an insight on this decision, and whether it is justified at all?.. At least, it should be documented somewhere, <a href="https://docs.ruby-lang.org/en/master/syntax/exceptions_rdoc.html" class="external">exception handling docs</a> doesn't mention this quirk.</p> Ruby master - Misc #18840 (Open): Top-level #using and other methods docshttps://bugs.ruby-lang.org/issues/188402022-06-18T19:12:56Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>I was looking into some docs problems, and the question I have is that we don't have any place where <code>main</code>'s methods documentation is rendered?</p>
<p>The <code>#using</code>, for example, is <a href="https://github.com/ruby/ruby/blob/ruby_3_1/eval.c#L1960" class="external">defined</a> on <code>main</code>'s singleton class (if I am reading the code correctly), and it has <a href="https://github.com/ruby/ruby/blob/ruby_3_1/eval.c#L1687" class="external">RDoc defined</a> in <code>*.c</code>, but for all I can tell it is rendered nowhere.</p>
<p>Theoretically, it would've been nice to have a place where <code>main</code> object's concept would be explained and the methods available in it, listed, right?..</p>
<p>Or am I missing something obvious?</p> Ruby master - Feature #18815 (Open): instance_{eval,exec} vs Proc#>>https://bugs.ruby-lang.org/issues/188152022-06-02T12:58:03Zzverok (Victor Shepelev)zverok.offline@gmail.com
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">measure</span> <span class="o">=</span> <span class="nb">proc</span> <span class="p">{</span> <span class="nb">p</span> <span class="s2">"self=</span><span class="si">#{</span><span class="nb">self</span><span class="si">}</span><span class="s2">"</span><span class="p">;</span> <span class="n">size</span> <span class="p">}</span>
<span class="n">multiply</span> <span class="o">=</span> <span class="nb">proc</span> <span class="p">{</span> <span class="s1">'*'</span> <span class="o">*</span> <span class="n">_1</span> <span class="p">}</span>
<span class="s1">'test'</span><span class="p">.</span><span class="nf">instance_eval</span><span class="p">(</span><span class="o">&</span><span class="n">measure</span><span class="p">)</span>
<span class="c1"># "self=test"</span>
<span class="c1"># => 4</span>
<span class="s1">'test'</span><span class="p">.</span><span class="nf">instance_eval</span><span class="p">(</span><span class="o">&</span><span class="n">measure</span> <span class="o">>></span> <span class="n">multiply</span><span class="p">)</span>
<span class="c1"># "self=main"</span>
<span class="c1"># NameError (undefined local variable or method `size' for main:Object)</span>
</code></pre>
<p>So, <code>Proc#>></code> produces a proc which is not suitable for <code>instance_eval</code>/<code>instance_exec</code>. The same as the "naive" implementation:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># Naive sequence</span>
<span class="n">sequence</span> <span class="o">=</span> <span class="nb">proc</span> <span class="p">{</span> <span class="n">measure</span><span class="p">.</span><span class="nf">call</span><span class="p">.</span><span class="nf">then</span><span class="p">(</span><span class="o">&</span><span class="n">multiply</span><span class="p">)</span> <span class="p">}</span>
</code></pre>
<p>...but is it possible to make the combination to behave like the first proc is not wrapped into an additional layer of indirection?.. Intuitively, it shouldn't be completely impossible.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># What I meant actually:</span>
<span class="n">intended</span> <span class="o">=</span> <span class="nb">proc</span> <span class="p">{</span> <span class="n">size</span><span class="p">.</span><span class="nf">then</span><span class="p">(</span><span class="o">&</span><span class="n">multilpy</span><span class="p">)</span> <span class="p">}</span>
<span class="s2">"test"</span><span class="p">.</span><span class="nf">instance_eval</span><span class="p">(</span><span class="o">&</span><span class="n">intended</span><span class="p">)</span>
<span class="c1"># => "****"</span>
</code></pre>
<p>The example is invented, the question is "whether this should work", not "how to solve this task with another approach".</p> Ruby master - Bug #18760 (Closed): Ractors vs "skynet" microbenchmarkhttps://bugs.ruby-lang.org/issues/187602022-04-28T08:52:36Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>I recently stumbled upon <a href="https://github.com/atemerev/skynet" class="external">skynet</a> concurrency microbenchmark and tried to adapt it to Ruby ractors.<br>
The microbenchmarks of this kind are obviously limited, but at least they allow to do super-rough estimations of "how we are doing". It just spawns an actor (or coroutine, or channel, etc., depending on the language), which spawns 10 more, each spawning 10 more... till there are a million of them; then each just returns its ordinal number, and parents sum them up.</p>
<p>Ruby code:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">N</span> <span class="o">=</span> <span class="mi">1_000_000</span>
<span class="k">def</span> <span class="nf">skynet</span><span class="p">(</span><span class="n">num</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="n">div</span><span class="p">)</span>
<span class="k">if</span> <span class="n">size</span> <span class="o">==</span> <span class="mi">1</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">yield</span> <span class="n">num</span>
<span class="k">else</span>
<span class="n">children</span> <span class="o">=</span> <span class="n">div</span><span class="p">.</span><span class="nf">times</span><span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">i</span><span class="o">|</span>
<span class="n">sub_num</span> <span class="o">=</span> <span class="n">num</span> <span class="o">+</span> <span class="n">i</span><span class="o">*</span><span class="p">(</span><span class="n">size</span><span class="o">/</span><span class="n">div</span><span class="p">)</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">sub_num</span><span class="p">,</span> <span class="n">size</span><span class="o">/</span><span class="n">div</span><span class="p">,</span> <span class="n">div</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">n</span><span class="p">,</span> <span class="n">sz</span><span class="p">,</span> <span class="n">d</span><span class="o">|</span> <span class="n">skynet</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">sz</span><span class="p">,</span> <span class="n">d</span><span class="p">)</span> <span class="p">}</span>
<span class="p">}</span>
<span class="n">sum</span> <span class="o">=</span> <span class="n">children</span><span class="p">.</span><span class="nf">sum</span><span class="p">(</span><span class="o">&</span><span class="ss">:take</span><span class="p">)</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">yield</span> <span class="n">sum</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">start_time</span> <span class="o">=</span> <span class="no">Time</span><span class="p">.</span><span class="nf">now</span>
<span class="n">main</span> <span class="o">=</span> <span class="no">Ractor</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="n">skynet</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="no">N</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span> <span class="p">}</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">main</span><span class="p">.</span><span class="nf">take</span>
<span class="n">end_time</span> <span class="o">=</span> <span class="no">Time</span><span class="p">.</span><span class="nf">now</span>
<span class="nb">puts</span> <span class="s2">"Result: </span><span class="si">#{</span><span class="n">result</span><span class="si">}</span><span class="s2"> in </span><span class="si">#{</span><span class="p">(</span><span class="n">end_time</span> <span class="o">-</span> <span class="n">start_time</span><span class="p">)</span><span class="si">}</span><span class="s2">s."</span>
</code></pre>
<p>(of course, it is better to use <code>Process.getclocktime</code> probably, but that's not the point at the moment)</p>
<p>The point is, what I found out seems to indicate we still have some room for improvement for Ractors, to say the least.</p>
<pre><code>$ ruby -v
ruby 3.2.0dev (2022-04-28T06:44:02Z master 5250210aa9) [x86_64-linux]
</code></pre>
<p>Results on my machine (Thinkpad E300, 8Gb mem):</p>
<ul>
<li>N=1000: <code>Result: 499500 in 1.614297829s.</code> (BTW, on <code>ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [x86_64-linux]</code> I also checked, it was much faster: 0.3-0.4s typically)</li>
<li>N=10_000: <code>Result: 49995000 in 46.89935975s.</code> (really long?)</li>
<li>N=100_000: <code>Aborted (core dumped)</code>
</li>
</ul>
<p>Not sure this is telling something useful (and not sure I ported benchmark properly, TBH)</p>
<ul>
<li>
</ul> Ruby master - Feature #18583 (Open): Pattern-matching: API for custom unpacking strategies?https://bugs.ruby-lang.org/issues/185832022-02-12T20:22:53Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>I started to think about it when discussing <a href="https://github.com/ruby/strscan/pull/30" class="external">https://github.com/ruby/strscan/pull/30</a>.<br>
The thing is, usage of StringScanner for many complicated parsers invokes some kind of branching.</p>
<p>In pseudocode, the "ideal API" would allow to write something like this:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">case</span> <span class="o"><</span><span class="n">what</span> <span class="k">next</span> <span class="n">matches</span><span class="o">></span>
<span class="k">in</span> <span class="sr">/regexp1/</span> <span class="o">=></span> <span class="n">value_that_matched</span>
<span class="c1"># use value_that_matched</span>
<span class="k">in</span> <span class="sr">/regexp2/</span> <span class="o">=></span> <span class="n">value_that_matched</span>
<span class="c1"># use value_that_matched</span>
<span class="c1"># ...</span>
</code></pre>
<p>This seems "intuitively" that there <em>should</em> be some way of implementing it, but we fall short. We can do some StringScanner-specific matcher object which defines its own <code>#===</code> and use it with pinning:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">case</span> <span class="n">scanner</span>
<span class="k">in</span> <span class="o">^</span><span class="p">(</span><span class="no">Matcher</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="sr">/regexp1/</span><span class="p">))</span> <span class="o">=></span> <span class="n">value_that_matched</span>
<span class="c1"># ...</span>
</code></pre>
<p>But there is no API to tell how the match result will be unpacked, just the whole <code>StringScanner</code> will be put into <code>value_that_matched</code>.</p>
<p>So, I thought that maybe it would be possible to define some kind of API for pattern-like objects, the method with signature like <code>try_match_pattern(value)</code>, which by default is implemented like <code>return value if self === value</code>, but can be redefined to return something different, like part of the object, or object transformed somehow.</p>
<p>This will open some interesting (if maybe uncanny) possibilities: not just slicing out the necessary part, but something like</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">value</span> <span class="o">=></span> <span class="o">^</span><span class="p">(</span><span class="n">type_caster</span><span class="p">(</span><span class="no">Integer</span><span class="p">))</span> <span class="o">=></span> <span class="n">int_value</span>
</code></pre>
<p>So... Just a discussion topic!</p> Ruby master - Bug #18580 (Closed): Range#include? inconsistency for beginless String rangeshttps://bugs.ruby-lang.org/issues/185802022-02-10T07:35:08Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>The follow-up of <a class="issue tracker-1 status-5 priority-4 priority-default closed" title="Bug: Range#include? returns wrong result for beginless range with exclusive string end (Closed)" href="https://bugs.ruby-lang.org/issues/18577">#18577</a>.</p>
<p><code>Range#include?</code> is <a href="https://github.com/ruby/ruby/blob/master/range.c#L1782" class="external">specialized for strings</a>. For all I can tell, this behavior is relatively new: it emerged when switching <code>#===</code> to use <code>#cover?</code> instead of <code>#include?</code>:</p>
<ul>
<li>in 2.6, it was decided that String Ranges should continue to use <code>#include?</code> (<a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Switch Range#=== to use cover? instead of include? (Closed)" href="https://bugs.ruby-lang.org/issues/14575">#14575</a>)</li>
<li>...but in 2.7, it was decided that they should use <code>#cover?</code> as all others (<a class="issue tracker-1 status-5 priority-4 priority-default closed" title="Bug: Range#=== is not using cover in Ruby 2.6 (Closed)" href="https://bugs.ruby-lang.org/issues/15449">#15449</a>)</li>
</ul>
<p>The "despecialization" in 2.7 was actually done by adding more specialization in <code>range_include_internal</code>.</p>
<p>This leads to the following:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># default Range behavior:</span>
<span class="p">(</span><span class="o">..</span><span class="no">Date</span><span class="p">.</span><span class="nf">today</span><span class="p">).</span><span class="nf">include?</span> <span class="no">Date</span><span class="p">.</span><span class="nf">today</span>
<span class="c1"># (irb):10:in `each': can't iterate from NilClass (TypeError)</span>
<span class="c1"># String Range behavior:</span>
<span class="p">(</span><span class="o">..</span><span class="s1">'z'</span><span class="p">).</span><span class="nf">include?</span><span class="p">(</span><span class="s1">'w'</span><span class="p">)</span>
<span class="c1"># => true </span>
</code></pre>
<p>Why I think <strong>it is bad</strong>:</p>
<ol>
<li>This is a leftover of the relatively recent change; why it is 3 versions old, I doubt there is a lot of code relying on this quirk (only beginless ranges are affected)</li>
<li>The more "invisible specialization", the more possibility of bugs (as <a class="issue tracker-1 status-5 priority-4 priority-default closed" title="Bug: Range#include? returns wrong result for beginless range with exclusive string end (Closed)" href="https://bugs.ruby-lang.org/issues/18577">#18577</a> shows)</li>
<li>It is easy to stumble upon while learning Ruby/experimenting (strings and ranges are the most basic objects to be seen in many examples and courses), and to become confused, because there is no reasonable explanation for the semantics other than "well... for String, it is so"</li>
<li>It <em>can</em> be said that the String ranges behavior have the same specialization as Numeric ones, but it is not so; while Numeric Ranges specialization is long-living legacy, it is at least consistent (<code>#include?</code> just behaves like <code>#cover?</code> for them, always):</li>
</ol>
<p>Numerics:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="p">(</span><span class="mi">1</span><span class="o">...</span><span class="mi">3</span><span class="p">).</span><span class="nf">include?</span> <span class="mf">1.5</span>
<span class="c1"># => true, like #cover?</span>
<span class="p">(</span><span class="o">...</span><span class="mi">3</span><span class="p">).</span><span class="nf">include?</span> <span class="mf">1.5</span>
<span class="c1"># => true, same, the explanation is "just like #cover?"</span>
</code></pre>
<p>Strings:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="p">(</span><span class="s1">'a'</span><span class="o">..</span><span class="s1">'z'</span><span class="p">).</span><span class="nf">include?</span><span class="p">(</span><span class="s1">'ww'</span><span class="p">)</span>
<span class="c1"># => false, like #include? for any other type</span>
<span class="p">(</span><span class="o">..</span><span class="s1">'z'</span><span class="p">).</span><span class="nf">include?</span><span class="p">(</span><span class="s1">'ww'</span><span class="p">)</span>
<span class="c1"># => true, this is deeply confusing</span>
</code></pre> Ruby master - Misc #18493 (Closed): zverok as a commiterhttps://bugs.ruby-lang.org/issues/184932022-01-14T19:32:41Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>I feel uneasy asking for myself (as far as I can understand, it is normal that other core team member proposes new contributors), but well... I am Ukrainian, and am not exactly subtle.</p>
<p>So... Can I receive a commit rights?</p>
<a name="About-me"></a>
<h2 >About me<a href="#About-me" class="wiki-anchor">¶</a></h2>
<p>I use Ruby professionally as my main language since 2004, and interested in language's evolution since around that time.</p>
<p>I am author of many <a href="https://zverok.github.io/blog/" class="external">blog articles on Ruby</a>, several <a href="https://rubygems.org/profiles/zverok" class="external">Ruby gems</a>, a few <a href="https://zverok.github.io/talks.html" class="external">conference talks</a>.</p>
<p>I maintain <a href="https://rubyreferences.github.io/rubychanges/" class="external">Ruby Changes</a> annotated changelog for each version since 2.4 (and plan to extend it back to historical versions); and <a href="https://rubyreferences.github.io/rubyref/" class="external">Ruby Reference</a> attempt to compile readable "semi-official" reference. This work was recognized by Fukuoka Ruby award.</p>
<p>I contributed several <a href="https://zverok.github.io/ruby.html#ruby-features" class="external">Ruby features</a> (mostly on idea level) and <a href="https://zverok.github.io/ruby.html#documenting-ruby" class="external">documentation improvements</a> (including reviewing of docs status before each version release and catching up on under-documented features).</p>
<a name="Why-do-I-want-it"></a>
<h2 >Why do I want it<a href="#Why-do-I-want-it" class="wiki-anchor">¶</a></h2>
<p>TBH, one of the reasons is some "sense of belonging". I would be happy to think about myself as just "Ruby commiter" not "that guy who did this and that but not a part of the team".</p>
<p>More seriously, I am dedicated to Ruby's documentation quality, and willing to take a responsibility to work on it on a more systematic way. It requires at least more rights on Issue Tracker (assigning tickets, closing tickets, etc) and GitHub (adding labels, reviewing PRs etc).</p>
<p><em>(It seems to me that sometimes others got confused assuming I can merge my small docs PRs myself <a href="https://bugs.ruby-lang.org/issues/18404#note-32" class="external">until reminded otherwise</a>)</em></p>
<a name="What-do-I-want-to-do-as-a-committer"></a>
<h2 >What do I want to do as a committer<a href="#What-do-I-want-to-do-as-a-committer" class="wiki-anchor">¶</a></h2>
<ul>
<li>Create and maintain some perpetual list of documentation problems and possible improvements;</li>
<li>Review new feature PRs and clarify what needs to be documented; provide follow-up documentation PRs;</li>
<li>Regular reviews of how docs are rendered to avoid rendering problems due to unrelated changes (<a href="https://github.com/ruby/ruby/pull/2810/files" class="external">example</a>)</li>
<li>Maybe move forward on the idea Matz once had (<a class="issue tracker-5 status-2 priority-4 priority-default" title="Misc: Improving `www.ruby-lang.org` reference by merging with `rubyreferences.github.io` (Assigned)" href="https://bugs.ruby-lang.org/issues/16512">#16512</a>) to merge my effort on more logical/modern official docs?</li>
</ul>
<p><em>NB: I have some ideas for new language features but obviously wouldn't abuse commit rights to merge them.</em></p>
<a name="Counter-points"></a>
<h2 >Counter-points<a href="#Counter-points" class="wiki-anchor">¶</a></h2>
<p>I am not 100% sure I am qualified to be responsible for (some parts of) documentation being non-native speaker. My English became better during recent years, but it is still far from flawless. I am trying to use spell- and grammar-checking software, but not sure it fully compensates for my flaws.</p> Ruby master - Misc #18451 (Closed): Undocumented GC.stat keyshttps://bugs.ruby-lang.org/issues/184512021-12-29T20:42:50Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>As of 3.1 these keys aren't <a href="https://docs.ruby-lang.org/en/master/GC.html#method-c-stat" class="external">documented</a>:</p>
<ul>
<li>
<code>:compact_count</code> (2.7)</li>
<li>
<code>:read_barrier_faults</code>, <code>:total_moved_objects</code> (3.0)</li>
<li>
<code>:time</code> (new in 3.1)</li>
</ul>
<p>(I am just judging by versions/keys available on my system, no keys except <code>:time</code> are mentioned in corresponding version's NEWS.)</p>
<p>I am not familiar with GC enough to easily handle this.<br>
Is there anybody who is?</p>
<p><a class="user active user-mention" href="https://bugs.ruby-lang.org/users/73">@tenderlovemaking (Aaron Patterson)</a>, it seems you've introduced all except <code>:time</code>? Can you provide some insight on what are they representing?</p> Ruby master - Bug #18417 (Closed): IO::Buffer problemshttps://bugs.ruby-lang.org/issues/184172021-12-19T07:26:09Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>Hello. While working for documentation for <code>IO::Buffer</code> (still WIP), I uncovered these problems. I believe at least some of them should be fixed before the release, though I understand not much time left.</p>
<p>The list is not final, probably (still on it, but wanted to raise awareness ASAP).</p>
<p><strong>These should be probably fixed/discussed before the release</strong></p>
<p>1. I believe <code>to_str</code> is an unfortunate name for "read string from buffer, maybe entire buffer. It is Ruby's convention that <code>to_str</code> is an implicit String-conversion, and a) I am not sure the Buffer is "string-like" enough to be implicitly converted, and b) it is really unexpected that <code>#to_s</code> and <code>#to_str</code> do different things:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">buf</span> <span class="o">=</span> <span class="no">IO</span><span class="o">::</span><span class="no">Buffer</span><span class="p">.</span><span class="nf">for</span><span class="p">(</span><span class="s1">'test'</span><span class="p">)</span>
<span class="s2">"buf: </span><span class="si">#{</span><span class="n">buf</span><span class="si">}</span><span class="s2">"</span>
<span class="c1"># => "buf: #<IO::Buffer 0x00007fdb7f4e3a20+4 SLICE>"</span>
<span class="s2">"buf: "</span> <span class="o">+</span> <span class="n">buf</span>
<span class="c1"># => "buf: test"</span>
<span class="nb">puts</span> <span class="n">buf</span>
<span class="c1"># #<IO::Buffer 0x00007fdb7f4e3a20+4 SLICE></span>
<span class="nb">puts</span> <span class="s1">''</span> <span class="o">+</span> <span class="n">buf</span>
<span class="c1"># test</span>
</code></pre>
<p>Another concern about <code>#to_str</code> naming is it is one of the main parts of the buffer interface (alongside <code>#copy</code>), but doesn't look this way, and looks like some utility method instead. Maybe copy/to_str pair should be symmetrical, like <code>write</code>/<code>read</code>, or something like that?</p>
<p>2. I am not sure that type names (<code>:U8</code>, <code>:S8</code>, <code>:U16</code>, <code>:u16</code> and so on) introduced "from scratch" (?) is a good thing. From one point, they are kinda nice, and consistent. From another, we already have abbreviated names for the types in <a href="https://docs.ruby-lang.org/en/master/Array.html#method-i-pack" class="external">Array#pack</a>, and having two completely unrelated sets of designation for the same thing in the <strong>core of the language</strong> seems weird to me. I understand that not all <code>#pack</code>s designations can be represented by Symbol, and their consistency is questionable (it is <code>S</code> for 16-bit unsigned LE, <code>s</code> for 16-bit signed LE, and <code>S></code>/<code>s></code> for the same in BE), but the discrepancy is questionable too.</p>
<p>3. About <code>flags</code> as a creation parameter:</p>
<ul>
<li>
<code>::new</code>: it seems that the only acceptable value is <code>MAPPED</code>; of others, <code>INTERNAL</code> is the default, <code>EXTERNAL</code>, <code>IMMUTABLE</code> and <code>LOCKED</code> raise on creation;</li>
<li>
<code>::for</code>: no flags accepted (though it seems that a method to make such buffer immutable might be of some utility?)</li>
<li>
<code>::map</code>: <code>INTERNAL</code>, <code>LOCKED</code>, <code>EXTERNAL</code> are ignored, <code>MAPPED</code> is the default; seems like the only usage of the parameter is <em>not</em> to pass <code>IMMUTABLE</code> which is the default: and for that, you'll need to also pass <code>size</code> and <code>offset</code>.</li>
</ul>
<p>It seems, that <strong>we shouldn't probably expose "flags" concept to the user</strong> at all, and instead make APIs like <code>::new(size, mapped: false)</code> and <code>::map(io, ..., immutable: true)</code>?</p>
<p><strong>Probably bugs</strong></p>
<ul>
<li>It seems that <code>#clear</code> behaves weirdly when <code>offset</code> is provided but <code>length</code> is not:</li>
</ul>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">buf</span> <span class="o">=</span> <span class="no">IO</span><span class="o">::</span><span class="no">Buffer</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
<span class="n">buf</span><span class="p">.</span><span class="nf">clear</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="c1"># it seems obvious that I want to clear from byte 2 to the end of the buffer</span>
<span class="c1"># in `clear': Offset + length out of bounds! (RuntimeError)</span>
</code></pre>
<ul>
<li>
<code>IO::Buffer.map('README.md')</code> (just a string, I experimented "whether it is smart enough") core dumps.</li>
<li>I suspect we might want to set buffer to <code>IMMUTABLE</code> if it is created from the frozen string. Currently, this works:</li>
</ul>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">str</span> <span class="o">=</span> <span class="s1">'test'</span><span class="p">.</span><span class="nf">freeze</span>
<span class="n">buf</span> <span class="o">=</span> <span class="no">IO</span><span class="o">::</span><span class="no">Buffer</span><span class="p">.</span><span class="nf">for</span><span class="p">(</span><span class="n">str</span><span class="p">)</span>
<span class="n">buf</span><span class="p">.</span><span class="nf">set</span><span class="p">(</span><span class="ss">:U8</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">111</span><span class="p">)</span>
<span class="n">str</span>
<span class="c1">#=> "tost"</span>
</code></pre>
<ul>
<li>It seems <code>to_str</code> always returns a string in <code>US-ASCII</code> encoding, I am not sure it is the right behavior. For a low-level buffer, I'd rather expect <code>ASCII-8BIT</code> (what file read in binary mode produces in Ruby).</li>
<li>(Not sure if a bug or just not very useful behavior?) <code>#resize</code> applied to a buffer mapped from file seems to "break" the link between the buffer and file; no further <code>copy</code> or <code>set</code> would be reflected on file.</li>
<li><del><code>buf = IO::Buffer.new(4); buf.free; buf.inspect</code> gives a core dump</del></li>
<li><del><code>buf = IO::Buffer.for("test"); buf.free</code> — after that, <code>buf.get(:U8, 0)</code> gives "Buffer has been invalidated! (RuntimeError)", but <code>buf.to_str</code> works and produces <code>"\x00\x00\x00\x00"</code>; <code>buf.to_str(2)</code> does a core dump</del></li>
</ul>
<p><strong>Inconsistencies and other problems</strong></p>
<ul>
<li>The buffer raises <code>RuntimeError</code> as the only type of the error. I believe that in some places, it should be other standard errors (for example, "Offset + length out of bounds" could be at least <code>ArumentError</code>?..), and in other occurrences, special error classes: for example, "Buffer is locked" would probably be helpful to have as some <code>IO::Buffer::LockedError</code>
</li>
<li><del>Current <code>#inspect</code> is kinda nice and useful, but it also might be really annoying when used by Ruby itself. Try <code>IO::Buffer.map(File.open("big file")).unexisting_method</code>, and you'll get REALLY long error message.</del></li>
<li><del>Can we maybe have for <code>#resize</code> a signature <code>resize(new_size, preserve = self.size)</code>? It seems a good default to allow just <code>resize(123)</code>, with preserving everything by default?</del></li>
</ul> Ruby master - Misc #18404 (Closed): 3.1 documentation problems tracking tickethttps://bugs.ruby-lang.org/issues/184042021-12-11T16:50:22Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>So far, comparing the NEWS.md to actual documentation at <a href="https://docs.ruby-lang.org/en/master/" class="external">https://docs.ruby-lang.org/en/master/</a>, I identified the following problems:</p>
<ul>
<li>
<a href="https://docs.ruby-lang.org/en/master/Random/Formatter.html" class="external">Random::Formatter</a>: the explanations of how to use it are murky, examples do show just context-less examples like <code>prng.hex(10)</code>
</li>
</ul>
<p>Merged:</p>
<ul>
<li>
<del>Hash/keyword argument value omission—completely undocumented. PR: <a href="https://github.com/ruby/ruby/pull/5244" class="external">https://github.com/ruby/ruby/pull/5244</a></del> Merged</li>
<li>
<del><a href="https://docs.ruby-lang.org/en/master/Thread/Queue.html#method-c-new" class="external">Queue::new</a> should be rewritten (bad rendering for example, non-informative parameter name). PR: <a href="https://github.com/ruby/ruby/pull/5273" class="external">https://github.com/ruby/ruby/pull/5273</a></del> Merged</li>
<li>
<del><code>Thread::Backtrace.limit</code> isn't mentioned anywhere (<code>Thread::Backtrace</code> doesn't even have its own page, only <a href="https://docs.ruby-lang.org/en/master/Thread/Backtrace/Location.html" class="external">Thread::Backtrace::Location</a> has). PR: <a href="https://github.com/ruby/ruby/pull/5305" class="external">https://github.com/ruby/ruby/pull/5305</a></del> Merged</li>
<li>
<del><a href="https://docs.ruby-lang.org/en/master/Fiber/SchedulerInterface.html" class="external">Fiber::SchedulerInterface<br>
</a> — no docs for new methods supported: <code>address_resolve</code>, <code>timeout_after</code>, <code>io_read</code>, <code>io_write</code>. PR: <a href="https://github.com/ruby/ruby/pull/5280" class="external">https://github.com/ruby/ruby/pull/5280</a></del> Merged</li>
<li>
<del>New <a href="https://docs.ruby-lang.org/en/master/Refinement.html" class="external">Refinement</a> class has no docs whatsoever. PR: <a href="https://github.com/ruby/ruby/pull/5272" class="external">https://github.com/ruby/ruby/pull/5272</a></del> Merged</li>
<li>
<del><a href="https://docs.ruby-lang.org/en/master/Struct.html" class="external">Struct</a>: <code>#keyword_init?</code> isn't mentioned anywhere. PR: <a href="https://github.com/ruby/ruby/pull/5279" class="external">https://github.com/ruby/ruby/pull/5279</a></del> Merged</li>
<li>
<del><a href="https://docs.ruby-lang.org/en/master/GC.html" class="external">GC</a>: <code>::measure_total_time</code>, <code>::measure_total_time=</code>, <code>::total_time</code> isn't mentioned anywhere. UPD: was just a documentation rendering glitch, PR: <a href="https://github.com/ruby/ruby/pull/5306" class="external">https://github.com/ruby/ruby/pull/5306</a></del> Merged</li>
<li>
<del><a href="https://docs.ruby-lang.org/en/master/String.html#method-i-unpack" class="external">String#unpack</a> and <a href="https://docs.ruby-lang.org/en/master/String.html#method-i-unpack1" class="external">#unpack1</a>: new <code>offset</code> param examples are given too early, before the main examples. <code>unpack1</code> also has code formatting glitch. PR: <a href="https://github.com/ruby/ruby/pull/5331" class="external">https://github.com/ruby/ruby/pull/5331</a></del> Merged</li>
<li>
<del><a href="https://docs.ruby-lang.org/en/master/Marshal.html#method-c-load" class="external">Marshal#load</a>: the new parameter <code>freeze:</code> is not documented. PR: <a href="https://github.com/ruby/ruby/pull/5332" class="external">https://github.com/ruby/ruby/pull/5332</a></del> Merged</li>
<li>
<del><a href="https://docs.ruby-lang.org/en/master/IO/Buffer.html" class="external">IO::Buffer</a> no docs. PR: <a href="https://github.com/ruby/ruby/pull/5302" class="external">https://github.com/ruby/ruby/pull/5302</a></del> Merged by hand by <a class="user active user-mention" href="https://bugs.ruby-lang.org/users/3344">@ioquatix (Samuel Williams)</a></li>
</ul>
<p>Small glitches in new docs:</p>
<ul>
<li>
<del><a href="https://docs.ruby-lang.org/en/master/Process.html#method-c-_fork" class="external">Process._fork</a> formatting of <code>tt</code>. PR: <a href="https://github.com/ruby/ruby/pull/5270" class="external">https://github.com/ruby/ruby/pull/5270</a></del> Merged</li>
<li>
<del><a href="https://docs.ruby-lang.org/en/master/Integer.html#method-c-try_convert" class="external">Integer#try_convert</a>: small typo: "Returns nil if object does not respond to <strong>:to_ary</strong>" (should be <code>:to_int</code>).</del> Fixed already.</li>
</ul>
<p>I'll try to address as much as I'll be able myself, but if some of the feature authors/other volunteers are willing to participate, I'll be glad :)</p>
<p>The ticket might be expanded if I'll notice something else.</p> Ruby master - Bug #18385 (Closed): Refinement#import_methods(Enumerable) doesn't workhttps://bugs.ruby-lang.org/issues/183852021-12-04T14:53:47Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>Very simple to reproduce:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">module</span> <span class="nn">M</span>
<span class="n">refine</span> <span class="no">String</span> <span class="k">do</span>
<span class="n">import_methods</span> <span class="no">Enumerable</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>Leads to: <code>import_methods': Can't import method: Enumerable#drop (ArgumentError)</code><br>
Which, grepping through code, seems to be raised <a href="https://github.com/ruby/ruby/blob/master/eval.c#L1529" class="external">here</a> but I am not versed enough in Ruby internals to debug further.</p>
<p>An attempt to <code>import_methods Comparable</code> leads to the same problem, BTW: <code>Can't import method: Comparable#between?</code></p>
<p>Am I missing something crucial about <code>import_method</code> behavior?</p> Ruby master - Feature #18368 (Open): Range#step semantics for non-Numeric rangeshttps://bugs.ruby-lang.org/issues/183682021-11-29T15:30:40Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>I am sorry if the question had already been discussed, can't find the relevant topic.</p>
<p>"Intuitively", this looks (for me) like a meaningful statement:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="p">(</span><span class="no">Time</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s1">'2021-12-01'</span><span class="p">)</span><span class="o">..</span><span class="no">Time</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s1">'2021-12-24'</span><span class="p">)).</span><span class="nf">step</span><span class="p">(</span><span class="mi">1</span><span class="p">.</span><span class="nf">day</span><span class="p">).</span><span class="nf">to_a</span>
<span class="c1"># ^^^^^ or just 24*60*60</span>
</code></pre>
<p>Unfortunately, it doesn't work with "TypeError (can't iterate from Time)".<br>
Initially it looked like a bug for me, but after digging a bit into code/docs, I understood that <code>Range#step</code> has an odd semantics of "advance the begin N times with <code>#succ</code>, and yield the result", with N being always integer:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="p">(</span><span class="s1">'a'</span><span class="o">..</span><span class="s1">'z'</span><span class="p">).</span><span class="nf">step</span><span class="p">(</span><span class="mi">3</span><span class="p">).</span><span class="nf">first</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
<span class="c1"># => ["a", "d", "g", "j", "m"]</span>
</code></pre>
<p>The fact that semantic is "odd" is confirmed by the fact that for Float it is redefined to do what I "intuitively" expected:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="p">(</span><span class="mf">1.0</span><span class="o">..</span><span class="mf">7.0</span><span class="p">).</span><span class="nf">step</span><span class="p">(</span><span class="mf">0.3</span><span class="p">).</span><span class="nf">first</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
<span class="c1"># => [1.0, 1.3, 1.6, 1.9, 2.2] </span>
</code></pre>
<p>(Like with <a href="https://bugs.ruby-lang.org/issues/14575" class="external"><code>Range#===</code> some time ago</a>, I believe that to be a strong proof of the wrong generic semantics, if for numbers the semantics needed to be redefined completely.)</p>
<p>Another thing to note is that "skip N elements" seem to be rather "generically Enumerable-related" yet it isn't defined on <code>Enumerable</code> (because nobody needs this semantics, typically!)</p>
<p>Hence, two questions:</p>
<ul>
<li>Can we redefine generic <code>Range#step</code> to new semantics (of using <code>begin + step</code> iteratively)? It is hard to imagine the amount of actual usage of the old behavior (with String?.. to what end?) in the wild</li>
<li>If the answer is "no", can we define a new method with new semantics, like, IDK, <code>Range#over(span)</code>?</li>
</ul>
<p><strong>UPD:</strong> More examples of useful behavior (it is NOT only about core <code>Time</code> class):</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="nb">require</span> <span class="s1">'active_support/all'</span>
<span class="p">(</span><span class="mi">1</span><span class="p">.</span><span class="nf">minute</span><span class="o">..</span><span class="mi">20</span><span class="p">.</span><span class="nf">minutes</span><span class="p">).</span><span class="nf">step</span><span class="p">(</span><span class="mi">2</span><span class="p">.</span><span class="nf">minutes</span><span class="p">).</span><span class="nf">to_a</span>
<span class="c1">#=> [1 minute, 3 minutes, 5 minutes, 7 minutes, 9 minutes, 11 minutes, 13 minutes, 15 minutes, 17 minutes, 19 minutes]</span>
<span class="nb">require</span> <span class="s1">'tod'</span>
<span class="p">(</span><span class="no">Tod</span><span class="o">::</span><span class="no">TimeOfDay</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s2">"8am"</span><span class="p">)</span><span class="o">..</span><span class="no">Tod</span><span class="o">::</span><span class="no">TimeOfDay</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="s2">"10am"</span><span class="p">)).</span><span class="nf">step</span><span class="p">(</span><span class="mi">30</span><span class="p">.</span><span class="nf">minutes</span><span class="p">).</span><span class="nf">to_a</span>
<span class="c1">#=> [#<Tod::TimeOfDay 08:00:00>, #<Tod::TimeOfDay 08:30:00>, #<Tod::TimeOfDay 09:00:00>, #<Tod::TimeOfDay 09:30:00>, #<Tod::TimeOfDay 10:00:00>]</span>
<span class="nb">require</span> <span class="s1">'matrix'</span>
<span class="p">(</span><span class="no">Vector</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span><span class="o">..</span><span class="p">).</span><span class="nf">step</span><span class="p">(</span><span class="no">Vector</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">]).</span><span class="nf">take</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="c1">#=> [Vector[1, 2, 3], Vector[2, 3, 4], Vector[3, 4, 5]]</span>
<span class="nb">require</span> <span class="s1">'unitwise'</span>
<span class="p">(</span><span class="no">Unitwise</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="s1">'km'</span><span class="p">)</span><span class="o">..</span><span class="no">Unitwise</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s1">'km'</span><span class="p">)).</span><span class="nf">step</span><span class="p">(</span><span class="no">Unitwise</span><span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="s1">'m'</span><span class="p">)).</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="ss">:to_s</span><span class="p">)</span>
<span class="c1">#=> ["0 km", "1/10 km", "1/5 km", "3/10 km", "2/5 km", "0.5 km", "3/5 km", "7/10 km", "4/5 km", "9/10 km", "1 km"]</span>
</code></pre>
<p><strong>UPD:</strong> Responding to discussion points:</p>
<p><strong>Q:</strong> Matz is concerned that the proposed simple definition will be confusing with the classes where <code>+</code> is redefined as concatenation.</p>
<p><strong>A:</strong> I believe that simplicity of semantics and ease of explaining ("it just uses <code>+</code> underneath, whatever <code>+</code> does, will be performed") will make the confusion minimal.</p>
<p><strong>Q:</strong> Why not introduce new API requirement (like "class of range's <code>begin</code> should implement <code>increment</code> method, and then it will be used in <code>step</code>)</p>
<p><strong>A:</strong> require <em>every</em> gem author to change <em>every</em> of their objects' behavior. For that, they should be aware of the change, consider it important enough to care, clearly understand the necessary semantics of implementation, have a resource to release a new version... Then all users of all such gems would be required to upgrade. The feature would be DOA (dead-on-arrival).</p>
<p>The two alternative ways I am suggesting: change the behavior of <code>#step</code> or introduce a new method with desired behavior:</p>
<ol>
<li>Easy to explain and announce</li>
<li>Require no other code changes to immediately become useful</li>
<li>With something like <a href="https://github.com/marcandre/backports" class="external">backports</a> or <a href="https://github.com/ruby-next/ruby-next" class="external">ruby-next</a> easy to start using even in older Ruby version, making the code more expressive even before it would be possible for some particular app/compny to upgrade to (say) 3.2</li>
</ol>
<p>All examples of behavior from the code above are real <code>irb</code> output with monkey-patched <code>Range#step</code>, demonstrating how little change will be needed to code outside of the <code>Range</code>.</p> Ruby master - Feature #18262 (Open): Enumerator::Lazy#partitionhttps://bugs.ruby-lang.org/issues/182622021-10-22T12:08:59Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>(Part of my set of proposals about making <code>.lazy</code> more useful/popular.)</p>
<p>Currently:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">file</span> <span class="o">=</span> <span class="no">File</span><span class="p">.</span><span class="nf">open</span><span class="p">(</span><span class="s1">'very-large-file.txt'</span><span class="p">)</span>
<span class="n">lines_with_errors</span><span class="p">,</span> <span class="n">lines_without_errors</span> <span class="o">=</span> <span class="n">file</span><span class="p">.</span><span class="nf">lazy</span><span class="p">.</span><span class="nf">partition</span> <span class="p">{</span> <span class="n">_1</span><span class="p">.</span><span class="nf">start_with?</span><span class="p">(</span><span class="s1">'E:'</span><span class="p">)</span> <span class="p">}</span>
<span class="n">lines_with_errors</span><span class="p">.</span><span class="nf">class</span>
<span class="c1"># => Array, all file is read by this moment</span>
</code></pre>
<p>This might be not very practical performance-wise and memory-wise.</p>
<p>I am thinking that maybe returning a pair of lazy enumerators might be a good addition to <code>Enumerator::Lazy</code></p>
<p>Naive prototype:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">Enumerator::Lazy</span>
<span class="k">def</span> <span class="nf">partition</span><span class="p">(</span><span class="o">&</span><span class="n">block</span><span class="p">)</span>
<span class="n">buffer1</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">buffer2</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">source</span> <span class="o">=</span> <span class="nb">self</span>
<span class="p">[</span>
<span class="no">Enumerator</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="o">|</span><span class="n">y</span><span class="o">|</span>
<span class="kp">loop</span> <span class="k">do</span>
<span class="k">if</span> <span class="n">buffer1</span><span class="p">.</span><span class="nf">empty?</span>
<span class="k">begin</span>
<span class="n">item</span> <span class="o">=</span> <span class="n">source</span><span class="p">.</span><span class="nf">next</span>
<span class="k">if</span> <span class="n">block</span><span class="p">.</span><span class="nf">call</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
<span class="n">y</span><span class="p">.</span><span class="nf">yield</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
<span class="k">else</span>
<span class="n">buffer2</span><span class="p">.</span><span class="nf">push</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">rescue</span> <span class="no">StopIteration</span>
<span class="k">break</span>
<span class="k">end</span>
<span class="k">else</span>
<span class="n">y</span><span class="p">.</span><span class="nf">yield</span> <span class="n">buffer1</span><span class="p">.</span><span class="nf">shift</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="p">}.</span><span class="nf">lazy</span><span class="p">,</span>
<span class="no">Enumerator</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="o">|</span><span class="n">y</span><span class="o">|</span>
<span class="kp">loop</span> <span class="k">do</span>
<span class="k">if</span> <span class="n">buffer2</span><span class="p">.</span><span class="nf">empty?</span>
<span class="k">begin</span>
<span class="n">item</span> <span class="o">=</span> <span class="n">source</span><span class="p">.</span><span class="nf">next</span>
<span class="k">if</span> <span class="o">!</span><span class="n">block</span><span class="p">.</span><span class="nf">call</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
<span class="n">y</span><span class="p">.</span><span class="nf">yield</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
<span class="k">else</span>
<span class="n">buffer1</span><span class="p">.</span><span class="nf">push</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">rescue</span> <span class="no">StopIteration</span>
<span class="k">break</span>
<span class="k">end</span>
<span class="k">else</span>
<span class="n">y</span><span class="p">.</span><span class="nf">yield</span> <span class="n">buffer2</span><span class="p">.</span><span class="nf">shift</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="p">}.</span><span class="nf">lazy</span>
<span class="p">]</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>Testing it:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">Enumerator</span><span class="p">.</span><span class="nf">produce</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">i</span><span class="o">|</span> <span class="nb">puts</span> <span class="s2">"processing </span><span class="si">#{</span><span class="n">i</span><span class="si">}</span><span class="s2">"</span><span class="p">;</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span> <span class="p">}.</span><span class="nf">lazy</span>
<span class="p">.</span><span class="nf">take</span><span class="p">(</span><span class="mi">30</span><span class="p">)</span>
<span class="p">.</span><span class="nf">partition</span><span class="p">(</span><span class="o">&</span><span class="ss">:odd?</span><span class="p">)</span>
<span class="p">.</span><span class="nf">then</span> <span class="p">{</span> <span class="o">|</span><span class="n">odd</span><span class="p">,</span> <span class="n">even</span><span class="o">|</span>
<span class="nb">p</span> <span class="n">odd</span><span class="p">.</span><span class="nf">first</span><span class="p">(</span><span class="mi">3</span><span class="p">),</span> <span class="n">even</span><span class="p">.</span><span class="nf">first</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1"># Prints:</span>
<span class="c1"># processing 1</span>
<span class="c1"># processing 2</span>
<span class="c1"># processing 3</span>
<span class="c1"># processing 4</span>
<span class="c1"># processing 5</span>
<span class="c1"># [1, 3, 5]</span>
<span class="c1"># [2, 4, 6]</span>
</code></pre>
<p>As you might notice by the "processing" log, it only fetched the amount of entries that was required by produced enumerators.</p>
<p>The <strong>drawback</strong> would be—as my prototype implementation shows—the need of internal "buffering" (I don't think it is possible to implement lazy partition without it), but it still might be worth a shot?</p> Ruby master - Feature #18136 (Open): take_while_afterhttps://bugs.ruby-lang.org/issues/181362021-08-27T09:26:06Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>Sorry, I already tried that once (<a class="issue tracker-2 status-6 priority-4 priority-default closed" title="Feature: Enumerable#take_while_after (Rejected)" href="https://bugs.ruby-lang.org/issues/16441">#16441</a>) but I failed to produce the persuasive example.<br>
So I am back with a couple of them, much simpler and clear than my initial.</p>
<p><strong>The proposal itself:</strong> Have <code>take_while_after</code> which behaves like <code>take_while</code> but also includes the last element (first where the condition failed). Reason: there are a lot of cases where "the last good item" in enumeration is the distinctive one (one where enumeration should stop, but the item is still good.</p>
<p><strong>Example 1:</strong> Take pages from paginated API, the last page will have less items than the rest (and that's how we know it is the last):</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="p">(</span><span class="mi">0</span><span class="o">..</span><span class="p">).</span><span class="nf">lazy</span>
<span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">offset</span><span class="o">|</span> <span class="n">get_page</span><span class="p">(</span><span class="n">offset</span><span class="p">,</span> <span class="n">limit</span><span class="p">)</span> <span class="p">}</span>
<span class="p">.</span><span class="nf">take_while_after</span> <span class="p">{</span> <span class="o">|</span><span class="n">response</span><span class="o">|</span> <span class="n">response</span><span class="p">.</span><span class="nf">count</span> <span class="o">==</span> <span class="n">limit</span> <span class="p">}</span> <span class="c1"># the last will have, say, 10 items, but should still be included!</span>
<span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="n">process</span> <span class="n">response</span> <span class="n">somehow</span> <span class="p">}</span>
</code></pre>
<p><strong>Example 2:</strong> Same as above, but "we should continue pagination" is specified with a separate data key "can_continue":</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="p">(</span><span class="mi">0</span><span class="o">..</span><span class="p">).</span><span class="nf">lazy</span>
<span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">offset</span><span class="o">|</span> <span class="n">get_page</span><span class="p">(</span><span class="n">offset</span><span class="p">,</span> <span class="n">limit</span><span class="p">)</span> <span class="p">}</span>
<span class="p">.</span><span class="nf">take_while_after</span> <span class="p">{</span> <span class="o">|</span><span class="n">response</span><span class="o">|</span> <span class="n">response</span><span class="p">[</span><span class="s1">'can_continue'</span><span class="p">]</span> <span class="p">}</span> <span class="c1"># the last will have can_continue=false, but still has data</span>
<span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="n">process</span> <span class="n">response</span> <span class="n">somehow</span> <span class="p">}</span>
</code></pre>
<p><strong>Exampe 3:</strong> Taking a sentence from a list of tokens like this:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">tokens</span> <span class="o">=</span> <span class="p">[</span>
<span class="p">{</span><span class="ss">text: </span><span class="s1">'Ruby'</span><span class="p">,</span> <span class="ss">type: :word</span><span class="p">},</span>
<span class="p">{</span><span class="ss">text: </span><span class="s1">'is'</span><span class="p">,</span> <span class="ss">type: :word</span><span class="p">},</span>
<span class="p">{</span><span class="ss">text: </span><span class="s1">'cool'</span><span class="p">,</span> <span class="ss">type: :word</span><span class="p">},</span>
<span class="p">{</span><span class="ss">text: </span><span class="s1">'.'</span><span class="p">,</span> <span class="ss">type: :punctuation</span><span class="p">,</span> <span class="ss">ends_sentence: </span><span class="kp">true</span><span class="p">},</span>
<span class="p">{</span><span class="ss">text: </span><span class="s1">'Rust'</span><span class="p">,</span> <span class="ss">type: :word</span><span class="p">},</span>
<span class="c1"># ...</span>
<span class="p">]</span>
<span class="n">sentence</span> <span class="o">=</span> <span class="n">tokens</span><span class="p">.</span><span class="nf">take_while_after</span> <span class="p">{</span> <span class="o">!</span><span class="n">_1</span><span class="p">[</span><span class="ss">:ends_sentence</span><span class="p">]</span> <span class="p">}</span>
</code></pre>
<p>(I can get more if it is necessary!)</p>
<p>Neither of those can be solved by "Using <code>take_while</code> with proper condition.", as <a class="user active user-mention" href="https://bugs.ruby-lang.org/users/13">@matz (Yukihiro Matsumoto)</a> suggested here: <a href="https://bugs.ruby-lang.org/issues/16441#note-9" class="external">https://bugs.ruby-lang.org/issues/16441#note-9</a></p>
<p>I typically solve it by <code>slice_after { condition }.first</code>, but that's a) uglier and b) greedy when we are working with lazy enumerator (so for API examples, all paginated pages would be fetched at once, and only then processed).</p>
<p>Another consideration in <a class="issue tracker-2 status-6 priority-4 priority-default closed" title="Feature: Enumerable#take_while_after (Rejected)" href="https://bugs.ruby-lang.org/issues/16441">#16441</a> was an unfortunate naming.<br>
I am leaving it to discussion, though I tend to like <code>#take_upto</code> from <a class="issue tracker-2 status-6 priority-4 priority-default closed" title="Feature: Enumerable#take_*, Enumerable#drop_* counterparts with positive conditions (Rejected)" href="https://bugs.ruby-lang.org/issues/16446">#16446</a>.</p> Ruby master - Bug #17534 (Closed): Pattern-matching is broken with find patternhttps://bugs.ruby-lang.org/issues/175342021-01-13T14:46:34Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>The minimal reproduction code:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">case</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span>
<span class="k">in</span> <span class="n">y</span>
<span class="nb">puts</span> <span class="s2">"branch1"</span>
<span class="k">in</span> <span class="p">[</span><span class="o">*</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="o">*</span><span class="p">]</span>
<span class="nb">puts</span> <span class="s2">"branch2"</span>
<span class="k">else</span>
<span class="nb">puts</span> <span class="s2">"branch3"</span>
<span class="k">end</span>
</code></pre>
<p>This outputs long "raw disasm" sequence, and then</p>
<pre><code>---------------------
break_pm.rb:6: argument stack underflow (-1)
break_pm.rb: compile error (SyntaxError)
</code></pre>
<pre><code>$ ruby -v
ruby 3.1.0dev (2021-01-13T09:12:49Z master 6f6dfdcc68) [x86_64-linux]
</code></pre> Ruby master - Misc #17499 (Closed): Documentation backportinghttps://bugs.ruby-lang.org/issues/174992021-01-01T12:09:44Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>I recently noticed that the documentation created after <code>x.y</code> release (even 1 day after) never reflected at <code>docs.ruby-lang.org/<x.y>.0/</code>.</p>
<p>In discussion <a href="https://github.com/ruby/docs.ruby-lang.org/issues/111" class="external">here</a> I was educated by <a class="user active user-mention" href="https://bugs.ruby-lang.org/users/572">@hsbt (Hiroshi SHIBATA)</a> that the source of <code>docs.ruby-lang.org/<x.y>.0/</code> is actually a branch <code>ruby_<x_y></code>, so in order to appear at docs.ruby-lang.org, documentation should be "backported" into the proper branch, which usually does not happen.</p>
<p>A few examples (documentation submitted by me, but it is not that I consider "my" documentation the most important, it is just easier for me to track):</p>
<ul>
<li>
<code>doc/syntax/pattern_matching.rdoc</code>: <a href="https://github.com/ruby/ruby/pull/2786" class="external">merged</a> on 2020-02-23, but is not represented here: <a href="https://docs.ruby-lang.org/en/2.7.0/" class="external">https://docs.ruby-lang.org/en/2.7.0/</a> -- because it isn't present in <a href="https://github.com/ruby/ruby/tree/ruby_2_7/doc/syntax" class="external">ruby_2_7</a> branch.</li>
<li>update <code>doc/syntax/methods.rdoc</code> (add endless methods and <code>...</code>-forwarding): <a href="https://github.com/ruby/ruby/pull/3997" class="external">merged</a> 2020-12-25 (just a few hours after 3.0 release), but not represented here: <a href="https://docs.ruby-lang.org/en/3.0.0/doc/syntax/methods_rdoc.html" class="external">https://docs.ruby-lang.org/en/3.0.0/doc/syntax/methods_rdoc.html</a> -- because it isn't in <a href="https://github.com/ruby/ruby/blob/ruby_3_0/doc/syntax/methods.rdoc" class="external">ruby_3_0</a> branch.</li>
</ul>
<p><strong>IMPORTANT</strong>: It is just two random examples, I believe there are numerous problems like that.</p>
<p>I wonder how this should be addressed:</p>
<ul>
<li>should somebody gather all the occurrences to "commits that should be picked into the proper branches"?</li>
<li>can it be done in some systematic manner?</li>
<li>can at least currently developed (3.0's) enhancements be "backported" in some automatic manner (PR labels?..)</li>
</ul> Ruby master - Misc #17422 (Closed): 3.0 documentation problems tracking tickethttps://bugs.ruby-lang.org/issues/174222020-12-21T22:21:06Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>A meta-ticket for tracking all documentation problems with 3.0's new features (which hopefully should be fixed before the release). I plan to work on those myself, but I have only so much time, so the help would be appreciated.</p>
<ul>
<li>Missing docs for new methods:
<ul>
<li>
<del><code>Symbol#name</code>, <code>Proc#==</code>, <code>Fiber#backtrace</code> (more or less trivial, handled by <a href="https://github.com/ruby/ruby/pull/3966" class="external">https://github.com/ruby/ruby/pull/3966</a>)</del> <strong>merged</strong>
</li>
</ul>
</li>
<li>Method docs to be changed
<ul>
<li>
<del><code>Fiber#transfer</code> changed limitations -- requires thorough redocumenting of <code>#transfer</code>, its use cases and limitations: <a href="https://github.com/ruby/ruby/pull/3981" class="external">https://github.com/ruby/ruby/pull/3981</a></del> <strong>merged</strong>
</li>
<li>
<code>Module#include</code> / <code>#prepend</code> -- I'd say the docs should be rewritten (now they are from the "internal" point of view), and only then there would be a place to describe the behavior change;</li>
</ul>
</li>
<li>Larger chunks:
<ul>
<li>
<code>doc/syntax/methods.rdoc</code> -- no sign of <code>...</code> or "endless" methods: <a href="https://github.com/ruby/ruby/pull/3997" class="external">https://github.com/ruby/ruby/pull/3997</a> <strong>to review</strong>
</li>
<li>
<del>Fiber scheduler and non-blocking fibers: my take is <a href="https://github.com/ruby/ruby/pull/3891" class="external">https://github.com/ruby/ruby/pull/3891</a> / <a class="issue tracker-1 status-5 priority-4 priority-default closed" title="Bug: New docs for non-blocking Fibers and scheduler (Closed)" href="https://bugs.ruby-lang.org/issues/17389">#17389</a>, now it is up to <a class="user active user-mention" href="https://bugs.ruby-lang.org/users/3344">@ioquatix (Samuel Williams)</a> to accept/reject or do it his way</del> <strong>merged</strong>
</li>
<li>
<code>doc/syntax/pattern_matching.rdoc</code> -- should be reviewed, cleaned up (at least formatting) and probably structured better after reintroduction of two kinds of one-line matching;</li>
</ul>
</li>
<li>Should it be updated?
<ul>
<li>
<code>doc/syntax/assignment.rdoc</code> -- "Class Variable" section, include more explanation on visibility (and new exceptions on "overtaking")?</li>
<li>
<code>Kernel#lambda</code> -- include info on "only literal blocks"?</li>
<li>
<code>Symbol#to_proc</code> -- explain about the block's lambdiness?</li>
<li>
<code>Mutex</code> -- mention it is owned per-Fiber?</li>
</ul>
</li>
<li>Documentation bugs:
<ul>
<li>
<del><code>Random</code> lost definitions of instance methods: fixed by <a href="https://github.com/ruby/ruby/pull/3966" class="external">https://github.com/ruby/ruby/pull/3966</a></del> <strong>merged</strong>
</li>
<li>
<code>Kernel</code>: lost docs for several methods (like <a href="https://docs.ruby-lang.org/en/master/Kernel.html#method-i-abort" class="external">#abort</a>, but also <code>#exec</code>, <code>#exit</code>) due to <code>NO_RETURN</code> macro, I am not yet sure how to fix it (moving comment before macro does not help)</li>
</ul>
</li>
</ul>
<p>To be continued...</p> Ruby master - Bug #17413 (Closed): --backtrace-limit: wrong level counterhttps://bugs.ruby-lang.org/issues/174132020-12-20T18:49:51Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>Having this <code>test.rb</code>:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">def</span> <span class="nf">inner</span>
<span class="k">raise</span> <span class="s1">'test'</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">outer</span>
<span class="n">inner</span>
<span class="k">end</span>
<span class="n">outer</span>
</code></pre>
<p>...one might observe the following:</p>
<pre><code>$ ruby test.rb
test.rb:2:in `inner': test (RuntimeError)
from test.rb:6:in `outer'
from test.rb:9:in `<main>'
# Print me 0 levels (only the report):
$ ruby --backtrace-limit=0 test.rb
test.rb:2:in `inner': test (RuntimeError)
... 3 levels... # <== Umm, which 3? I saw 2 there!
$ ruby --backtrace-limit=1 test.rb
test.rb:2:in `inner': test (RuntimeError)
from test.rb:6:in `outer'
... 2 levels... # <== Which 2?..
# So, the next increase of the limit will leave just 1 hidden, right?..
$ ruby --backtrace-limit=2 test.rb
test.rb:2:in `inner': test (RuntimeError)
from test.rb:6:in `outer'
from test.rb:9:in `<main>'
# Nope!
</code></pre> Ruby master - Misc #17412 (Closed): Regexp vs Range: freezing differenceshttps://bugs.ruby-lang.org/issues/174122020-12-19T23:38:18Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>While working on the changelog, I noticed the following in the 3.0:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="sr">/foo/</span><span class="p">.</span><span class="nf">frozen?</span> <span class="c1"># => true </span>
<span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">3</span><span class="p">).</span><span class="nf">frozen?</span> <span class="c1"># => true </span>
<span class="c1"># but...</span>
<span class="no">Regexp</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="s1">'foo'</span><span class="p">).</span><span class="nf">frozen?</span> <span class="c1"># => false </span>
<span class="c1"># ...whereupon</span>
<span class="no">Range</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">).</span><span class="nf">frozen?</span> <span class="c1"># => true </span>
</code></pre>
<p>I'd like to understand, what is there a reason for non-literal regexps to not being frozen, and for ranges to be?<br>
Corresponding tickets (<a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Regexp literals should be frozen (Closed)" href="https://bugs.ruby-lang.org/issues/16377">#16377</a> and <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Freeze all Range objects (Closed)" href="https://bugs.ruby-lang.org/issues/15504">#15504</a>) doesn't clarify the reasoning:</p>
<blockquote>
<p>For the record: <code>Regexp.new</code> should continue to return unfrozen Regexp instance. (<a class="user active user-mention" href="https://bugs.ruby-lang.org/users/18">@mame (Yusuke Endoh)</a> at <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Regexp literals should be frozen (Closed)" href="https://bugs.ruby-lang.org/issues/16377#note-7">#16377#note-7</a>)</p>
</blockquote>
<p>and:</p>
<blockquote>
<p>I'm for freezing all Ranges, not only Range literals. I hate the idea of freezing only literals because casually mixing frozen and unfrozen objects leads to hard-to-debug bugs that depend upon data flow. (<a class="user active user-mention" href="https://bugs.ruby-lang.org/users/18">@mame (Yusuke Endoh)</a> at <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Freeze all Range objects (Closed)" href="https://bugs.ruby-lang.org/issues/15504#note-3">#15504#note-3</a>)</p>
</blockquote>
<p>I understand that the matter is probably negligible, but will be very grateful for some explanations.</p> Ruby master - Feature #17411 (Closed): Allow expressions in pattern matchinghttps://bugs.ruby-lang.org/issues/174112020-12-19T18:16:33Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>Code:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">version</span> <span class="o">=</span> <span class="p">{</span><span class="ss">name: </span><span class="s1">'2.6'</span><span class="p">,</span> <span class="ss">released_at: </span><span class="no">Time</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">2018</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">25</span><span class="p">)}</span>
<span class="n">version</span> <span class="k">in</span> <span class="p">{</span><span class="ss">released_at: </span><span class="no">Time</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">2010</span><span class="p">)</span><span class="o">..</span><span class="no">Time</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">2020</span><span class="p">)}</span>
<span class="c1"># ^ syntax error, unexpected '.', expecting '}'</span>
<span class="c1"># This works:</span>
<span class="n">range</span> <span class="o">=</span> <span class="no">Time</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">2010</span><span class="p">)</span><span class="o">..</span><span class="no">Time</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">2020</span><span class="p">)</span>
<span class="n">version</span> <span class="k">in</span> <span class="p">{</span><span class="ss">released_at: </span><span class="o">^</span><span class="n">range</span><span class="p">}</span>
<span class="c1">#=> true</span>
</code></pre>
<p>(Fails with all versions of the pattern matching, <code>in</code>, <code>=></code> and <code>case ... in</code>, and on Ruby 2.7 too.)</p>
<p>Am I missing something about the syntax?..</p> Ruby master - Bug #17410 (Closed): One-line pattern-matching deprecation is lost on singular vari...https://bugs.ruby-lang.org/issues/174102020-12-19T11:57:01Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>Is this intentional? Probably an effect of changing the behavior of warning categories?</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">RUBY_REVISION</span>
<span class="c1"># => "1ba8d63b49318e5682a22502c5f5b70e3298da8f" </span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span> <span class="o">=></span> <span class="p">[</span><span class="n">x</span><span class="p">,</span> <span class="o">*</span><span class="p">]</span>
<span class="c1"># (irb):8: warning: One-line pattern matching is experimental, and the behavior may change in future versions of Ruby!</span>
<span class="mi">1</span> <span class="o">=></span> <span class="n">x</span>
<span class="c1"># ....no deprecation...</span>
</code></pre> Ruby master - Bug #17408 (Closed): Fiber.backtrace returns [] when unavailable, unlike Threadhttps://bugs.ruby-lang.org/issues/174082020-12-18T23:32:51Zzverok (Victor Shepelev)zverok.offline@gmail.com
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">t</span> <span class="o">=</span> <span class="no">Thread</span><span class="p">.</span><span class="nf">new</span><span class="p">{}</span>
<span class="nb">p</span> <span class="n">t</span><span class="p">.</span><span class="nf">backtrace</span>
<span class="c1"># => nil</span>
<span class="n">f</span> <span class="o">=</span> <span class="no">Fiber</span><span class="p">.</span><span class="nf">new</span><span class="p">{}</span>
<span class="nb">p</span> <span class="n">f</span><span class="p">.</span><span class="nf">backtrace</span>
<span class="c1"># => []</span>
</code></pre>
<p>It seems inconsistent, is this intentional?</p>
<p>Ping <a class="user active user-mention" href="https://bugs.ruby-lang.org/users/3344">@ioquatix (Samuel Williams)</a></p> Ruby master - Feature #17407 (Closed): Fiber.current and require 'fiber'https://bugs.ruby-lang.org/issues/174072020-12-18T23:08:43Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>Maybe it is not the right time to ask, but why one need to do <code>require 'fiber'</code> before using <code>Fiber.current</code>?</p>
<p>For what I can see,</p>
<ul>
<li>it is this way since <a href="https://docs.ruby-lang.org/en/2.0.0/Fiber.html#method-c-current" class="external">their introduction</a>,</li>
<li>the actual code is defined in the core <a href="https://github.com/ruby/ruby/blob/master/cont.c#L2480" class="external">cont.c</a>
</li>
<li>the <code>ext/fiber.c</code> <a href="https://github.com/ruby/ruby/blob/master/ext/fiber/fiber.c" class="external">does very little</a>
</li>
</ul>
<p>I was just bitten by it again preparing the changelog (stuck with <code>NoMethodError</code> and for a few minutes thought the build is broken), is there a reason to have it this way?..<br>
Just clarifying for docs sake, at least.</p> Ruby master - Misc #17399 (Closed): Are endless methods experimental?https://bugs.ruby-lang.org/issues/173992020-12-16T19:57:32Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>They are marked as such in NEWS, but an attempt to define one doesn't produce a warning (unlike other features marked this way, like one-line pattern-matching or find patterns)</p> Ruby master - Feature #17398 (Closed): SyntaxError in endless methodhttps://bugs.ruby-lang.org/issues/173982020-12-16T19:52:17Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>This works:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">def</span> <span class="nf">foo</span><span class="p">()</span> <span class="o">=</span> <span class="nb">puts</span><span class="p">(</span><span class="s2">"bar"</span><span class="p">)</span>
</code></pre>
<p>This does not:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">def</span> <span class="nf">foo</span><span class="p">()</span> <span class="o">=</span> <span class="nb">puts</span> <span class="s2">"bar"</span>
<span class="c1"># ^ syntax error, unexpected string literal, expecting `do' or '{' or '('</span>
</code></pre>
<p>Is this intentional or accidental? Not sure how it is reasoned.</p> Ruby master - Misc #17390 (Closed): Class and method-level docs for Ractorhttps://bugs.ruby-lang.org/issues/173902020-12-13T18:33:21Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p><strong>GitHub PR: <a href="https://github.com/ruby/ruby/pull/3895" class="external">https://github.com/ruby/ruby/pull/3895</a></strong></p>
<p>Copying from PR descriptioin:</p>
<p>Currently, ractors documented only by <a href="https://docs.ruby-lang.org/en/master/doc/ractor_md.html" class="external">doc/ractor.md</a>, which has a flavor of a design/discussion document. I wanted to establish some <em>base</em> documentation, which will allow to understand and use the concept immediately. Currently, Ractor class <a href="https://docs.ruby-lang.org/en/master/Ractor.html" class="external">has no docs at all</a> -- it is partially due to the fact that <code>ractor.rb</code> is not included in the <code>.document</code> list for RDoc, but even so, per-method <a href="https://github.com/ruby/ruby/blob/master/ractor.rb" class="external">docs in ractor.rb</a> seem kinda sparse and chaotic to me.</p>
<p>I've completely rewritten the class docs. They are now somewhat duplicating <code>doc/ractor.md</code>, but from a different perspective.</p>
<p>Test rendering of the docs on my personal site: <a href="https://zverok.github.io/ruby-rdoc/Ractor.html" class="external">Ractor.html</a></p>
<p><a class="user active user-mention" href="https://bugs.ruby-lang.org/users/17">@ko1 (Koichi Sasada)</a> <a class="user active user-mention" href="https://bugs.ruby-lang.org/users/182">@marcandre (Marc-Andre Lafortune)</a> <a class="user active user-mention" href="https://bugs.ruby-lang.org/users/772">@Eregon (Benoit Daloze)</a> Can you please review it?</p>
<p>And one question I couldn't clarify by myself: can somebody please show an example of the code throwing <code>Ractor::UnsafeError</code> and maybe provide some brief explanation about it?.. From the sources I kinda get the idea, but my knowledge is not deep enough.</p> Ruby master - Bug #17389 (Closed): New docs for non-blocking Fibers and scheduler https://bugs.ruby-lang.org/issues/173892020-12-12T19:08:30Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p><strong>GitHub PR: <a href="https://github.com/ruby/ruby/pull/3891" class="external">https://github.com/ruby/ruby/pull/3891</a></strong></p>
<p>Copying from its description:</p>
<p>I propose new documentation approach for new features in Ruby 3.0: non-blocking Fiber and the scheduler.</p>
<p>Right now, the documentation is in a confusing state. First, the <code>doc/scheduler.md</code> is even not rendered correclty: <a href="https://docs.ruby-lang.org/en/master/doc/scheduler_md.html" class="external">https://docs.ruby-lang.org/en/master/doc/scheduler_md.html</a> (relatively easy to fix: wrong markdown markup for the code-blocks), and Fiber class itself has no mention for new concepts, and no docs for new methods: <a href="https://docs.ruby-lang.org/en/master/Fiber.html#method-c-schedule" class="external">https://docs.ruby-lang.org/en/master/Fiber.html#method-c-schedule</a>.</p>
<p>But on the bigger level, the documentation is quite hard to follow unless you are already fully in context of asynchronous loops and schedulers.</p>
<p>I am trying to fix it by:</p>
<ul>
<li>adding to the <code>Fiber</code> class necessary docs, both high-level overview and particular method details</li>
<li>redocumenting the expected scheduler interface via "imaginary" <code>Fiber::SchedulerInterface</code> class: it is present only in docs to leverage RDoc's method-by-method documentation, be able to link to them separately and so on</li>
</ul>
<p>Test rendering of the docs on my personal site:</p>
<ul>
<li><a href="https://zverok.github.io/ruby-rdoc/Fiber.html" class="external">Fiber</a></li>
<li><a href="https://zverok.github.io/ruby-rdoc/Fiber/SchedulerInterface.html" class="external">Fiber::SchedulerInterface</a></li>
</ul>
<p>I have not yet in this PR removed doc/scheduler.md, but I suggest to, as it is completely superseded by new docs.</p>
<p>I'd be really grateful if <a class="user active user-mention" href="https://bugs.ruby-lang.org/users/3344">@ioquatix (Samuel Williams)</a> will find some time to review this initiative in the upcoming days.</p> Ruby master - Feature #17330 (Open): Object#nonhttps://bugs.ruby-lang.org/issues/173302020-11-17T12:49:02Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>(As always "with core" method proposals, I don't expect quick success, but hope for a fruitful discussion)</p>
<a name="Reasons"></a>
<h3 >Reasons:<a href="#Reasons" class="wiki-anchor">¶</a></h3>
<p>Ruby always tried to be very chainability-friendly. Recently, with introduction of <code>.then</code> and <code>=></code>, even more so. But one pattern that frequently emerges and doesn't have good idiomatic expression: calculate something, and if it is not a "good" value, return <code>nil</code> (or provide default value with <code>||</code>). There are currently two partial solutions:</p>
<ol>
<li>
<code>nonzero?</code> in Ruby core (frequently mocked for "inadequate" behavior, as it is looking like predicate method, but instead of <code>true</code>/<code>false</code> returns an original value or <code>nil</code>)</li>
<li>ActiveSupport <code>Object#presence</code>, which also returns an original value or <code>nil</code> if it is not "present" (e.g. <code>nil</code> or <code>empty?</code> in AS-speak)</li>
</ol>
<p>Both of them prove themselves quite useful in some domains, but they are targeting only those particular domains, look unlike each other, and can be confusing.</p>
<a name="Proposal"></a>
<h3 >Proposal:<a href="#Proposal" class="wiki-anchor">¶</a></h3>
<p>Method <code>Object#non</code> (or <code>Kernel#non</code>), which receives a block, calls it with receiver and returns <code>nil</code> (if block matched) or receiver otherwise.</p>
<a name="Prototype-implementation"></a>
<h5 >Prototype implementation:<a href="#Prototype-implementation" class="wiki-anchor">¶</a></h5>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">Object</span>
<span class="k">def</span> <span class="nf">non</span>
<span class="nb">self</span> <span class="k">unless</span> <span class="k">yield</span><span class="p">(</span><span class="nb">self</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<a name="Usage-examples"></a>
<h5 >Usage examples:<a href="#Usage-examples" class="wiki-anchor">¶</a></h5>
<ol>
<li>
<p>With number:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">limit</span> <span class="o">=</span> <span class="n">calculate</span><span class="p">.</span><span class="nf">some</span><span class="p">.</span><span class="nf">limit</span>
<span class="n">limit</span><span class="p">.</span><span class="nf">zero?</span> <span class="p">?</span> <span class="no">DEFAULT_LIMIT</span> <span class="p">:</span> <span class="n">limit</span>
<span class="c1"># or, with nonzero?</span>
<span class="n">calculate</span><span class="p">.</span><span class="nf">some</span><span class="p">.</span><span class="nf">limit</span><span class="p">.</span><span class="nf">nonzero?</span> <span class="o">||</span> <span class="no">DEFAULT_LIMIT</span>
<span class="c1"># with non:</span>
<span class="n">calculate</span><span class="p">.</span><span class="nf">some</span><span class="p">.</span><span class="nf">limit</span><span class="p">.</span><span class="nf">non</span><span class="p">(</span><span class="o">&</span><span class="ss">:zero?</span><span class="p">)</span> <span class="o">||</span> <span class="no">DEFAULT_LIMIT</span>
<span class="c1"># ^ Note here, how, unlike `nonzero?`, we see predicate-y ?, but it is INSIDE the `non()` and less confusing</span>
</code></pre>
</li>
<li>
<p>With string:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="nb">name</span> <span class="o">=</span> <span class="n">params</span><span class="p">[</span><span class="ss">:name</span><span class="p">]</span> <span class="k">if</span> <span class="n">params</span><span class="p">[</span><span class="ss">:name</span><span class="p">]</span> <span class="o">&&</span> <span class="o">!</span><span class="n">params</span><span class="p">[</span><span class="ss">:name</span><span class="p">].</span><span class="nf">empty?</span>
<span class="c1"># or, with ActiveSupport:</span>
<span class="nb">name</span> <span class="o">=</span> <span class="n">params</span><span class="p">[</span><span class="ss">:name</span><span class="p">].</span><span class="nf">presence</span>
<span class="c1"># with non:</span>
<span class="nb">name</span> <span class="o">=</span> <span class="n">params</span><span class="p">[</span><span class="ss">:name</span><span class="p">]</span><span class="o">&</span><span class="p">.</span><span class="nf">non</span><span class="p">(</span><span class="o">&</span><span class="ss">:empty?</span><span class="p">)</span>
</code></pre>
</li>
<li>
<p>More complicated example</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">action</span> <span class="o">=</span> <span class="n">payload</span><span class="p">.</span><span class="nf">dig</span><span class="p">(</span><span class="s1">'action'</span><span class="p">,</span> <span class="s1">'type'</span><span class="p">)</span>
<span class="k">return</span> <span class="k">if</span> <span class="no">PROHIBITED_ACTIONS</span><span class="p">.</span><span class="nf">include?</span><span class="p">(</span><span class="n">action</span><span class="p">)</span>
<span class="nb">send</span><span class="p">(</span><span class="s2">"do_</span><span class="si">#{</span><span class="n">action</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="c1"># with non & then:</span>
<span class="n">payload</span><span class="p">.</span><span class="nf">dig</span><span class="p">(</span><span class="s1">'action'</span><span class="p">,</span> <span class="s1">'type'</span><span class="p">)</span>
<span class="p">.</span><span class="nf">non</span> <span class="p">{</span> <span class="o">|</span><span class="n">action</span><span class="o">|</span> <span class="no">PROHIBITED_ACTIONS</span><span class="p">.</span><span class="nf">include?</span><span class="p">(</span><span class="n">action</span><span class="p">)</span> <span class="p">}</span>
<span class="o">&</span><span class="p">.</span><span class="nf">then</span> <span class="p">{</span> <span class="o">|</span><span class="n">action</span><span class="o">|</span> <span class="nb">send</span><span class="p">(</span><span class="s2">"do_</span><span class="si">#{</span><span class="n">action</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="p">}</span>
</code></pre>
</li>
</ol>
<p>Basically, the proposal is a "chainable guard clause" that allows to "chain"ify and DRYify code like:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">value</span> <span class="o">=</span> <span class="n">fetch_something</span>
<span class="k">return</span> <span class="n">value</span> <span class="k">unless</span> <span class="n">value</span><span class="p">.</span><span class="nf">with_problems?</span>
<span class="c1"># which turns into</span>
<span class="n">fetch_something</span><span class="p">.</span><span class="nf">non</span><span class="p">(</span><span class="o">&</span><span class="ss">:with_problems?</span><span class="p">)</span>
<span class="c1"># or</span>
<span class="n">value</span> <span class="o">=</span> <span class="n">fetch_something</span>
<span class="n">value</span> <span class="o">=</span> <span class="n">reasonable_default</span> <span class="k">if</span> <span class="n">value</span><span class="p">.</span><span class="nf">with_problems?</span>
<span class="c1"># turns into</span>
<span class="n">value</span> <span class="o">=</span> <span class="n">fetch_something</span><span class="p">.</span><span class="nf">non</span><span class="p">(</span><span class="o">&</span><span class="ss">:with_problems?</span><span class="p">)</span> <span class="o">||</span> <span class="n">reasonable_default</span>
</code></pre>
<p>I believe that this idiom is frequent enough, in combinations like (assorted examples) "read config file but return <code>nil</code> if it is empty/wrong version", "fetch latest invoice, but ignore if it has an <code>unpayable</code> flag", "fetch a list of last user's searches, but if it is empty, provide default search hints" etc.</p>
<p>I believe there <em>is</em> un unreflected need for idiom like this, the need that is demonstrated by the existence of <code>nonzero?</code> and <code>presence</code>.</p> Ruby master - Feature #17312 (Closed): New methods in Enumerable and Enumerator::Lazy: flatten, p...https://bugs.ruby-lang.org/issues/173122020-11-09T13:27:12Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>(The offspring of <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Enumerator::Lazy vs Array methods (Closed)" href="https://bugs.ruby-lang.org/issues/16987">#16987</a>, which was too vague/philosophical)</p>
<p>I propose to add to <code>Enumerable</code> and <code>Enumerator::Lazy</code> the following methods:</p>
<ul>
<li><code>compact</code></li>
<li><code>product</code></li>
<li><code>flatten</code></li>
</ul>
<p>All of them can be performed with a one-way enumerator. All of them make sense for situations other than "just an array". All of them can be used for processing large sequences, and therefore meaningful to add to <code>Lazy</code>.</p> Ruby master - Feature #16987 (Closed): Enumerator::Lazy vs Array methodshttps://bugs.ruby-lang.org/issues/169872020-06-26T14:57:48Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>Enumerations are designed to be greedy (immediately executed on each method call within a chain) by default. Sometimes, that is not useful for practical purposes (e.g. 2 mln strings array, drop comments, split into fields, find the first ten whose field 2 is equal to some value). So one needs to either do everything in one <code>each</code> block, or use <code>Enumerable#lazy</code>. There are three problems with the latter:</p>
<ol>
<li>It is much less known,</li>
<li>It is said to be <em>almost always</em> slower than non-lazy, and is therefore not recommended,</li>
<li>It lacks some methods that are often necessary in processing large data chunks.</li>
</ol>
<p>I want to discuss (3) here. <code>Enumerator::Lazy</code> would better, but actually doesn't, have methods such as: <code>#flatten</code>, <code>#product</code>, and <code>#compact</code>. They are all methods of Array, not Enumerable. In fact,</p>
<ol>
<li>They probably <em>should</em> belong to <code>Enumerable</code> (none of them requires anything besides <code>#each</code> to function),</li>
<li>They are definitely useful for lazily processing large sequences.</li>
</ol> Ruby master - Feature #16812 (Closed): Allow slicing arrays with ArithmeticSequencehttps://bugs.ruby-lang.org/issues/168122020-04-23T15:32:53Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>I believe when concepts of ArithmeticSequence and <code>Range#%</code> were introduced, one of the main intended usages was array slicing in scientific data processing. So, it seems to make sense to allow this in <code>Array#[]</code>:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">ary</span><span class="p">[(</span><span class="mi">5</span><span class="o">..</span><span class="mi">20</span><span class="p">)</span> <span class="o">%</span> <span class="mi">2</span><span class="p">]</span> <span class="c1"># each second element between 5 and 20</span>
<span class="n">ary</span><span class="p">[(</span><span class="mi">0</span><span class="o">..</span><span class="p">)</span> <span class="o">%</span> <span class="mi">3</span><span class="p">]</span> <span class="c1"># each third element</span>
<span class="n">ary</span><span class="p">[</span><span class="mi">10</span><span class="p">.</span><span class="nf">step</span><span class="p">(</span><span class="ss">by: </span><span class="o">-</span><span class="mi">1</span><span class="p">)]</span> <span class="c1"># elements 10, 9, 8, 7 ....</span>
</code></pre>
<p>PR is <a href="https://github.com/ruby/ruby/pull/3055" class="external">here</a>.</p>
<p>My reasoning is as follows:</p>
<ol>
<li>As stated above, ArithmeticSequence and <code>Range#%</code> seem to have been introduced exactly for this goal</li>
<li>Python has its slicing syntax as <code>begin:end:step</code> (with a possibility to omit either), and it seems to be well respected and used feature for data processing. So I believe it is useful, and relatively easy to integrate into existing functionality</li>
</ol>
<p>I expect the usual "it is ugly and unreadable!" backlash.<br>
I don't have an incentive, nor energy, to "defend" the proposal, so I would not.</p> Ruby master - Bug #16497 (Assigned): StringIO#internal_encoding is broken (more severely in 2.7)https://bugs.ruby-lang.org/issues/164972020-01-10T11:18:31Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>To the best of my understanding from <a href="https://docs.ruby-lang.org/en/master/Encoding.html" class="external">Encoding</a> docs, the following is true:</p>
<ul>
<li>external encoding (explicitly specified or taken from <code>Encoding.default_external</code>) specifies how the IO understands input and stores it internally</li>
<li>internal encoding (explicitly specified or taken from <code>Encoding.default_internal</code>) specifies how the IO converts what it reads.</li>
</ul>
<p>Demonstration with regular files:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># prepare data</span>
<span class="no">File</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="s1">'test.txt'</span><span class="p">,</span> <span class="s1">'Україна'</span><span class="p">.</span><span class="nf">encode</span><span class="p">(</span><span class="s1">'KOI8-U'</span><span class="p">),</span> <span class="ss">encoding: </span><span class="s1">'KOI8-U'</span><span class="p">)</span> <span class="c1">#=> 7</span>
<span class="k">def</span> <span class="nf">test</span><span class="p">(</span><span class="n">io</span><span class="p">)</span>
<span class="n">str</span> <span class="o">=</span> <span class="n">io</span><span class="p">.</span><span class="nf">read</span>
<span class="p">[</span><span class="n">io</span><span class="p">.</span><span class="nf">external_encoding</span><span class="p">,</span> <span class="n">io</span><span class="p">.</span><span class="nf">internal_encoding</span><span class="p">,</span> <span class="n">str</span><span class="p">,</span> <span class="n">str</span><span class="p">.</span><span class="nf">encoding</span><span class="p">]</span>
<span class="k">end</span>
<span class="c1"># read it:</span>
<span class="nb">test</span><span class="p">(</span><span class="no">File</span><span class="p">.</span><span class="nf">open</span><span class="p">(</span><span class="s1">'test.txt'</span><span class="p">,</span> <span class="s1">'r:KOI8-U'</span><span class="p">))</span>
<span class="c1"># => [#<Encoding:KOI8-U>, nil, "\xF5\xCB\xD2\xC1\xA7\xCE\xC1", #<Encoding:KOI8-U>]</span>
<span class="c1"># We can specify internal encoding when opening the file:</span>
<span class="nb">test</span><span class="p">(</span><span class="no">File</span><span class="p">.</span><span class="nf">open</span><span class="p">(</span><span class="s1">'test.txt'</span><span class="p">,</span> <span class="s1">'r:KOI8-U:UTF-8'</span><span class="p">))</span>
<span class="c1"># => [#<Encoding:KOI8-U>, #<Encoding:UTF-8>, "Україна", #<Encoding:UTF-8>]</span>
<span class="c1"># ...or when it is already opened</span>
<span class="nb">test</span><span class="p">(</span><span class="no">File</span><span class="p">.</span><span class="nf">open</span><span class="p">(</span><span class="s1">'test.txt'</span><span class="p">).</span><span class="nf">tap</span> <span class="p">{</span> <span class="o">|</span><span class="n">f</span><span class="o">|</span> <span class="n">f</span><span class="p">.</span><span class="nf">set_encoding</span><span class="p">(</span><span class="s1">'KOI8-U'</span><span class="p">,</span> <span class="s1">'UTF-8'</span><span class="p">)</span> <span class="p">})</span>
<span class="c1"># => [#<Encoding:KOI8-U>, #<Encoding:UTF-8>, "Україна", #<Encoding:UTF-8>]</span>
<span class="c1"># ...or with Encoding.default_internal</span>
<span class="no">Encoding</span><span class="p">.</span><span class="nf">default_internal</span> <span class="o">=</span> <span class="s1">'UTF-8'</span>
<span class="nb">test</span><span class="p">(</span><span class="no">File</span><span class="p">.</span><span class="nf">open</span><span class="p">(</span><span class="s1">'test.txt'</span><span class="p">,</span> <span class="s1">'r:KOI8-U'</span><span class="p">))</span>
<span class="c1"># => [#<Encoding:KOI8-U>, #<Encoding:UTF-8>, "Україна", #<Encoding:UTF-8>]</span>
</code></pre>
<p>But with StringIO, <strong>internal encoding can't be set</strong> in Ruby <strong>2.6</strong>:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="nb">require</span> <span class="s1">'stringio'</span>
<span class="no">Encoding</span><span class="p">.</span><span class="nf">default_internal</span> <span class="o">=</span> <span class="kp">nil</span>
<span class="n">str</span> <span class="o">=</span> <span class="s1">'Україна'</span><span class="p">.</span><span class="nf">encode</span><span class="p">(</span><span class="s1">'KOI8-U'</span><span class="p">)</span>
<span class="c1"># Simplest form:</span>
<span class="nb">test</span><span class="p">(</span><span class="no">StringIO</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">str</span><span class="p">))</span>
<span class="c1"># => [#<Encoding:KOI8-U>, nil, "\xF5\xCB\xD2\xC1\xA7\xCE\xC1", #<Encoding:KOI8-U>]</span>
<span class="c1"># Try to set via mode</span>
<span class="nb">test</span><span class="p">(</span><span class="no">StringIO</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">str</span><span class="p">,</span> <span class="s1">'r:KOI8-U:UTF-8'</span><span class="p">))</span>
<span class="c1"># => [#<Encoding:KOI8-U>, nil, "\xF5\xCB\xD2\xC1\xA7\xCE\xC1", #<Encoding:KOI8-U>]</span>
<span class="c1"># Try to set via set_encoding:</span>
<span class="nb">test</span><span class="p">(</span><span class="no">StringIO</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">str</span><span class="p">,</span> <span class="s1">'r:KOI8-U:UTF-8'</span><span class="p">).</span><span class="nf">tap</span> <span class="p">{</span> <span class="o">|</span><span class="n">f</span><span class="o">|</span> <span class="n">f</span><span class="p">.</span><span class="nf">set_encoding</span><span class="p">(</span><span class="s1">'KOI8-U'</span><span class="p">,</span> <span class="s1">'UTF-8'</span><span class="p">)</span> <span class="p">})</span>
<span class="c1"># => [#<Encoding:KOI8-U>, nil, "\xF5\xCB\xD2\xC1\xA7\xCE\xC1", #<Encoding:KOI8-U>]</span>
<span class="c1"># Try to set via Enoding.default_internal:</span>
<span class="no">Encoding</span><span class="p">.</span><span class="nf">default_internal</span> <span class="o">=</span> <span class="s1">'UTF-8'</span>
<span class="nb">test</span><span class="p">(</span><span class="no">StringIO</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">str</span><span class="p">))</span>
<span class="c1"># => [#<Encoding:KOI8-U>, nil, "\xF5\xCB\xD2\xC1\xA7\xCE\xC1", #<Encoding:KOI8-U>]</span>
</code></pre>
<p>So, in 2.6, any attempt to do something with StringIO's internal encoding are <strong>just ignored</strong>.</p>
<p>In <strong>2.7</strong>, though, matters became much worse:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="nb">require</span> <span class="s1">'stringio'</span>
<span class="no">Encoding</span><span class="p">.</span><span class="nf">default_internal</span> <span class="o">=</span> <span class="kp">nil</span>
<span class="n">str</span> <span class="o">=</span> <span class="s1">'Україна'</span><span class="p">.</span><span class="nf">encode</span><span class="p">(</span><span class="s1">'KOI8-U'</span><span class="p">)</span>
<span class="c1"># Behaves same as 2.6</span>
<span class="nb">test</span><span class="p">(</span><span class="no">StringIO</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">str</span><span class="p">))</span>
<span class="c1"># => [#<Encoding:KOI8-U>, nil, "\xF5\xCB\xD2\xC1\xA7\xCE\xC1", #<Encoding:KOI8-U>]</span>
<span class="c1"># Try to set via mode: WEIRD behavior starts</span>
<span class="nb">test</span><span class="p">(</span><span class="no">StringIO</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">str</span><span class="p">,</span> <span class="s1">'r:KOI8-U:UTF-8'</span><span class="p">))</span>
<span class="c1"># => [#<Encoding:UTF-8>, nil, "\xF5\xCB\xD2\xC1\xA7\xCE\xC1", #<Encoding:UTF-8>]</span>
<span class="c1"># Try to set via set_encoding: still just ignored</span>
<span class="nb">test</span><span class="p">(</span><span class="no">StringIO</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">str</span><span class="p">,</span> <span class="s1">'r:KOI8-U:UTF-8'</span><span class="p">).</span><span class="nf">tap</span> <span class="p">{</span> <span class="o">|</span><span class="n">f</span><span class="o">|</span> <span class="n">f</span><span class="p">.</span><span class="nf">set_encoding</span><span class="p">(</span><span class="s1">'KOI8-U'</span><span class="p">,</span> <span class="s1">'UTF-8'</span><span class="p">)</span> <span class="p">})</span>
<span class="c1"># => [#<Encoding:KOI8-U>, nil, "\xF5\xCB\xD2\xC1\xA7\xCE\xC1", #<Encoding:KOI8-U>]</span>
<span class="c1"># Try to set via Enoding.default_internal: WEIRD behavior again</span>
<span class="no">Encoding</span><span class="p">.</span><span class="nf">default_internal</span> <span class="o">=</span> <span class="s1">'UTF-8'</span>
<span class="nb">test</span><span class="p">(</span><span class="no">StringIO</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">str</span><span class="p">))</span>
<span class="c1"># => [#<Encoding:UTF-8>, nil, "\xF5\xCB\xD2\xC1\xA7\xCE\xC1", #<Encoding:UTF-8>]</span>
</code></pre>
<p>So, <strong>2.7</strong> not just ignores attempts to set <strong>internal</strong> encoding, but erroneously sets it to <strong>external</strong> one, so strings are not recoded, but their encoding is forced to change.</p>
<p>I believe it is severe bug (more severe than 2.6's "just ignoring").</p>
<p><a href="https://www.reddit.com/r/ruby/comments/emd6q4/is_this_a_stringio_bug_in_ruby_270/" class="external">This Reddit thread</a> shows how it breaks existing code:</p>
<ul>
<li>the author uses <code>StringIO</code> to work with <code>ASCII-8BIT</code> strings;</li>
<li>the code is performed in Rails environment (which sets <code>internal_encoding</code> to <code>UTF-8</code> by default);</li>
<li>under <strong>2.7</strong>, <code>StringIO#read</code> returns <code>ASCII-8BIT</code> content in Strings saying their encoding is <code>UTF-8</code>.</li>
</ul> Ruby master - Bug #16472 (Closed): OStruct documentation is losthttps://bugs.ruby-lang.org/issues/164722020-01-02T14:41:33Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>After this commit: <a href="https://github.com/ruby/ruby/commit/9be3295d53b6fd9f8a3ad8157aa0655b1976d8ac" class="external">https://github.com/ruby/ruby/commit/9be3295d53b6fd9f8a3ad8157aa0655b1976d8ac</a> -- RDoc loses documentation attahed to OpenStruct class, see <a href="https://docs.ruby-lang.org/en/master/OpenStruct.html" class="external">https://docs.ruby-lang.org/en/master/OpenStruct.html</a>, which currently looks this way:</p>
<pre><code>class OpenStruct
<here shoud be explanations>
Public Class Methods
</code></pre>
<p><code>require</code> should be put above docs.</p>
<p>I'd provide PR myself, but I kinda lost between core repository and <a href="https://github.com/ruby/ostruct" class="external">https://github.com/ruby/ostruct</a> (which seems outdated)?..</p> Ruby master - Misc #16464 (Closed): Which core objects should support deconstruct/deconstruct_keys?https://bugs.ruby-lang.org/issues/164642019-12-28T13:04:07Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>Now, when pattern matching is out, I believe it is worth discussing which core and standard library objects should be matchable "out of the box".</p>
<p>My proposals, as of now, are:</p>
<p><strong>1. <code>Object#deconstruct</code>, returning <code>[self]</code>.</strong></p>
<p>Justification:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># this works:</span>
<span class="mi">1</span> <span class="k">in</span> <span class="no">Integer</span>
<span class="c1"># this works:</span>
<span class="mi">1</span> <span class="k">in</span> <span class="o">..</span><span class="mi">0</span>
<span class="c1"># this does NOT:</span>
<span class="mi">1</span> <span class="k">in</span> <span class="no">Integer</span><span class="p">(</span><span class="o">..</span><span class="mi">0</span><span class="p">)</span>
<span class="c1"># NoMatchingPatternError (1)</span>
</code></pre>
<p>I believe the latter example looks pretty logical (and can be used in some flexible methods like "if it is a positive integer, it is index in the array, if it is negative integer, it is backward index, and if it is float, it should be calculated as a mean of nearby elements")</p>
<p><strong>2. <code>Time#deconstruct_keys</code></strong></p>
<p>Justification is obvious:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">case</span> <span class="n">created_at</span>
<span class="k">when</span> <span class="ss">year: </span><span class="mi">2019</span><span class="p">,</span> <span class="ss">month: </span><span class="mi">11</span><span class="o">..</span><span class="mi">12</span> <span class="o">=></span> <span class="n">m</span><span class="p">,</span> <span class="ss">day:
</span><span class="nb">p</span> <span class="s2">"Created at </span><span class="si">#{</span><span class="n">day</span><span class="si">}</span><span class="s2">.</span><span class="si">#{</span><span class="n">m</span><span class="si">}</span><span class="s2"> this year"</span>
<span class="k">else</span>
<span class="c1"># ...</span>
<span class="k">end</span>
</code></pre>
<p>(Probably the same for <code>Date</code> and <code>DateTime</code>)</p>
<p><strong>3. <code>Set#deconstruct</code></strong></p>
<p>Seems "logical" as set is a sequence, but I can't think of a good realistic example :)</p> Ruby master - Misc #16452 (Closed): Document pattern matchinghttps://bugs.ruby-lang.org/issues/164522019-12-25T18:44:27Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>I noticed 2.7 was released without any official pattern matching documentation, other than the link to the (outdated) presentation.</p>
<p>I don't know, maybe this work is already in progress internally, or maybe it consciously left this way, but I'd like it to be fixed for wider feature adoption.</p>
<p>In this PR, I am trying to document PM to the best of my understanding: <a href="https://github.com/ruby/ruby/pull/2786" class="external">https://github.com/ruby/ruby/pull/2786</a>.</p> Ruby master - Misc #16447 (Closed): Merge JSON doc updates?https://bugs.ruby-lang.org/issues/164472019-12-23T09:54:23Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>Sorry for bothering the core tracker.<br>
I understand (though, only partially) that JSON standard library is in a weird state "it is kinda maintained but we can't contact maintaner" (is there no way to just move the project's main repo under <code>ruby</code> github organization or make <code>ruby/json</code> fork the "master" one?..), but <strong>if</strong> it is possible, I'd be really happy if those two PRs from <strong>MARCH OF 2018</strong> that just improve documentation: <a href="https://github.com/flori/json/pull/347" class="external">https://github.com/flori/json/pull/347</a>, <a href="https://github.com/flori/json/pull/349" class="external">https://github.com/flori/json/pull/349</a>, would be merged before Ruby 2.7.</p>
<p>I am trying to make Ruby's documentation more consistent and friendly in general, and JSON's one is beside the most outdated. The PRs are zero-risk (change not a symbol in code), so I <em>hope</em> their merging can be managed.</p> Ruby master - Misc #16443 (Closed): GC.compact is not documentedhttps://bugs.ruby-lang.org/issues/164432019-12-21T18:27:10Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>See <a href="https://docs.ruby-lang.org/en/master/GC.html#method-c-compact" class="external">https://docs.ruby-lang.org/en/master/GC.html#method-c-compact</a></p>
<p>Typically, I'd just submit a documentation PR, but I am not sure I can properly explain what it does and when it should be used.</p> Ruby master - Bug #16422 (Closed): IO#set_encoding_by_bom when encoding is already sethttps://bugs.ruby-lang.org/issues/164222019-12-14T19:13:58Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p><code>IO#set_encoding_by_bom</code> docs <a href="https://ruby-doc.org/core-2.7.0.preview3/IO.html#method-i-set_encoding_by_bom" class="external">say</a>:</p>
<blockquote>
<p>If ios is not binmode or <strong>its encoding has been set already</strong>, an exception will be raised.</p>
</blockquote>
<p>In reality this does not happen (or I don't understand the meaning of bold statement above):</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">File</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="s2">"tmp/bom.txt"</span><span class="p">,</span> <span class="s2">"</span><span class="se">\u</span><span class="s2">{FEFF}мама"</span><span class="p">)</span>
<span class="n">ios</span> <span class="o">=</span> <span class="no">File</span><span class="p">.</span><span class="nf">open</span><span class="p">(</span><span class="s2">"tmp/bom.txt"</span><span class="p">,</span> <span class="s2">"rb"</span><span class="p">,</span> <span class="ss">encoding: </span><span class="s1">'Windows-1251'</span><span class="p">)</span>
<span class="n">ios</span><span class="p">.</span><span class="nf">external_encoding</span>
<span class="c1"># => #<Encoding:Windows-1251></span>
<span class="n">ios</span><span class="p">.</span><span class="nf">set_encoding_by_bom</span>
<span class="c1"># => #<Encoding:UTF-8></span>
<span class="n">ios</span><span class="p">.</span><span class="nf">external_encoding</span>
<span class="c1"># => #<Encoding:UTF-8></span>
</code></pre>
<p>...though <a href="https://github.com/ruby/ruby/blob/master/io.c#L8334" class="external">in code</a>, seems that error <em>should</em> be raised.</p>
<p>I wonder if it is an error in docs, bug in code, or just my misunderstanding?..</p> Ruby master - Bug #16421 (Closed): public_send with empty keyword arguments (Ruby version < 2.7)https://bugs.ruby-lang.org/issues/164212019-12-14T15:08:20Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>Probably it is something that I've just missed, so it is more a question than bug report.<br>
In Ruby < 2.7, I'd implement "delegate everything" method as such:</p>
<pre><code class="ruby syntaxhl" data-language="ruby">
<span class="k">def</span> <span class="nf">delegate</span><span class="p">(</span><span class="nb">name</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">&</span><span class="n">block</span><span class="p">)</span>
<span class="vi">@something</span><span class="p">.</span><span class="nf">public_send</span><span class="p">(</span><span class="nb">name</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">&</span><span class="n">block</span><span class="p">)</span>
<span class="k">end</span>
</code></pre>
<p>Now, while updating one of my gems to Ruby 2.7, I receive a warning (if SOME of the delegated methods accept keyword args):</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">A</span>
<span class="k">def</span> <span class="nf">simple_method</span><span class="p">(</span><span class="n">arg</span><span class="p">)</span>
<span class="nb">p</span> <span class="n">arg</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">kwargs_method</span><span class="p">(</span><span class="n">arg</span><span class="p">,</span> <span class="n">kwarg</span><span class="p">:)</span>
<span class="nb">p</span> <span class="p">[</span><span class="n">arg</span><span class="p">,</span> <span class="n">kwarg</span><span class="p">]</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">delegate</span><span class="p">(</span><span class="nb">name</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">&</span><span class="n">block</span><span class="p">)</span>
<span class="n">public_send</span><span class="p">(</span><span class="nb">name</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">&</span><span class="n">block</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="no">A</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">delegate</span><span class="p">(</span><span class="ss">:kwargs_method</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="ss">kwarg: </span><span class="mi">2</span><span class="p">)</span> <span class="c1"># warning: The last argument is used as the keyword parameter</span>
</code></pre>
<p>OK, that's understandable!<br>
Now, I am trying to rewrite kode to work with both 2.6 and 2.7:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">A</span>
<span class="k">def</span> <span class="nf">simple_method</span><span class="p">(</span><span class="n">arg</span><span class="p">)</span>
<span class="nb">p</span> <span class="n">arg</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">kwargs_method</span><span class="p">(</span><span class="n">arg</span><span class="p">,</span> <span class="n">kwarg</span><span class="p">:)</span>
<span class="nb">p</span> <span class="p">[</span><span class="n">arg</span><span class="p">,</span> <span class="n">kwarg</span><span class="p">]</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">delegate</span><span class="p">(</span><span class="nb">name</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">,</span> <span class="o">&</span><span class="n">block</span><span class="p">)</span>
<span class="n">public_send</span><span class="p">(</span><span class="nb">name</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">,</span> <span class="o">&</span><span class="n">block</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="no">A</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">delegate</span><span class="p">(</span><span class="ss">:kwargs_method</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="ss">kwarg: </span><span class="mi">2</span><span class="p">)</span>
<span class="no">A</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">delegate</span><span class="p">(</span><span class="ss">:simple_method</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
</code></pre>
<p>It warks as expected under 2.7, but under 2.6, the last line throws:</p>
<pre><code>tmp/delegate.rb:2:in `simple_method': wrong number of arguments (given 2, expected 1) (ArgumentError)
</code></pre>
<p>Is it something obvious that I am missing (I tried to search through the tracker, but can't detect related issues...). It there something I can do to have code working in both 2.6 and 2.7 without falling back to <code>ruby_2keywords</code> or <code>if RUBY_VERSION < '2.7'</code> (or checking for kwargs in <code>if method(name).parameters</code>)?..</p> Ruby master - Feature #16264 (Closed): Real "callable instance method" object.https://bugs.ruby-lang.org/issues/162642019-10-19T09:06:37Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>It is a part of thinking about the "argument-less call style" I already made several proposals about.</p>
<a name="Preface"></a>
<h3 >Preface<a href="#Preface" class="wiki-anchor">¶</a></h3>
<p><em><strong>Argument-less call style</strong></em> is what I now call things like <code>map(&:foo)</code> and <code>each(&Notifications.:send)</code> approaches, and I believe that naming the concept (even if my initial name is clamsy) will help to think about it. After using it a lot on a large production codebase (not only symbols, but method references too, which seem to be less widespread technique), I have a strong opinion that it not just "helps to save the keypresses" (which is less important), but also helps to clearer separate the concepts on a micro-level of the code. E.g. if you feel that <code>each(&Notifications.:send)</code> is "more right" than <code>select { |el| Notifications.send(el, something, something) }</code>, it makes you think about <code>Notifications.send</code> design in a way that allows to pass there <em>exactly</em> that combination of arguments so it would be easily callable that way, clarifying modules responsibilities.</p>
<blockquote>
<p>(And I believe that "nameless block parameters", while helping to shorter the code, lack this important characteristic of clarification.)</p>
</blockquote>
<a name="The-problem"></a>
<h3 >The problem<a href="#The-problem" class="wiki-anchor">¶</a></h3>
<p>One of the problems of "argument-less calling" is passing additional arguments, things like those aren't easy to shorten:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">ary1</span><span class="p">.</span><span class="nf">zip</span><span class="p">(</span><span class="n">ary2</span><span class="p">,</span> <span class="n">ary3</span><span class="p">).</span><span class="nf">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">lines</span><span class="o">|</span> <span class="n">lines</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span> <span class="p">}</span>
<span class="c1"># ^^^^</span>
<span class="n">construct_url</span><span class="p">.</span><span class="nf">then</span><span class="p">(</span><span class="o">&</span><span class="no">HTTP</span><span class="o">.</span><span class="ss">:get</span><span class="p">).</span><span class="nf">body</span><span class="p">.</span><span class="nf">then</span> <span class="p">{</span> <span class="o">|</span><span class="n">text</span><span class="o">|</span> <span class="no">JSON</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="n">text</span><span class="p">,</span> <span class="ss">symbolize_names: </span><span class="kp">true</span><span class="p">)</span> <span class="p">}</span>
<span class="c1"># ^^^^^^^^^^^^^^^^^^^^^^</span>
</code></pre>
<p>(BTW, here is <a href="https://zverok.github.io/blog/2019-10-18-each_with_object.html" class="external">a blog post</a> where I show recently found technique for solving this, pretty nice and always existing in Ruby, if slightly esotheric.)</p>
<p>There's a lot of proposals for "partial applications" which would be more expressive than <code>.curry</code> (<a href="https://bugs.ruby-lang.org/issues/16113" class="external">guilty</a> <a href="https://bugs.ruby-lang.org/issues/15301" class="external">myself</a>), but the problematic part in all of this proposals is:</p>
<blockquote>
<p><strong>The most widespread "shortening" is <code>&:symbol</code>, and <code>Symbol</code> itself is NOT a functional object, and it is wrong to extend it with functional abilities.</strong></p>
</blockquote>
<p>One of consequences of the above is, for example, that you can't use 2.6's proc combination with symbols, like <code>File.:read >> :strip >> :reverse</code>. You want, but you can't.</p>
<p>Here (while discussing aforementioned blog posts), I stumbled upon an idea of how to solve this dilemma.</p>
<a name="The-proposal"></a>
<h3 >The proposal<a href="#The-proposal" class="wiki-anchor">¶</a></h3>
<p>I propose to have a syntax for creating a functional object that when being called, sends the specified method to its first argument. Basically, what <code>Symbol#to_proc</code> does, but without "hack" of "we allow our symbols to be convertible to functional objects". Proposed syntax:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">].</span><span class="nf">map</span><span class="p">(</span><span class="o">&.</span><span class="ss">:to_s</span><span class="p">)</span>
</code></pre>
<p>Justification of the syntax:</p>
<ul>
<li>It is like <code>Foo.:method</code> (producing functional object that calls <code>method</code>)</li>
<li>Orphan <code>.:method</code> isn't allowed currently (you need to say <code>self.:method</code> to refer to "current <code>self</code>s method"), and Matz's justification was "it would be too confusable with <code>:method</code>, small typo will change the result" -- which in PROPOSED case is not as bad, as <code>:foo</code> and <code>.:foo</code> both meaning the same thing;</li>
<li>It looks kinda nice, similar to (<a href="https://bugs.ruby-lang.org/issues/16120" class="external">proposed and rejected</a>) <code>map { .to_s }</code> → with my proposal, it is <code>map(&.:to_s)</code>, implying somehow applying <code>.to_s</code> to the previous values in the chain.</li>
</ul>
<p><strong>The behavior:</strong> <code>.:foo</code> produces object of class, say, <code>MethodOfArgument</code> (class name is subject to discuss) — which makes differences of "Proc created from Symbol" (existing internally, but almost invisible) obvious and hackable.</p>
<a name="Potential-gains"></a>
<h3 >Potential gains<a href="#Potential-gains" class="wiki-anchor">¶</a></h3>
<ul>
<li>
<p>New object could be used in proc composition: <code>File.:read >> .:strip >> JSON.:parse >> .:compact</code></p>
</li>
<li>
<p>When both "method" and "method of argument" are proper functional objects, a new partial application syntax can be discussed, common for them both. For example (but <strong>not necessary this method name!</strong>)</p>
<pre><code class="ruby syntaxhl" data-language="ruby"></code></pre>
</li>
</ul>
<p>paragraph_hashes.map(&.:merge.with(author: current_author))<br>
filenames.map(&File.:read.with(mode: 'rb'))</p>
<pre><code>* (I believe at this point we'll be able to finally switch from discussing "show we extend Symbol with more callable-alike functionality" to just method's name and exact behavior)
* I am not an expert, but probably some optimizations could be applied, too
* Currently, `:sym.to_proc` is internally different from other proc, but this can't be introspected:
```ruby
:read.to_proc.inspect # => "#<Proc:0x0000556216192198(&:read)>"
# ^^^^^
</code></pre>
<ul>
<li>Probably, exposure of this fact could lead to some new interesting metaprogrammin/optimization techniques.</li>
</ul>
<a name="Transition"></a>
<h3 >Transition<a href="#Transition" class="wiki-anchor">¶</a></h3>
<p><code>:foo</code> and <code>.:foo</code> could work similarly for some upcoming versions (or indefinitely), with <code>.:foo</code> being more powerful alternative, allowing features like <code>groups_of_lines.map(&.:join.partial_apply(' '))</code> or something.</p>
<p>It would be like "real" and "imitated" keyword arguments. "Last hash without braces" was good at the beginning of the language lifecycle, but then it turned out that real ones provide a lot of benefits. Same thing here: <code>&:symbol</code> is super-nice, but, honestly, it is semantically questionable, so may be slow switch to a "real thing" would be gainful for everybody?..</p> Ruby master - Feature #16260 (Closed): Symbol#to_proc behaves like lambda, but doesn't aknowledge ithttps://bugs.ruby-lang.org/issues/162602019-10-18T09:21:42Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>Seems that <code>Symbol#to_proc</code> returns <code>Proc</code> that has lambda semantics:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="nb">proc</span> <span class="o">=</span> <span class="p">:</span><span class="o">+</span><span class="p">.</span><span class="nf">to_proc</span>
<span class="nb">proc</span><span class="p">.</span><span class="nf">call</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="c1"># => 3</span>
<span class="nb">proc</span><span class="p">.</span><span class="nf">call</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">])</span> <span class="c1"># ArgumentError (wrong number of arguments (given 0, expected 1))</span>
</code></pre>
<p>But if you ask...</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="nb">proc</span><span class="p">.</span><span class="nf">lambda?</span> <span class="c1"># => false</span>
</code></pre>
<p>That seems to be an inconsistency, which I'd like to clarify. There are obviously two ways to fix it:</p>
<ol>
<li>Make it respond <code>true</code> to <code>lambda?</code> (and mention the semantics in docs)</li>
<li>Make it behave like non-lambda.</li>
</ol>
<p>The second one seems to produce some useful behavior:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># Currently:</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">].</span><span class="nf">zip</span><span class="p">([</span><span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">]).</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="p">:</span><span class="o">+</span><span class="p">)</span> <span class="c1"># ArgumentError (wrong number of arguments (given 0, expected 1))</span>
<span class="c1"># With non-lambda:</span>
<span class="k">class</span> <span class="nc">Symbol</span>
<span class="k">def</span> <span class="nf">to_proc</span>
<span class="nb">proc</span> <span class="p">{</span> <span class="o">|</span><span class="n">o</span><span class="p">,</span> <span class="o">*</span><span class="n">a</span><span class="o">|</span> <span class="n">o</span><span class="p">.</span><span class="nf">send</span><span class="p">(</span><span class="nb">self</span><span class="p">,</span> <span class="o">*</span><span class="n">a</span><span class="p">)</span> <span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">].</span><span class="nf">zip</span><span class="p">([</span><span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">]).</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="p">:</span><span class="o">+</span><span class="p">)</span> <span class="c1"># => [4, 6] </span>
</code></pre>
<p>Probably all of it was discussed when <code>Symbol#to_proc</code> was introduced, but as old NEWS-files doesn't link to tickets/discussions, I can't find the reasoning for current behavior.</p> Ruby master - Feature #16249 (Open): Dir#empty? and File#empty?https://bugs.ruby-lang.org/issues/162492019-10-12T09:48:22Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>I don't think it is a super-valuable addition (as nobody complained about it, or I can't find similar proposals), but seems logical to me:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># Existed since 2.4</span>
<span class="no">Dir</span><span class="p">.</span><span class="nf">empty?</span><span class="p">(</span><span class="s1">'dirname'</span><span class="p">)</span> <span class="c1">#=> true/false</span>
<span class="no">File</span><span class="p">.</span><span class="nf">empty?</span><span class="p">(</span><span class="s1">'filename'</span><span class="p">)</span> <span class="c1"># => true/false</span>
<span class="c1"># Proposed and implemented:</span>
<span class="no">Dir</span><span class="p">.</span><span class="nf">open</span><span class="p">(</span><span class="s1">'dirname'</span><span class="p">).</span><span class="nf">empty?</span>
<span class="no">File</span><span class="p">.</span><span class="nf">open</span><span class="p">(</span><span class="s1">'filename'</span><span class="p">).</span><span class="nf">empty?</span>
</code></pre> Ruby master - Feature #16183 (Open): Hash#with_defaulthttps://bugs.ruby-lang.org/issues/161832019-09-26T13:01:22Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>Reasons: there is no way, currently, to <em>declaratively</em> define Hash with default value (for example, to store it in constant, or use in an expression). Which leads to code more or less like this:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">FONTS</span> <span class="o">=</span> <span class="p">{</span>
<span class="ss">title: </span><span class="s1">'Arial'</span><span class="p">,</span>
<span class="ss">body: </span><span class="s1">'Times New Roman'</span><span class="p">,</span>
<span class="ss">blockquote: </span><span class="s1">'Tahoma'</span>
<span class="p">}.</span><span class="nf">tap</span> <span class="p">{</span> <span class="o">|</span><span class="n">h</span><span class="o">|</span> <span class="n">h</span><span class="p">.</span><span class="nf">default</span> <span class="o">=</span> <span class="s1">'Courier'</span> <span class="p">}.</span><span class="nf">freeze</span>
<span class="c1"># Grouping indexes:</span>
<span class="n">ary</span><span class="p">.</span><span class="nf">each_with_object</span><span class="p">(</span><span class="no">Hash</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="o">|</span><span class="n">h</span><span class="p">,</span> <span class="n">k</span><span class="o">|</span> <span class="n">h</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span> <span class="p">}).</span><span class="nf">with_index</span> <span class="p">{</span> <span class="o">|</span><span class="p">(</span><span class="n">el</span><span class="p">,</span> <span class="n">h</span><span class="p">),</span> <span class="n">idx</span><span class="o">|</span> <span class="n">h</span><span class="p">[</span><span class="n">el</span><span class="p">.</span><span class="nf">downcase</span><span class="p">]</span> <span class="o"><<</span> <span class="n">idx</span> <span class="p">}</span>
</code></pre>
<p>With proposed method:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">FONTS</span> <span class="o">=</span> <span class="p">{</span>
<span class="ss">title: </span><span class="s1">'Arial'</span><span class="p">,</span>
<span class="ss">body: </span><span class="s1">'Times New Roman'</span><span class="p">,</span>
<span class="ss">blockquote: </span><span class="s1">'Tahoma'</span>
<span class="p">}.</span><span class="nf">with_default</span><span class="p">(</span><span class="s1">'Courier'</span><span class="p">).</span><span class="nf">freeze</span>
<span class="n">ary</span><span class="p">.</span><span class="nf">each_with_object</span><span class="p">({}.</span><span class="nf">with_default</span> <span class="p">{</span> <span class="p">[]</span> <span class="p">}).</span><span class="nf">with_index</span> <span class="p">{</span> <span class="o">|</span><span class="p">(</span><span class="n">el</span><span class="p">,</span> <span class="n">h</span><span class="p">),</span> <span class="n">idx</span><span class="o">|</span> <span class="n">h</span><span class="p">[</span><span class="n">el</span><span class="p">.</span><span class="nf">downcase</span><span class="p">]</span> <span class="o"><<</span> <span class="n">idx</span> <span class="p">}</span>
</code></pre>
<p>About the block synopsys: I am not 100% sure, but I believe that <em>most</em> of the time when <code>default_proc</code> provided, it looks like <code>{ |h, k| h[k] = some_calculation }</code>. So, I believe for this "declarative simplification" of defaults, it is acceptable to assume it as the only behavior (pass only key to block, and always store block's result); more flexible form would still be accessible with <code>Hash.new</code>.</p> Ruby master - Feature #16175 (Closed): Object#clone(freeze: true) is inconsistent with Object#clo...https://bugs.ruby-lang.org/issues/161752019-09-21T17:33:27Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>In <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Allow Object#clone to take freeze: false keyword argument to not freeze the clone (Closed)" href="https://bugs.ruby-lang.org/issues/12300">#12300</a>, the new keyword <code>freeze:</code> was introduced, allowing this:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">h</span> <span class="o">=</span> <span class="p">{}.</span><span class="nf">freeze</span>
<span class="n">h</span><span class="p">.</span><span class="nf">clone</span><span class="p">.</span><span class="nf">frozen?</span>
<span class="c1"># => true</span>
<span class="n">h</span><span class="p">.</span><span class="nf">clone</span><span class="p">(</span><span class="ss">freeze: </span><span class="kp">false</span><span class="p">).</span><span class="nf">frozen?</span>
<span class="c1"># => false</span>
</code></pre>
<p>Though, it turns to me that behavior is not symmetric:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">h</span> <span class="o">=</span> <span class="p">{}</span>
<span class="n">h</span><span class="p">.</span><span class="nf">frozen?</span>
<span class="c1"># => false</span>
<span class="n">h</span><span class="p">.</span><span class="nf">clone</span><span class="p">.</span><span class="nf">frozen?</span>
<span class="c1"># => false</span>
<span class="n">h</span><span class="p">.</span><span class="nf">clone</span><span class="p">(</span><span class="ss">freeze: </span><span class="kp">true</span><span class="p">).</span><span class="nf">frozen?</span>
<span class="c1"># => false -- I expected true here!</span>
</code></pre>
<p>I wonder, if it is "by design" and should be addressed in docs, or just an implementation inconsistency that can be fixed?</p> Ruby master - Bug #16169 (Closed): rescue in a method argumenthttps://bugs.ruby-lang.org/issues/161692019-09-17T11:38:38Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>At 2.4, <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Allowing a postposed rescue in a method argument (Closed)" href="https://bugs.ruby-lang.org/issues/12686">#12686</a> was introduced, allowing code like this:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">foo</span> <span class="p">(</span><span class="no">Integer</span><span class="p">(</span><span class="no">ENV</span><span class="p">[</span><span class="s1">'FOO'</span><span class="p">])</span> <span class="k">rescue</span> <span class="kp">nil</span><span class="p">)</span>
</code></pre>
<p>Though, I noticed that in current Ruby correctness of this syntax depends on space after the method name:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">foo</span> <span class="p">(</span><span class="no">Integer</span><span class="p">(</span><span class="no">ENV</span><span class="p">[</span><span class="s1">'FOO'</span><span class="p">])</span> <span class="k">rescue</span> <span class="kp">nil</span><span class="p">)</span> <span class="c1"># => OK</span>
<span class="n">foo</span><span class="p">(</span><span class="no">Integer</span><span class="p">(</span><span class="no">ENV</span><span class="p">[</span><span class="s1">'FOO'</span><span class="p">])</span> <span class="k">rescue</span> <span class="kp">nil</span><span class="p">)</span>
<span class="c1"># SyntaxError ((irb):4: syntax error, unexpected modifier_rescue, expecting ')')</span>
<span class="c1"># foo(Integer(ENV['FOO']) rescue nil)</span>
<span class="c1"># ^~~~~~</span>
</code></pre>
<p>I wonder, whether it is just a bug or a parser limitation (I can't guess which ambiguity the space-less version produces, but I could be missing something)?..</p> Ruby master - Misc #16126 (Closed): Fix small docs inconsistencieshttps://bugs.ruby-lang.org/issues/161262019-08-24T17:35:27Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>The patch with some small-ish fixes:</p>
<ul>
<li>Enumerator refers to <code>#enum_for</code>/<code>#to_enum</code> under <code>Kernel</code>, while in fact they are documented under <code>Object</code>;</li>
<li>
<code>Kernel#warn</code> has outdated call sequence;</li>
<li>
<code>ClosedQueueError</code> is not mentioned in errors hierarchy;</li>
<li>
<code>then</code> is referred to as <code>yield_self</code>, obscuring the usage.</li>
</ul>
<p>Not fixed yet, though should be:</p>
<ul>
<li>
<code>File::Constants</code> documented as <code>File::File::Constants</code>: <a href="https://github.com/ruby/rdoc/issues/741" class="external">https://github.com/ruby/rdoc/issues/741</a>
</li>
<li>
<code>ClosedQueueError</code> doesn't renders its parent right (probably RDoc bug also, will investigate).</li>
</ul>
<p>For the next, step, I plan to provide a PRs with core and stdlib docs that are just missing, outdated or confusing.</p> Ruby master - Feature #16122 (Closed): Data: simple immutable value objecthttps://bugs.ruby-lang.org/issues/161222019-08-23T17:40:36Zzverok (Victor Shepelev)zverok.offline@gmail.com
<a name="Intro-original-theoretical-part-of-the-proposal"></a>
<h2 >Intro (original theoretical part of the proposal)<a href="#Intro-original-theoretical-part-of-the-proposal" class="wiki-anchor">¶</a></h2>
<p><strong>Value Object</strong> is a useful concept, introduced by Martin Fowler (<a href="https://martinfowler.com/bliki/ValueObject.html" class="external">his post</a>, <a href="https://en.wikipedia.org/wiki/Value_object" class="external">Wikipedia Entry</a>) with the following properties (simplifying the idea):</p>
<ul>
<li>representing some relatively simple data;</li>
<li>immutable;</li>
<li>compared by type & value;</li>
<li>nicely represented.</li>
</ul>
<p>Value objects are super-useful especially for defining APIs, their input/return values. Recently, there were some movement towards using more immutability-friendly approach in Ruby programming, leading to creating several discussions/libraries with value objects. For example, <a href="https://github.com/tomdalling/value_semantics" class="external">Tom Dalling's gem</a>, <a href="https://github.com/zverok/good-value-object" class="external">Good Ruby Value object convention</a> (disclaimer: the latter is maintained by yours truly).</p>
<p>I propose to introduce <strong>native value objects</strong> to Ruby as a core class.</p>
<p><strong>Why not a gem?</strong></p>
<ul>
<li>I believe that concept is that simple, that nobody <em>will even try</em> to use a gem for representing it with, unless the framework/library used already provides one.</li>
<li>Potentially, a lot of standard library (and probably even core) APIs could benefit from the concept.</li>
</ul>
<p><strong>Why <code>Struct</code> is not enough</strong></p>
<p>Core <code>Struct</code> class is "somewhat alike" value-object, and frequently used instead of one: it is compared by value and consists of simple attributes. On the other hand, <code>Struct</code> is:</p>
<ul>
<li>mutable;</li>
<li>collection-alike (defines <code>to_a</code> and is <code>Enumerable</code>);</li>
<li>dictionary-alike (has <code>[]</code> and <code>.values</code> methods).</li>
</ul>
<p>The above traits somehow erodes the semantics, making code less clear, especially when duck-typing is used.</p>
<p>For example, this code snippet shows why <code>to_a</code> is problematic:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">Result</span> <span class="o">=</span> <span class="no">Struct</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="ss">:success</span><span class="p">,</span> <span class="ss">:content</span><span class="p">)</span>
<span class="c1"># Now, imagine that other code assumes `data` could be either Result, or [Result, Result, Result]</span>
<span class="c1"># So, ...</span>
<span class="n">data</span> <span class="o">=</span> <span class="no">Result</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="kp">true</span><span class="p">,</span> <span class="s1">'it is awesome'</span><span class="p">)</span>
<span class="no">Array</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="c1"># => expected [Result(true, 'it is awesome')], got [true, 'it is awesome']</span>
<span class="c1"># or...</span>
<span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="n">arg1</span><span class="p">,</span> <span class="n">arg2</span> <span class="o">=</span> <span class="kp">nil</span><span class="p">)</span>
<span class="nb">p</span> <span class="n">arg1</span><span class="p">,</span> <span class="n">arg2</span>
<span class="k">end</span>
<span class="n">foo</span><span class="p">(</span><span class="o">*</span><span class="n">data</span><span class="p">)</span> <span class="c1"># => expected [Result(true, 'it is awesome'), nil], got [true, 'it is awesome']</span>
</code></pre>
<p>Having <code>[]</code> and <code>each</code> defined on something that is thought as "just value" can also lead to subtle bugs, when some method checks "if the received argument is collection-alike", and value object's author doesn't thought of it as a collection.</p>
<h2>
<code>Data</code> class: consensus proposal/implementation, Sep 2022</h2>
<ul>
<li>Name: <code>Data</code>
</li>
<li>PR: <a href="https://github.com/ruby/ruby/pull/6353" class="external">https://github.com/ruby/ruby/pull/6353</a>
</li>
<li>Example docs rendering: <a href="https://zverok.space/ruby-rdoc/Data.html" class="external">https://zverok.space/ruby-rdoc/Data.html</a>
</li>
<li>Full API:
<ul>
<li>
<code>Data::define</code> creates a new Data class; accepts only symbols (no <code>keyword_init:</code>, no "first argument is the class name" like the <code>Struct</code> had)</li>
<li>
<code><data_class>::members</code>: list of member names</li>
<li>
<code><data_class>::new</code>: accepts either keyword or positional arguments (but not mix); converts all of the to keyword args; raises <code>ArgumentError</code> if there are <strong>too many positional arguments</strong>
</li>
<li>
<code>#initialize</code>: accepts only keyword arguments; the default implementation raises <code>ArgumentError</code> on missing or extra arguments; it is easy to redefine <code>initialize</code> to provide defaults or handle extra args.</li>
<li><code>#==</code></li>
<li><code>#eql?</code></li>
<li>
<code>#inspect</code>/<code>#to_s</code> (same representation)</li>
<li><code>#deconstruct</code></li>
<li><code>#deconstruct_keys</code></li>
<li><code>#hash</code></li>
<li><code>#members</code></li>
<li><code>#to_h</code></li>
</ul>
</li>
</ul>
<a name="Historical-original-proposal"></a>
<h2 >Historical original proposal<a href="#Historical-original-proposal" class="wiki-anchor">¶</a></h2>
<ul>
<li>Class name: <code>Struct::Value</code>: lot of Rubyists are used to have <code>Struct</code> as a quick "something-like-value" drop-in, so alternative, more strict implementation, being part of <code>Struct</code> API, will be quite discoverable; <em>alternative: just <code>Value</code></em>
</li>
<li>Class API is copying <code>Struct</code>s one (most of the time -- even reuses the implementation), with the following exceptions <em>(note: the immutability is <strong>not</strong> the only difference)</em>:
<ul>
<li>Not <code>Enumerable</code>;</li>
<li>Immutable;</li>
<li>Doesn't think of itself as "almost hash" (doesn't have <code>to_a</code>, <code>values</code> and <code>[]</code> methods);</li>
<li>Can have empty members list (fun fact: <code>Struct.new('Foo')</code> creating member-less <code>Struct::Foo</code>, is allowed, but <code>Struct.new()</code> is not) to allow usage patterns like:</li>
</ul>
</li>
</ul>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">MyService</span>
<span class="no">Success</span> <span class="o">=</span> <span class="no">Struct</span><span class="o">::</span><span class="no">Value</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="ss">:results</span><span class="p">)</span>
<span class="no">NotFound</span> <span class="o">=</span> <span class="no">Struct</span><span class="o">::</span><span class="no">Value</span><span class="p">.</span><span class="nf">new</span>
<span class="k">end</span>
</code></pre>
<p><code>NotFound</code> here, unlike, say, <code>Object.new.freeze</code> (another pattern for creating "empty typed value object"), has nice inspect <code>#<value NotFound></code>, and created consistently with the <code>Success</code>, making the code more readable. And if it will evolve to have some attributes, the code change would be easy.</p>
<p><strong>Patch is provided</strong></p>
<p><a href="https://zverok.github.io/ruby-rdoc/Struct-Value.html" class="external">Sample rendered RDoc documentation</a></p> Ruby master - Feature #16113 (Open): Partial applicationhttps://bugs.ruby-lang.org/issues/161132019-08-19T10:07:16Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p><strong>Preface:</strong> One of the main "microstructures" of the code we use is chaining methods-with-blocks; and we really love to keep those blocks DRY when they are simple. Currently, for DRY-ing up simple blocks, we have:</p>
<ul>
<li><code>foo(&:symbol)</code></li>
<li>
<code>foo(&some.method(:name))</code> (as of 2.7, <code>foo(&some.:name)</code>)</li>
<li>Currently disputed "nameless block args": <code>foo { something(@1) }</code> or <code>foo { something(@) }</code> or <code>foo { something(it) }</code>
</li>
</ul>
<p><strong>Proposal:</strong> I argue that short and easy-to-remember partial application of blocks and methods can make methods-with-blocks much more pleasant and consistent to write, and continue softly shifting Ruby towards "functional" (while staying true to language's spirit).</p>
<p>In order to achieve this, I propose method <code>{Symbol,Method,Proc}#w</code> (from <code>with</code>), which will produce <code>Proc</code> with <em>last</em> arguments bound.</p>
<p>Example of usability:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># No-shortcuts: fetch something and parse as JSON:</span>
<span class="n">fetch</span><span class="p">(</span><span class="n">urls</span><span class="p">).</span><span class="nf">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">body</span><span class="o">|</span> <span class="no">JSON</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="n">body</span><span class="p">)</span> <span class="p">}</span>
<span class="c1"># Could be already (2.7+) shortened to:</span>
<span class="n">fetch</span><span class="p">(</span><span class="n">urls</span><span class="p">).</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="no">JSON</span><span class="o">.</span><span class="ss">:parse</span><span class="p">)</span>
<span class="c1"># But if you have this:</span>
<span class="n">fetch</span><span class="p">(</span><span class="n">urls</span><span class="p">).</span><span class="nf">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">body</span><span class="o">|</span> <span class="no">JSON</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="n">body</span><span class="p">,</span> <span class="ss">symbolize_names: </span><span class="kp">true</span><span class="p">)</span> <span class="p">}</span>
<span class="c1"># How to shorten it, to don't repeat body?</span>
<span class="c1"># "Nameless block args" answer:</span>
<span class="n">fetch</span><span class="p">(</span><span class="n">urls</span><span class="p">).</span><span class="nf">map</span> <span class="p">{</span> <span class="no">JSON</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="err">@</span><span class="mi">1</span><span class="p">,</span> <span class="ss">symbolize_names: </span><span class="kp">true</span><span class="p">)</span> <span class="p">}</span>
<span class="c1"># Partial application answer:</span>
<span class="n">fetch</span><span class="p">(</span><span class="n">urls</span><span class="p">).</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="no">JSON</span><span class="o">.</span><span class="ss">:parse</span><span class="p">.</span><span class="nf">w</span><span class="p">(</span><span class="ss">symbolize_names: </span><span class="kp">true</span><span class="p">))</span>
</code></pre>
<p>I believe that the latter (while can be easily met with usual "hard to understand for a complete novice") provides the added value of producing proper "functional object", that can be stored in variables and constants, and generally lead to new approaches to writing Ruby code.</p>
<p>Another example:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="p">(</span><span class="mi">6</span><span class="o">..</span><span class="mi">11</span><span class="p">).</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="ss">:**</span><span class="p">.</span><span class="nf">w</span><span class="p">(</span><span class="mi">2</span><span class="p">)).</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="ss">:clamp</span><span class="p">.</span><span class="nf">w</span><span class="p">(</span><span class="mi">20</span><span class="p">,</span> <span class="mi">50</span><span class="p">))</span>
<span class="c1"># => [36, 49, 50, 50, 50, 50]</span>
</code></pre>
<p>Reference implementation:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">Symbol</span>
<span class="k">def</span> <span class="nf">w</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">)</span>
<span class="nb">proc</span> <span class="p">{</span> <span class="o">|</span><span class="n">receiver</span><span class="p">,</span> <span class="o">*</span><span class="n">rest</span><span class="o">|</span> <span class="n">receiver</span><span class="p">.</span><span class="nf">send</span><span class="p">(</span><span class="nb">self</span><span class="p">,</span> <span class="o">*</span><span class="n">rest</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">)</span> <span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">class</span> <span class="nc">Method</span>
<span class="k">def</span> <span class="nf">w</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">)</span>
<span class="nb">proc</span> <span class="p">{</span> <span class="o">|</span><span class="n">receiver</span><span class="p">,</span> <span class="o">*</span><span class="n">rest</span><span class="o">|</span> <span class="nb">self</span><span class="p">.</span><span class="nf">call</span><span class="p">(</span><span class="n">receiver</span><span class="p">,</span> <span class="o">*</span><span class="n">rest</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">)</span> <span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">class</span> <span class="nc">Proc</span>
<span class="k">def</span> <span class="nf">w</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">)</span>
<span class="n">prc</span> <span class="o">=</span> <span class="nb">self</span>
<span class="nb">proc</span> <span class="p">{</span> <span class="o">|*</span><span class="n">rest</span><span class="o">|</span> <span class="n">prc</span><span class="p">.</span><span class="nf">call</span><span class="p">(</span><span class="o">*</span><span class="n">rest</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">)</span> <span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre> Ruby master - Bug #15988 (Closed): Time#dst? vs "real" timezoneshttps://bugs.ruby-lang.org/issues/159882019-07-05T09:00:36Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>My current timezone is Europe/Kiev.</p>
<p>With default zone (how it was previously):</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">summer</span> <span class="o">=</span> <span class="no">Time</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">2019</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">14</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="mi">12</span><span class="p">)</span> <span class="c1"># => 2019-06-01 14:30:12 +0300 </span>
<span class="n">summer</span><span class="p">.</span><span class="nf">zone</span> <span class="c1"># => "EEST" </span>
<span class="n">summer</span><span class="p">.</span><span class="nf">dst?</span> <span class="c1"># => true </span>
<span class="n">winter</span> <span class="o">=</span> <span class="no">Time</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">2019</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">14</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="mi">12</span><span class="p">)</span> <span class="c1"># => 2019-12-01 14:30:12 +0200 </span>
<span class="n">winter</span><span class="p">.</span><span class="nf">zone</span> <span class="c1"># => "EET" </span>
<span class="n">winter</span><span class="p">.</span><span class="nf">dst?</span> <span class="c1"># => false </span>
</code></pre>
<p>(Note it is correctly <code>true</code> in summer and <code>false</code> in winter.)</p>
<p>With "real" timezone object</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="nb">require</span> <span class="s1">'tzinfo'</span>
<span class="n">z</span> <span class="o">=</span> <span class="no">TZInfo</span><span class="o">::</span><span class="no">Timezone</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="s1">'Europe/Kiev'</span><span class="p">)</span>
<span class="c1"># => #<TZInfo::DataTimezone: Europe/Kiev> </span>
<span class="n">summer</span> <span class="o">=</span> <span class="no">Time</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">2019</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">14</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="n">z</span><span class="p">)</span> <span class="c1"># => 2019-06-01 14:30:12 +0300 </span>
<span class="n">summer</span><span class="p">.</span><span class="nf">zone</span> <span class="c1"># => #<TZInfo::DataTimezone: Europe/Kiev> </span>
<span class="n">summer</span><span class="p">.</span><span class="nf">dst?</span> <span class="c1"># => true </span>
<span class="n">winter</span> <span class="o">=</span> <span class="no">Time</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">2019</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">14</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="n">z</span><span class="p">)</span> <span class="c1"># => 2019-12-01 14:30:12 +0200 -- offset is correct</span>
<span class="n">winter</span><span class="p">.</span><span class="nf">zone</span> <span class="c1"># => #<TZInfo::DataTimezone: Europe/Kiev></span>
<span class="n">winter</span><span class="p">.</span><span class="nf">dst?</span> <span class="c1"># => true -- this is wrong!</span>
</code></pre>
<p>Note that offsets are calculated correctly (+3 in summer, +2 in winter), but <code>dst?</code> is always <code>true</code>.</p>
<p>TZInfo itself calculates DST properly:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">z</span><span class="p">.</span><span class="nf">dst?</span><span class="p">(</span><span class="n">summer</span><span class="p">)</span> <span class="c1"># => true </span>
<span class="n">z</span><span class="p">.</span><span class="nf">dst?</span><span class="p">(</span><span class="n">winter</span><span class="p">)</span> <span class="c1"># => false </span>
</code></pre>
<p>Seems like a serious bug to me (or at least very hard to explain behavior).</p>
<p>PS: With arithmetics, everything works correctly:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">six_months</span> <span class="o">=</span> <span class="mi">6</span> <span class="o">*</span> <span class="mi">30</span> <span class="o">*</span> <span class="mi">24</span> <span class="o">*</span> <span class="mi">60</span> <span class="o">*</span> <span class="mi">60</span>
<span class="n">summer</span> <span class="o">+</span> <span class="n">six_months</span> <span class="c1"># => 2019-11-28 13:30:12 +0200</span>
<span class="p">(</span><span class="n">summer</span> <span class="o">+</span> <span class="n">six_months</span><span class="p">).</span><span class="nf">dst?</span> <span class="c1"># => false -- that is correct</span>
</code></pre> Ruby master - Misc #15893 (Closed): open-uri: URI.open statushttps://bugs.ruby-lang.org/issues/158932019-06-01T05:29:14Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>On the one hand, Ruby 2.5's NEWS <a href="https://github.com/ruby/ruby/commit/bf287424fd00c0304c836525bb52d89fc1f4a84a#diff-ff4e2dc4962dc25a1512353299992c8dR298" class="external">stated</a>:</p>
<blockquote>
<p>URI.open method defined as an alias to open-uri's Kernel.open. open-uri's Kernel.open will be deprecated in future.</p>
</blockquote>
<p>I believe there were good reasons for that decision.</p>
<p>On the other hand,</p>
<ul>
<li>no movements in this direction were done since 2.5</li>
<li>
<code>URI.open</code> <a href="https://github.com/ruby/ruby/blob/trunk/lib/open-uri.rb#L43" class="external">is excluded</a> from <code>open-uri</code>'s docs, and the main library's <a href="https://ruby-doc.org/stdlib-2.5.0/libdoc/open-uri/rdoc/OpenURI.html" class="external">documentation</a> doesn't mention this option as preferred or even existing.</li>
</ul>
<p>I'd like to know what the real status of this library and its migration to (safer) <code>URI.open</code>?<br>
Should a patch be provided to change the library's docs accordingly?<br>
Maybe even change the code (still leaving <code>Kernel.open</code> option, but just as an alias, moving the implementation away from that method)?</p> Ruby master - Misc #15529 (Closed): Document Enumerator::Lazyhttps://bugs.ruby-lang.org/issues/155292019-01-12T14:21:33Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>In this patch:</p>
<ul>
<li>explanation of the class concept, with examples;</li>
<li>docs for all class methods (most of them just say "Like <code>Enumerable#<methodname></code>, but chains operation to be lazy-evaluated.", but I believe they are useful this way because now have proper call-sequences and link to corresponding Enumerable's explanations)</li>
<li>simplified example for <code>::new</code> to emphasize the main concept</li>
<li>
<code>Enumerable#lazy</code> docs are slightly lightened and linked to this class for more in-depth explanations.</li>
</ul>
<p>For the reference, currently class docs are looking like this: <a href="https://ruby-doc.org/core-2.6/Enumerator/Lazy.html" class="external">https://ruby-doc.org/core-2.6/Enumerator/Lazy.html</a></p> Ruby master - Bug #15528 (Closed): Bundler stdlib is not documentedhttps://bugs.ruby-lang.org/issues/155282019-01-12T11:08:19Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>Since 2.6, Bundler is a part of the standard library. But the logic of standard library docs for "default gems" (only gem's <code>lib</code> is copied to Ruby source tree, and libraries main class/module is rendered as a docs entry point) lead to unusable documentation for Bundler: <a href="https://docs.ruby-lang.org/en/2.6.0/Bundler.html" class="external">https://docs.ruby-lang.org/en/2.6.0/Bundler.html</a></p>
<p>I am not sure how the situation should be handled, I can imagine at least 3 possible approaches:</p>
<ol>
<li>Work with Bundler team to have "basic usage" docs in <code>lib/bundler.rb</code>
</li>
<li>More sophisticated code copying (like copying parts of bundler's docs into <code>lib/bundler.rb</code> <strong>only in Ruby source tree</strong>)</li>
<li>Rethink stdlib docs rendering to include default gems READMEs (will also require READMEs to be copied)</li>
</ol>
<p>(1) is probably the simplest, though I am not sure Bundler team is ready to participate.</p> Ruby master - Feature #15527 (Closed): Redesign of timezone object requirementshttps://bugs.ruby-lang.org/issues/155272019-01-12T09:02:21Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>In <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Add official API for setting timezone on Time (Closed)" href="https://bugs.ruby-lang.org/issues/14850">#14850</a>, there was timezone support introduced, there were pretty specific requirements for the Timezone object:</p>
<blockquote>
<p>A timezone argument must have <code>local_to_utc</code> and <code>utc_to_local</code> methods... The <code>local_to_utc</code> method should convert a <code>Time</code>-like object from the timezone to UTC, and <code>utc_to_local</code> is the opposite. ... The zone of the result is just ignored.</p>
</blockquote>
<p>I understand this requirements were modelled after existing TZInfo gem, but the problem with them are:</p>
<ul>
<li>they are too ad-hoc (in fact, return values of methods aren't used as a "Time object", but as a tuple of time components)</li>
<li>they belong to outdated tzinfo API (ignoring of offsets is due to support of <strong>Ruby 1.8</strong>, which didn't allowed constructing <code>Time</code> object with arbitrary offset, see <a href="https://github.com/tzinfo/tzinfo/issues/49" class="external">discussion</a>), recent <a href="https://github.com/tzinfo/tzinfo/pull/52" class="external">release</a> introduces also <code>#to_local</code>, which returns <code>Time</code> with proper offset.</li>
</ul>
<p>The latter is a bit of time paradox: Ruby <strong>2.6</strong> new feature is designed after the library which works this way to support Ruby <strong>1.8</strong> :)<br>
The bad thing is, this approach somehow "codifies" outdated API (so in future, any alternative timezone library should support pretty arbitrary API).</p>
<p>I believe, that in order to do everything that <code>Time</code> needs, <em>timezone</em> object should be able to answer exactly one question: "what offset from UTC is/was observed in this timezone at particular date". In fact, TZInfo <strong>has</strong> the <a href="https://www.rubydoc.info/gems/tzinfo/TZInfo/Timezone#observed_utc_offset-instance_method" class="external">API</a> for this:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">tz</span> <span class="o">=</span> <span class="no">TZInfo</span><span class="o">::</span><span class="no">Timezone</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="s1">'America/New_York'</span><span class="p">)</span>
<span class="c1"># => #<TZInfo::DataTimezone: America/New_York> </span>
<span class="n">tz</span><span class="p">.</span><span class="nf">utc_offset</span><span class="p">(</span><span class="no">Time</span><span class="p">.</span><span class="nf">now</span><span class="p">)</span>
<span class="c1"># => -18000 </span>
</code></pre>
<p>If I understand correctly, this requirement ("A timezone argument must have <code>#utc_offset(at_time)</code>") will greatly simplify the implementation of <code>Time</code>, while also being compatible with <code>TZInfo</code> gem and much more explainable. With this requirement, alternative implementations could now be much simpler and focus only on "find the proper timezone/period/offset", omitting any (hard) details of deconstructing/constructing Time objects.</p> Ruby master - Misc #15487 (Assigned): Clarify default gems maintanance policyhttps://bugs.ruby-lang.org/issues/154872018-12-29T12:30:23Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>In addition to <a class="issue tracker-5 status-7 priority-4 priority-default closed" title="Misc: Default gems README.md (Feedback)" href="https://bugs.ruby-lang.org/issues/15486">#15486</a>, I'd like to raise the question of the general <em>maintanance policy</em> for "default" Ruby gems, in particular:</p>
<ul>
<li>who is responsible for each gem and how they should be contacted?</li>
<li>what are goals and policies for gems code quality and documentation?</li>
<li>where do default gems are discussed?</li>
<li>what are some promises/guarantees default gems maintainers try to fulfill?</li>
</ul>
<p>The most demonstrative example I'd like to point is <code>json</code> gem:</p>
<ul>
<li>The source at <a href="https://github.com/ruby/json" class="external">ruby/json</a> is NOT authoritative as far as I can tell, the authoritative one is <a href="https://github.com/flori/json" class="external">flori/json</a>
</li>
<li>The gem still holds signs of the times it was independent (<code>Pure</code> and <code>Ext</code> JSON implementations, but <code>Pure</code> is not copied into the <code>ruby/lib</code> on releases, rendering standard docs pretty weird), and has NO mention it is THE json gem of Ruby</li>
<li>The gem seems unmaintained, considering the amount of <a href="https://github.com/flori/json/pulls" class="external">PRs</a> and <a href="https://github.com/flori/json/issues" class="external">issues</a>, lot of them without any reaction for months</li>
<li>When I tried to update JSON docs, in <a href="https://bugs.ruby-lang.org/issues/14581" class="external">core tracker issue</a> I was asked to make a PR to "upstream repository", but there, the PRs (<a href="https://github.com/flori/json/pull/347" class="external">#347</a>, <a href="https://github.com/flori/json/pull/349" class="external">#349</a>) was simply ignored; Ruby 2.6 was released without new docs, despite the fact PRs were made at <strong>March</strong> and require almost no code review (unlike even some promising optimization PRs, that were also hanging there since Feb/Mar)</li>
</ul>
<p>It is just one unfortunate case (TBH, my experience with contributing to other libraries, like <code>csv</code> and <code>psych</code> was much smoother), but it demonstrates some common lack of transparency in maintaining of Ruby's standard library</p> Ruby master - Bug #15484 (Closed): Improve TracePoint docshttps://bugs.ruby-lang.org/issues/154842018-12-29T11:32:44Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>Improvements:</p>
<ul>
<li>Mention new <code>:script_compiled</code> event;</li>
<li>Deduplicate <code>__enable</code>/<code>enable</code> methods;</li>
<li>Document <code>target:</code> and <code>target_line:</code> arguments.</li>
</ul>
<p>I am not sure that <code>target</code> explanations and examples are sufficient and unambiguous, just copied them from the <a href="https://bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/66003" class="external">commit message</a></p> Ruby master - Bug #15482 (Closed): Document RubyVM.resolve_feature_pathhttps://bugs.ruby-lang.org/issues/154822018-12-29T10:40:15Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>RDoc requires method docs to be in the same <code>.c</code> file as <code>rb_define_method</code>, so despite method docs were present in source, they were ignored: <a href="https://ruby-doc.org/core-2.6/RubyVM.html" class="external">https://ruby-doc.org/core-2.6/RubyVM.html</a></p>
<p>The patch fixes it.</p> Ruby master - Bug #15481 (Closed): Update NoMethodError/NameError docshttps://bugs.ruby-lang.org/issues/154812018-12-29T10:23:05Zzverok (Victor Shepelev)zverok.offline@gmail.com
<ul>
<li>Add missing <code>receiver:</code> to <code>NoMethodError.new</code>;</li>
<li>Document <code>receiver</code> and <code>private</code> argument of <code>NoMethodError.new</code>;</li>
<li>Add explanations/examples about receiver to <code>NameError.new</code>;</li>
<li>Reformat both methods call-sequences in a modern way (<code>KeyError.new</code> already looks like this, so I took the liberty)</li>
</ul>
<p>PS: In general, I believe that all method call-sequences should be, if possible, formatted in a "modern" way:</p>
<pre><code># bad:
new([msg, *, name [, args [, priv]]])
# good:
new(msg=nil, name=nil, args=nil, private=false, receiver: nil)
</code></pre>
<p>Some exceptions are, probably, legacy (pre-keyword-argument) methods with odd call-sequences, like <code>Kernel#system</code> which has optional FIRST argument, and described as:</p>
<pre><code>system([env, ] command, ...)
</code></pre> Ruby master - Bug #15480 (Closed): Update Kernel#system docshttps://bugs.ruby-lang.org/issues/154802018-12-29T09:44:32Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>While working on <a href="https://rubyreferences.github.io/rubychanges/" class="external">Ruby Changelog</a>, I noticed some of new functionality of 2.6 is not documented.</p>
<p>I'll create several tickets to address problems discovered.</p>
<p>Current one is about <code>Kernel#system</code>:</p>
<ul>
<li>
<code>exception: false</code> argument and explanations added</li>
<li>links to "see <code>other_method</code>" fixed to be linkable</li>
<li>command variants fixed to be <code>dl</code> instead of preformatted text</li>
</ul> Ruby master - Bug #15452 (Closed): Document exception: false for Integer() and othershttps://bugs.ruby-lang.org/issues/154522018-12-22T19:46:22Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>Just noticed that after <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: An option to pass to `Integer`, `Float`, to return `nil` instead of raise an exception (Closed)" href="https://bugs.ruby-lang.org/issues/12732">#12732</a> there is no docs about new <code>exception:</code> option.<br>
The patch fixes this.</p>
<p>I understand it is pretty late, but I hope this patch could be merged before 2.6 release.</p>
<p>PS: I'll provide a patch for BigDecimal repo, too.</p> Ruby master - Bug #15411 (Closed): Enhance Tempfile docshttps://bugs.ruby-lang.org/issues/154112018-12-13T22:46:36Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>Several small style enhancements + I don't know why the "fake" docs for <code>::new</code> were done this way, but they hid the <code>mode:</code> keyword argument, which once cost me several dozens of minutes to investigate.</p>
<p>Docs <a href="https://ruby-doc.org/stdlib-2.5.0/libdoc/tempfile/rdoc/Tempfile.html#method-c-new" class="external">currently</a> insist on this call-sequence:</p>
<pre><code>new(basename = "", [tmpdir = Dir.tmpdir], [options])
</code></pre>
<p>...insisiting that it will pass <strong>all</strong> the options to <code>File.new</code>. So, when I wanted to pass <code>mode</code> for File, and ... well, it haven't ended well :)</p>
<p>So, I believe that it is better to represent actual call-sequence in the docs, and mention <code>mode</code> quirks.</p> Ruby master - Bug #15405 (Closed): Endless range docshttps://bugs.ruby-lang.org/issues/154052018-12-12T18:47:37Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>Sorry if this is the redundant work and the core team plans to do it themselves, but I noticed that new endless ranges have no mentions in Range documentation.<br>
Trying to fix that.</p> Ruby master - Feature #15301 (Closed): Symbol#call, returning method bound with argumentshttps://bugs.ruby-lang.org/issues/153012018-11-13T13:43:19Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>In one Reddit discussion I've got stuck with this simple, yet seemingly powerful idea, not sure if it was discussed anytime previously (can't find on the bug tracker, but maybe I am just bad at searching):</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">Symbol</span>
<span class="k">def</span> <span class="nf">call</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">&</span><span class="n">block</span><span class="p">)</span>
<span class="nb">proc</span> <span class="p">{</span> <span class="o">|</span><span class="n">x</span><span class="o">|</span> <span class="n">x</span><span class="p">.</span><span class="nf">send</span><span class="p">(</span><span class="nb">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">&</span><span class="n">block</span><span class="p">)</span> <span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="p">[</span><span class="mi">10</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">30</span><span class="p">].</span><span class="nf">map</span> <span class="o">&</span><span class="ss">:modulo</span><span class="o">.</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="c1"># => [1, 2, 0]</span>
<span class="p">[[</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">2</span><span class="p">],</span> <span class="p">[</span><span class="o">-</span><span class="mi">3</span><span class="p">,</span> <span class="o">-</span><span class="mi">4</span><span class="p">]].</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="ss">:map</span><span class="o">.</span><span class="p">(</span><span class="o">&</span><span class="ss">:abs</span><span class="p">))</span> <span class="c1"># => [[1, 2], [3, 4]]</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">].</span><span class="nf">map</span> <span class="o">&</span><span class="ss">:**</span><span class="o">.</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="c1"># => [1, 4, 9, 16]</span>
</code></pre>
<p>I understand and respect core team's reluctance for adding new methods to core classes, but from the top of my head I can't invent <em>incredibly</em> bad consequences (there, of course, could be some codebases that defined their own <code>Symbol#call</code> in a different way, but I don't estimate the probability as super-high; and the same could be said almost for any new method).</p>
<p>On the other hand, resulting code seems pretty nice, "Rubyish", explainable and mostly unambiguous.</p>
<p>Would like to hear other's opinions.</p>
<p>PS: One obvious objection could be that it is almost a de-facto standard to have any object's <code>#to_proc</code> to return proc doing exactly the same what the <code>#call</code> does (if the object happen to have both). It is unfortunate, but I believe the people will use to it, considering the possible gains. And, anyway, that's only "de-facto" rule, not the language standard :)</p> Ruby master - Feature #15144 (Closed): Enumerator#chainhttps://bugs.ruby-lang.org/issues/151442018-09-21T19:00:23Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>I am not sure I am not missing something, but...</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">].</span><span class="nf">each</span><span class="p">.</span><span class="nf">chain</span><span class="p">([</span><span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">].</span><span class="nf">each</span><span class="p">)</span> <span class="c1"># => Enumerator</span>
</code></pre>
<p>...seem to be a useful pattern.</p>
<p>It especially shows itself in case of lazy enumerators, representing several long-calculated sequences, like something...</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># just data from several sources, abstracted into enumerator, fetching it on demand</span>
<span class="n">process</span> <span class="o">=</span> <span class="no">URLS</span><span class="p">.</span><span class="nf">lazy</span><span class="p">.</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="no">Faraday</span><span class="p">.</span><span class="nf">method</span><span class="p">(</span><span class="ss">:get</span><span class="p">)))</span>
<span class="p">.</span><span class="nf">chain</span><span class="p">(</span><span class="no">LOCAL_FILES</span><span class="p">.</span><span class="nf">lazy</span><span class="p">.</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="no">File</span><span class="p">.</span><span class="nf">method</span><span class="p">(</span><span class="ss">:read</span><span class="p">)))</span>
<span class="p">.</span><span class="nf">chain</span><span class="p">(</span><span class="no">FALLBACK_FILE</span><span class="p">.</span><span class="nf">then</span><span class="p">.</span><span class="nf">lazy</span><span class="p">.</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="no">File</span><span class="p">.</span><span class="nf">method</span><span class="p">(</span><span class="ss">:read</span><span class="p">)))</span> <span class="c1"># with yield_self aka then we can even chain ONE value</span>
<span class="n">process</span><span class="p">.</span><span class="nf">detect</span> <span class="p">{</span> <span class="o">|</span><span class="n">val</span><span class="o">|</span> <span class="n">found?</span><span class="p">(</span><span class="n">val</span><span class="p">)</span> <span class="p">}</span> <span class="c1"># uniformely search several sources (lazy-loading them) for some value</span>
<span class="c1"># tty-progressbar is able to work with enumerables:</span>
<span class="n">bar</span> <span class="o">=</span> <span class="no">TTY</span><span class="o">::</span><span class="no">ProgressBar</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="s2">"[:bar]"</span><span class="p">,</span> <span class="ss">total: </span><span class="no">URLS</span><span class="p">.</span><span class="nf">count</span> <span class="o">+</span> <span class="no">LOCAL_FILES</span><span class="p">.</span><span class="nf">count</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">bar</span><span class="p">.</span><span class="nf">iterate</span><span class="p">(</span><span class="n">process</span><span class="p">).</span><span class="nf">detect</span> <span class="p">{</span> <span class="o">|</span><span class="n">val</span><span class="o">|</span> <span class="n">found?</span><span class="p">(</span><span class="n">val</span><span class="p">)</span> <span class="p">}</span> <span class="c1"># shows progress-bar for uniform process of detection</span>
</code></pre>
<p>Prototype impl. is dead simple, of course:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">Enumerator</span>
<span class="k">def</span> <span class="nf">chain</span><span class="p">(</span><span class="o">*</span><span class="n">others</span><span class="p">)</span>
<span class="no">Enumerator</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="o">|</span><span class="n">y</span><span class="o">|</span>
<span class="p">[</span><span class="nb">self</span><span class="p">,</span> <span class="o">*</span><span class="n">others</span><span class="p">].</span><span class="nf">each</span> <span class="p">{</span> <span class="o">|</span><span class="n">e</span><span class="o">|</span> <span class="n">e</span><span class="p">.</span><span class="nf">each</span> <span class="p">{</span> <span class="o">|</span><span class="n">v</span><span class="o">|</span> <span class="n">y</span> <span class="o"><<</span> <span class="n">v</span> <span class="p">}</span> <span class="p">}</span>
<span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>Obviously, the effect could be reached with <code>flat_map</code>, but it seems "chaining" of iterations is pretty common and clear concept (and Google search for "ruby enumerator chain" shows people constantly ask about the way).</p> Ruby master - Misc #15109 (Closed): Improve safe navigation operator's docshttps://bugs.ruby-lang.org/issues/151092018-09-12T18:43:18Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>Reason: <a href="http://ruby-doc.org/core-2.5.1/doc/syntax/calling_methods_rdoc.html" class="external">current docs</a> look this way (in "Receiver" section, one paragraph before last):</p>
<blockquote>
<p>You may use <code>&.</code> to designate a receiver, then <code>my_method</code> is not invoked and the result is <code>nil</code> when the receiver is <code>nil</code>. In that case, the arguments of <code>my_method</code> are not evaluated.</p>
</blockquote>
<p>There are several problems:</p>
<ul>
<li>"safe navigation operator" is never spelled explicitly (so nobody can google these docs);</li>
<li>it is rendered as "unimportant secondary" feature (just before note about legacy <code>::</code> call syntax).</li>
</ul>
<p>While mentoring newcomers, I noticed it is hard for them just to be aware of the feature. So, the proposed rendering is subsection of "Receiver" spelled this way:</p>
<pre><code>=== Safe navigation operator
<code>&.</code>, called "safe navigation operator", allows to skip method call
when receiver is +nil+. It returns +nil+ and doesn't evaluate method's arguments
if the call is skipped.
REGEX = /(ruby) is (\w+)/i
"Ruby is awesome!".match(REGEX).values_at(1, 2)
# => ["Ruby", "awesome"]
"Python is fascinating!".match(REGEX).values_at(1, 2)
# NoMethodError: undefined method `values_at' for nil:NilClass
"Python is fascinating!".match(REGEX)&.values_at(1, 2)
# => nil
This allows to easily chain methods which could return empty value. Note that
<code>&.</code> skips only one next call, so for a longer chain it is necessary
to add operator on each level:
"Python is fascinating!".match(REGEX)&.values_at(1, 2).join(' - ')
# NoMethodError: undefined method `join' for nil:NilClass
"Python is fascinating!".match(REGEX)&.values_at(1, 2)&.join(' - ')
# => nil
</code></pre> Ruby master - Feature #14913 (Closed): Extend case to match several values at oncehttps://bugs.ruby-lang.org/issues/149132018-07-15T13:35:34Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>This proposal is part of the search for how pattern matching or its elements could be gently introduced to Ruby.<br>
<a href="https://zverok.github.io/blog/2018-06-26-pattern-matching.html" class="external">This blog post</a> (authored by me) provides context and "full" (more powerful than the current proposal, but more questionable also) idea, but I believe that this, more moderate, extension, could be a reasonable start.</p>
<p><strong>Proposal:</strong></p>
<p>Allow matching several values at once, when matching by case.<br>
Example:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">case</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
<span class="k">when</span> <span class="p">(</span><span class="mi">0</span><span class="o">..</span><span class="mi">10</span><span class="p">,</span> <span class="mi">0</span><span class="o">..</span><span class="mi">10</span><span class="p">)</span>
<span class="o">...</span>
<span class="k">when</span> <span class="p">(</span><span class="no">Array</span><span class="p">,</span> <span class="kp">nil</span><span class="p">)</span> <span class="c1"># coordinates were passed as an array in first argument</span>
<span class="o">...</span>
<span class="k">when</span> <span class="p">(</span><span class="no">Numeric</span><span class="p">,</span> <span class="kp">nil</span><span class="p">),</span> <span class="p">(</span><span class="kp">nil</span><span class="p">,</span> <span class="no">Numeric</span><span class="p">)</span> <span class="c1"># somehow one of coordinates were missing</span>
<span class="o">...</span>
</code></pre>
<p><strong>Justification:</strong></p>
<ul>
<li>The syntax change is minimal (no new keywords/special chars), yet visible (very little possibility of hidden incompatibilities)</li>
<li>It is alike deconstruction when passing arguments to methods or blocks</li>
<li>It allows <em>gradual</em> adding of more features in the future versions of Ruby once the new syntax will become familiar:</li>
</ul>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">case</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="o">*</span><span class="n">y</span><span class="p">)</span> <span class="c1"># flatten y</span>
<span class="k">when</span> <span class="p">(</span><span class="o">*</span><span class="no">Numeric</span><span class="p">)</span> <span class="c1"># match only an array of numerics</span>
<span class="o">...</span>
<span class="k">when</span> <span class="p">(</span><span class="n">_</span><span class="p">,</span> <span class="no">Array</span><span class="p">)</span> <span class="c1"># skip any</span>
<span class="o">...</span>
<span class="k">when</span> <span class="p">(</span><span class="no">Numeric</span><span class="p">,</span> <span class="no">Numeric</span> <span class="o">=></span> <span class="n">y</span><span class="p">,</span> <span class="no">Hash</span> <span class="o">=></span> <span class="n">options</span><span class="p">)</span> <span class="c1"># match & assign, like in rescue</span>
<span class="o">...</span>
</code></pre>
<p><strong>Links:</strong></p>
<ul>
<li>
<a href="https://zverok.github.io/blog/2018-06-26-pattern-matching.html" class="external">Blog post</a> with reasons, links to previous discussions and more examples</li>
<li>
<a href="https://github.com/zverok/pattern-matching-prototype" class="external">Experimental gem</a> to try as much of new proposals as possible to imitate in the current Ruby version.</li>
</ul>
<p>PS:</p>
<p>Alternative syntax (reusing "block arguments" sign):</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">case</span> <span class="o">|</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="o">|</span>
<span class="k">when</span> <span class="o">|</span><span class="mi">0</span><span class="o">..</span><span class="mi">10</span><span class="p">,</span> <span class="mi">0</span><span class="o">..</span><span class="mi">10</span><span class="o">|</span>
<span class="o">...</span>
<span class="k">when</span> <span class="o">|</span><span class="no">Array</span><span class="p">,</span> <span class="kp">nil</span><span class="o">|</span>
<span class="o">...</span>
<span class="k">when</span> <span class="o">|</span><span class="no">Numeric</span><span class="p">,</span> <span class="kp">nil</span><span class="o">|</span><span class="p">,</span> <span class="o">|</span><span class="kp">nil</span><span class="p">,</span> <span class="no">Numeric</span><span class="o">|</span> <span class="c1"># OK, that's probably weird</span>
<span class="o">...</span>
</code></pre> Ruby master - Feature #14799 (Closed): Startless rangehttps://bugs.ruby-lang.org/issues/147992018-05-31T07:56:34Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>On introduction of endless range at <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: An endless range `(1..)` (Closed)" href="https://bugs.ruby-lang.org/issues/12912">#12912</a>, "startless range" was discussed this way:</p>
<blockquote>
<p><a class="user active user-mention" href="https://bugs.ruby-lang.org/users/7501">@sowieso (So Wieso)</a>: Not having the opposite (<code>..5</code> and <code>..-2</code>) feels like this is rather a hack than a thoroughly planned feature.</p>
</blockquote>
<blockquote>
<p><a class="user active user-mention" href="https://bugs.ruby-lang.org/users/50">@duerst (Martin Dürst)</a>: I don't understand the need for a <code>..5</code> Range. The feature is called "endless range". Although mathematically, it's possible to think about startless ranges, they don't work in a program. Maybe some programming languages have <code>..5</code> as a shortcut for <code>0..5</code>, but that's in any way a usual, bounded, range with a start and an end. It's conceptually totally different from <code>5..</code>, which is a range with a start but no end, an unbound range.</p>
</blockquote>
<p>In the context of that ticket (ranges used mostly for slicing arrays) having <code>..5</code> was indeed hard to justify, but there are other cases when <code>..5</code> being <code>-Infinity..5</code> is absolutely reasonable:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">case</span> <span class="n">release_date</span>
<span class="k">when</span> <span class="o">..</span><span class="mi">1</span><span class="p">.</span><span class="nf">year</span><span class="p">.</span><span class="nf">ago</span>
<span class="nb">puts</span> <span class="s2">"ancient"</span>
<span class="k">when</span> <span class="mi">1</span><span class="p">.</span><span class="nf">year</span><span class="p">.</span><span class="nf">ago</span><span class="o">..</span><span class="mi">3</span><span class="p">.</span><span class="nf">months</span><span class="p">.</span><span class="nf">ago</span>
<span class="nb">puts</span> <span class="s2">"old"</span>
<span class="k">when</span> <span class="mi">3</span><span class="p">.</span><span class="nf">months</span><span class="p">.</span><span class="nf">ago</span><span class="o">..</span><span class="no">Date</span><span class="p">.</span><span class="nf">today</span>
<span class="nb">puts</span> <span class="s2">"recent"</span>
<span class="k">when</span> <span class="no">Date</span><span class="p">.</span><span class="nf">today</span><span class="o">..</span>
<span class="nb">puts</span> <span class="s2">"upcoming"</span>
<span class="k">end</span>
<span class="n">log</span><span class="p">.</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="ss">:logged_at</span><span class="p">).</span><span class="nf">grep</span><span class="p">(</span><span class="o">..</span><span class="no">Date</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">1980</span><span class="p">))</span> <span class="c1"># => outliers due to bad log parsing...</span>
</code></pre>
<p>E.g., whenever case equality operator is acting, having startless range to express "below this value" is the most concise and readable way. Also, for expressing constants (mostly decorative, but very readable):</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># Celsius degrees</span>
<span class="no">WORK_RANGES</span> <span class="o">=</span> <span class="p">{</span>
<span class="o">..-</span><span class="mi">10</span> <span class="o">=></span> <span class="ss">:turn_off</span><span class="p">,</span>
<span class="o">-</span><span class="mi">10</span><span class="o">..</span><span class="mi">0</span> <span class="o">=></span> <span class="ss">:energy_saving</span><span class="p">,</span>
<span class="mi">0</span><span class="o">..</span><span class="mi">20</span> <span class="o">=></span> <span class="ss">:main</span><span class="p">,</span>
<span class="mi">20</span><span class="o">..</span><span class="mi">35</span> <span class="o">=></span> <span class="ss">:cooling</span><span class="p">,</span>
<span class="mi">35</span><span class="o">..</span> <span class="o">=></span> <span class="ss">:turn_off</span>
<span class="p">}</span>
</code></pre>
<p>In addition, my related proposal <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Comparable#clamp with a range (Closed)" href="https://bugs.ruby-lang.org/issues/14784">#14784</a> suggests that this kind of ranges could be utilized by more powerful clamp too:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">updated_at</span><span class="p">.</span><span class="nf">clamp</span><span class="p">(</span><span class="o">..</span><span class="no">Date</span><span class="p">.</span><span class="nf">today</span><span class="p">)</span>
</code></pre>
<p><strong>Uncertainty points:</strong></p>
<ul>
<li>Would it be hard to add to parser? I am not sure, I am not very good at it :(</li>
<li>Should <code>..</code> be a thing? I guess not, unless there would be convincing real-life examples, which for me it is hard to think of.</li>
</ul> Ruby master - Feature #14784 (Closed): Comparable#clamp with a rangehttps://bugs.ruby-lang.org/issues/147842018-05-23T13:04:07Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p><strong>Proposal</strong></p>
<p>Allow "one-sided" <code>clamp</code> to limit only upper bound (and, ideally, only lower too).</p>
<p>Proposed implementation: allow <code>clamp(begin..end)</code> call sequence (without deprecating <code>clamp(begin, end)</code>), to take advantage from open-ended ranges with <code>clamp(begin..)</code>.</p>
<p><strong>Reasoning about range</strong></p>
<p>I looked through <code>#clamp</code> <a href="https://bugs.ruby-lang.org/issues/10594" class="external">discussion</a>, but couldn't find there why syntax <code>clamp(b, e)</code> was preferred to <code>clamp(b..e)</code>. The only one I could think of is possible confuse of how <code>clamp(b..e)</code> and <code>clamp(b...e)</code> behaviors should differ.</p>
<p>The problem becomes more important with the introduction of <a href="https://bugs.ruby-lang.org/issues/12912" class="external">open-ended ranges</a>. I believe this is pretty natural:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">some_calculation</span><span class="p">.</span><span class="nf">clamp</span><span class="p">(</span><span class="mi">0</span><span class="o">..</span><span class="p">)</span> <span class="c1"># now, I use clamp(0, Float::INFINITY)</span>
<span class="n">timestamp</span><span class="p">.</span><span class="nf">clamp</span><span class="p">(</span><span class="no">Date</span><span class="p">.</span><span class="nf">today</span><span class="o">..</span><span class="p">)</span> <span class="c1"># now, I typically use clamp(Date.today..INFINITE_FUTURE_DATE) with custom defined constant</span>
</code></pre>
<p>Counter-arguments:</p>
<ol>
<li>This is "one-sided", you can't do <code>clamp(..Date.today)</code>. To this I can answer than from my experience "clamping only minimum" is more frequent, and if you need to clamp only maximum, most of the time there is some "reasonable minumum". Another idea is that maybe this is a proof why "start-less" ranges are necessary, after all, <a href="https://bugs.ruby-lang.org/issues/12912#note-12" class="external">doubted here</a>
</li>
<li>Why not just leave current <code>clamp(b, e)</code> and allow <code>clamp(b)</code>? Answer: because when you see <code>clamp(10)</code>, is it <code>clamp(10, nil)</code>, or <code>clamp(nil, 10)</code> (yes, logically it is the first argument that is left, but from readability point of view it is not that obvious). Possible alternative: <code>clamp(min: 0, max: 10)</code>, where you can omit any of two.</li>
<li>Why do you need one-sided clamp at all? Because alternatives is much more wordy, making reader think:</li>
</ol>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># with clamp</span>
<span class="n">chain</span><span class="p">.</span><span class="nf">of</span><span class="p">.</span><span class="nf">calculations</span><span class="p">.</span><span class="nf">clamp</span><span class="p">(</span><span class="mi">0</span><span class="o">..</span><span class="p">)</span>
<span class="c1"># without clamp</span>
<span class="n">v</span> <span class="o">=</span> <span class="n">chain</span><span class="p">.</span><span class="nf">of</span><span class="p">.</span><span class="nf">calculations</span>
<span class="n">v</span> <span class="o"><</span> <span class="mi">0</span> <span class="p">?</span> <span class="mi">0</span> <span class="p">:</span> <span class="n">v</span>
<span class="c1"># or, with yield_self (renamed to then)</span>
<span class="n">chain</span><span class="p">.</span><span class="nf">of</span><span class="p">.</span><span class="nf">calculations</span><span class="p">.</span><span class="nf">then</span> <span class="p">{</span> <span class="o">|</span><span class="n">v</span><span class="o">|</span> <span class="n">v</span> <span class="o"><</span> <span class="mi">0</span> <span class="p">?</span> <span class="mi">0</span> <span class="p">:</span> <span class="n">v</span> <span class="p">}</span>
</code></pre>
<p>Both alternatives "without <code>#clamp</code>" shows intentions much less clear.</p> Ruby master - Feature #14781 (Closed): Enumerator.generatehttps://bugs.ruby-lang.org/issues/147812018-05-22T10:23:20Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>This is alternative proposal to <code>Object#enumerate</code> (<a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Enumerator from single object (Closed)" href="https://bugs.ruby-lang.org/issues/14423">#14423</a>), which was considered by many as a good idea, but with unsure naming and too radical (<code>Object</code> extension). This one is <em>less</em> radical, and, at the same time, more powerful.</p>
<p><strong>Synopsys</strong>:</p>
<ul>
<li>
<code>Enumerator.generate(initial, &block)</code>: produces infinite sequence where each next element is calculated by applying block to previous; <code>initial</code> is first sequence element;</li>
<li>
<code>Enumerator.generate(&block)</code>: the same; first element of sequence is a result of calling the block with no args.</li>
</ul>
<p>This method allows to produce enumerators replacing a lot of common <code>while</code> and <code>loop</code> cycles in the same way <code>#each</code> replaces <code>for</code>.</p>
<p><strong>Examples:</strong></p>
<p>With initial value</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># Infinite sequence</span>
<span class="nb">p</span> <span class="no">Enumerator</span><span class="p">.</span><span class="nf">generate</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="o">&</span><span class="ss">:succ</span><span class="p">).</span><span class="nf">take</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
<span class="c1"># => [1, 2, 3, 4, 5]</span>
<span class="c1"># Easy Fibonacci</span>
<span class="nb">p</span> <span class="no">Enumerator</span><span class="p">.</span><span class="nf">generate</span><span class="p">([</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">])</span> <span class="p">{</span> <span class="o">|</span><span class="n">f0</span><span class="p">,</span> <span class="n">f1</span><span class="o">|</span> <span class="p">[</span><span class="n">f1</span><span class="p">,</span> <span class="n">f0</span> <span class="o">+</span> <span class="n">f1</span><span class="p">]</span> <span class="p">}.</span><span class="nf">take</span><span class="p">(</span><span class="mi">10</span><span class="p">).</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="ss">:first</span><span class="p">)</span>
<span class="c1">#=> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]</span>
<span class="nb">require</span> <span class="s1">'date'</span>
<span class="c1"># Find next Tuesday</span>
<span class="nb">p</span> <span class="no">Enumerator</span><span class="p">.</span><span class="nf">generate</span><span class="p">(</span><span class="no">Date</span><span class="p">.</span><span class="nf">today</span><span class="p">,</span> <span class="o">&</span><span class="ss">:succ</span><span class="p">).</span><span class="nf">detect</span> <span class="p">{</span> <span class="o">|</span><span class="n">d</span><span class="o">|</span> <span class="n">d</span><span class="p">.</span><span class="nf">wday</span> <span class="o">==</span> <span class="mi">2</span> <span class="p">}</span>
<span class="c1"># => #<Date: 2018-05-22 ((2458261j,0s,0n),+0s,2299161j)></span>
<span class="c1"># Tree navigation</span>
<span class="c1"># ---------------</span>
<span class="nb">require</span> <span class="s1">'nokogiri'</span>
<span class="nb">require</span> <span class="s1">'open-uri'</span>
<span class="c1"># Find some element on page, then make list of all parents</span>
<span class="nb">p</span> <span class="no">Nokogiri</span><span class="o">::</span><span class="no">HTML</span><span class="p">(</span><span class="nb">open</span><span class="p">(</span><span class="s1">'https://www.ruby-lang.org/en/'</span><span class="p">))</span>
<span class="p">.</span><span class="nf">at</span><span class="p">(</span><span class="s1">'a:contains("Ruby 2.2.10 Released")'</span><span class="p">)</span>
<span class="p">.</span><span class="nf">yield_self</span> <span class="p">{</span> <span class="o">|</span><span class="n">a</span><span class="o">|</span> <span class="no">Enumerator</span><span class="p">.</span><span class="nf">generate</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="o">&</span><span class="ss">:parent</span><span class="p">)</span> <span class="p">}</span>
<span class="p">.</span><span class="nf">take_while</span> <span class="p">{</span> <span class="o">|</span><span class="n">node</span><span class="o">|</span> <span class="n">node</span><span class="p">.</span><span class="nf">respond_to?</span><span class="p">(</span><span class="ss">:parent</span><span class="p">)</span> <span class="p">}</span>
<span class="p">.</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="ss">:name</span><span class="p">)</span>
<span class="c1"># => ["a", "h3", "div", "div", "div", "div", "div", "div", "body", "html"]</span>
<span class="c1"># Pagination</span>
<span class="c1"># ----------</span>
<span class="nb">require</span> <span class="s1">'octokit'</span>
<span class="no">Octokit</span><span class="p">.</span><span class="nf">stargazers</span><span class="p">(</span><span class="s1">'rails/rails'</span><span class="p">)</span>
<span class="c1"># ^ this method returned just an array, but have set `.last_response` to full response, with data</span>
<span class="c1"># and pagination. So now we can do this:</span>
<span class="nb">p</span> <span class="no">Enumerator</span><span class="p">.</span><span class="nf">generate</span><span class="p">(</span><span class="no">Octokit</span><span class="p">.</span><span class="nf">last_response</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">response</span><span class="o">|</span>
<span class="n">response</span><span class="p">.</span><span class="nf">rels</span><span class="p">[</span><span class="ss">:next</span><span class="p">].</span><span class="nf">get</span> <span class="c1"># pagination: `get` fetches next Response</span>
<span class="p">}</span>
<span class="p">.</span><span class="nf">first</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="c1"># take just 3 pages of stargazers</span>
<span class="p">.</span><span class="nf">flat_map</span><span class="p">(</span><span class="o">&</span><span class="ss">:data</span><span class="p">)</span> <span class="c1"># `data` is parsed response content (stargazers themselves)</span>
<span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">h</span><span class="o">|</span> <span class="n">h</span><span class="p">[</span><span class="ss">:login</span><span class="p">]</span> <span class="p">}</span>
<span class="c1"># => ["wycats", "brynary", "macournoyer", "topfunky", "tomtt", "jamesgolick", ...</span>
</code></pre>
<p>Without initial value</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># Random search</span>
<span class="n">target</span> <span class="o">=</span> <span class="mi">7</span>
<span class="nb">p</span> <span class="no">Enumerator</span><span class="p">.</span><span class="nf">generate</span> <span class="p">{</span> <span class="nb">rand</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span> <span class="p">}.</span><span class="nf">take_while</span> <span class="p">{</span> <span class="o">|</span><span class="n">i</span><span class="o">|</span> <span class="n">i</span> <span class="o">!=</span> <span class="n">target</span> <span class="p">}.</span><span class="nf">to_a</span>
<span class="c1"># => [0, 6, 3, 5,....]</span>
<span class="c1"># External while condition</span>
<span class="nb">require</span> <span class="s1">'strscan'</span>
<span class="n">scanner</span> <span class="o">=</span> <span class="no">StringScanner</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="s1">'7+38/6'</span><span class="p">)</span>
<span class="nb">p</span> <span class="no">Enumerator</span><span class="p">.</span><span class="nf">generate</span> <span class="p">{</span> <span class="n">scanner</span><span class="p">.</span><span class="nf">scan</span><span class="p">(</span><span class="sr">%r{</span><span class="se">\d</span><span class="sr">+|[-+*/]}</span><span class="p">)</span> <span class="p">}.</span><span class="nf">slice_after</span> <span class="p">{</span> <span class="n">scanner</span><span class="p">.</span><span class="nf">eos?</span> <span class="p">}.</span><span class="nf">first</span>
<span class="c1"># => ["7", "+", "38", "/", "6"]</span>
<span class="c1"># Potential message loop system:</span>
<span class="no">Enumerator</span><span class="p">.</span><span class="nf">generate</span> <span class="p">{</span> <span class="no">Message</span><span class="p">.</span><span class="nf">receive</span> <span class="p">}.</span><span class="nf">take_while</span> <span class="p">{</span> <span class="o">|</span><span class="n">msg</span><span class="o">|</span> <span class="n">msg</span> <span class="o">!=</span> <span class="ss">:exit</span> <span class="p">}</span>
</code></pre>
<p><strong>Reference implementation</strong>: <a href="https://github.com/zverok/enumerator_generate" class="external">https://github.com/zverok/enumerator_generate</a></p>
<p>I want to <strong>thank</strong> all peers that participated in the discussion here, on Twitter and Reddit.</p> Ruby master - Feature #14709 (Closed): Proper pattern matchinghttps://bugs.ruby-lang.org/issues/147092018-04-24T15:19:23Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>On RubyKaigi 2017, there was a <a href="http://rubykaigi.org/2017/presentations/yotii23.html" class="external">presentation</a> of Yuki Torii about possible implementation of pattern matching.</p>
<p>The syntax proposed in presentation was:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">res</span> <span class="o">=</span> <span class="p">[</span><span class="ss">:ng</span><span class="p">,</span> <span class="mi">500</span><span class="p">]</span>
<span class="k">case</span> <span class="n">res</span>
<span class="k">when</span> <span class="o">%</span><span class="nb">p</span><span class="p">([</span><span class="ss">:ng</span><span class="p">,</span> <span class="n">status</span><span class="p">])</span>
<span class="nb">p</span> <span class="n">status</span>
<span class="k">end</span>
</code></pre>
<p>The proposed syntax seem to feel pretty consistent, and the implementation (forked Ruby interpreter) was working at this moment.</p>
<p>As <a class="user active user-mention" href="https://bugs.ruby-lang.org/users/17">@ko1 (Koichi Sasada)</a> was one of the contributors to the experiment, I don't suppose Ruby core team is not aware of the proposal, so I'd like to know what the status of it? Are there some plans for full-strength pattern matching in Ruby 3 (with proposed, or any other, syntax)?</p>
<p>PS: There are many existing gems with some kind "almost real" pattern matching (including recently emerged <a href="https://github.com/baweaver/qo" class="external">Qo</a>), yet I believe that the <em>only</em> useful pattern matching can be provided language core. Otherwise, two main goals can't be achieved:</p>
<ul>
<li>reasonable performance (as the pattern-matching is useful mostly in complicated algorithms, which tend to repeat matches thousands of times);</li>
<li>unpacking of parts of the patterns into local variables.</li>
</ul> Ruby master - Misc #14610 (Closed): Enhance Proc docshttps://bugs.ruby-lang.org/issues/146102018-03-16T11:10:13Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>What caught me recently while mentoring students: there is almost no "canonical" explanation about procs in <a href="https://docs.ruby-lang.org/en/trunk/" class="external">Ruby's core docs</a>: Nothing in <code>doc/*.rdoc</code>, and for the <code>Proc</code> class, documentation of what it is and what it does is pretty spartan.</p>
<p>I am trying to fix this by adding to <code>Proc</code> class header documentation.<br>
Things added:</p>
<ol>
<li>More friendly and detailed explanation of the whole concept.</li>
<li>Different methods of creating lambda and non-lambda procs.</li>
<li>Lambda semantics.</li>
<li>Conversion to proc from other objects and <code>&</code>.</li>
</ol>
<p>About (3): currently, Proc docs <em>do have</em> an explanation about it, but there are two problems:</p>
<ul>
<li>it all placed in docs for predicate method <code>#lambda?</code> (like nobody should be interested in the concept unless uses this method);</li>
<li>from my perspective, it uses pretty unfortunate wording: instead of talking about proc object semantics, it calls non-lambdas behavior "tricks", and informally tells about "procs with tricks"/"procs without tricks".</li>
</ul>
<p>If my class documentation would be accepted, I propose to cut the explanations in <code>#lambda?</code> method down to a one-liner ("If the proc has lambda semantics. See class docs for an explanation about lambdas." or something like that.)</p> Ruby master - Feature #14594 (Closed): Rethink yield_self's namehttps://bugs.ruby-lang.org/issues/145942018-03-09T09:46:15Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p><em>I feel really uncomfortable raising the question again, but...</em></p>
<p>In several months since 2.5 release I've written a lot of code with <code>yield_self</code> (using <code>backports</code> gem with earlier versions of Ruby when necessary), and explained it several times to students, and colleagues (and in this <a href="https://zverok.github.io/blog/2018-01-24-yield_self.html" class="external">blog post</a> which have gained pretty decent attention).</p>
<p>I should say that I am still assured the name chosen is really not optimal. Reasons:</p>
<ul>
<li>it is just too long for such a basic operation;</li>
<li>it does not say "what it does", but rather "how it is implemented"; it is like having <code>each_returning_block_result</code> instead of <code>map</code>;</li>
<li>
<code>self</code> is really misguiding and obscure in situations like this:</li>
</ul>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">MyClass</span>
<span class="k">def</span> <span class="nf">some_method</span>
<span class="vi">@path</span><span class="p">.</span><span class="nf">yield_self</span><span class="p">(</span><span class="o">&</span><span class="no">File</span><span class="p">.</span><span class="nf">method</span><span class="p">(</span><span class="ss">:read</span><span class="p">)).</span><span class="nf">yield_self</span><span class="p">(</span><span class="o">&</span><span class="no">Parser</span><span class="p">.</span><span class="nf">method</span><span class="p">(</span><span class="ss">:new</span><span class="p">))</span> <span class="o">...</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>Intuitively, word "self" inside instance method is read like it somehow related to current context's <code>self</code> (e.g. instance of <code>MyClass</code>), which it is absolutely not. In other words, "self" in caller's context has nothing to do with "self" implied by method's name.</p>
<p>After reconsidering a lot of options, <strong>my current proposal is: <code>#then</code></strong>.</p>
<p>Reasons:</p>
<ul>
<li>despite being a keyword, <code>something.then(something)</code> is not a conflicting Ruby syntax, and allowed by current Ruby;</li>
<li>it is short!</li>
<li>it shows intention pretty well, and reads natural, in both cases: when receives block and when returns Enumerator:</li>
</ul>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">File</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="n">filename</span><span class="p">).</span><span class="nf">then</span><span class="p">(</span><span class="o">&</span><span class="no">JSON</span><span class="p">.</span><span class="nf">method</span><span class="p">(</span><span class="ss">:parse</span><span class="p">))</span>
<span class="nb">rand</span><span class="p">(</span><span class="mi">10</span><span class="p">).</span><span class="nf">then</span><span class="p">.</span><span class="nf">detect</span><span class="p">(</span><span class="o">&</span><span class="ss">:odd?</span><span class="p">)</span>
</code></pre>
<p>In many languages, <code>.then</code> or <code>.and_then</code> is useful construct, meaning the same (calculate next value from the result of the previous operation), just in a narrower context of futures/promises. I believe that even when/if Ruby will have those as a language feature, that syntax will play well:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">value</span><span class="p">.</span><span class="nf">then</span><span class="p">(</span><span class="o">&</span><span class="ss">:computation</span><span class="p">)</span> <span class="c1"># => value</span>
<span class="n">promise</span><span class="p">.</span><span class="nf">then</span><span class="p">(</span><span class="o">&</span><span class="ss">:computation</span><span class="p">)</span> <span class="c1"># => promise</span>
</code></pre>
<p>PS: For historical reasons, <a href="https://bugs.ruby-lang.org/issues/12760#note-5" class="external">here</a> is huge list of previous proposals I've gathered for this method name.</p> Ruby master - Feature #14575 (Closed): Switch Range#=== to use cover? instead of include?https://bugs.ruby-lang.org/issues/145752018-03-04T09:00:55Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p><em>This is <strong>a conscious duplicate</strong> of the bug I've created <a href="https://bugs.ruby-lang.org/issues/12612" class="external">more than a year ago</a>. I believe that the previous one was rejected too easy, mostly due to the fact I haven't provided enough evidence to support my proposal. I also believe that writing the new, better-grounded proposal would be more visible than adding more comments to the rejected ticket.</em></p>
<p><strong>The problem</strong>: <code>Range#===</code> (used in <code>case</code> and <code>grep</code>) uses <code>include?</code> to check the value against the range, which could be:<br>
a) really ineffective or b) simply unavailable.</p>
<p>Here are real-life and real-life-alike examples of types that suffer from the problem:</p>
<ul>
<li>
<a href="https://github.com/ipaddress-gem/ipaddress" class="external">ipaddress</a> <code>IPAddress("172.16.10.1")..IPAddress("172.16.11.255")</code>: it is really readable to describe in some server config "for this range allow this, for that range allow that", yet it could be fascinatingly slow, calculating thousands of IPs inside range just to check with <code>include?</code>;</li>
<li>
<a href="https://github.com/joshwlewis/unitwise" class="external">Measurement units</a>: <code>(Unitwise(1, 'm')...Unitwise(10, 'm')) === Unitwise(5, 'm')</code> throws "can't iterate from Unitwise::Measurement", which is reasonable: there is no <code>.succ</code> for numeric types; Ruby itself has an ugly workaround of "if this is a numeric type, behave like <code>cover?</code>"</li>
<li>Dates and times: <code>(Date.today..Date.today + 1) === DateTime.now</code> is <code>false</code>; it is hard to imagine code where it is a desired behavior.</li>
</ul>
<p>Matz's objections to the previous ticket were:</p>
<blockquote>
<p>I see no real-world use-case for <code>Range#===</code> with strings. (Because I have provided only string ranges example initially -- VS)</p>
</blockquote>
<p>That is addressed, hopefully, with the new set of examples.</p>
<blockquote>
<p>Besides that, using <code>cover?</code> behavior for [string] ranges would introduce incompatibility.</p>
</blockquote>
<p>I don't know how to estimate amount of incompatibilities introduced by this behavior change.<br>
Yet it is really hard (for me) to invent some reasonable real-life use case which could be broken by it.</p> Ruby master - Misc #14567 (Closed): Clarify YAML docshttps://bugs.ruby-lang.org/issues/145672018-03-02T12:44:30Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>The problem I am trying to solve: If you try to understand what methods do <code>YAML</code> have, and what parameters, say, <code>YAML.load_file</code> accept, the "official" docs look <a href="https://docs.ruby-lang.org/en/2.5.0/YAML.html" class="external">pretty unhelpful</a>, saying, basically "This is not the docs you are looking for".</p>
<p>The Psych library is mentioned and even hyperlinked, yet it mentioned as an "underlying implementation", like "something low-level that helps stdlib's YAML to do the job, don't think about Psych".</p>
<p>So, I felt it as a necessity to add a clarification that Psych, in fact, IS YAML, and all options and methods are documented there.</p> Ruby master - Bug #14483 (Closed): Enchance Method docshttps://bugs.ruby-lang.org/issues/144832018-02-16T11:55:02Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>Main changes:</p>
<ul>
<li>fix conflict with <code>Proc</code> methods documentation (currently <a href="http://ruby-doc.org/core-2.5.0/Method.html#method-i-call" class="external">one can see</a> in fact <code>Proc#call</code> docs in place of <code>Method#call</code>);</li>
<li>add code examples to various <code>Method</code>'s methods and <code>Object#method</code>;</li>
<li>emphasize (in code examples and docs) the <code>&method(:name)</code> idiom.</li>
</ul> Ruby master - Bug #14450 (Closed): Enchance MatchData docshttps://bugs.ruby-lang.org/issues/144502018-02-05T17:52:06Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>The wording is changed from stating something like "This is (unimporant) helper, which is a wrapper for (primary) global variables" more to the "This is (primary) regexp matching result, which also (eventually) related to global variables".</p>
<p>Explanations expanded and several code examples added to the class description.</p> Ruby master - Feature #14444 (Closed): MatchData: alias for #[]https://bugs.ruby-lang.org/issues/144442018-02-05T10:16:36Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>After introduction of safe navigation operator and other latest features, it could be pretty idiomatic to do this:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">next_page</span> <span class="o">=</span> <span class="n">response</span><span class="p">.</span><span class="nf">dig</span><span class="p">(</span><span class="s1">'meta'</span><span class="p">,</span> <span class="s1">'pagination'</span><span class="p">,</span> <span class="s1">'next'</span><span class="p">)</span><span class="o">&</span><span class="p">.</span><span class="nf">match</span><span class="p">(</span><span class="sr">/&page=(\d+)/</span><span class="p">)</span><span class="o">&</span><span class="p">.</span><span class="nf">[</span><span class="p">](</span><span class="mi">1</span><span class="p">)</span>
</code></pre>
<p>The ugly thing here is, obviously, <code>&.[](1)</code>. When dealing with other classes we can use <code>dig(one_key)</code> or <code>at(index)</code> for arrays, but for <code>MatchData</code> there are no good synonym/alias.</p>
<p>Two options come to mind:</p>
<ul>
<li>
<code>&.capture(1)</code> — singular from "captures"</li>
<li>
<code>&.at(1)</code> — like it is in Array, from which MatchData already borrows <code>values_at</code>
</li>
</ul>
<p>Yes, several <code>&.</code> in a row could be said a code smell and example may feel a bit extreme.</p>
<p>But for simple matches, the principle stays the same: "match something and extract if matched" otherwise should be written in much less readable code like this:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">if</span> <span class="n">url</span> <span class="o">=~</span> <span class="sr">/&page=(\d+)/</span>
<span class="n">next_page</span> <span class="o">=</span> <span class="no">Regexp</span><span class="p">.</span><span class="nf">last_match</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="k">end</span>
<span class="c1"># or</span>
<span class="k">if</span> <span class="p">(</span><span class="n">m</span> <span class="o">=</span> <span class="n">url</span><span class="p">.</span><span class="nf">match</span><span class="p">(</span><span class="sr">/&page=(\d+)/</span><span class="p">))</span>
<span class="n">next_page</span> <span class="o">=</span> <span class="n">m</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="k">end</span>
<span class="c1"># this seems the most reasonable syntax anyways:</span>
<span class="n">next_page</span> <span class="o">=</span> <span class="n">url</span><span class="p">.</span><span class="nf">match</span><span class="p">(</span><span class="sr">/&page=(\d+)/</span><span class="p">)</span><span class="o">&</span><span class="p">.</span><span class="nf">at</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</code></pre> Ruby master - Bug #14436 (Closed): Enchance yield_self docshttps://bugs.ruby-lang.org/issues/144362018-02-02T17:30:31Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>Trying to explain by example how it could be used.</p> Ruby master - Feature #14423 (Closed): Enumerator from single objecthttps://bugs.ruby-lang.org/issues/144232018-01-30T09:47:03Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p><strong>UPD: Current proposal</strong></p>
<p>Introduce method <code>Object#enumerate</code> for producing infinite enumerator by applying block to result of previous call.</p>
<p>Reference implementation:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">Object</span>
<span class="k">def</span> <span class="nf">enumerate</span><span class="p">(</span><span class="o">&</span><span class="n">block</span><span class="p">)</span>
<span class="no">Enumerator</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="o">|</span><span class="n">y</span><span class="o">|</span>
<span class="n">val</span> <span class="o">=</span> <span class="nb">self</span>
<span class="n">y</span> <span class="o"><<</span> <span class="n">val</span>
<span class="kp">loop</span> <span class="k">do</span>
<span class="n">val</span> <span class="o">=</span> <span class="n">block</span><span class="p">.</span><span class="nf">call</span><span class="p">(</span><span class="n">val</span><span class="p">)</span>
<span class="n">y</span> <span class="o"><<</span> <span class="n">val</span>
<span class="k">end</span>
<span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>Possible usages:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># Most idiomatic "infinite sequence" possible:</span>
<span class="nb">p</span> <span class="mi">1</span><span class="p">.</span><span class="nf">enumerate</span><span class="p">(</span><span class="o">&</span><span class="ss">:succ</span><span class="p">).</span><span class="nf">take</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
<span class="c1"># => [1, 2, 3, 4, 5]</span>
<span class="c1"># Easy Fibonacci</span>
<span class="nb">p</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">].</span><span class="nf">enumerate</span> <span class="p">{</span> <span class="o">|</span><span class="n">f0</span><span class="p">,</span> <span class="n">f1</span><span class="o">|</span> <span class="p">[</span><span class="n">f1</span><span class="p">,</span> <span class="n">f0</span> <span class="o">+</span> <span class="n">f1</span><span class="p">]</span> <span class="p">}.</span><span class="nf">take</span><span class="p">(</span><span class="mi">10</span><span class="p">).</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="ss">:first</span><span class="p">)</span>
<span class="c1">#=> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]</span>
<span class="c1"># Enumerable pagination</span>
<span class="n">page</span><span class="p">.</span><span class="nf">enumerate</span> <span class="p">{</span> <span class="o">|</span><span class="n">page</span><span class="o">|</span> <span class="no">Faraday</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="n">page</span><span class="p">.</span><span class="nf">next</span><span class="p">)</span> <span class="k">if</span> <span class="n">page</span><span class="p">.</span><span class="nf">next</span> <span class="p">}.</span><span class="nf">take_while</span> <span class="p">{</span> <span class="o">|</span><span class="nb">p</span><span class="o">|</span> <span class="o">!</span><span class="nb">p</span><span class="p">.</span><span class="nf">nil?</span> <span class="p">}</span>
</code></pre>
<p>Reference to similar things:</p>
<ul>
<li>Clojure <a href="https://clojuredocs.org/clojure.core/iterate" class="external">iterate</a> "Returns a lazy sequence of <code>x</code>, <code>(f x)</code>, <code>(f (f x))</code> etc." No converging, just infinite sequence... And maybe that is even more basic and idiomatic. The name is nice, too.</li>
<li>WolframLang <a href="http://reference.wolfram.com/language/ref/FixedPoint.html" class="external">FixedPoint</a>
</li>
<li>Ramda <a href="http://ramdajs.com/docs/#converge" class="external">converge</a>
</li>
<li>Elixir <a href="https://hexdocs.pm/elixir/Stream.html#unfold/2" class="external">Stream#unfold</a> (ends iteration when <code>nil</code> is returned)</li>
<li>Scala <a href="https://www.scala-lang.org/api/current/scala/collection/Iterator%24.html#iterate%5BT%5D%28start%3AT%29%28f%3AT%3D%3ET%29%3AIterator%5BT%5D" class="external">Iterator#iterate</a> (just infinite sequence)</li>
</ul>
<hr>
<p><strong>Initial proposal</strong></p>
<p>Sometimes (or rather often), there is a programming pattern of "start from one object, do something, look at the result, do the same, look at the result (and so on)".</p>
<p>Examples:</p>
<ul>
<li>fetch page by URL, if pagination present, fetch next page;</li>
<li>take 10 jobs from the queue, process them, exit when queue is empty;</li>
</ul>
<p>Typically, those are represented by <code>while</code> or <code>loop</code> + <code>break</code> which somehow feels "not functional enough", and even "not Ruby enough", so much less expressive than <code>map</code> and other <code>Enumerable</code>/<code>Enumerator</code>-based cycles.</p>
<p>In some functional languages or libraries, there is function named <code>FixedPoint</code> or <code>converge</code>, whose meaning is "take an initial value, repeat the block provided on the result on prev computation, till it will not 'stable'". I believe this notion can be useful for Rubyists too.</p>
<p>Reference implementation (name is disputable!):</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">Object</span>
<span class="k">def</span> <span class="nf">converge</span><span class="p">(</span><span class="o">&</span><span class="n">block</span><span class="p">)</span>
<span class="no">Enumerator</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="o">|</span><span class="n">y</span><span class="o">|</span>
<span class="n">prev</span> <span class="o">=</span> <span class="nb">self</span>
<span class="n">y</span> <span class="o"><<</span> <span class="nb">self</span>
<span class="kp">loop</span> <span class="k">do</span>
<span class="n">cur</span> <span class="o">=</span> <span class="n">block</span><span class="p">.</span><span class="nf">call</span><span class="p">(</span><span class="n">prev</span><span class="p">)</span>
<span class="k">raise</span> <span class="no">StopIteration</span> <span class="k">if</span> <span class="n">cur</span> <span class="o">==</span> <span class="n">prev</span>
<span class="n">y</span> <span class="o"><<</span> <span class="n">cur</span>
<span class="n">prev</span> <span class="o">=</span> <span class="n">cur</span>
<span class="k">end</span>
<span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>Examples of usage:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># Functional kata: find the closest number to sqrt(2):</span>
<span class="mf">1.0</span><span class="p">.</span><span class="nf">converge</span> <span class="p">{</span> <span class="o">|</span><span class="n">x</span><span class="o">|</span> <span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">/</span> <span class="n">x</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span> <span class="p">}.</span><span class="nf">to_a</span><span class="p">.</span><span class="nf">last</span> <span class="c1"># => 1.414213562373095</span>
<span class="no">Math</span><span class="p">.</span><span class="nf">sqrt</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="c1"># => 1.4142135623730951</span>
<span class="c1"># Next page situation:</span>
<span class="n">get</span><span class="p">(</span><span class="n">url</span><span class="p">).</span><span class="nf">converge</span> <span class="p">{</span> <span class="o">|</span><span class="n">page</span><span class="o">|</span> <span class="n">page</span><span class="p">.</span><span class="nf">next</span> <span class="p">}</span>
<span class="c1"># => returns [page, page.next, page.next.next, ...til the result is nil, or same page repeated]</span>
<span class="c1"># Job queue situation:</span>
<span class="n">queue</span><span class="p">.</span><span class="nf">top</span><span class="p">(</span><span class="mi">10</span><span class="p">).</span><span class="nf">converge</span> <span class="p">{</span> <span class="o">|</span><span class="n">jobs</span><span class="o">|</span>
<span class="n">jobs</span><span class="p">.</span><span class="nf">each</span><span class="p">(</span><span class="o">&</span><span class="ss">:perform</span><span class="p">)</span>
<span class="n">queue</span><span class="p">.</span><span class="nf">top</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1"># => takes top 10 jobs, till queue is empty (`[]` is returned two successful times)</span>
<span class="c1"># Useful for non-converging situations, too:</span>
<span class="mi">2</span><span class="p">.</span><span class="nf">converge</span> <span class="p">{</span> <span class="o">|</span><span class="n">x</span><span class="o">|</span> <span class="n">x</span> <span class="o">**</span> <span class="mi">2</span> <span class="p">}.</span><span class="nf">take</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
<span class="c1"># => [2, 4, 16, 256]</span>
<span class="c1"># Idiomatic Fibonacci:</span>
<span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">].</span><span class="nf">converge</span> <span class="p">{</span> <span class="o">|</span><span class="n">f0</span><span class="p">,</span> <span class="n">f1</span><span class="o">|</span> <span class="p">[</span><span class="n">f1</span><span class="p">,</span> <span class="n">f0</span> <span class="o">+</span> <span class="n">f1</span><span class="p">]</span> <span class="p">}.</span><span class="nf">take</span><span class="p">(</span><span class="mi">10</span><span class="p">).</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="ss">:first</span><span class="p">)</span>
<span class="c1"># => [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]</span>
</code></pre>
<p>Reference to similar things:</p>
<ul>
<li>Clojure <a href="https://clojuredocs.org/clojure.core/iterate" class="external">iterate</a> "Returns a lazy sequence of <code>x</code>, <code>(f x)</code>, <code>(f (f x))</code> etc." No converging, just infinite sequence... And maybe that is even more basic and idiomatic. The name is nice, too.</li>
<li>WolframLang <a href="http://reference.wolfram.com/language/ref/FixedPoint.html" class="external">FixedPoint</a>
</li>
<li>Ramda <a href="http://ramdajs.com/docs/#converge" class="external">converge</a>
</li>
<li>Elixir <a href="https://hexdocs.pm/elixir/Stream.html#unfold/2" class="external">Stream#unfold</a> (ends iteration when <code>nil</code> is returned)</li>
<li>Scala <a href="https://www.scala-lang.org/api/current/scala/collection/Iterator%24.html#iterate%5BT%5D%28start%3AT%29%28f%3AT%3D%3ET%29%3AIterator%5BT%5D" class="external">Iterator#iterate</a> (just infinite sequence)</li>
</ul>
<p>Possible call-seq:</p>
<ul>
<li>If converges: <code>Object#converge(&block)</code>, <code>Enumerator.converge(object, &block)</code>;</li>
<li>If just an infinite sequence: <code>Object#iterate(&block)</code>, <code>Object#deduce(&block)</code> (as opposed to <code>reduce</code>), <code>Enumerator.iterate(object, &block)</code>, <code>Enumerator#enumerate(object, &block)</code>.</li>
</ul>
<p>WDYT?..</p>
<p>PS: Can imagine somebody already proposed that, yet can't find nothing similar in the tracker for all keywords I've tried.</p> Ruby master - Feature #14145 (Closed): Proposal: Better Method#inspecthttps://bugs.ruby-lang.org/issues/141452017-11-30T10:42:08Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>The idea: When investigating (in example scripts, debugger or console) the library you are unfamiliar with, Ruby's reflection is very useful mechanism to understand "what it can": classes, modules, their constants, methods and so on.<br>
I propose to expose a bit more information Ruby has internally in <code>Method#inspect</code>:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># before:</span>
<span class="n">some_interesting_object</span><span class="p">.</span><span class="nf">method</span><span class="p">(</span><span class="ss">:foo</span><span class="p">)</span> <span class="c1"># => #<Method Klass#foo></span>
<span class="c1"># after:</span>
<span class="n">some_interesting_object</span><span class="p">.</span><span class="nf">method</span><span class="p">(</span><span class="ss">:foo</span><span class="p">)</span> <span class="c1"># => #<Method Klass#foo(first_arg, *other_args, keyword_arg:)></span>
</code></pre>
<p>Dead-naive implementation:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">Method</span>
<span class="k">def</span> <span class="nf">signature</span>
<span class="n">recv</span> <span class="o">=</span> <span class="k">case</span> <span class="n">receiver</span>
<span class="k">when</span> <span class="no">Module</span>
<span class="s2">"</span><span class="si">#{</span><span class="n">receiver</span><span class="p">.</span><span class="nf">name</span><span class="si">}</span><span class="s2">."</span>
<span class="k">else</span>
<span class="s2">"</span><span class="si">#{</span><span class="n">receiver</span><span class="p">.</span><span class="nf">class</span><span class="si">}</span><span class="s2">#"</span>
<span class="k">end</span>
<span class="n">parameters</span><span class="p">.</span><span class="nf">map</span><span class="p">.</span><span class="nf">with_index</span> <span class="p">{</span> <span class="o">|</span><span class="p">(</span><span class="n">type</span><span class="p">,</span> <span class="nb">name</span><span class="p">),</span> <span class="n">i</span><span class="o">|</span>
<span class="k">case</span> <span class="n">type</span>
<span class="k">when</span> <span class="ss">:req</span> <span class="k">then</span> <span class="s2">"</span><span class="si">#{</span><span class="nb">name</span> <span class="o">||</span> <span class="s2">"param</span><span class="si">#{</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="si">}</span><span class="s2">"</span><span class="si">}</span><span class="s2">"</span>
<span class="k">when</span> <span class="ss">:opt</span> <span class="k">then</span> <span class="s2">"</span><span class="si">#{</span><span class="nb">name</span> <span class="o">||</span> <span class="s2">"param</span><span class="si">#{</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="si">}</span><span class="s2">"</span><span class="si">}</span><span class="s2"> = <default>"</span>
<span class="k">when</span> <span class="ss">:keyreq</span> <span class="k">then</span> <span class="s2">"</span><span class="si">#{</span><span class="nb">name</span> <span class="o">||</span> <span class="s2">"kw</span><span class="si">#{</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="si">}</span><span class="s2">"</span><span class="si">}</span><span class="s2">:"</span>
<span class="k">when</span> <span class="ss">:key</span> <span class="k">then</span> <span class="s2">"</span><span class="si">#{</span><span class="nb">name</span> <span class="o">||</span> <span class="s2">"kwparam</span><span class="si">#{</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="si">}</span><span class="s2">"</span><span class="si">}</span><span class="s2">: <default>"</span>
<span class="k">when</span> <span class="ss">:rest</span> <span class="k">then</span> <span class="s2">"*</span><span class="si">#{</span><span class="nb">name</span> <span class="o">||</span> <span class="s2">"rest"</span><span class="si">}</span><span class="s2">"</span>
<span class="k">when</span> <span class="ss">:keyrest</span> <span class="k">then</span> <span class="s2">"**</span><span class="si">#{</span><span class="nb">name</span> <span class="o">||</span> <span class="s2">"kwrest"</span><span class="si">}</span><span class="s2">"</span>
<span class="k">end</span>
<span class="p">}.</span><span class="nf">join</span><span class="p">(</span><span class="s1">', '</span><span class="p">).</span><span class="nf">prepend</span><span class="p">(</span><span class="s2">"</span><span class="si">#{</span><span class="n">recv</span><span class="si">}#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">("</span><span class="p">)</span> <span class="o"><<</span> <span class="s2">")"</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">inspect</span>
<span class="s2">"#<</span><span class="si">#{</span><span class="nb">self</span><span class="p">.</span><span class="nf">class</span><span class="p">.</span><span class="nf">name</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="n">signature</span><span class="si">}</span><span class="s2">>"</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>This works "sub-optimal" for methods implemented in C, yet pretty decently for Ruby-implemented methods:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># C method, default param names</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">].</span><span class="nf">method</span><span class="p">(</span><span class="ss">:at</span><span class="p">)</span>
<span class="c1"># => #<Method Array#at(param1)></span>
<span class="c1"># Ruby method, proper param names</span>
<span class="no">CGI</span><span class="p">.</span><span class="nf">method</span><span class="p">(</span><span class="ss">:escape</span><span class="p">)</span>
<span class="c1"># => #<Method CGI.escape(string)></span>
<span class="no">Addressable</span><span class="o">::</span><span class="no">URI</span><span class="p">.</span><span class="nf">method</span><span class="p">(</span><span class="ss">:parse</span><span class="p">)</span>
<span class="c1"># => #<Method Addressable::URI.parse(uri)></span>
<span class="no">Addressable</span><span class="o">::</span><span class="no">URI</span><span class="p">.</span><span class="nf">method</span><span class="p">(</span><span class="ss">:join</span><span class="p">)</span>
<span class="o">=></span> <span class="c1">#<Method Addressable::URI.join(*uris)></span>
<span class="c1"># We can't extract default values, but at least we can say they are there</span>
<span class="no">Addressable</span><span class="o">::</span><span class="no">URI</span><span class="p">.</span><span class="nf">method</span><span class="p">(</span><span class="ss">:heuristic_parse</span><span class="p">)</span>
<span class="c1"># => #<Method Addressable::URI.heuristic_parse(uri, hints = <default>)></span>
</code></pre>
<p>If the proposal is accepted, I am ready to implement it properly in C (for all callable objects: <code>Method</code>, <code>UnboundMethod</code>, <code>Proc</code>)</p> Ruby master - Bug #14130 (Closed): Keyword arguments are ripped from the middle of hash if argume...https://bugs.ruby-lang.org/issues/141302017-11-25T14:00:20Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>Here is the code:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">def</span> <span class="nf">test1</span><span class="p">(</span><span class="n">source</span> <span class="o">=</span> <span class="p">{},</span> <span class="o">**</span><span class="n">opts</span><span class="p">)</span>
<span class="nb">puts</span> <span class="s2">"SOURCE: </span><span class="si">#{</span><span class="n">source</span><span class="si">}</span><span class="s2">, OPTS: </span><span class="si">#{</span><span class="n">opts</span><span class="si">}</span><span class="s2">"</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">test2</span><span class="p">(</span><span class="n">source</span><span class="p">,</span> <span class="o">**</span><span class="n">opts</span><span class="p">)</span>
<span class="nb">puts</span> <span class="s2">"SOURCE: </span><span class="si">#{</span><span class="n">source</span><span class="si">}</span><span class="s2">, OPTS: </span><span class="si">#{</span><span class="n">opts</span><span class="si">}</span><span class="s2">"</span>
<span class="k">end</span>
<span class="nb">puts</span> <span class="s2">"No source"</span>
<span class="n">test1</span><span class="p">(</span><span class="ss">length: </span><span class="mi">2000</span><span class="p">)</span>
<span class="c1"># 1. SOURCE: {}, OPTS: {:length=>2000} -- OK, it is reasonable.</span>
<span class="n">test2</span><span class="p">(</span><span class="ss">length: </span><span class="mi">2000</span><span class="p">)</span>
<span class="c1"># 2. SOURCE: {:length=>2000}, OPTS: {} -- Exactly as expected.</span>
<span class="nb">puts</span>
<span class="nb">puts</span> <span class="s2">"Source is mixed hash"</span>
<span class="n">test1</span><span class="p">(</span><span class="s1">'River name'</span> <span class="o">=></span> <span class="s1">'Mississippi'</span><span class="p">,</span> <span class="ss">length: </span><span class="mi">2000</span><span class="p">,</span> <span class="s1">'Country'</span> <span class="o">=></span> <span class="s1">'USA'</span><span class="p">)</span>
<span class="c1"># 3. SOURCE: {"River name"=>"Mississippi", "Country"=>"USA"}, OPTS: {:length=>2000} -- It is already a bit weird</span>
<span class="n">test2</span><span class="p">(</span><span class="s1">'River name'</span> <span class="o">=></span> <span class="s1">'Mississippi'</span><span class="p">,</span> <span class="ss">length: </span><span class="mi">2000</span><span class="p">,</span> <span class="s1">'Country'</span> <span class="o">=></span> <span class="s1">'USA'</span><span class="p">)</span>
<span class="c1"># 4. SOURCE: {"River name"=>"Mississippi", :length=>2000, "Country"=>"USA"}, OPTS: {} -- The most weird thing!</span>
</code></pre>
<p>My concern is cases (3) and (4). Ripping keyword argument from what from any logic looks like a <em>middle</em> of a hash (3) is already pretty strange. But the fact that this behavior depends on whether first argument has or has not default value (3 vs 4) clearly looks like some bug?</p>
<p>Checked on several recent versions, including <code>ruby 2.5.0dev (2017-09-11 trunk 59836) [x86_64-linux]</code> (the last that is available on my RVM), behavior is consistent between them.</p> Ruby master - Misc #13072 (Closed): Current state of date standard libraryhttps://bugs.ruby-lang.org/issues/130722016-12-25T13:04:30Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>The facts that I've been able to gather (not supported by links, so please forgive me if I am misquoting/misunderstanding):</p>
<ul>
<li>date library was initially developed and maintained by Tadayoshi Funaba, who was the "single point of truth" for its design and features;</li>
<li>for at least year, initial creator/maintainer of the library is inactive in Ruby community, so library mostly considered unmaintained;</li>
<li>as far as I can "sense"/guess from ticket responses about the library, the core team doesn't see it as crucial/important to maintain.</li>
</ul>
<p>At the same time, the library provides:</p>
<ul>
<li>Widely and extensively used <code>Date</code> class;</li>
<li>Pretty controversial <code>DateTime</code> class, which has a huge feature intersection but almost no compatibility with core <code>Time</code> class;</li>
<li>Date parsing functionality (<code>Date._parse</code>), which is also used by <code>lib/time</code>.</li>
</ul>
<p>The latter also leads to a really confusing situation, where one of the core Ruby classes has "optional additional functionality" in stdlib.</p>
<p>Overall, the situation looks pretty "dirty" (as in "dirty code"), and seems like needing improvement.</p>
<p>WDYT about this plan (for Ruby 2.5, for ex.):</p>
<ul>
<li>make <code>Date</code> and <code>Date._parse</code> parts of language core (with probably renaming <code>_parse</code> to something more readable, or even extracting something like <code>Date::Parser</code> module);</li>
<li>merge <code>DateTime</code> and <code>Time</code> (while preferring <code>Time</code>s interface where possible);</li>
<li>on the way, gather all requests/bugs from this tracker, related to dates and times parsing, representing and so on.</li>
</ul> Ruby master - Feature #12790 (Open): Better inspect for stdlib classeshttps://bugs.ruby-lang.org/issues/127902016-09-26T14:17:16Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p><code>#inspect</code> is important for understanding "what I have" in irb/pry, and in <a href="https://tenderlovemaking.com/2016/02/05/i-am-a-puts-debuggerer.html" class="external">puts-debugging</a>, and in ton of other cases.</p>
<p>Sadly, some of important stdlib classes (in my opinion) fail to provide concise and readable representation for <code>#inspect</code>.</p>
<p>Some examples below:</p>
<h2>
<code>BigDecimal</code> (important for representing money values, for example)</h2>
<p>Current behavior:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1">#<BigDecimal:128d34f4,'0.25E2',9(18)></span>
<span class="c1"># ^ ^ ^ ^</span>
<span class="c1"># 1 2 3 4</span>
</code></pre>
<ol>
<li>OK, this is reasonable</li>
<li>Do we really need object id here? As far as I can understand, developer is typically concerned only about numeric value identity, not object identity for bigdecimals.</li>
<li>OK, I understand about scientific representation, but it is hard to read (at least for me), and also single quotes around add to a confusion.</li>
<li>I'm not sure. Number of significant digits it is. So, what are the situation when you need to look at it constantly?..</li>
</ol>
<p>So, ideal behavior:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1">#<BigDecimal: 250></span>
<span class="c1"># or, preserving num. of sig.dig.</span>
<span class="c1">#<BigDecimal: 250 digits: 9(18)></span>
<span class="c1"># ...or something like this</span>
<span class="c1"># But for really large numbers it is still</span>
<span class="c1">#<BigDecimal: 1.5E35></span>
</code></pre>
<p>Side note: try to guess what <code>BigDecimal.new(2)**10_000</code> looks like?.. And whether this look is really useful for anything.</p>
<h2>
<code>Date</code> and <code>DateTime</code>
</h2>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">Date</span><span class="p">.</span><span class="nf">today</span>
<span class="c1"># => #<Date: 2016-09-26 ((2457658j,0s,0n),+0s,2299161j)></span>
<span class="no">DateTime</span><span class="p">.</span><span class="nf">now</span>
<span class="c1"># => #<DateTime: 2016-09-26T16:40:17+03:00 ((2457658j,49217s,186886101n),+10800s,2299161j)></span>
</code></pre>
<p>Maybe it is just me, but it does not look like part in parenthises (and double parenthises!) contain information of such real importance that it can change our perception of "what's going on"?.. If you work with current dates, it is just unnecessary; if you work with some really complicated historical dates, it is not enough, being too concise and enigmatic to give some understanding of epochs and calendars.</p>
<p>(And to add, it is awfully inconsistent with Time's <code>#inspect</code>, which does provide just <code>2016-09-26 17:03:59 +0300</code>.)</p> Ruby master - Feature #12760 (Closed): Optional block argument for `itself`https://bugs.ruby-lang.org/issues/127602016-09-14T07:39:36Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>That's an another attempt to choose good metaphor for <code>object.(yield self)</code> (previously: <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Object#yield_self (Closed)" href="https://bugs.ruby-lang.org/issues/6721">#6721</a> and ton of duplicates).</p>
<p>In discussion here: <a href="https://bugs.ruby-lang.org/issues/11717#note-3" class="external">https://bugs.ruby-lang.org/issues/11717#note-3</a> it was said:</p>
<blockquote>
<p>Clearly this is something the Ruby community wants. Just as clearly, it's something nobody can name.</p>
</blockquote>
<p>But suddenly... I've thought about this!</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">some</span><span class="p">.</span><span class="nf">long</span><span class="p">.</span><span class="nf">method</span><span class="p">.</span><span class="nf">chain</span><span class="p">.</span><span class="nf">constructing</span><span class="p">.</span><span class="nf">string</span>
<span class="p">.</span><span class="nf">itself</span> <span class="p">{</span> <span class="o">|</span><span class="n">s</span><span class="o">|</span> <span class="s2">"("</span> <span class="o">+</span> <span class="n">s</span> <span class="o">+</span> <span class="s2">")"</span> <span class="p">}</span>
<span class="p">.</span><span class="nf">itself</span><span class="p">(</span><span class="o">&</span><span class="nb">method</span><span class="p">(</span><span class="ss">:puts</span><span class="p">))</span>
<span class="c1"># or</span>
<span class="nb">require</span> <span class="s1">'open-uri'</span>
<span class="n">construct_url</span><span class="p">(</span><span class="o">*</span><span class="n">params</span><span class="p">)</span>
<span class="p">.</span><span class="nf">itself</span><span class="p">(</span><span class="o">&</span><span class="nb">method</span><span class="p">(</span><span class="ss">:open</span><span class="p">))</span>
<span class="p">.</span><span class="nf">read</span>
<span class="p">.</span><span class="nf">itself</span><span class="p">(</span><span class="o">&</span><span class="no">JSON</span><span class="p">.</span><span class="nf">method</span><span class="p">(</span><span class="ss">:parse</span><span class="p">))</span>
<span class="p">.</span><span class="nf">to_yaml</span>
<span class="p">.</span><span class="nf">itself</span><span class="p">(</span><span class="o">&</span><span class="no">File</span><span class="p">.</span><span class="nf">method</span><span class="p">(</span><span class="ss">:write</span><span class="p">))</span>
<span class="c1"># NB: I understand that _last_ `itself` in both examples</span>
<span class="c1"># could as well be `tap`. But not all the previous.</span>
</code></pre>
<p>Pros:</p>
<ul>
<li>method is already here, it has pretty name and underused (almost nothing except <code>group_by(&:itself)</code> comes to mind);</li>
<li>it is not 100% good English, but readable: <code>itself(&JSON.method(:parse))</code> = "parse itself with json";</li>
<li>implementation is trivial, no backwards-compatibility issues (like new methods shadowing something important in third-party library) are expected.</li>
</ul>
<p>Cons:</p>
<ul>
<li>???</li>
</ul> Ruby master - Feature #11717 (Closed): Object#trap -- pass object to block and return resulthttps://bugs.ruby-lang.org/issues/117172015-11-19T20:50:33Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p><code>Object#trap</code> can be thought as useful counterpart for <code>Object#tap</code>: like tap, it passes object to the block; <strong>unlike</strong> tap, it returns results of the block, not object itself.</p>
<p><strong>Rationale</strong></p>
<p><code>#trap</code> could allow to gracefully chain processing of objects, which isn't <code>Enumerable</code>, therefore enforcing "functional" style in Ruby (which considered good).</p>
<p><strong>Use case</strong></p>
<pre><code># Assume we grab some resource from web:
SomeWebClient.get('http://some/url', param1: 'value1', param2: 'value2').body
# And now, the body of response is JSON, and we want it parsed. How to express it?
# Option 1: wrap:
JSON.parse(SomeWebClient.get('http://some/url', param1: 'value1', param2: 'value2').body)
# Downside: messy parenthesis, and need to jump back and forth to understand the meaning
# Option 2: intermediate variable:
s = SomeWebClient.get('http://some/url', param1: 'value1', param2: 'value2').body
JSON.parse(s)
# Downside: intermediate variable is not elegant
# Option 3: monkey-patch (or refine)
SomeWebClient.get('http://some/url', param1: 'value1', param2: 'value2').body.from_json
# Downside: monkey-patching is a last resort; also, your classes should be already patched when you stuck with this case
# Option 4 (proposed): trap
SomeWebClient.get('http://some/url', param1: 'value1', param2: 'value2').body.
trap{|s| JSON.parse(s)} # => parsed JSON
</code></pre>
<p>And when you are thinking with code, experimenting with code (especially in irb, but in editor too), only last option is "natural" river of thoughts: do this, then do that (extract data from web, then parse it).</p>
<p><strong>Naming</strong></p>
<ul>
<li>it is similar enough to <code>tap</code>;</li>
<li>it is specific enough to not be used widely in some popular library (or isn't it?);</li>
<li>mnemonic is "do something and trap (catch) the value".</li>
</ul>
<p>WDYT?</p> Backport191 - Backport #1723 (Closed): respond_to? vs. to_aryhttps://bugs.ruby-lang.org/issues/17232009-07-05T07:00:06Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>=begin</p>
<a name="class-with-respond_to-redefined"></a>
<h1 >class with respond_to? redefined<a href="#class-with-respond_to-redefined" class="wiki-anchor">¶</a></h1>
<p>class A<br>
def respond_to?(m); false end<br>
end</p>
<p>x = [A.new]</p>
<a name="deconstructing-iteration"></a>
<h1 >deconstructing iteration<a href="#deconstructing-iteration" class="wiki-anchor">¶</a></h1>
<p>x.each do |a, b| p [a,b] end</p>
<p>It outputs [nil, nil], while [#<A:0xb5c9d0>, nil] is expected. If I comment out respond_to? in A, all works as expected<br>
=end</p>