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 #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 #19377 (Rejected): Rename Fiber#storage to Fiber.storagehttps://bugs.ruby-lang.org/issues/193772023-01-25T20:49:30Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>Justification:</p>
<ul>
<li>
<code>#storage</code>, which pretends to be an instance method, is always available only on <code>Fiber.current</code>, which is <a href="https://github.com/ruby/ruby/pull/6985#discussion_r1055796069" class="external">problematic to document</a>, and needs special handling when <code>storage</code> is called for non-current Fiber;</li>
<li>with class method + docs "storage of the current fiber" it all will be self-evident;</li>
<li>Most of the time, when we declare methods that are available only in the current {something}, they are class methods. (like storage's itself interface of <code>Fiber::[]</code> and <code>Fiber::[]=</code>, or <code>Module.used_modules</code>, which is modules and refinements of the <em>current context</em>, but it is not <code>self.used_modules</code>, for example)</li>
<li>Code like</li>
</ul>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">Fiber</span><span class="p">.</span><span class="nf">current</span><span class="p">.</span><span class="nf">storage</span> <span class="o">=</span> <span class="p">{</span><span class="ss">foo: </span><span class="s1">'bar'</span><span class="p">}</span>
<span class="no">Fiber</span><span class="p">[</span><span class="ss">:foo</span><span class="p">]</span>
</code></pre>
<p>...looks like it is two unrelated things (storage of the <em>current</em> fiber vs some "global" key getter/setter?)</p>
<p>I don't see much discussion of this in <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Introduce `Fiber#storage` for inheritable fiber-scoped variables. (Closed)" href="https://bugs.ruby-lang.org/issues/19078">#19078</a>. Matz in <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Introduce `Fiber#storage` for inheritable fiber-scoped variables. (Closed)" href="https://bugs.ruby-lang.org/issues/19078#note-22">#19078#note-22</a>, while accepting the interface, describes it as homogenous:</p>
<blockquote>
<p>(1) fiber[key]/fiber[key]=val - accepted.<br>
(2) fiber.storage => hash - accepted<br>
(3) fiber.storage=hash - should be experimental<br>
...</p>
</blockquote>
<p>So I believe it should be either <code>Fiber.current[]</code> and <code>Fiber.current.storage</code>, or <code>Fiber[]</code>, and <code>Fiber.storage</code>. The latter is preferable to underline it is only one, related to the current fiber, interface, not "every fiber instance has one (but actually you can use only <code>current</code>'s)</p> 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 #19300 (Rejected): Move public methods from Kernel to Objecthttps://bugs.ruby-lang.org/issues/193002023-01-02T21:48:28Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>In my understanding, <code>Kernel</code> is a container for methods that are perceived as "global", and are available inside every objects as its private methods, like <code>puts</code></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">foo</span> <span class="o">=</span> <span class="nb">puts</span> <span class="s2">"foo"</span>
<span class="k">end</span>
<span class="n">a</span> <span class="o">=</span> <span class="no">A</span><span class="p">.</span><span class="nf">new</span>
<span class="n">a</span><span class="p">.</span><span class="nf">foo</span> <span class="c1"># prints "foo"</span>
<span class="n">a</span><span class="p">.</span><span class="nf">puts</span> <span class="s1">'bar'</span>
<span class="c1"># private method `puts' called for #<A:0x00007f39a683db28> (NoMethodError)</span>
</code></pre>
<p>There are, though, exactly three (if I am not missing something) methods that, <strong>according to documentation</strong>, break this intuition and belong to <a href="https://docs.ruby-lang.org/en/master/Kernel.html" class="external">Kernel</a>:</p>
<ul>
<li><code>clone</code></li>
<li><code>tap</code></li>
<li><code>then</code></li>
</ul>
<p>All those methods are public method for a receiver, and they mostly make sense with an explicit receiver. The most confusing of them is <code>#clone</code>, which is close cousin of <code>#dup</code>, but it is <a href="https://docs.ruby-lang.org/en/master/Kernel.html#method-i-clone" class="external">Kernel#clone</a> and <a href="https://docs.ruby-lang.org/en/master/Object.html#method-i-dup" class="external">Object#dup</a>.</p>
<p>This state of things is mostly harmless in practical code, but very inconvenient for teaching, reasoning about the meaning of modules and objects, and lookup for documentation.</p>
<p><strong>But</strong>, in the description above, <strong>according to documentation</strong> is important statement. Because according to introspection, method definitions are spread differently:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="nb">puts</span> <span class="s2">"Public"</span>
<span class="no">Object</span><span class="p">.</span><span class="nf">new</span>
<span class="p">.</span><span class="nf">then</span> <span class="p">{</span> <span class="o">|</span><span class="n">o</span><span class="o">|</span> <span class="n">o</span><span class="p">.</span><span class="nf">methods</span><span class="p">.</span><span class="nf">group_by</span> <span class="p">{</span> <span class="n">o</span><span class="p">.</span><span class="nf">method</span><span class="p">(</span><span class="n">_1</span><span class="p">).</span><span class="nf">owner</span> <span class="p">}</span> <span class="p">}</span>
<span class="p">.</span><span class="nf">each</span> <span class="p">{</span> <span class="nb">puts</span> <span class="s2">"</span><span class="si">#{</span><span class="n">_1</span><span class="si">}</span><span class="s2">: </span><span class="si">#{</span><span class="n">_2</span><span class="p">.</span><span class="nf">sort</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="s1">', '</span><span class="p">)</span><span class="si">}</span><span class="s2">"</span> <span class="p">}</span>
<span class="nb">puts</span>
<span class="nb">puts</span> <span class="s2">"Private:"</span>
<span class="no">Object</span><span class="p">.</span><span class="nf">new</span>
<span class="p">.</span><span class="nf">then</span> <span class="p">{</span> <span class="o">|</span><span class="n">o</span><span class="o">|</span> <span class="n">o</span><span class="p">.</span><span class="nf">private_methods</span><span class="p">.</span><span class="nf">group_by</span> <span class="p">{</span> <span class="n">o</span><span class="p">.</span><span class="nf">method</span><span class="p">(</span><span class="n">_1</span><span class="p">).</span><span class="nf">owner</span> <span class="p">}</span> <span class="p">}</span>
<span class="p">.</span><span class="nf">each</span> <span class="p">{</span> <span class="nb">puts</span> <span class="s2">"</span><span class="si">#{</span><span class="n">_1</span><span class="si">}</span><span class="s2">: </span><span class="si">#{</span><span class="n">_2</span><span class="p">.</span><span class="nf">sort</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="s1">', '</span><span class="p">)</span><span class="si">}</span><span class="s2">"</span> <span class="p">}</span>
</code></pre>
<p>Output:</p>
<pre><code>Public
Kernel: !~, <=>, ===, class, clone, define_singleton_method, display, dup, enum_for, eql?, extend, freeze, frozen?, hash, inspect, instance_of?, instance_variable_defined?, instance_variable_get, instance_variable_set, instance_variables, is_a?, itself, kind_of?, method, methods, nil?, object_id, private_methods, protected_methods, public_method, public_methods, public_send, remove_instance_variable, respond_to?, send, singleton_class, singleton_method, singleton_methods, tap, then, to_enum, to_s, yield_self
BasicObject: !, !=, ==, __id__, __send__, equal?, instance_eval, instance_exec
Private:
Kernel: Array, Complex, Float, Hash, Integer, Rational, String, __callee__, __dir__, __method__, `, abort, at_exit, autoload, autoload?, binding, block_given?, caller, caller_locations, catch, eval, exec, exit, exit!, fail, fork, format, gem, gem_original_require, gets, global_variables, initialize_clone, initialize_copy, initialize_dup, iterator?, lambda, load, local_variables, loop, open, p, pp, print, printf, proc, putc, puts, raise, rand, readline, readlines, require, require_relative, respond_to_missing?, select, set_trace_func, sleep, spawn, sprintf, srand, syscall, system, test, throw, trace_var, trap, untrace_var, warn
BasicObject: initialize, method_missing, singleton_method_added, singleton_method_removed, singleton_method_undefined
</code></pre>
<p>E.g., internally, <code>Object</code> doesn't have <em>any</em> method defined, and is just a <code>BasicObject</code> with <code>Kernel</code> included.</p>
<p>So, there are three questions/proposals:</p>
<ol>
<li>Does this disposition has some internal sense, or it is more of a historical thing?</li>
<li>Can it be changed so that public methods belonged to <code>Object</code> instead of <code>Kernel</code>?</li>
<li>If the answer to (2) is "no", can at least docs for <code>clone</code>, <code>tap</code> and <code>then</code> be adjusted to follow other public methods in pretending they are <code>Object</code>'s features?</li>
</ol> 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 - Feature #19272 (Rejected): Hash#merge: smarter protocol depending on passed block a...https://bugs.ruby-lang.org/issues/192722022-12-27T20:36:01Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>Usage of <code>Hash#merge</code> with a "conflict resolution block" is almost always clumsy: due to the fact that the block accepts <code>|key, old_val, new_val|</code> arguments, and many trivial usages just somehow sum up old and new keys, the thing that should be "intuitively trivial" becomes longer than it should be:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># I just want a sum!</span>
<span class="p">{</span><span class="ss">apples: </span><span class="mi">1</span><span class="p">,</span> <span class="ss">oranges: </span><span class="mi">2</span><span class="p">}.</span><span class="nf">merge</span><span class="p">(</span><span class="ss">apples: </span><span class="mi">3</span><span class="p">,</span> <span class="ss">bananas: </span><span class="mi">5</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">_</span><span class="p">,</span> <span class="n">o</span><span class="p">,</span> <span class="n">n</span><span class="o">|</span> <span class="n">o</span> <span class="o">+</span> <span class="n">n</span> <span class="p">}</span>
<span class="c1"># I just want a group!</span>
<span class="p">{</span><span class="ss">words: </span><span class="sx">%w[I just]</span><span class="p">}.</span><span class="nf">merge</span><span class="p">(</span><span class="ss">words: </span><span class="sx">%w[want a group]</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">_</span><span class="p">,</span> <span class="n">o</span><span class="p">,</span> <span class="n">n</span><span class="o">|</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">n</span><span class="p">]</span> <span class="p">}</span>
<span class="c1"># I just want to unify flags!</span>
<span class="p">{</span><span class="s1">'file1'</span> <span class="o">=></span> <span class="no">File</span><span class="o">::</span><span class="no">READABLE</span><span class="p">,</span> <span class="s1">'file2'</span> <span class="o">=></span> <span class="no">File</span><span class="o">::</span><span class="no">READABLE</span> <span class="o">|</span> <span class="no">File</span><span class="o">::</span><span class="no">WRITABLE</span><span class="p">}</span>
<span class="p">.</span><span class="nf">merge</span><span class="p">(</span><span class="s1">'file1'</span> <span class="o">=></span> <span class="no">File</span><span class="o">::</span><span class="no">WRITABLE</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">_</span><span class="p">,</span> <span class="n">o</span><span class="p">,</span> <span class="n">n</span><span class="o">|</span> <span class="n">o</span> <span class="o">|</span> <span class="n">n</span> <span class="p">}</span>
<span class="c1"># ...or, vice versa:</span>
<span class="p">{</span><span class="s1">'file1'</span> <span class="o">=></span> <span class="no">File</span><span class="o">::</span><span class="no">READABLE</span><span class="p">,</span> <span class="s1">'file2'</span> <span class="o">=></span> <span class="no">File</span><span class="o">::</span><span class="no">READABLE</span> <span class="o">|</span> <span class="no">File</span><span class="o">::</span><span class="no">WRITABLE</span><span class="p">}</span>
<span class="p">.</span><span class="nf">merge</span><span class="p">(</span><span class="s1">'file1'</span> <span class="o">=></span> <span class="no">File</span><span class="o">::</span><span class="no">WRITABLE</span><span class="p">,</span> <span class="s1">'file2'</span> <span class="o">=></span> <span class="no">File</span><span class="o">::</span><span class="no">WRITABLE</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">_</span><span class="p">,</span> <span class="n">o</span><span class="p">,</span> <span class="n">n</span><span class="o">|</span> <span class="n">o</span> <span class="o">&</span> <span class="n">n</span> <span class="p">}</span>
</code></pre>
<p>It is especially noticeable in the last two examples, but the <em>usual</em> problem is there are too many "unnecessary" punctuation, where the essential might be lost.</p>
<p>There are proposals like <a class="issue tracker-2 status-1 priority-4 priority-default" title="Feature: Define Hash#merge_sum as Hash#merge with sum block (Open)" href="https://bugs.ruby-lang.org/issues/19148">#19148</a>, which struggle to define <em>another</em> method (what would be the name? isn't it just merging?)</p>
<p>But I've been thinking, can't the implementation be chosen based on the arity of the passed block?.. Prototype:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">Hash</span>
<span class="k">alias</span> <span class="n">old_merge</span> <span class="n">merge</span>
<span class="k">def</span> <span class="nf">merge</span><span class="p">(</span><span class="n">other</span><span class="p">,</span> <span class="o">&</span><span class="n">block</span><span class="p">)</span>
<span class="k">return</span> <span class="n">old_merge</span><span class="p">(</span><span class="n">other</span><span class="p">)</span> <span class="k">unless</span> <span class="n">block</span>
<span class="k">if</span> <span class="n">block</span><span class="p">.</span><span class="nf">arity</span><span class="p">.</span><span class="nf">abs</span> <span class="o">==</span> <span class="mi">2</span>
<span class="n">old_merge</span><span class="p">(</span><span class="n">other</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">_</span><span class="p">,</span> <span class="n">o</span><span class="p">,</span> <span class="n">n</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">o</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span> <span class="p">}</span>
<span class="k">else</span>
<span class="n">old_merge</span><span class="p">(</span><span class="n">other</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="k">end</span>
</code></pre>
<p>E.g.: <strong>If, and only if, the passed block is of arity 2, treat it as an operation on old and new values.</strong> Otherwise, proceed as before (maintaining backward compatibility.)</p>
<p>Usage:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="p">{</span><span class="ss">apples: </span><span class="mi">1</span><span class="p">,</span> <span class="ss">oranges: </span><span class="mi">2</span><span class="p">}.</span><span class="nf">merge</span><span class="p">(</span><span class="ss">apples: </span><span class="mi">3</span><span class="p">,</span> <span class="ss">bananas: </span><span class="mi">5</span><span class="p">,</span> <span class="o">&</span><span class="p">:</span><span class="o">+</span><span class="p">)</span>
<span class="c1">#=> {:apples=>4, :oranges=>2, :bananas=>5}</span>
<span class="p">{</span><span class="ss">words: </span><span class="sx">%w[I just]</span><span class="p">}.</span><span class="nf">merge</span><span class="p">(</span><span class="ss">words: </span><span class="sx">%w[want a group]</span><span class="p">,</span> <span class="o">&</span><span class="ss">:concat</span><span class="p">)</span>
<span class="c1">#=> {:words=>["I", "just", "want", "a", "group"]}</span>
<span class="p">{</span><span class="s1">'file1'</span> <span class="o">=></span> <span class="no">File</span><span class="o">::</span><span class="no">READABLE</span><span class="p">,</span> <span class="s1">'file2'</span> <span class="o">=></span> <span class="no">File</span><span class="o">::</span><span class="no">READABLE</span> <span class="o">|</span> <span class="no">File</span><span class="o">::</span><span class="no">WRITABLE</span><span class="p">}</span>
<span class="p">.</span><span class="nf">merge</span><span class="p">(</span><span class="s1">'file1'</span> <span class="o">=></span> <span class="no">File</span><span class="o">::</span><span class="no">WRITABLE</span><span class="p">,</span> <span class="o">&</span><span class="ss">:|</span><span class="p">)</span>
<span class="c1">#=> {"file1"=>5, "file2"=>5}</span>
<span class="p">{</span><span class="s1">'file1'</span> <span class="o">=></span> <span class="no">File</span><span class="o">::</span><span class="no">READABLE</span><span class="p">,</span> <span class="s1">'file2'</span> <span class="o">=></span> <span class="no">File</span><span class="o">::</span><span class="no">READABLE</span> <span class="o">|</span> <span class="no">File</span><span class="o">::</span><span class="no">WRITABLE</span><span class="p">}</span>
<span class="p">.</span><span class="nf">merge</span><span class="p">(</span><span class="s1">'file1'</span> <span class="o">=></span> <span class="no">File</span><span class="o">::</span><span class="no">WRITABLE</span><span class="p">,</span> <span class="s1">'file2'</span> <span class="o">=></span> <span class="no">File</span><span class="o">::</span><span class="no">WRITABLE</span><span class="p">,</span> <span class="o">&</span><span class="ss">:&</span><span class="p">)</span>
<span class="c1">#=> {"file1"=>0, "file2"=>4}</span>
<span class="c1"># If necessary, the old protocol still works:</span>
<span class="p">{</span><span class="ss">apples: </span><span class="mi">1</span><span class="p">,</span> <span class="ss">oranges: </span><span class="mi">2</span><span class="p">}.</span><span class="nf">merge</span><span class="p">(</span><span class="ss">apples: </span><span class="mi">3</span><span class="p">,</span> <span class="ss">bananas: </span><span class="mi">5</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">k</span><span class="p">,</span> <span class="n">o</span><span class="p">,</span> <span class="n">n</span><span class="o">|</span> <span class="n">k</span> <span class="o">==</span> <span class="ss">:apples</span> <span class="p">?</span> <span class="mi">0</span> <span class="p">:</span> <span class="n">o</span> <span class="o">+</span> <span class="n">n</span> <span class="p">}</span>
<span class="c1"># => {:apples=>0, :oranges=>2, :bananas=>5}</span>
</code></pre>
<p>As far as I can remember, Ruby core doesn't have methods like this (that change implementation depending on the arity of passed callable), but I think I saw this approach in other languages. Can't remember particular examples, but always found this idea appealing.</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 - Feature #18934 (Rejected): Proposal: Introduce method results memoization API in th...https://bugs.ruby-lang.org/issues/189342022-07-21T17:01:25Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p><strong>Abstract:</strong> I propose to introduce a simple core API for memoizing argument-less method return values.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">Test</span>
<span class="k">def</span> <span class="nf">foo</span>
<span class="nb">puts</span> <span class="s2">"call!"</span>
<span class="mi">5</span>
<span class="k">end</span>
<span class="n">memoized</span> <span class="ss">:foo</span>
<span class="k">end</span>
<span class="n">o</span> <span class="o">=</span> <span class="no">Test</span><span class="p">.</span><span class="nf">new</span>
<span class="n">o</span><span class="p">.</span><span class="nf">foo</span> <span class="c1"># prints "call!", returns 5</span>
<span class="n">o</span><span class="p">.</span><span class="nf">foo</span> <span class="c1"># returns 5 immediately</span>
</code></pre>
<p>The full proposal is below.</p>
<a name="Intro"></a>
<h2 >Intro<a href="#Intro" class="wiki-anchor">¶</a></h2>
<p>For further reasoning, I'll be using the following class. It is simplified/invented for demonstrative purposes, so I'd prefer to discuss problems/solutions described in general and not focus on "you could rewrite this class that way".</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">Sentence</span>
<span class="nb">attr_reader</span> <span class="ss">:text</span>
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">text</span><span class="p">)</span> <span class="o">=</span> <span class="vi">@text</span> <span class="o">=</span> <span class="n">text</span>
<span class="k">def</span> <span class="nf">tokens</span><span class="p">()</span> <span class="o">=</span> <span class="no">SomeTokenizer</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">text</span><span class="p">).</span><span class="nf">call</span>
<span class="k">def</span> <span class="nf">size</span><span class="p">()</span> <span class="o">=</span> <span class="n">tokens</span><span class="p">.</span><span class="nf">size</span>
<span class="k">def</span> <span class="nf">empty?</span><span class="p">()</span> <span class="o">=</span> <span class="n">tokens</span><span class="p">.</span><span class="nf">all?</span><span class="p">(</span><span class="o">&</span><span class="ss">:whitespace?</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">words</span><span class="p">()</span> <span class="o">=</span> <span class="n">tokens</span><span class="p">.</span><span class="nf">select</span><span class="p">(</span><span class="o">&</span><span class="ss">:word?</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">service?</span><span class="p">()</span> <span class="o">=</span> <span class="n">words</span><span class="p">.</span><span class="nf">empty?</span>
<span class="k">end</span>
</code></pre>
<a name="The-problem"></a>
<h2 >The problem<a href="#The-problem" class="wiki-anchor">¶</a></h2>
<p>The class above is nice, clean, and easy to read. The problem with it is efficiency: if we imagine that <code>SomeTokenizer#call</code> is not cheap (it probably isn't), then creating a few sentences and then processing them with some algorithm will be—with the demonstrated definition of the class—much less efficient then it could be. Every statement like...</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">many_sentences</span><span class="p">.</span><span class="nf">reject</span><span class="p">(</span><span class="o">&</span><span class="ss">:empty?</span><span class="p">).</span><span class="nf">select</span> <span class="p">{</span> <span class="n">_1</span><span class="p">.</span><span class="nf">words</span><span class="p">.</span><span class="nf">include?</span><span class="p">(</span><span class="s1">'Ruby'</span><span class="p">)</span> <span class="p">}.</span><span class="nf">map</span> <span class="p">{</span> <span class="n">_1</span><span class="p">.</span><span class="nf">words</span><span class="p">.</span><span class="nf">count</span> <span class="o">/</span> <span class="n">_1</span><span class="p">.</span><span class="nf">tokens</span><span class="p">.</span><span class="nf">count</span> <span class="p">}</span>
</code></pre>
<p>...is much less efficient than it "intuitively" should be because tokenization happens again and again.</p>
<p>Caching just <code>tokens</code> would probably not be enough for a complex algorithm working with some notable amounts of data: <code>whitespace?</code> and <code>word?</code> might also be non-trivial; but even trivial methods like <code>select</code> and <code>empty?</code> when needlessly repeated thousands of times, would be visible in profiling.</p>
<p>So, can we stop recalculating them constantly?</p>
<a name="Existing-solutions"></a>
<h2 >Existing solutions<a href="#Existing-solutions" class="wiki-anchor">¶</a></h2>
<h3>Just pre-calculate everything in <code>initialize</code>
</h3>
<p>We could create a lot of instance variables in <code>initialize</code>:</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">text</span><span class="p">)</span>
<span class="vi">@text</span> <span class="o">=</span> <span class="n">text</span>
<span class="vi">@tokens</span> <span class="o">=</span> <span class="no">SomeTokenizer</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="vi">@text</span><span class="p">).</span><span class="nf">call</span>
<span class="vi">@size</span> <span class="o">=</span> <span class="vi">@tokens</span><span class="p">.</span><span class="nf">size</span>
<span class="vi">@empty</span> <span class="o">=</span> <span class="vi">@tokens</span><span class="p">.</span><span class="nf">all?</span><span class="p">(</span><span class="o">&</span><span class="ss">:whitespace?</span><span class="p">)</span>
<span class="vi">@words</span> <span class="o">=</span> <span class="vi">@tokens</span><span class="p">.</span><span class="nf">select</span><span class="p">(</span><span class="o">&</span><span class="ss">:word?</span><span class="p">)</span>
<span class="vi">@service</span> <span class="o">=</span> <span class="vi">@words</span><span class="p">.</span><span class="nf">empty?</span>
<span class="k">end</span>
</code></pre>
<p>It will work, of course, but it loses nice visibility of what's objects main data and what is derivative; and it is more code (now we need to define attr_readers and predicate methods for all of that!). And adding every new small service method (like <code>def question?() = tokens.last&.text == '?'</code>) would require rethinking "is it efficient enough to be a method, or should I add one more instance var"?</p>
<h3>
<code>||=</code> idiom</h3>
<p>The common idiom for caching is <code>||=</code>:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">def</span> <span class="nf">words</span><span class="p">()</span><span class="o">=</span> <span class="vi">@words</span> <span class="o">||=</span> <span class="n">tokens</span><span class="p">.</span><span class="nf">select</span><span class="p">(</span><span class="o">&</span><span class="ss">:word?</span><span class="p">)</span>
</code></pre>
<p>It has its drawbacks, though:</p>
<ol>
<li>doesn't suit methods that can return <code>false</code> or <code>nil</code> (like <code>service?</code>)</li>
<li>harder to use with methods that need several statements to calculate the end result</li>
<li>it mixes the concerns of "how it is calculated" and "it is memoized" (looking at the method's code, you don't immediately know if the variable is used only for memoization, or it could've been set elsewhere, and here we just providing default value)</li>
<li>it pollutes the object's data representation</li>
</ol>
<p>1-2 is typically advised to solve with a less elegant but futureproof solution (which also makes it impossible to define in one-line methods, even if the main code is short):</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">def</span> <span class="nf">empty?</span>
<span class="k">return</span> <span class="vi">@empty</span> <span class="k">if</span> <span class="k">defined?</span><span class="p">(</span><span class="vi">@empty</span><span class="p">)</span>
<span class="vi">@empty</span> <span class="o">=</span> <span class="n">tokens</span><span class="p">.</span><span class="nf">all?</span><span class="p">(</span><span class="o">&</span><span class="ss">:whitespace?</span><span class="p">)</span>
<span class="k">end</span>
</code></pre>
<p>About 4: while using this solution, we'll have a lot of instance vars (that are derivative and logically not the part of object's state) now visible in default <code>#inspect</code> and serialization:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">s</span> <span class="o">=</span> <span class="no">Sentence</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="s1">'Ruby is cool'</span><span class="p">)</span>
<span class="nb">p</span> <span class="n">s</span>
<span class="c1"># #<Sentence:0x00007fe21d8fd138 @text="Ruby is cool"></span>
<span class="nb">puts</span> <span class="n">s</span><span class="p">.</span><span class="nf">to_yaml</span>
<span class="c1"># --- !ruby/object:Sentence</span>
<span class="c1"># text: Ruby is cool</span>
<span class="nb">p</span> <span class="n">s</span><span class="p">.</span><span class="nf">empty?</span>
<span class="c1"># false</span>
<span class="nb">p</span> <span class="n">s</span>
<span class="c1"># #<Sentence:0x00007fe21d8fd138 @text="Ruby is cool", @empty=false></span>
<span class="nb">puts</span> <span class="n">s</span><span class="p">.</span><span class="nf">to_yaml</span>
<span class="c1"># --- !ruby/object:Sentence</span>
<span class="c1"># text: Ruby is cool</span>
<span class="c1"># empty: false</span>
</code></pre>
<a name="Existing-memoization-libraries"></a>
<h3 >Existing memoization libraries<a href="#Existing-memoization-libraries" class="wiki-anchor">¶</a></h3>
<p>There are several well-known memoization libraries out there, to name a couple: old and reliable <a href="https://github.com/matthewrudy/memoist" class="external">memoist</a>, new and efficient <a href="https://github.com/panorama-ed/memo_wise" class="external">memo_wise</a>.</p>
<p>They solve problems 1-3 of <code>||=</code>, and also add several cool features (like argument-dependent memoization) with a macro (this is <code>memo_wise</code>, <code>memoist</code> behaves the same, just different macro name):</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">Sentence</span>
<span class="n">prepend</span> <span class="no">MemoWise</span>
<span class="c1"># ...</span>
<span class="n">memo_wise</span> <span class="k">def</span> <span class="nf">empty?</span><span class="p">()</span> <span class="o">=</span> <span class="n">tokens</span><span class="p">.</span><span class="nf">all?</span><span class="p">(</span><span class="ss">:whitespace?</span><span class="p">)</span>
<span class="k">end</span>
</code></pre>
<p>Now we have a nice declarative and decoupled statement "it is memoized", which also supports booleans and <code>nil</code>s and multi-statement methods.</p>
<p>The problem of "detail leaking" isn't solved, though:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="nb">p</span> <span class="n">s</span><span class="p">.</span><span class="nf">empty?</span>
<span class="c1"># false</span>
<span class="nb">p</span> <span class="n">s</span>
<span class="c1"># #<Sentence:0x00007f0f474eb418 @_memo_wise={:empty?=>false}, @text="Ruby is cool"></span>
<span class="nb">puts</span> <span class="n">s</span><span class="p">.</span><span class="nf">to_yaml</span>
<span class="c1"># --- !ruby/object:Sentence</span>
<span class="c1"># _memo_wise:</span>
<span class="c1"># :empty?: false</span>
<span class="c1"># text: Ruby is cool</span>
</code></pre>
<p>Also, using third-party gems introduces a few new problems:</p>
<ol>
<li>Performance penalty. However well it is optimized, Ruby-land "redefine method, then check it is there, then calculate" has not zero overhead.</li>
<li>Dependency penalty. If the memoizing gem is not in the project yet, it is a decision whether to introduce it or not and for small no-dependencies gems or for the strictly-controlled codebase, it might be a problem. Also, doing <code>prepend MemoWise</code> (or <code>extend Memoist</code>) is another point where the question "should I introduce this dependency?" arises (in a small class with exactly one method to memoize, for example!)</li>
</ol>
<a name="Feature-proposal-amp-Design-decisions"></a>
<h2 >Feature proposal & Design decisions<a href="#Feature-proposal-amp-Design-decisions" class="wiki-anchor">¶</a></h2>
<p>I propose to introduce the <code>Module#memoized(*symbols)</code> method in the core, implemented in C.</p>
<ol>
<li>Name: <code>memoize</code> is a typical name that the community is used to. I want the new method to look uniform with other existing "macros" that have wording suitable for the phrase "this method is {word}": <code>private</code> or <code>module_function</code>; that's why I propose the name <code>memoized</code>
</li>
<li>I believe that the memoisation should be fully opaque: not visible on <code>#inspect</code> or serialization; no settings or API to interact with the internal state of memoization.</li>
<li>Only argument-less methods are memoizable, <code>memoize def foo(any, args)</code> should raise an exception</li>
<li>(Not sure about that one) "Memoised" state of the method should be inheritable. Probably we might need a symmetric <code>unmemoized :foo</code> to overwrite that in descendants.</li>
</ol>
<a name="Non-features"></a>
<h3 >Non-features<a href="#Non-features" class="wiki-anchor">¶</a></h3>
<p>There are several more features typically seen in memoization gems considered unsuitable for core functionality:</p>
<ul>
<li>No arguments-dependent memoization. I believe it is a "business logic" concern: how exactly the arguments should be stored, cache growth control (with too many argument-result pairs memoized), cache cleanup, etc. Third-party libraries can handle that.</li>
<li>No cache presetting/resetting API. If "it is memoized in general, but sometimes reset", it is again a business-layer concern and shouldn't be solved by a language-level declaration. Third-party libraries can handle that.</li>
<li>No extra API to memoize class methods, like we don't have a specific API for making class methods private.</li>
</ul> 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 - 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 - 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 - 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 #16822 (Rejected): Array slicing: nils and edge caseshttps://bugs.ruby-lang.org/issues/168222020-04-30T10:44:28Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>(First of all, I understand that the proposed change can break code, but I expect it not to be a large amount empirically.)</p>
<p>I propose that methods that slice an array (<code>#slice</code> and <code>#[]</code>) and return a sub-array in the normal case, should <strong>never</strong> return <code>nil</code>. E.g.,</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">ary</span> <span class="o">=</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>
</code></pre>
<ul>
<li>
<ol>
<li>Non-empty slice--how it works currently</li>
</ol>
</li>
</ul>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">a</span><span class="p">[</span><span class="mi">1</span><span class="o">..</span><span class="mi">2</span><span class="p">]</span> <span class="c1"># => [2, 3]</span>
<span class="n">a</span><span class="p">[</span><span class="mi">1</span><span class="o">...-</span><span class="mi">1</span><span class="p">]</span> <span class="c1"># => [2]</span>
</code></pre>
<ul>
<li>
<ol start="2">
<li>Empty slice--how it works currently</li>
</ol>
</li>
</ul>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">a</span><span class="p">[</span><span class="mi">1</span><span class="o">...</span><span class="mi">1</span><span class="p">]</span> <span class="c1"># => []</span>
<span class="n">a</span><span class="p">[</span><span class="mi">3</span><span class="o">...</span><span class="p">]</span> <span class="c1"># => []</span>
<span class="n">a</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="o">..-</span><span class="mi">2</span><span class="p">]</span> <span class="c1"># => [] </span>
</code></pre>
<ul>
<li>
<ol start="3">
<li>Sudden <code>nil</code>—<strong>what I am proposing to change</strong>
</li>
</ol>
</li>
</ul>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">a</span><span class="p">[</span><span class="mi">4</span><span class="o">..</span><span class="p">]</span> <span class="c1"># => nil </span>
<span class="n">a</span><span class="p">[</span><span class="o">-</span><span class="mi">10</span><span class="o">..-</span><span class="mi">9</span><span class="p">]</span> <span class="c1"># => nil </span>
</code></pre>
<p>I believe that it would be better because the method would have cleaner "type definition" (If there is nothing in the array at the requested address, you'll have an empty array).</p>
<p>Most of the time, the empty array doesn't require any special handling; thus, <code>ary[start...end].map { ... }</code> will behave as expected if the requested range is outside of the array boundary.</p>
<p>It is especially painful with off-by-one errors; for an array of three elements, if <code>ary[3...]</code> (just outside the boundary) is <code>[]</code> while <code>a[4...]</code> (one more step outside) is <code>nil</code>, it typically results in some nasty <code>NoMethodError for NilClass</code>.</p>
<p>A similar example is <code>ary[1..].reduce { }</code> (everything except the first element--probably the first element was used to construct the initial value for reducing) with <code>ary</code> being non-empty 99.9% of the times. Then you meet one of the 0.1% cases, and instead of no-op reducing nothing, <code>NoMethodError</code> is fired.</p> 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 - Feature #16494 (Rejected): Allow hash unpacking in non-lambda Prochttps://bugs.ruby-lang.org/issues/164942020-01-09T10:13:35Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>First of all, I fully understand the value of separating "real" keyword arguments and disallowing implicit and unexpected conversions to/from hashes.</p>
<p>There is, though, one <strong>convenient style which is now broken</strong>:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># words is array of hashes:</span>
<span class="n">words</span>
<span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">text</span><span class="p">:,</span> <span class="n">paragraph_id</span><span class="p">:,</span> <span class="o">**</span><span class="n">rest</span><span class="o">|</span>
<span class="p">{</span><span class="ss">text: </span><span class="n">text</span><span class="p">.</span><span class="nf">strip</span><span class="p">,</span> <span class="ss">paragraph_id: </span><span class="n">paragraph_id</span><span class="p">.</span><span class="nf">to_i</span><span class="p">,</span> <span class="o">**</span><span class="n">rest</span><span class="p">}</span>
<span class="p">}</span>
<span class="p">.</span><span class="nf">reject</span> <span class="p">{</span> <span class="o">|</span><span class="n">text</span><span class="p">:,</span> <span class="ss">is_punctuation: </span><span class="kp">false</span><span class="p">,</span> <span class="o">**|</span> <span class="n">text</span><span class="p">.</span><span class="nf">end_with?</span><span class="p">(</span><span class="s1">'!'</span><span class="p">)</span> <span class="o">||</span> <span class="n">is_punctuation</span> <span class="p">}</span>
<span class="p">.</span><span class="nf">chunk</span> <span class="p">{</span> <span class="o">|</span><span class="n">paragraph_id</span><span class="p">:,</span> <span class="ss">timestamp: </span><span class="mi">0</span><span class="p">,</span> <span class="o">**|</span> <span class="p">[</span><span class="n">paragraph_id</span><span class="p">,</span> <span class="n">timestamp</span> <span class="o">%</span> <span class="mi">60</span><span class="p">]</span> <span class="p">}</span>
<span class="c1"># ...and so on</span>
</code></pre>
<p>There is several important elements to this style, making it hard to replace:</p>
<ul>
<li>informative errors on unexpected data structure ("missing keyword: text")</li>
<li>ability to provide default values</li>
<li>clear separation of declaration "what this block expects" / "what it does with expected data", especially valuable in data processing pipelines</li>
</ul>
<p>One may argue that in some Big Hairy Very Architectured Application you should instead wrap everything in objects/extract every processing step into method or service/extract validation as a separate concern etc... But in smaller utility scripts, or deep inside of complicated algorithmic libraries, the ability to write short and clear code with explicitly declared and controlled by language arguments is pretty valuable.</p>
<p>This style has <em>no clean alternative</em>, all possible alternatives are either less powerful or much less readable. Compare:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># Try to rewrite this:</span>
<span class="n">words</span><span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">text</span><span class="p">:,</span> <span class="n">paragraph_id</span><span class="p">:,</span> <span class="ss">timestamp: </span><span class="mi">0</span><span class="p">,</span> <span class="ss">is_punctuation: </span><span class="kp">false</span><span class="o">|</span>
<span class="n">log</span><span class="p">.</span><span class="nf">info</span> <span class="s2">"Processing </span><span class="si">#{</span><span class="n">timestamp</span> <span class="o">/</span> <span class="mi">60</span><span class="si">}</span><span class="s2"> minute"</span>
<span class="n">full_text</span> <span class="o">=</span> <span class="n">is_punctiation</span> <span class="p">?</span> <span class="n">text</span> <span class="p">:</span> <span class="n">text</span> <span class="o">+</span> <span class="s1">' '</span>
<span class="s2">"<span class='word paragraph-</span><span class="si">#{</span><span class="n">paragraph_id</span><span class="si">}</span><span class="s2">' data-time=</span><span class="si">#{</span><span class="n">timestamp</span><span class="si">}</span><span class="s2"> data-original-text=</span><span class="si">#{</span><span class="n">text</span><span class="si">}</span><span class="s2">></span><span class="si">#{</span><span class="n">full_text</span><span class="si">}</span><span class="s2"></span>"</span>
<span class="p">}</span>
<span class="c1"># Alternative with just hashes:</span>
<span class="n">words</span><span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">word</span><span class="o">|</span>
<span class="c1"># those two used several times</span>
<span class="n">text</span> <span class="o">=</span> <span class="n">word</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="ss">:text</span><span class="p">)</span>
<span class="n">timestamp</span> <span class="o">=</span> <span class="n">word</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="ss">:timestamp</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">log</span><span class="p">.</span><span class="nf">info</span> <span class="s2">"Processing </span><span class="si">#{</span><span class="n">timestamp</span> <span class="o">/</span> <span class="mi">60</span><span class="si">}</span><span class="s2"> minute"</span>
<span class="c1"># Absent is_punctuation is ok, it default to false</span>
<span class="n">full_text</span> <span class="o">=</span> <span class="n">word</span><span class="p">[</span><span class="ss">:is_punctiation</span><span class="p">]</span> <span class="p">?</span> <span class="n">text</span> <span class="p">:</span> <span class="n">text</span> <span class="o">+</span> <span class="s1">' '</span>
<span class="s2">"<span class='word paragraph-</span><span class="si">#{</span><span class="n">word</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="ss">:paragraph_id</span><span class="p">)</span><span class="si">}</span><span class="s2">' data-time=</span><span class="si">#{</span><span class="n">timestamp</span><span class="si">}</span><span class="s2"> data-original-text=</span><span class="si">#{</span><span class="n">text</span><span class="si">}</span><span class="s2">></span><span class="si">#{</span><span class="n">full_text</span><span class="si">}</span><span class="s2"></span>"</span>
<span class="p">}</span>
<span class="c1"># Alternative with pattern-matching: to unpack variables, and handle default values, it will be something like...</span>
<span class="k">case</span> <span class="n">word</span>
<span class="k">in</span> <span class="n">text</span><span class="p">:,</span> <span class="n">paragraph_id</span><span class="p">:,</span> <span class="ss">timestamp:
</span><span class="c1"># skip, just unpacked</span>
<span class="k">in</span> <span class="n">text</span><span class="p">:,</span> <span class="ss">paragraph_id: </span><span class="c1"># no timestamp:</span>
<span class="n">timestamp</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">end</span>
<span class="c1"># I am even not trying to handle TWO default values</span>
</code></pre>
<p>As shown above, <code>Hash#fetch</code>/<code>Hash#[]</code> style makes it much harder to understand what block expects hash to have, and how it uses hash components — and just makes the code longer and less pleasant to write and read. Pattern-matching (at least for now) is just not powerful enough for this particular case (it also has non-informative error messages, but it obviously can be improved).</p>
<p>My <strong>proposal</strong> is to <strong>allow implicit hash unpacking</strong> into keyword arguments in <strong>non-lambda procs</strong>. It would be <strong>consistent</strong> with implicit array unpacking, which is an important property of non-lambda procs, useful for reasons <em>very similar to described above</em>.</p> 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 #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 - Feature #16441 (Rejected): Enumerable#take_while_afterhttps://bugs.ruby-lang.org/issues/164412019-12-21T13:22:15Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>The method is just like <code>#take_while</code>, but also includes the item where condition became true.</p>
<p>Examples of usefulness:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">str</span> <span class="o">=</span> <span class="o"><<</span><span class="no">DOC</span><span class="sh">
prologue
<<
1
2
3
>>
epilogue
</span><span class="no">DOC</span>
</code></pre>
<p>Imagine we want to take everything starting from <code><<</code> to <code>>></code> in short and clean Ruby. Surprisingly, our best guess would be infamous flip-flop:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">str</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="nf">filter_map</span> <span class="p">{</span> <span class="n">_1</span> <span class="k">if</span> <span class="n">_1</span> <span class="o">==</span> <span class="s1">'<<'</span><span class="o">..</span><span class="n">_1</span> <span class="o">==</span> <span class="s1">'>>'</span> <span class="p">}</span>
<span class="c1"># => ["<<", "1", "2", "3", ">>"]</span>
</code></pre>
<p>Trying to achieve this with <code>Enumerator</code>, you <em>almost</em> can express it, but the last line is lost:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">str</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="nf">drop_while</span> <span class="p">{</span> <span class="n">_1</span> <span class="o">!=</span> <span class="s1">'<<'</span> <span class="p">}.</span><span class="nf">take_while</span> <span class="p">{</span> <span class="n">_1</span> <span class="o">!=</span> <span class="s1">'>>'</span> <span class="p">}</span>
<span class="c1"># => ["<<", "1", "2", "3"]</span>
</code></pre>
<p>So, Enumerable leaves us with this (which is harder to read, due to additional <code>.first</code>):</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">str</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="nf">drop_while</span> <span class="p">{</span> <span class="n">_1</span> <span class="o">!=</span> <span class="s1">'<<'</span> <span class="p">}.</span><span class="nf">slice_after</span> <span class="p">{</span> <span class="n">_1</span> <span class="o">==</span> <span class="s1">'>>'</span> <span class="p">}.</span><span class="nf">first</span>
<span class="c1"># => ["<<", "1", "2", "3", ">>"]</span>
</code></pre>
<p>With proposed method:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">str</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="nf">drop_while</span> <span class="p">{</span> <span class="n">_1</span> <span class="o">!=</span> <span class="s1">'<<'</span> <span class="p">}.</span><span class="nf">take_while_after</span> <span class="p">{</span> <span class="n">_1</span> <span class="o">!=</span> <span class="s1">'>>'</span> <span class="p">}</span>
<span class="c1"># => ["<<", "1", "2", "3", ">>"]</span>
</code></pre>
<p>The idea is the same as with flip-flops <code>..</code> vs <code>...</code> (sometimes we need to include the last element matching the condition, sometimes don't), and <code>while ... end</code> vs <code>do ... while</code>. Another example (from <code>Enumerator.produce</code> <a href="https://bugs.ruby-lang.org/issues/14781" class="external">proposal</a>):</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><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="no">Enumerator</span><span class="p">.</span><span class="nf">produce</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">take_while</span> <span class="p">{</span> <span class="o">!</span><span class="n">scanner</span><span class="p">.</span><span class="nf">eos?</span> <span class="p">}</span>
<span class="c1"># => ["7", "+", "38", "/"]</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="no">Enumerator</span><span class="p">.</span><span class="nf">produce</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">take_while_after</span> <span class="p">{</span> <span class="o">!</span><span class="n">scanner</span><span class="p">.</span><span class="nf">eos?</span> <span class="p">}</span>
<span class="c1"># => ["7", "+", "38", "/", "6"]</span>
</code></pre>
<p>PS: Not sure about the name, suggestions are welcome</p> Ruby master - Feature #16435 (Rejected): Array#to_prochttps://bugs.ruby-lang.org/issues/164352019-12-19T16:01:38Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>The idea is obvious, but I couldn't find it discussed anywhere on tracker before. Please point me at the previous discussions if any.</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">to_proc</span>
<span class="nb">proc</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="p">.</span><span class="nf">dig</span><span class="p">(</span><span class="o">*</span><span class="nb">self</span><span class="p">)</span> <span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="c1"># Or, alternatively, see about alternatives at the end of proposal:</span>
<span class="k">class</span> <span class="nc">Array</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">v</span><span class="o">|</span> <span class="n">v</span><span class="p">[</span><span class="o">*</span><span class="nb">self</span><span class="p">]</span> <span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>The implementation seems to provide clean and unambiguous collections indexing in Enumerators:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># Basic objects data, which could be obtained from JSON, CSV, Database...</span>
<span class="n">data</span> <span class="o">=</span> <span class="p">[</span>
<span class="p">{</span><span class="ss">name: </span><span class="s1">'John'</span><span class="p">,</span> <span class="ss">department: </span><span class="p">{</span><span class="ss">id: </span><span class="mi">1</span><span class="p">,</span> <span class="ss">title: </span><span class="s1">'Engineering'</span><span class="p">},</span> <span class="ss">salary: </span><span class="mi">1000</span><span class="p">},</span>
<span class="p">{</span><span class="ss">name: </span><span class="s1">'Jane'</span><span class="p">,</span> <span class="ss">department: </span><span class="p">{</span><span class="ss">id: </span><span class="mi">1</span><span class="p">,</span> <span class="ss">title: </span><span class="s1">'Engineering'</span><span class="p">},</span> <span class="ss">salary: </span><span class="mi">1200</span><span class="p">},</span>
<span class="p">{</span><span class="ss">name: </span><span class="s1">'Boris'</span><span class="p">,</span> <span class="ss">department: </span><span class="p">{</span><span class="ss">id: </span><span class="mi">2</span><span class="p">,</span> <span class="ss">title: </span><span class="s1">'Accounting'</span><span class="p">},</span> <span class="ss">salary: </span><span class="mi">800</span><span class="p">},</span>
<span class="p">{</span><span class="ss">name: </span><span class="s1">'Alice'</span><span class="p">,</span> <span class="ss">department: </span><span class="p">{</span><span class="ss">id: </span><span class="mi">3</span><span class="p">,</span> <span class="ss">title: </span><span class="s1">'Management'</span><span class="p">},</span> <span class="ss">salary: </span><span class="mi">1500</span><span class="p">}</span>
<span class="p">]</span>
<span class="n">data</span><span class="p">.</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="p">[</span><span class="ss">:name</span><span class="p">])</span>
<span class="c1"># => ["John", "Jane", "Boris", "Alice"] </span>
<span class="n">data</span><span class="p">.</span><span class="nf">min_by</span><span class="p">(</span><span class="o">&</span><span class="p">[</span><span class="ss">:salary</span><span class="p">])</span>
<span class="c1"># => {:name=>"Boris", :department=>{:id=>2, :title=>"Accounting"}, :salary=>800} </span>
<span class="n">pp</span> <span class="n">data</span><span class="p">.</span><span class="nf">group_by</span><span class="p">(</span><span class="o">&</span><span class="p">[</span><span class="ss">:department</span><span class="p">,</span> <span class="ss">:title</span><span class="p">])</span>
<span class="c1"># {"Engineering"=></span>
<span class="c1"># [{:name=>"John",</span>
<span class="c1"># :department=>{:id=>1, :title=>"Engineering"},</span>
<span class="c1"># :salary=>1000},</span>
<span class="c1"># {:name=>"Jane",</span>
<span class="c1"># :department=>{:id=>1, :title=>"Engineering"},</span>
<span class="c1"># :salary=>1200}],</span>
<span class="c1"># "Accounting"=></span>
<span class="c1"># [{:name=>"Boris",</span>
<span class="c1"># :department=>{:id=>2, :title=>"Accounting"},</span>
<span class="c1"># :salary=>800}],</span>
<span class="c1"># "Management"=></span>
<span class="c1"># [{:name=>"Alice",</span>
<span class="c1"># :department=>{:id=>3, :title=>"Management"},</span>
<span class="c1"># :salary=>1500}]}</span>
<span class="c1"># Works with arrays, too:</span>
<span class="n">data</span><span class="p">.</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="ss">:values</span><span class="p">).</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
<span class="c1"># => ["John", "Jane", "Boris", "Alice"]</span>
<span class="c1"># And with mixes:</span>
<span class="n">data</span><span class="p">.</span><span class="nf">group_by</span><span class="p">(</span><span class="o">&</span><span class="p">[</span><span class="ss">:department</span><span class="p">,</span> <span class="ss">:title</span><span class="p">]).</span><span class="nf">values</span><span class="p">.</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="ss">:name</span><span class="p">])</span>
<span class="c1"># => ["John", "Boris", "Alice"]</span>
</code></pre>
<p>Naked structured data seems to be a common enough thing to make working with them easier.</p>
<p>Some prior info:</p>
<ul>
<li>Googling it around, I found the idea was first invented <a href="https://thepugautomatic.com/2014/11/array-to-proc-for-hash-access/" class="external">back in 2014</a>, and another one <a href="https://gist.github.com/geowy/39fde25ec2966f90a54b" class="external">in 2015</a>, not sure if it was proposed on the tracker.</li>
<li>Other proposals for <code>Array#to_proc</code> was: to call several methods in sequence <a href="http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/199820" class="external">1</a>, <a href="https://rails.lighthouseapp.com/projects/8994/tickets/1253-arrayto_proc" class="external">2</a>, and to call method with argument <a href="https://www.sanityinc.com/articles/adding-array-to-proc-to-ruby/" class="external">1</a>, <a href="https://bugs.ruby-lang.org/issues/10829" class="external">2</a>, <a href="https://www.rubydoc.info/github/estum/console_utils/Array:to_proc" class="external">3</a>, to call several methods in parallel: <a href="https://gist.github.com/shell/1120249" class="external">1</a>
</li>
</ul>
<p>Honestly, I feel that proposed usage is the most frequently needed.</p>
<p>Also, the readability of the version seems more or less straightforward:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># Existing shortcut, for example:</span>
<span class="n">data</span><span class="p">.</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="ss">:keys</span><span class="p">)</span>
<span class="c1"># Is equivalent to</span>
<span class="n">data</span><span class="p">.</span><span class="nf">map</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">keys</span> <span class="p">}</span>
<span class="c1"># ^^^^^ -- "just remove this part"</span>
<span class="c1"># Proposed shortcut:</span>
<span class="n">data</span><span class="p">.</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="p">[</span><span class="ss">:name</span><span class="p">])</span>
<span class="c1"># Is equivalent to</span>
<span class="n">data</span><span class="p">.</span><span class="nf">map</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="ss">:name</span><span class="p">]</span> <span class="p">}</span>
<span class="c1"># ^^^^^ -- "just remove this part"</span>
</code></pre>
<p><strong><code>dig</code> or <code>[]</code> alternative implementations</strong></p>
<p>It is up to discussion (if the whole idea holds water) whether <code>dig</code> should be used or just <code>[]</code>. The <code>dig</code> version is convenient for nested structures but slightly breaks "equivalency" shown above, and just <code>[]</code> version will allow this:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">data</span><span class="p">.</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="ss">:values</span><span class="p">).</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="p">[</span><span class="mi">1</span><span class="o">..-</span><span class="mi">1</span><span class="p">])</span>
<span class="c1"># => [[{:id=>1, :title=>"Engineering"}, 1000], [{:id=>1, :title=>"Engineering"}, 1200], [{:id=>2, :title=>"Accounting"}, 800], [{:id=>3, :title=>"Management"}, 1500]]</span>
</code></pre>
<p>Maybe, for the sake of explainability, "just <code>[]</code>" should be preferred, with digging performed by other means.</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 #16261 (Rejected): Enumerable#each_splat and Enumerator#splathttps://bugs.ruby-lang.org/issues/162612019-10-18T10:11:49Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p><strong>UPD:</strong> After discussion in comments, method names changed to "splat"-based.</p>
<p>New methods proposal.</p>
<p>Prototype code:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">module</span> <span class="nn">Enumerable</span>
<span class="k">def</span> <span class="nf">each_splat</span>
<span class="k">return</span> <span class="n">to_enum</span><span class="p">(</span><span class="n">__method__</span><span class="p">)</span> <span class="k">unless</span> <span class="nb">block_given?</span>
<span class="n">each_entry</span> <span class="p">{</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span> <span class="k">yield</span><span class="p">(</span><span class="o">*</span><span class="n">item</span><span class="p">)</span> <span class="p">}</span> <span class="c1"># unpacking possible array into several args</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">class</span> <span class="nc">Enumerator</span>
<span class="k">def</span> <span class="nf">splat</span>
<span class="k">return</span> <span class="n">to_enum</span><span class="p">(</span><span class="ss">:splat</span><span class="p">)</span> <span class="k">unless</span> <span class="nb">block_given?</span>
<span class="n">each_entry</span> <span class="p">{</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span> <span class="k">yield</span><span class="p">(</span><span class="o">*</span><span class="n">item</span><span class="p">)</span> <span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>Supposed documentation/explanation:</p>
<blockquote>
<p>For enumerable with Array items, passes all items in the block provided as a separate arguments. t could be useful if the provided block has lambda semantics, e.g. doesn't unpack arguments automatically. For example:</p>
</blockquote>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">files</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"README.md"</span><span class="p">,</span> <span class="s2">"LICENSE.txt"</span><span class="p">,</span> <span class="s2">"Contributing.md"</span><span class="p">]</span>
<span class="n">content</span> <span class="o">=</span> <span class="p">[</span><span class="n">fetch_readme</span><span class="p">,</span> <span class="n">fetch_license</span><span class="p">,</span> <span class="n">fetch_contributing</span><span class="p">]</span> <span class="c1"># somehow make a content for the files</span>
<span class="n">files</span><span class="p">.</span><span class="nf">zip</span><span class="p">(</span><span class="n">content</span><span class="p">).</span><span class="nf">each_splat</span><span class="p">(</span><span class="o">&</span><span class="no">File</span><span class="o">.</span><span class="ss">:write</span><span class="p">)</span> <span class="c1"># writes to each file its content</span>
</code></pre>
<blockquote>
<p>When no block passed, returns enumerator of the tuples:</p>
</blockquote>
<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">zip</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="mi">6</span><span class="p">]).</span><span class="nf">each_splat</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"># => [5, 7, 9] </span>
</code></pre> 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 - 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 - Misc #15486 (Feedback): Default gems README.mdhttps://bugs.ruby-lang.org/issues/154862018-12-29T12:12:34Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>While working on <a href="https://bugs.ruby-lang.org/issues/15485" class="external">Ruby Changelog</a>, I noticed the following.</p>
<p>A lot of parts of stdlib is extracted currently into "default gems". This, in my understanding, means (amongst other things) their development is now in separate repositories on GitHub, and their development is semi-independent.</p>
<p>The problem I'd like to emphasize is <strong>their README is unclear</strong> about "what is it". Let's look at ostruct for example: <a href="https://github.com/ruby/ostruct" class="external">https://github.com/ruby/ostruct</a>. There are two huge problems:</p>
<ul>
<li>Stumbling upon this repo, how should one know it is a) a part of Ruby stdlib? and b) the authoritative source of this part (and not a mirror of the code in ruby/ruby repo)?</li>
<li>There is some basic documentation explaining the usage of the library, but it would NOT be rendered anywhere in the standard library docs, so it is basically useless (which is not obvious for repo contributors).</li>
</ul>
<p>I believe that for standard library gems the README should look somehow as following:</p>
<blockquote>
<p>This is the development repository of Ruby <code>ostruct</code> (<code>OpenStruct</code>) standard library.</p>
<p>The library provides an <code>OpenStruct</code> data structure, similar to a <code>Hash</code>, that allows the definition of arbitrary attributes with their accompanying values.</p>
<p>Canonical library docs: <a href="https://ruby-doc.org/stdlib-2.6/libdoc/ostruct/rdoc/OpenStruct.html" class="external">OpenStruct</a></p>
<p>Before participating in the development of <code>ostruct</code>, you should know the following:</p>
<ul>
<li>The development process is standard "fork => commit => pull request"</li>
<li>New versions of the standard library are released with new versions of Ruby</li>
<li>Versioning policy: ...</li>
<li>Code quality policy: ...</li>
</ul>
</blockquote>
<p>The last two points should probably link to the common documentation for all "default gems"... Well, as well as the whole text. So, all in all, there should be README template, where only gem names, short descriptions, and links to "canonical docs" are different (and maybe some code structure/contribution details for bigger libraries).</p>
<p>WDYT?</p> Ruby master - Feature #15485 (Rejected): Refactor String#splithttps://bugs.ruby-lang.org/issues/154852018-12-29T11:48:05Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>In <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: String#split with a block (Closed)" href="https://bugs.ruby-lang.org/issues/4780">#4780</a>, new "block form" of <code>#split</code> was introduced. It behaves this way:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="s2">"several</span><span class="se">\n</span><span class="s2">long</span><span class="se">\n</span><span class="s2">lines"</span><span class="p">.</span><span class="nf">split</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="o">|</span><span class="n">part</span><span class="o">|</span> <span class="nb">puts</span> <span class="n">part</span> <span class="k">if</span> <span class="n">part</span><span class="p">.</span><span class="nf">start_with?</span><span class="p">(</span><span class="s1">'l'</span><span class="p">)</span> <span class="p">}</span>
<span class="c1"># prints:</span>
<span class="c1"># long</span>
<span class="c1"># lines</span>
<span class="c1"># => "several\nlong\nlines"</span>
</code></pre>
<p>Justification is stated as: "If the string is very long, and I only need to play with the split string one by one, this will not create a useless expensive array."</p>
<p>I understand the justification, but strongly believe that <strong>implementation is unfortunate</strong>. In the current implementation, the only way to "play with the split string one by one" is side-effect-full, like this:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">result</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">lines</span><span class="p">.</span><span class="nf">split</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="o">|</span><span class="n">ln</span><span class="o">|</span> <span class="n">result</span> <span class="o"><<</span> <span class="n">ln</span> <span class="k">if</span> <span class="n">ln</span><span class="p">.</span><span class="nf">match?</span><span class="p">(</span><span class="no">PATTERN</span><span class="p">)</span> <span class="p">}</span>
</code></pre>
<p>This is very unidiomatic and unlike most of other methods that accept both block and no-block forms (it is understandable as original ticket is 7 years old, community practices were pretty different back then).</p>
<p>Our typical modern solution of the same problem is <strong>enumerators</strong>.</p>
<p>I propose redefining method as following:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">lines</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span> <span class="c1"># => Array, calculated immediately</span>
<span class="n">lines</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="p">,</span> <span class="ss">enumerator: </span><span class="kp">true</span><span class="p">)</span> <span class="c1"># => Enumerator, yielding split results one by one</span>
</code></pre>
<p>It will allow all kind of idiomatic processing without any intermediate Array creation, like:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">lines</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="p">,</span> <span class="ss">enumerator: </span><span class="kp">true</span><span class="p">).</span><span class="nf">take_while</span> <span class="p">{</span> <span class="o">|</span><span class="n">ln</span><span class="o">|</span> <span class="n">ln</span> <span class="o">==</span> <span class="s1">'__END__'</span> <span class="p">}</span>
<span class="n">lines</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="p">,</span> <span class="ss">enumerator: </span><span class="kp">true</span><span class="p">).</span><span class="nf">grep</span><span class="p">(</span><span class="no">PATTERN</span><span class="p">)</span>
<span class="c1"># ...and so on...</span>
</code></pre>
<p>One more thing to note, that this call-sequence underlines "just an optimization" nature of the change: When you have "too large string" to process, you just add <code>enumerator: true</code> to your code without changing anything else.</p>
<p>PS: We can't change <code>split</code> to return enumerator <strong>always</strong>, because it would break a lot of sane code like <code>lines.split("\n").join("\r\n")</code></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 #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 #15428 (Feedback): Refactor Proc#>> and #<<https://bugs.ruby-lang.org/issues/154282018-12-17T21:56:35Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p><a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Add composition for procs (Closed)" href="https://bugs.ruby-lang.org/issues/6284">#6284</a> introduced <code>Proc#>></code> and <code>Proc#<<</code>, but the requirements to the argument is totally inconsistent with ANY other place in Ruby.</p>
<p>Currently, it is the <strong>only</strong> place in Ruby where coercing argument to <code>Proc</code> is done with <code>#call</code> method. Everywhere else it is done with <code>#to_proc</code>, and <code>#call</code> method never had any special significance except for <code>.()</code> sugar. I believe there are two possible actions:</p>
<ol>
<li>change <code>#>></code> and <code>#<<</code> to use <code>#to_proc</code> (which will give Symbols composability for free), <strong>or, alternatively</strong>
</li>
<li>state that <code>#call</code> from now on has a special meaning in Ruby and probably decide on other APIs that should respect it (for example, auto-define <code>#to_proc</code> on any object that has <code>#call</code>)</li>
</ol>
<p>Either is OK, the current situation is not.</p>
<p>PS: One more problem (that probably should be discussed separately) is that check for <code>#call</code> existence is performed pretty late, which can lead to this kind of errors:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># At code loading time:</span>
<span class="c1"># I erroneously thought this is correct. It is not, but the line would perform without</span>
<span class="c1"># any error.</span>
<span class="no">PROCESSOR</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="o">>></span> <span class="ss">:symbolize_keys</span>
<span class="c1"># Later, in runtime:</span>
<span class="s1">'{"foo": "bar"}'</span><span class="p">.</span><span class="nf">then</span><span class="p">(</span><span class="o">&</span><span class="no">PROCESSOR</span><span class="p">)</span>
<span class="c1"># NoMethodError (undefined method `call' for :symbolize_keys:Symbol)</span>
</code></pre>
<p><strong>UPD 2018-12-29:</strong> As this ticket was ignored prior to 2.6 release, I rewrote it in an "actionable" instead of "question" manner.</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 #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 - 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 #12361 (Rejected): Proposal: add Geo::Coord class to standard libraryhttps://bugs.ruby-lang.org/issues/123612016-05-09T17:26:55Zzverok (Victor Shepelev)zverok.offline@gmail.com
<a name="Proposal"></a>
<h2 >Proposal<a href="#Proposal" class="wiki-anchor">¶</a></h2>
<p>Add <code>Geo::Coord</code> class to Ruby standard library, representing <code>[latitude, longitude]</code> pair + convenience methods. Add <code>Geo</code> standard library with additional calculations and convenience methods.</p>
<a name="Rationale"></a>
<h2 >Rationale<a href="#Rationale" class="wiki-anchor">¶</a></h2>
<p>In modern applications, working with geographical coordinates is frequent. We propose to think of such coordinates (namely, <code>latitude, longitude</code> pair) as of "basic" type that should be supported by standard library - the same way as we support <code>Time</code>/<code>Date</code>/<code>DateTime</code> instead of having it defined<br>
by user/gems.</p>
<p>This type is too "small" to be defined by separate gem, so, all of existing geo gems (GeoKit, RGeo, GeoRuby, Graticule etc.) define their own <code>LatLng</code>, or <code>Location</code>, or <code>Point</code>, whatever.</p>
<p>On other hand, API design for this "small" type is vague enough for all those similar types to be incompatible and break small habits and conventions when you change from one geo library to another, or try to use several<br>
simultaneously.</p>
<p>Additionaly, many gems somehow working with geo coordinates (for weather, or timezone, or another tasks) generally prefer not to introduce type, and just work with <code>[lat, lng]</code> array, which is not very convenient, faithfully.</p>
<p>So, having "geo coordinates" functionality in standard library seems reasonable and valuable.</p>
<a name="Existingreference-solutions"></a>
<h2 >Existing/reference solutions<a href="#Existingreference-solutions" class="wiki-anchor">¶</a></h2>
<p>Ruby:</p>
<ul>
<li>
<a href="http://www.rubydoc.info/github/geokit/geokit/master/Geokit/LatLng" class="external">GeoKit::LatLng</a>;</li>
<li>
<a href="http://www.rubydoc.info/gems/rgeo/RGeo/Feature/Point" class="external">RGeo::Feature::Point</a> (with several "private" implementation classes); RGeo implements full <a href="https://en.wikipedia.org/wiki/Simple_Features" class="external">OGC Simple Features</a> specification, so, its points have <code>z</code> and <code>m</code> coordinates, projection and much more;</li>
<li>
<a href="http://www.rubydoc.info/github/collectiveidea/graticule/Graticule/Location" class="external">Graticule::Location</a> (not strictly a <code>[lat,lng]</code> wrapper);</li>
<li>
<a href="http://www.rubydoc.info/gems/rosemary/0.4.4/Rosemary/Node" class="external">Rosamary::Node</a> (follows naming convention of underlying OpenStreetMap API);</li>
</ul>
<p>Other sources:</p>
<ul>
<li>Python: <a href="http://geopy.readthedocs.org/en/latest/#geopy.point.Point" class="external">geopy.Point</a>;</li>
<li>
<a href="https://www.elastic.co/blog/geo-location-and-search" class="external">ElasticSearch</a> uses hash with "lat" and "lon" keys;</li>
<li>Google Maps <a href="https://developers.google.com/maps/documentation/geocoding/intro#GeocodingResponses" class="external">Geocoding API</a> uses hash with "lat" and "lng" keys;</li>
<li>PostGIS: <a href="http://postgis.net/docs/manual-2.2/using_postgis_dbmanagement.html#RefObject" class="external">pretty complicated</a> has <em>geometrical</em> (projected) and <em>geographical</em> (lat, lng) points and stuff.</li>
</ul>
<a name="Design-decisions"></a>
<h2 >Design decisions<a href="#Design-decisions" class="wiki-anchor">¶</a></h2>
<p>While designing <code>Geo</code> library, our reference point was standard <code>Time</code> class (and, to lesser extent, <code>Date</code>/<code>DateTime</code>). It has this responsibilities:</p>
<ul>
<li>stores data in simple internal form;</li>
<li>helps to parse and format data to and from strings;</li>
<li>provides easy access to logical components of data;</li>
<li>allows most simple and unambiguous calculations.</li>
</ul>
<p><strong>Main type name</strong>: as far as we can see, there's no good singular name for <code>(lat, lng)</code> pair concept. As mentioned above, there can be seen names like <code>LatLng</code>, or <code>Location</code>, or <code>Point</code>; and in natural language<br>
just "coordinates" used frequently. We propose the name <code>Coord</code>, which is pretty short, easy to remember, demonstrates intentions (and looks like singular, so you can have "one coord object" and "array of coords",<br>
which is not 100% linguistically correct, yet convenient). Alternative <code>Point</code> name seems to be too ambigous, being used in many contexts.</p>
<p><code>Geo::Coord</code> object is <strong>immutable</strong>, there's no semantical sense in <code>location.latitude = ...</code> or something like this.</p>
<p><strong>Units</strong>: <code>Geo</code> calculations (just like <code>Time</code> calculations) provide no units options, just returning numbers measured in "default" units: metres for distances (as they are SI unit) and degrees for azimuth. Latitude and longitude are stored in degrees, but radians values accessors are provided (being widely used in geodesy math).</p>
<p>All coordinates and calculations are thought to be in WGS 84 coordinates reference system, being current standard for maps and GPS.</p>
<p>There's introduced <strong>concept of globe</strong> used internally for calculations. Only generic (sphere) and Earth globes are implemented, but for 2016 we feel like current design of basic types should take in consideration<br>
possibility of writing Ruby scripts for Mars maps analysis. Only one geodesy formula is implemented (Vincenty, generally considered one of the most precise), as for standard library class it considered unnecessary to provide user with geodesy formulae options.</p>
<p>No <strong>map projection</strong> math was added into current proposal, but it may be a good direction for further work. No <strong>elevation</strong> data considered either.</p>
<a name="Proposal-details"></a>
<h2 >Proposal details<a href="#Proposal-details" class="wiki-anchor">¶</a></h2>
<h3>
<code>Geo::Coord</code> class</h3>
<p>Represents <code>[latitude, longitude]</code> pair. Latitude is -90 to +90 (degrees). Longitude is -180 to +180.</p>
<p>Class methods:</p>
<ul>
<li>
<code>new(lat, lng)</code> creates instance from two Numerics (in degrees);</li>
<li>
<code>new(lat:, lng:)</code> keyword arguments form of above;</li>
<li>
<code>new(latd:, latm:, lats:, lath:, lngd: lngm:, lngs: lngh:)</code> creates instance from coordinates in (deg, min, sec, hemisphere) form; hemispheres are "N"/"S" for latitude and "W"/E" for longitude; any component except for degrees can be omitted; if hemisphere is omitted, it is decided by degrees sign (lat: positive is "N", lng: positive is "E");</li>
<li>
<code>from_h(hash)</code> creates instance from hash with <code>"lat"</code> or <code>"latitude"</code> key and <code>"lon"</code> or <code>"lng"</code> or <code>"longitude"</code> key (case-independent);</li>
<li>
<code>from_radians(phi, la)</code> creates instance from radian values;</li>
<li>
<code>strpcoord</code> parses string into coordinates by provided pattern (see below for pattern description);</li>
<li>
<code>parse_ll</code> parses coordinates string in <code>"float, float"</code> form;</li>
<li>
<code>parse_dms</code> parses coordinates string in <code>d m s h, d m s h</code> format (considering several widely used symbols for degrees, minutes and seconds);</li>
<li>
<code>parse</code> tries to parse string into coordinates from various formats.</li>
</ul>
<p>Instance methods:</p>
<ul>
<li>
<code>lat</code> and <code>lng</code>, returning <code>Float</code>s, signed;</li>
<li>
<code>latitude</code> and <code>longitude</code> as an aliases; <code>lon</code> as an additional aliases for longitude;</li>
<li>
<code>latd</code>, <code>latm</code>, <code>lats</code>, <code>lath</code>: degree, minute, second, hemisphere; <code>latd</code> and <code>latm</code> are <code>Fixnum</code>, <code>lats</code> is <code>Float</code>, <code>lath</code> is "N"/"S"; all numbers are unsigned;</li>
<li>
<code>lngd</code>, <code>lngm</code>, <code>lngs</code>, <code>lngh</code>: the same for longitude (hemisphere is "W"/"E");</li>
<li>
<code>latdms(nohemisphere = false)</code> returns <code>[latd, latm, lats, lath]</code> with <code>nohemisphere</code> param equal to <code>false</code>, and <code>[±latd, latm, lats]</code> with <code>true</code>; same with <code>lngdms</code> for longitude;</li>
<li>
<code>phi</code> and <code>φ</code> is latitude in radians (helpful for math), <code>la</code> or <code>λ</code> is longitude in radians (not <code>lambda</code> to not confuse with Kernel method);</li>
<li>
<code>to_s</code> returning string like "50.004444,36.231389" (good for map URLs construction, for example);</li>
<li>
<code>to_h(lat: :lat, lng: :lng)</code> converts coord to hash (with desired key names);</li>
<li>
<code>to_a</code> converts coord to simple <code>[lat, lng]</code> pair;</li>
<li>
<code>strfcoord(formatstr)</code> for complex coordinate formatting (see below for format string description);</li>
<li>
<code>distance(other)</code> calculates distance to another point (in metres);</li>
<li>
<code>azimuth(other)</code> calculates direction to target (in degrees);</li>
<li>
<code>endpoint(direction, azimuth)</code> calculates final point of the line of <code>distance</code> metres going in <code>azimuth</code> direction from current point.</li>
</ul>
<h4>
<code>strpcoord</code>/<code>strfcoord</code>
</h4>
<p>Example:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">kharkiv</span><span class="p">.</span><span class="nf">strfcoord</span><span class="p">(</span><span class="s1">'%latdu°%latm′%lats″ %lath, %lngdu°%lngm′%lngs″ %lngh'</span><span class="p">)</span>
<span class="c1"># => "50°0′16″ N, 36°13′53″ E"</span>
</code></pre>
<p>Directives:</p>
<ul>
<li>
<code>%lat</code> - full latitude, float; can be formatted with more control like <code>%.4lat</code> (four digits after point) or <code>%+lat</code> (explicit plus sign for positive latitudes);</li>
<li>
<code>%latd</code> - latitude degrees, unsigned, integer</li>
<li>
<code>%latds</code> - latitude degrees, signed</li>
<li>
<code>%latm</code> - latitude minutes, unsigned, integer</li>
<li>
<code>%lats</code> - latitude seconds, unsigned, integer, but can be formatted as float: <code>%.2lats</code>
</li>
<li>
<code>%lath</code> - latitude hemisphere, one letter ("N"/"S")</li>
<li>
<code>%lng</code>, <code>%lngd</code>, <code>%lngds</code>, <code>%lngs</code>, <code>%lngh</code>, <code>%lngH</code> - same for longitude</li>
<li>
<code>%%</code> literal <code>%</code> sign</li>
</ul>
<a name="Current-implementation"></a>
<h3 >Current implementation<a href="#Current-implementation" class="wiki-anchor">¶</a></h3>
<p>Proposed implementation can be found at <a href="https://github.com/zverok/geo_coord" class="external">https://github.com/zverok/geo_coord</a>.</p>
<p>It was created with thoughts of standard library, so, all docs are in RDoc format, and tests/specs are in mspec-compatible rspec flavour.</p> Ruby master - Bug #12027 (Rejected): Hash descendants are ignoring custom to_hash methodhttps://bugs.ruby-lang.org/issues/120272016-01-27T00:20:38Zzverok (Victor Shepelev)zverok.offline@gmail.com
<p>I'm not sure it is not intended, but feels really strange:</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">to_hash</span>
<span class="p">{</span><span class="ss">foo: </span><span class="s1">'bar'</span><span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">class</span> <span class="nc">B</span> <span class="o"><</span> <span class="no">Hash</span>
<span class="k">def</span> <span class="nf">to_hash</span>
<span class="p">{</span><span class="ss">foo: </span><span class="s1">'bar'</span><span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">a</span> <span class="o">=</span> <span class="no">A</span><span class="p">.</span><span class="nf">new</span>
<span class="n">b</span> <span class="o">=</span> <span class="no">B</span><span class="p">.</span><span class="nf">new</span>
<span class="nb">p</span><span class="p">({</span><span class="o">**</span><span class="n">a</span><span class="p">})</span> <span class="c1"># => {foo: 'bar'}</span>
<span class="nb">p</span><span class="p">({</span><span class="o">**</span><span class="n">b</span><span class="p">})</span> <span class="c1"># => {} -- ooops. Seems "internal" hash state is used</span>
<span class="c1"># therefore:</span>
<span class="n">b</span><span class="p">[</span><span class="s1">'test'</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span>
<span class="nb">p</span><span class="p">({</span><span class="o">**</span><span class="n">b</span><span class="p">})</span> <span class="c1"># => wrong argument type String (expected Symbol) (TypeError)</span>
</code></pre>
<p>What am I missing here?</p> 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>