<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>zhengpd (Zheng Piaodan)</title>
    <link>https://ruby-china.org/zhengpd</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>启用 chrome custom doh 打不开 ruby china</title>
      <description>&lt;p&gt;如图，Chrome 如果启用 Secure DNS 和用自定义的 doh，就打不开 ruby-china.org，不知道其他人有没有这个问题&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/zhengpd/65e6246a-dab0-418c-96c3-1880944acad0.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/zhengpd/478b284f-94a8-406e-b7ea-987eac50e7e7.png!large" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>zhengpd</author>
      <pubDate>Fri, 24 Dec 2021 19:34:27 +0800</pubDate>
      <link>https://ruby-china.org/topics/42023</link>
      <guid>https://ruby-china.org/topics/42023</guid>
    </item>
    <item>
      <title>tmux + nvim 的 pane/window 快速切换</title>
      <description>&lt;h2 id="tmux + nvim 的 pane/window 快速切换"&gt;tmux + nvim 的 pane/window 快速切换&lt;/h2&gt;&lt;h2 id="缘由"&gt;缘由&lt;/h2&gt;
&lt;p&gt;13 寸的屏幕有点小，所以 tmux 一般是多用 window 少用 pane，至少一个放 neovim 一个跑 zsh，来回切 window 多了觉得 &lt;code&gt;&amp;lt;prefix&amp;gt; l&lt;/code&gt; 或者 &lt;code&gt;&amp;lt;prefix&amp;gt; h&lt;/code&gt;这样的组合键还是有点繁琐。之前一直是用 vim-tmux-navigator 做 tmux pane 之间的切换，于是就想着能不能把 &lt;code&gt;&amp;lt;C-l&amp;gt;&lt;/code&gt; 也绑到 tmux window 切换上去。由于 vim-tmux-navigator 不支持 tmux window，只能自己搞了个配置，分享给有类似需求的朋友看下。&lt;/p&gt;

&lt;p&gt;逻辑比较简单，以 &lt;code&gt;&amp;lt;C-l&amp;gt;&lt;/code&gt; 为例：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;当前为 nvim 左边 window&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;C-l&amp;gt;&lt;/code&gt; 切到 nvim 右边 window&lt;/li&gt;
&lt;li&gt;再 &lt;code&gt;&amp;lt;C-l&amp;gt;&lt;/code&gt; 一下切到右边的 tmux pane&lt;/li&gt;
&lt;li&gt;再 &lt;code&gt;&amp;lt;C-l&amp;gt;&lt;/code&gt; 一下切到下一个 tmux window&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="配置"&gt;配置&lt;/h2&gt;
&lt;p&gt;vim conf:&lt;/p&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="c"&gt;" ~/.vimrc&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt; TmuxWinJump&lt;span class="p"&gt;(&lt;/span&gt;direction&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; direction_edges &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="se"&gt;        \&lt;/span&gt; &lt;span class="s1"&gt;'h'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'pane_at_left'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="se"&gt;        \&lt;/span&gt; &lt;span class="s1"&gt;'j'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'pane_at_bottom'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="se"&gt;        \&lt;/span&gt; &lt;span class="s1"&gt;'k'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'pane_at_top'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="se"&gt;        \&lt;/span&gt; &lt;span class="s1"&gt;'l'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'pane_at_right'&lt;/span&gt;
&lt;span class="se"&gt;        \&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;let&lt;/span&gt; edge_cmd &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'tmux display-message -p "#{'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;direction_edges&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;a:direction&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="s1"&gt;'}"'&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;edge_cmd&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; tmux_direction &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'h'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'j'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'+'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'k'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'l'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'+'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;call&lt;/span&gt; &lt;span class="nb"&gt;system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'tmux select-window -t :'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;tmux_direction&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;a:direction&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; pane_direction &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;a:direction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'hjkl'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'LDUR'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;call&lt;/span&gt; &lt;span class="nb"&gt;system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'tmux select-pane -'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;pane_direction&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;endif&lt;/span&gt;
&lt;span class="k"&gt;endfunction&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt; VimWinJump&lt;span class="p"&gt;(&lt;/span&gt;direction&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; nr &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;winnr&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  exec &lt;span class="s1"&gt;'wincmd '&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;a:direction&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;winnr&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; nr &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="k"&gt;call&lt;/span&gt; TmuxWinJump&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;a:direction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="k"&gt;endif&lt;/span&gt;
&lt;span class="k"&gt;endfunction&lt;/span&gt;

nnoremap &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;silent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;C&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="k"&gt;h&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="k"&gt;call&lt;/span&gt; VimWinJump&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'h'&lt;/span&gt;&lt;span class="p"&gt;)&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;cr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
nnoremap &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;silent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;C&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="k"&gt;j&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="k"&gt;call&lt;/span&gt; VimWinJump&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'j'&lt;/span&gt;&lt;span class="p"&gt;)&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;cr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
nnoremap &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;silent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;C&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="k"&gt;k&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="k"&gt;call&lt;/span&gt; VimWinJump&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'k'&lt;/span&gt;&lt;span class="p"&gt;)&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;cr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
nnoremap &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;silent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;C&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="k"&gt;l&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="k"&gt;call&lt;/span&gt; VimWinJump&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'l'&lt;/span&gt;&lt;span class="p"&gt;)&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;cr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;tmux conf:&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~/.tmux.conf
&lt;span class="nv"&gt;is_nvim&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'tmux list-panes -F "#{l:#{pane_current_command}} #{l:#{pane_id}}" | grep #{pane_id} | grep -iqE "nvim|fzf"'&lt;/span&gt;

&lt;span class="nv"&gt;h_handler&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'if -F  "#{pane_at_left}" "select-window -t :-" "select-pane -L"'&lt;/span&gt;
bind-key &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'C-h'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="nt"&gt;-shell&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$is_nvim&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s1"&gt;'send-keys C-h'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$h_handler&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nv"&gt;j_handler&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'if -F  "#{pane_at_bottom}" "select-window -t :+" "select-pane -D"'&lt;/span&gt;
bind-key &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'C-j'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="nt"&gt;-shell&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$is_nvim&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s1"&gt;'send-keys C-j'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$j_handler&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nv"&gt;k_handler&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'if -F  "#{pane_at_top}" "select-window -t :-" "select-pane -U"'&lt;/span&gt;
bind-key &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'C-k'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="nt"&gt;-shell&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$is_nvim&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s1"&gt;'send-keys C-k'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$k_handler&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nv"&gt;l_handler&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'if -F  "#{pane_at_right}" "select-window -t :+" "select-pane -R"'&lt;/span&gt;
bind-key &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'C-l'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="nt"&gt;-shell&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$is_nvim&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s1"&gt;'send-keys C-l'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$l_handler&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="Credits"&gt;Credits&lt;/h2&gt;
&lt;p&gt;Inspired by &lt;a href="https://github.com/christoomey/vim-tmux-navigator" rel="nofollow" target="_blank"&gt;https://github.com/christoomey/vim-tmux-navigator&lt;/a&gt;&lt;/p&gt;</description>
      <author>zhengpd</author>
      <pubDate>Thu, 05 Aug 2021 22:25:57 +0800</pubDate>
      <link>https://ruby-china.org/topics/41543</link>
      <guid>https://ruby-china.org/topics/41543</guid>
    </item>
    <item>
      <title>【建议】关于删除最后回复后的贴子列表排序</title>
      <description>&lt;p&gt;目前如果一个贴子的最后回复被删除，这个贴子会被认为有了新动态从而排序在贴子列表的前面。&lt;/p&gt;

&lt;p&gt;建议 topic.last_active_mark 在贴子有回复的情况下不用 Time.now 而是用 reply.created_at，跟最后一条未删除回复的创建时间绑定。&lt;/p&gt;</description>
      <author>zhengpd</author>
      <pubDate>Sat, 30 May 2020 17:46:49 +0800</pubDate>
      <link>https://ruby-china.org/topics/39924</link>
      <guid>https://ruby-china.org/topics/39924</guid>
    </item>
    <item>
      <title>Demystify Ruby Class Variables Lookup</title>
      <description>&lt;h2 id="发帖前言"&gt;发帖前言&lt;/h2&gt;
&lt;p&gt;在社区看到一个&lt;a href="https://ruby-china.org/topics/39618" title=""&gt;类变量查询问题的帖子&lt;/a&gt;，做了些关于 Ruby 类变量查找的研究。如有错误，欢迎指正。&lt;/p&gt;
&lt;h2 id="Demystify Ruby Class Variables Lookup"&gt;Demystify Ruby Class Variables Lookup&lt;/h2&gt;
&lt;p&gt;Zheng Piaodan @ 2020-03-19&lt;/p&gt;
&lt;h2 id="Preface"&gt;Preface&lt;/h2&gt;
&lt;p&gt;A post caught my attention recently. It's at &lt;a href="https://ruby-china.org/topics/39618" title=""&gt;https://ruby-china.org/topics/39618&lt;/a&gt;. It's about class variables collecting and lookup in Ruby. And I decided to find out what's happening under the hood.&lt;/p&gt;
&lt;h2 id="Issue Description"&gt;Issue Description&lt;/h2&gt;
&lt;p&gt;See how &lt;code&gt;class_variables&lt;/code&gt; and &lt;code&gt;class_variable_get&lt;/code&gt; behave differently in sample:&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="vc"&gt;@@foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'foo'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Bar&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;RUBY_VERSION&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; "2.6.5"&lt;/span&gt;
&lt;span class="no"&gt;Bar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class_variables&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; []&lt;/span&gt;
&lt;span class="no"&gt;Bar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;singleton_class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class_variables&lt;/span&gt; 
&lt;span class="c1"&gt;# =&amp;gt; [:@@foo]&lt;/span&gt;
&lt;span class="no"&gt;Bar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;singleton_class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class_variable_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:@@foo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="c1"&gt;# NameError (uninitialized class variable @@foo in #&amp;lt;Class:Bar&amp;gt;)&lt;/span&gt;
&lt;span class="no"&gt;Bar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;singleton_class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;singleton_class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class_variable_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:@@foo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="c1"&gt;# =&amp;gt; "foo"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="Details Investigation"&gt;Details Investigation&lt;/h2&gt;
&lt;p&gt;NOTE: all code analyses are based on Ruby 2.6.5 if ruby version not specified.&lt;/p&gt;
&lt;h3 id="Related issue report and code fix"&gt;Related issue report and code fix&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The issue was reported back in 2013 at &lt;a href="https://bugs.ruby-lang.org/issues/8297" rel="nofollow" target="_blank" title=""&gt;https://bugs.ruby-lang.org/issues/8297&lt;/a&gt;. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Jeremy Evans made a fix in 2019, merged in Ruby 2.7.0, at &lt;a href="https://github.com/ruby/ruby/pull/2478" rel="nofollow" target="_blank" title=""&gt;https://github.com/ruby/ruby/pull/2478&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In our case here, Evans' fix makes &lt;code&gt;Bar.singleton_class.class_variables&lt;/code&gt; returns &lt;code&gt;[]&lt;/code&gt; instead of &lt;code&gt;[:@@name]&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="How extend method works"&gt;How extend method works&lt;/h3&gt;
&lt;p&gt;In short, ruby's &lt;code&gt;extend&lt;/code&gt; puts module as direct ancestor to the singleton class of current class, while &lt;code&gt;include&lt;/code&gt; puts module as direct ancestor to current class.&lt;/p&gt;

&lt;p&gt;In long, I don't have the long story right now...I'll tell you all about it when I dig it again.&lt;/p&gt;
&lt;h3 id="How class_variables method works"&gt;How class_variables method works&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Let &lt;code&gt;ST = Bar.singleton_class&lt;/code&gt; for convenience.&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id="Ruby 2.6.5 behavior"&gt;Ruby 2.6.5 behavior&lt;/h4&gt;
&lt;p&gt;For Ruby 2.6, when &lt;code&gt;Bar.class_variables&lt;/code&gt; or &lt;code&gt;ST.class_variables&lt;/code&gt;is called, Ruby will collect class variables defined on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;receiver itself , &lt;code&gt;Bar&lt;/code&gt; or &lt;code&gt;ST&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;receivers' ancestors&lt;/strong&gt; , &lt;code&gt;Bar.ancestors&lt;/code&gt; or &lt;code&gt;ST.ancestors&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Normal class and singleton class behave the same. &lt;code&gt;ST.ancestors&lt;/code&gt; includes &lt;code&gt;Foo&lt;/code&gt;, so &lt;code&gt;ST.class_variables&lt;/code&gt; collects &lt;code&gt;[:@@foo]&lt;/code&gt; from &lt;code&gt;Foo&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id="Ruby 2.7.0 behavior"&gt;Ruby 2.7.0 behavior&lt;/h4&gt;
&lt;p&gt;After Evans' fix, &lt;strong&gt;things changed for singleton class&lt;/strong&gt;. Ruby will collect class variables for &lt;code&gt;ST.class_variables&lt;/code&gt; from: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;singleton class itself, &lt;code&gt;ST&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;instance class of singleton class&lt;/strong&gt;, &lt;code&gt;Bar&lt;/code&gt;, see &lt;code&gt;cvar_front_klass&lt;/code&gt; below&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;ancestors of instance class of singleton class&lt;/strong&gt;, &lt;code&gt;Bar.ancestors&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that &lt;code&gt;Bar.ancestors&lt;/code&gt; doesn't include &lt;code&gt;Foo&lt;/code&gt;, so &lt;code&gt;ST.class_variables&lt;/code&gt; couldn't find &lt;code&gt;@@foo&lt;/code&gt; and returns &lt;code&gt;[]&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id="Take a trip into Evans' code fix"&gt;Take a trip into Evans' code fix&lt;/h4&gt;
&lt;p&gt;Let's take a deeper look at Evans' fix (section with &lt;code&gt;+&lt;/code&gt;):&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* file: variable.c */&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;
 &lt;span class="nf"&gt;mod_cvar_of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;tmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FL_TEST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FL_SINGLETON&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb_namespace_p&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb_ivar_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id__attached__&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;            &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mod_cvar_at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;            &lt;span class="n"&gt;tmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cvar_front_klass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(;;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mod_cvar_at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;tmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RCLASS_SUPER&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;if&lt;/code&gt; section was added to use &lt;code&gt;cvar_front_klass&lt;/code&gt; of singleton class for later class variables lookup. Here's the &lt;code&gt;cvar_front_klass&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* file: variable.c */&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt;
&lt;span class="nf"&gt;cvar_front_klass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FL_TEST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FL_SINGLETON&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rb_ivar_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id__attached__&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb_namespace_p&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;RCLASS_SUPER&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From the code we can see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;If &lt;code&gt;klass&lt;/code&gt; is singleton class, &lt;code&gt;cvar_front_klass&lt;/code&gt; would return the attached object of the singleton class.&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;For normal class, it just returns ancestor class.&lt;/strong&gt;  &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Remember these two behaviors. Below is how Ruby attach object to singleton clss:&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* file: class.c */&lt;/span&gt;
&lt;span class="cm"&gt;/*!
 * Attach a object to a singleton class.
 * @pre \a klass is the singleton class of \a obj.
 */&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="nf"&gt;rb_singleton_class_attached&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FL_TEST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FL_SINGLETON&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;RCLASS_IV_TBL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;RCLASS_IV_TBL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;st_init_numtable&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;rb_class_ivar_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id_attached&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From the code we can know that &lt;strong&gt;the attached object of a singleton class is actually a class itself&lt;/strong&gt;. According to this, &lt;code&gt;cvar_front_klass&lt;/code&gt; for &lt;code&gt;Bar.singleton_class&lt;/code&gt; is &lt;code&gt;Bar&lt;/code&gt;. That's why &lt;code&gt;ST.class_variables&lt;/code&gt; in Ruby 2.7.0 collects class variables from &lt;code&gt;Bar.ancestors&lt;/code&gt; not &lt;code&gt;ST.ancestors&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="How class_variable_get method works"&gt;How class_variable_get method works&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Let &lt;code&gt;ST = Bar.singleton_class&lt;/code&gt; for convenience.&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id="Class variable lookup workflow"&gt;Class variable lookup workflow&lt;/h4&gt;
&lt;p&gt;Lookup steps for &lt;code&gt;class_variable_get&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Look up on receiver itself. Go to next step if not found.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Look up on &lt;code&gt;cvar_front_klass&lt;/code&gt;. Front class for &lt;code&gt;Bar&lt;/code&gt; is &lt;code&gt;Bar&lt;/code&gt; itself. Front class for &lt;code&gt;ST&lt;/code&gt; is &lt;strong&gt;the object attached to singleton class&lt;/strong&gt;, which is also &lt;code&gt;Bar&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Look up in ancestors of &lt;code&gt;cvar_front_klass&lt;/code&gt;, which is &lt;code&gt;Bar.ancestors&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;From step 2, we can know that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;In our case &lt;code&gt;ST.class_variable_get(:@@foo)&lt;/code&gt; is actually equal to &lt;code&gt;Bar.class_variable_get(:@@foo)&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;And &lt;code&gt;ST.singleton_class.class_variable_get(:@@foo)&lt;/code&gt; would look up &lt;code&gt;@@foo&lt;/code&gt; on ST and ST's ancestors&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="Take a trip into class_variable_get method"&gt;Take a trip into class_variable_get method&lt;/h4&gt;
&lt;p&gt;Let's find out what's inside &lt;code&gt;class_variable_get&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* file: variable.c */&lt;/span&gt;
&lt;span class="n"&gt;VALUE&lt;/span&gt;
&lt;span class="nf"&gt;rb_cvar_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ID&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;front&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;st_data_t&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;tmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;CVAR_LOOKUP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;front&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;front&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;;});&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;rb_name_err_raise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"uninitialized class variable %1$s in %2$s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ID2SYM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;cvar_overtaken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;front&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VALUE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The core is &lt;code&gt;CVAR_LOOKUP&lt;/code&gt;, which is defined as:&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* file: variable.c */&lt;/span&gt;
&lt;span class="cp"&gt;#define CVAR_LOOKUP(v,r) do {\
    if (cvar_lookup_at(klass, id, (v))) {r;}\
    CVAR_FOREACH_ANCESTORS(klass, v, r);\
} while(0)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It first look up at &lt;code&gt;klass&lt;/code&gt; itself, and then in each ancestor of &lt;code&gt;klass&lt;/code&gt;.  The &lt;code&gt;cvar_lookup_at&lt;/code&gt; is a method to find class variable from klass's instance variables table. Think it as a black box with output of a class variable or 0. Right now it's not important in our short trip. Let's see what's in&lt;code&gt;CVAR_FOREACH_ANCESTORS&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* file: variable.c */&lt;/span&gt;
&lt;span class="cp"&gt;#define CVAR_FOREACH_ANCESTORS(klass, v, r) \
    for (klass = cvar_front_klass(klass); klass; klass = RCLASS_SUPER(klass)) { \
        if (cvar_lookup_at(klass, id, (v))) { \
            r; \
        } \
    }
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Got &lt;code&gt;cvar_front_klass&lt;/code&gt; again! So if &lt;code&gt;klass&lt;/code&gt; here is a singleton class, the &lt;code&gt;for&lt;/code&gt; loop would &lt;strong&gt;start with instance class of the singleton class&lt;/strong&gt;, and then iterate through the ancestors of the instance class. That means, when &lt;code&gt;ST.class_variable_get(:@@foo)&lt;/code&gt; reaches here, it actually finds &lt;code&gt;@@foo&lt;/code&gt; in &lt;code&gt;Bar&lt;/code&gt; and &lt;code&gt;Bar.ancestors&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="How ST.singleton_class.class_variable_get(:@@foo) works"&gt;How ST.singleton_class.class_variable_get(:@@foo) works&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Let &lt;code&gt;ST = Bar.singleton_class&lt;/code&gt; for convenience.&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id="Lookup steps for ST.class_variable_get(:@@foo)"&gt;Lookup steps for &lt;code&gt;ST.class_variable_get(:@@foo)&lt;/code&gt;
&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;@@foo&lt;/code&gt; is not found on &lt;code&gt;ST&lt;/code&gt;, then it goes to its front class &lt;code&gt;Bar&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Bar&lt;/code&gt; doesn't define &lt;code&gt;@@foo&lt;/code&gt;. Go to &lt;code&gt;Bar.ancestors&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Oops! &lt;code&gt;Bar.ancestors&lt;/code&gt; doesn't contains &lt;code&gt;Foo&lt;/code&gt; module, so we can never found &lt;code&gt;@@foo&lt;/code&gt;! &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Nothing found. End of story.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="Lookup steps for ST.singleton_class.class_variable_get(:@@foo)"&gt;Lookup steps for &lt;code&gt;ST.singleton_class.class_variable_get(:@@foo)&lt;/code&gt;
&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;@@foo&lt;/code&gt; is not found on &lt;code&gt;ST.singleton_class&lt;/code&gt;, then it goes to its front class ST.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;ST&lt;/code&gt; doesn't define &lt;code&gt;@@foo&lt;/code&gt;. Go to &lt;code&gt;ST.ancestors&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;@@foo&lt;/code&gt; is found on &lt;code&gt;Foo&lt;/code&gt;, one of &lt;code&gt;ST.ancestors&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cool! Let's show &lt;code&gt;'foo'&lt;/code&gt;, the value of &lt;code&gt;@@foo&lt;/code&gt;!&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="Final Words"&gt;Final Words&lt;/h2&gt;
&lt;p&gt;The key to the mystery of original issue is the collecting and lookup workflows of class variables. Mystery is no mystery once you know how it happens.&lt;/p&gt;

&lt;p&gt;At last, the following code works at Ruby 2.7.0:&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Bar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;singleton_class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class_variables&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; []  # &amp;lt;= This is what fixed since 2.7.0&lt;/span&gt;

&lt;span class="no"&gt;Bar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;singleton_class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;singleton_class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class_variable_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:@foo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; "foo"  # &amp;lt;= This does not change.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="小尾巴"&gt;小尾巴&lt;/h2&gt;
&lt;p&gt;目前待业求职，欢迎介绍远程中高级开发岗位，不胜感激&lt;/p&gt;</description>
      <author>zhengpd</author>
      <pubDate>Thu, 19 Mar 2020 01:22:22 +0800</pubDate>
      <link>https://ruby-china.org/topics/39628</link>
      <guid>https://ruby-china.org/topics/39628</guid>
    </item>
    <item>
      <title>有远程 Ruby 岗位招人不</title>
      <description>&lt;p&gt;新年好！打算看看有没有合适的远程工作岗位。五年 Ruby on Rails 开发经验。&lt;/p&gt;</description>
      <author>zhengpd</author>
      <pubDate>Sat, 01 Feb 2020 10:26:54 +0800</pubDate>
      <link>https://ruby-china.org/topics/39475</link>
      <guid>https://ruby-china.org/topics/39475</guid>
    </item>
    <item>
      <title>bash-bgnotify: shell command 执行完后自动发出通知</title>
      <description>&lt;p&gt;Zsh 插件有个很实用的插件叫 &lt;a href="https://github.com/t413/zsh-background-notify" rel="nofollow" target="_blank" title=""&gt;bgnotify&lt;/a&gt; ，可以在执行完一个耗时较长的命令后自动发出通知。Bash 不能直接用这个，网上搜了下也没找到类似的插件，于是自己动手把 Zsh 的 bgnotify fork 过来做了些修改用在 Bash 上。因为 Bash 本身并没有 &lt;code&gt;preexec&lt;/code&gt; 和 &lt;code&gt;precmd&lt;/code&gt; ，所以需要依赖 &lt;a href="https://github.com/rcaloras/bash-preexec" rel="nofollow" target="_blank" title=""&gt;Bash-Preexec&lt;/a&gt; 提供这两个 hook functions。&lt;/p&gt;

&lt;p&gt;源码请戳 &lt;a href="https://github.com/zhengpd/bash-bgnotify" rel="nofollow" target="_blank" title=""&gt;bash-bgnotify&lt;/a&gt; &lt;/p&gt;</description>
      <author>zhengpd</author>
      <pubDate>Fri, 22 Apr 2016 22:04:51 +0800</pubDate>
      <link>https://ruby-china.org/topics/29833</link>
      <guid>https://ruby-china.org/topics/29833</guid>
    </item>
    <item>
      <title>写了个管理本地 repo 的小工具</title>
      <description>&lt;p&gt;写了个管理本地 repo 的小工具，适用场景：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;快速 clone 一个感兴趣的 repo 到本地&lt;/li&gt;
&lt;li&gt;快速打开本地项目的源码&lt;/li&gt;
&lt;li&gt;直接访问本地项目的 README 文件&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;源码在 &lt;a href="https://github.com/zhengpd/vl" rel="nofollow" target="_blank"&gt;https://github.com/zhengpd/vl&lt;/a&gt;&lt;/p&gt;</description>
      <author>zhengpd</author>
      <pubDate>Sat, 09 Apr 2016 21:35:56 +0800</pubDate>
      <link>https://ruby-china.org/topics/29641</link>
      <guid>https://ruby-china.org/topics/29641</guid>
    </item>
    <item>
      <title>写了个 Ruby China 忽略话题的油猴脚本</title>
      <description>&lt;p&gt;实现方法是用 &lt;code&gt;localStorage&lt;/code&gt; 结合 &lt;code&gt;display: none&lt;/code&gt; 隐藏忽略过的话题&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/zhengpd/userscripts/blob/master/ruby-china-ignore-topics.user.js" rel="nofollow" target="_blank"&gt;https://github.com/zhengpd/userscripts/blob/master/ruby-china-ignore-topics.user.js&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;列表页：
&lt;img src="https://l.ruby-china.com/photo/2016/6327f94494691b330e44a2b7199614ec.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;详情页：
&lt;img src="https://l.ruby-china.com/photo/2016/01b337fd97118a714b91e96a68a914f0.png" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>zhengpd</author>
      <pubDate>Sat, 27 Feb 2016 17:05:55 +0800</pubDate>
      <link>https://ruby-china.org/topics/29128</link>
      <guid>https://ruby-china.org/topics/29128</guid>
    </item>
  </channel>
</rss>
