<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>xjz19901211 (Select)</title>
    <link>https://ruby-china.org/xjz19901211</link>
    <description>不是很努力，也不是很有理想</description>
    <language>en-us</language>
    <item>
      <title>[开源] XJZProxy - 使用 Ruby 做三平台的 GUI 开发</title>
      <description>&lt;p&gt;先简单的说说开发经历吧。&lt;/p&gt;

&lt;p&gt;项目总共我大概花了两个月的工作日时间吧。说说比较折腾的事情：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;协议细节具体没啥好说的，主要是花很多时间去了解协议具体细节。&lt;/li&gt;
&lt;li&gt;发布像编译程序一样安装运行的程序，由于我使用的 ruby 2.6.3，还支持了三平台 (Mac, Linux, Win10) 。为了让程序可以像其它编译语言一样的运行使用，在基于前人老项目的基础上，折腾了不少时间。由其是 Windows，花了几天折腾折腾，最后虽然可以运行了，但启动速度不是一般的慢。了解了下，发现是 Ruby 没对 Windows 平台做啥优化，所以虽然进行了一些简单的优化，但启动速度还是要十来秒。。。Windows 上的 Ruby 实在太慢了&lt;/li&gt;
&lt;li&gt;GUI 还是研究了下，最后还是简单起见，直接使用 webview 了，使用别的 webview 库。&lt;/li&gt;
&lt;li&gt;兴趣原因就研究了下代码的加密和防破解，其实就自己编码一下，然后随便压缩一下，再加点无用字节。读取时直接使用 c 编写了 c 库去读。不过 c 代码库没做加密了。也就简单防防不会破解的人了。然后编译时在代码中加入随机的证书检查代码。。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;本来想要想试试有没有感兴趣的人，然后做付费的。但后来，因为没啥人用，然后自己也懒的去推广了。其实之前做这个主要是想用 Ruby 折腾下不同的东西（Web 做烦了。。）&lt;/p&gt;

&lt;p&gt;项目地址  &lt;a href="https://github.com/xiejiangzhi/xjzproxy" rel="nofollow" target="_blank"&gt;https://github.com/xiejiangzhi/xjzproxy&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="好了，下面复制自项目介绍"&gt;好了，下面复制自项目介绍&lt;/h2&gt;
&lt;p&gt;项目介绍&lt;/p&gt;

&lt;p&gt;官网 &lt;a href="https://xjzproxy.xjz.pw/zh-cn/" rel="nofollow" target="_blank"&gt;https://xjzproxy.xjz.pw/zh-cn/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="主要功能"&gt;主要功能&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;本地 API Mock 基于 YAML 文档，支持动态数据生成&lt;/li&gt;
&lt;li&gt;自动生成、导出 API 文档&lt;/li&gt;
&lt;li&gt;自动基于文档对比 API 请求响应数据格式，提前发现有问题的接口&lt;/li&gt;
&lt;li&gt;根据请求历史生成统计数据&lt;/li&gt;
&lt;li&gt;HTTP/HTTPS/HTTP2/GRPC 代理&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="想要解决的问题"&gt;想要解决的问题&lt;/h2&gt;
&lt;p&gt;通过为了方便合作，在需求确定后，我们会先写一份接口文档给前端，然后前端按文档定义的接口去开发。&lt;/p&gt;

&lt;p&gt;在这期间我们可能会遇见很多问题：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;结构松散或是没有结构的文档格式，很难去管理、协作，写起来也麻烦&lt;/li&gt;
&lt;li&gt;只有文档，自己 mock 各种数据很麻烦，等真实接口好了才方便开发&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;h2 id="解决方案"&gt;解决方案&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;使用 YAML 书写结构化的文档，数据结构可以复用。一个接口甚至可以简单到只需要几行&lt;/li&gt;
&lt;li&gt;文档完成后，你就相当于有了一个本地的后端服务器，文档中所有接口可以直接调用&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;h3 id="Example"&gt;Example&lt;/h3&gt;
&lt;p&gt;一个最简单的项目文档示例&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mydomain.com&lt;/span&gt;

&lt;span class="na"&gt;apis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Get a user&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GET&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/api/v1/users/\d+&lt;/span&gt;
    &lt;span class="na"&gt;response&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;http_code&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt;
        &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.t/name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后就可以通过文档代理来访问了&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl http://mydomain.com/api/v1/users/123 --proxy localhost:9898
{"id": 1, "name": "random name"}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当然，你可以在移动设备、浏览器中通过代理地址访问接口。更多文档书写帮助请参考&lt;a href="https://xjzproxy.xjz.pw/quick-start" rel="nofollow" target="_blank" title=""&gt;这里&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;请求参数和文档对不上时，会有提示
&lt;img src="https://xjzproxy.xjz.pw/imgs/app-1.png" title="" alt="error_params"&gt;&lt;/p&gt;
&lt;h3 id="GRPC"&gt;GRPC&lt;/h3&gt;
&lt;p&gt;如果你在使用 GRPC 的话，只要配置好 protobufs 的路径，就可以直接调用接口了。当然，如果你想定制 GRPC 接口返回的数据内容，还是需要在文档中定义好一些数据模板。&lt;/p&gt;
&lt;h3 id="Preview"&gt;Preview&lt;/h3&gt;
&lt;p&gt;在工具中查看渲染好的漂亮文档也是不能少的。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://xjzproxy.xjz.pw/imgs/app-2.png" title="" alt="doc_preview"&gt;&lt;/p&gt;</description>
      <author>xjz19901211</author>
      <pubDate>Fri, 02 Aug 2019 19:45:47 +0800</pubDate>
      <link>https://ruby-china.org/topics/38898</link>
      <guid>https://ruby-china.org/topics/38898</guid>
    </item>
    <item>
      <title>刚撸的 InfluxDB ORM</title>
      <description>&lt;p&gt;最近在使用 InfluxDB，没有看到啥 ORM，只有一个 &lt;a href="https://github.com/palkan/influxer" rel="nofollow" target="_blank" title=""&gt;palkan/influxer&lt;/a&gt;，看着使用方式不是很喜欢，于是自己撸了，虽然比较粗糙，不过胜在简单方便，代码也就三四百来行，有兴趣的可以试试 &lt;a href="https://github.com/xiejiangzhi/influx_orm" rel="nofollow" target="_blank" title=""&gt;InfluxORM&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="Init"&gt;Init&lt;/h2&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;InfluxORM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="ss"&gt;connection: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;# 具体见 InfluxDB::Client.new&lt;/span&gt;
    &lt;span class="ss"&gt;database: &lt;/span&gt;&lt;span class="s1"&gt;'test'&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;h2 id="Define MEASUREMENTS"&gt;Define MEASUREMENTS&lt;/h2&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Memory&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;InfluxORM&lt;/span&gt;

  &lt;span class="n"&gt;influx_tag&lt;/span&gt; &lt;span class="ss"&gt;:host&lt;/span&gt;
  &lt;span class="n"&gt;influx_tag&lt;/span&gt; &lt;span class="ss"&gt;:region&lt;/span&gt;
  &lt;span class="n"&gt;influx_value&lt;/span&gt; &lt;span class="ss"&gt;:free&lt;/span&gt;
  &lt;span class="n"&gt;influx_value&lt;/span&gt; &lt;span class="ss"&gt;:used&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:int&lt;/span&gt; &lt;span class="c1"&gt;# support :int, :float, :boolean, :string&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="Write"&gt;Write&lt;/h2&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;host: &lt;/span&gt;&lt;span class="s1"&gt;'A'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;region: &lt;/span&gt;&lt;span class="s1"&gt;'US'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;free: &lt;/span&gt;&lt;span class="mi"&gt;1234&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;used: &lt;/span&gt;&lt;span class="mi"&gt;2234&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="no"&gt;Memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;import&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;host: &lt;/span&gt;&lt;span class="s1"&gt;'A'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;region: &lt;/span&gt;&lt;span class="s1"&gt;'US'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;free: &lt;/span&gt;&lt;span class="mi"&gt;1234&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;used: &lt;/span&gt;&lt;span class="mi"&gt;2234&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;timestamp: &lt;/span&gt;&lt;span class="mi"&gt;1234567890&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;host: &lt;/span&gt;&lt;span class="s1"&gt;'A'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;region: &lt;/span&gt;&lt;span class="s1"&gt;'US'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;free: &lt;/span&gt;&lt;span class="mi"&gt;1244&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;used: &lt;/span&gt;&lt;span class="mi"&gt;2224&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;timestamp: &lt;/span&gt;&lt;span class="mi"&gt;1234567900&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;host: &lt;/span&gt;&lt;span class="s1"&gt;'B'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;region: &lt;/span&gt;&lt;span class="s1"&gt;'US'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;free: &lt;/span&gt;&lt;span class="mi"&gt;234&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;used: &lt;/span&gt;&lt;span class="mi"&gt;3234&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;h2 id="Query"&gt;Query&lt;/h2&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; 4&lt;/span&gt;
&lt;span class="no"&gt;Memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;host: &lt;/span&gt;&lt;span class="s1"&gt;'A'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; 1&lt;/span&gt;
&lt;span class="no"&gt;Memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'mean(*)'&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="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;host: &lt;/span&gt;&lt;span class="s1"&gt;'A'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;time: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;gte: &lt;/span&gt;&lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;day&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;lte: &lt;/span&gt;&lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;day&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="nf"&gt;group_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'time(1m) fill(0)'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;result&lt;/span&gt;

&lt;span class="no"&gt;Memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;host: &lt;/span&gt;&lt;span class="s1"&gt;'B'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;host: &lt;/span&gt;&lt;span class="s1"&gt;'C'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;result&lt;/span&gt;
&lt;span class="no"&gt;Memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"host = 'A' AND time &amp;gt; now() - 1d"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;free: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;lt: &lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;result&lt;/span&gt;

&lt;span class="n"&gt;query_obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;host: &lt;/span&gt;&lt;span class="s1"&gt;'A'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;host: &lt;/span&gt;&lt;span class="s1"&gt;'B'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="no"&gt;Memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;region: &lt;/span&gt;&lt;span class="s1"&gt;'US'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query_obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# select * from memorys where region = 'US' OR (host = 'A' OR host = 'B')&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;More query methods&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;select&lt;/code&gt;: &lt;code&gt;select('mean(*)')&lt;/code&gt;, &lt;code&gt;select({mean: 'tag_name', sum: 'tag_name'})&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;where&lt;/code&gt;: &lt;code&gt;where('tag = \'value\'')&lt;/code&gt;, &lt;code&gt;where(tag: 'value', time: {gt: Time.now - 1.day})&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;or&lt;/code&gt;: &lt;code&gt;or('tag = \'value\'')&lt;/code&gt;, &lt;code&gt;or(tag: 'value', time: {gt: Time.now - 1.day})&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;group_by&lt;/code&gt;: &lt;code&gt;group_by('host')&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fill&lt;/code&gt;: &lt;code&gt;fill(0)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;limit&lt;/code&gt;: &lt;code&gt;limit(1)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;slimit&lt;/code&gt;: &lt;code&gt;slimit(1)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;offset&lt;/code&gt;: &lt;code&gt;offset(1)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;soffset&lt;/code&gt;: &lt;code&gt;soffset(1)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>xjz19901211</author>
      <pubDate>Sun, 11 Jun 2017 21:27:24 +0800</pubDate>
      <link>https://ruby-china.org/topics/33213</link>
      <guid>https://ruby-china.org/topics/33213</guid>
    </item>
    <item>
      <title>TTTYGateway：开源的 HTTP 访问限制模块</title>
      <description>&lt;p&gt;&lt;a href="https://github.com/tiantiantouyan/ttty-gateway" rel="nofollow" target="_blank" title=""&gt;TTTYGateway&lt;/a&gt; 是基于 &lt;a href="https://github.com/openresty/openresty" rel="nofollow" target="_blank" title=""&gt;OpenResty&lt;/a&gt; 的一套 HTTP 请求限制模块&lt;/p&gt;
&lt;h2 id="可以做什么"&gt;可以做什么&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;设置请求的 IP 黑名单及白名单&lt;/li&gt;
&lt;li&gt;针对 Path 设置请求的白名单&lt;/li&gt;
&lt;li&gt;根据 IP 针对 Path 设置请求频率限制&lt;/li&gt;
&lt;li&gt;根据 User 针对 Path 设置请求频率限制&lt;/li&gt;
&lt;li&gt;为 Prometheus 提供简单的 Nginx 状态信息，可以不用再去分析日志，直接得到请求数、返回状态、请求耗时等数据&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Path: 可以对指定请求方法 (GET POST PUT DELETE) 及请求的路径进行匹配，路径支持简单的通配符&lt;/p&gt;

&lt;p&gt;User: 代码中自由定义，可以未登陆时使用 IP，登陆后使用用户 ID 或 TOKEN 等 &lt;/p&gt;
&lt;h2 id="性能如何"&gt;性能如何&lt;/h2&gt;
&lt;p&gt;OpenResty 主体就是 Nginx + JITLua，通常 JITLua 的性能和 C 在一个量级，使用 OpenResty 写的应用，运行起来通常会有 C 程序运行的快感。&lt;/p&gt;

&lt;p&gt;下面一个简单的对比示例，只是简单表示 JITLua 是很快的，因为只使用了简单的逻辑，在整体上他们性能还是有些差别的&lt;/p&gt;

&lt;p&gt;C Code&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#include "stdio.h"

int main() {
  int sum=0;
  int n = 5000000;

  for(int i=1; i&amp;lt;=n; i++){
    if(i%2==0){
      sum = sum - i;
    } else {
      sum = sum + i;
    }
  }

  printf("%i", sum);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Lua Code&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;local sum = 0
local n = 5000000

for i = 1, n do
  if i % 2 == 0 then
    sum = sum - i
  else
    sum = sum + i
  end
end

print(sum)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C 编译后运行&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ time ./add
-2500000
real   0m0.025s
user   0m0.021s
sys    0m0.001s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Lua 编译后运行&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ time luajit ./add.lua
-2500000
real    0m0.025s
user    0m0.022s
sys     0m0.001s
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="TTTYGateway 怎么用"&gt;TTTYGateway 怎么用&lt;/h2&gt;
&lt;p&gt;只需要把你的 nginx 配置复制到项目的 &lt;code&gt;nginx/conf/nginx.conf&lt;/code&gt; 下面，然后做如下配置&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http {
  # 指向 ttty-gateway 目录 下的 nginx/lualib
  lua_package_path "/path/to/ttty-gateway/nginx/lualib/?.lua;;";
  init_by_lua_file 'lualib/init.lua'; # load gateway config

  server {
    listen 80;

    -- 为了直接在 nginx 中取得用户实际的 IP
    set_real_ip_from 127.0.0.1;
    set_real_ip_from 192.168.0.0/16;
    set_real_ip_from 10.0.0.0/8;
    set_real_ip_from 172.0.0.0/12;

    real_ip_header 'X-Forwarded-For';
    real_ip_recursive on;

    # 关键点，运行 API 请求检查器
    # 如果放在 server block 中，所检查所有请求，像静态资源什么的
    access_by_lua_file 'lualib/access_checker.lua';

    location /api {
      # 通常我们都是在 location 下只限制 API 的请求
      # access_by_lua_file 'lualib/access_checker.lua';

      proxy_pass http://xxx;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后跑起来&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;openresty -p /path/to/ttty-gateway
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;或者使用 docker&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker run -v /path/to/ttty-gateway:/openresty -e REDIS_HOST={IP} openresty/openresty -p /openresty
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当然，你也可以先 build 好，再用 docker 运行&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker build ./ -t ttty_gateway:latest
$ docker run -v /path/to/your/nginx.conf:/openresty/conf/nginx.conf -e REDIS_HOST={IP} ttty_gateway
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面运行时会需要 Redis，如果没有，所有请求都不会进行限制，也就是说，运行一半时，如果你的 Redis 服务挂了，就不会进行限制检查了。而 Mysql 服务是可选的，可以让你动态配置限制规则。&lt;/p&gt;

&lt;p&gt;上面运行的是默认规则，详见 &lt;code&gt;nginx/lualib/web_shield_config.lua&lt;/code&gt;，我们只是将内网 IP 设置为白名单：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;whitelist: {'127.0.0.1', '192.168.0.1/16', '172.17.0.1/12', '10.0.0.0/8'}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;并对所有的请求做了一个简单的分级限制&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  -- level 1
  {matcher = {method = READ, path = '*'}, period = 20, limit = 15},
  {matcher = {method = WRITE, path = '*'}, period = 20, limit = 7},
  -- level 2
  {matcher = {method = READ, path = '*'}, period = 60, limit = 30},
  {matcher = {method = WRITE, path = '*'}, period = 60, limit = 14},
  -- level 3
  {matcher = {method = READ, path = '*'}, period = 120, limit = 45},
  {matcher = {method = WRITE, path = '*'}, period = 120, limit = 21},
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;更详细的可以参考文档。上面的这些配置都支持直接在 Mysql 中动态配置的，ttty-gateway 会自动缓存 Mysql 中的配置，并简单的检查是否正确，所以，如果 Mysql 挂了的话，他会使用缓存，如果配置错误的话，将会直接忽略 Mysql 中的配置，而是使用默认文件中的配置&lt;/p&gt;

&lt;p&gt;Mysql 配置的话，我们写了一个简单的配置项目 &lt;a href="https://github.com/tiantiantouyan/ttty-manager" rel="nofollow" target="_blank" title=""&gt;TTTYManager&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="如何按用户来限制请求"&gt;如何按用户来限制请求&lt;/h2&gt;
&lt;p&gt;在 &lt;code&gt;nginx/lualib/access_checker.lua&lt;/code&gt; 文件中，默认使用了我们自己的逻辑取得用户标识，应该不会是你想要的，你需要做一些修改，告诉系统你的用户标识，比如下面的逻辑&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;local WebShield = require 'resty.web_shield'

local ip = ngx.var.remote_addr
local h = ngx.req.get_headers()
local uid = h['X-User-ID'] or 'nil'

-- 简单的取得 `nginx/lualib/web_shield_config' 中的配置，为了简单不再去取 mysql 配置
local config = require('web_shield_config')
local web_shield = WebShield.new(config.web_shield, config.shield)

-- 使用自定的用户标识进行检查，如果失败就返回 429
if not web_shield:check(ip, uid, ngx.var.request_method, ngx.var.uri) then
  ngx.status = ngx.HTTP_TOO_MANY_REQUESTS
  ngx.say("Too many requests")
  ngx.exit(ngx.HTTP_TOO_MANY_REQUESTS)
end
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="结语"&gt;结语&lt;/h2&gt;
&lt;p&gt;先就介绍到这，如果有问题的话，可以邮件 (github) 联系我，或是直接在项目中提 issue，此项目将会持续改进，欢迎关注我们“天天投研”团队。&lt;/p&gt;</description>
      <author>xjz19901211</author>
      <pubDate>Tue, 16 May 2017 11:31:21 +0800</pubDate>
      <link>https://ruby-china.org/topics/32999</link>
      <guid>https://ruby-china.org/topics/32999</guid>
    </item>
    <item>
      <title>Ruby 中的多进程与多线程</title>
      <description>&lt;h2 id="Ruby 中的多进程与多线程"&gt;Ruby 中的多进程与多线程&lt;/h2&gt;
&lt;p&gt;今天讲解的是 Ruby 的多进程与多线程的问题。选择这个话题的初衷，是因为在我参与流利说招聘 Ruby 工程师的面试中，发现很多候选人对于该话题不是很了解。而在平时的工作中，我们又存在不少业务的处理需要对该问题有认识才可进行。希望该文章对感兴趣的同学有所启发。&lt;/p&gt;

&lt;hr&gt;
&lt;h2 id="目标"&gt;目标&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;知道 Ruby 中线程和进程的区别&lt;/li&gt;
&lt;li&gt;知道在什么情况下使用多线程/多进程可以获得性能的提升&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="Ruby 线程的一些前置知识"&gt;Ruby 线程的一些前置知识&lt;/h2&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;Ruby 由于 GIL(Global interpreter lock) 的原因，多线程并不能同时在多个 CPU 上执行&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="Ruby 进程的一些前置知识"&gt;Ruby 进程的一些前置知识&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;进程间无法共享内存数据进行读写&lt;/li&gt;
&lt;li&gt;2.0 开始 Copy On Write 功能可以让 fork 的进程共享内存数据，只在数据修改时才会复制数据&lt;/li&gt;
&lt;li&gt;每个进程可以运行于不同的 CPU 核心上，更充分的利用多核 CPU&lt;/li&gt;
&lt;li&gt;进程间的数据隔离的同时也提高了安全性，避免了像多线程间数据错乱的风险&lt;/li&gt;
&lt;li&gt;同样由于进程间的数据隔离，在进程间的通信相对来说更加困难&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="多线程与多进程的应用场景"&gt;多线程与多进程的应用场景&lt;/h2&gt;&lt;h3 id="场景一"&gt;场景一&lt;/h3&gt;
&lt;p&gt;我们先假想一个场景：A 先生需要向同一房间中的 20 位员工收集个人信息，需要先给员工发送纸质表格，等待员工填写好后再收集起来。&lt;/p&gt;

&lt;p&gt;首先来看看我们的任务场景，向 20 位员工收集个人信息，就是我们要处理的任务，给员工发送表格，是 A 先生要做的事件可以看做是 CPU 任务，而等待员工填写完成这个过程可以看做是 IO 等待，而 A 先生呢，就是那个处理任务的进程了。在这个场景中，A 先生要做的事件比较少，更多的时间是在等待员工完成表格。&lt;/p&gt;

&lt;p&gt;接下来，我们拟定几个方案来一一分析：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A 先生自己每发送一份一份表格后，等待对方填写完成并上交后，再找到下一位填写完并上交，直到任务完成&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这个方案基本就是一个单进程 + 单线程模式了，一个完成上交了再找另一个，一看就知道效率是非常低的&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A 先生雇佣 4 个人来做这事件，每个人分别向不重复的 5 位员工做与方案一相同的流程，完成后将表格交给 A 先生&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这个方案相当于使用了多进程来处理，我们可以把每个雇佣来的人看成一个进程，此方案中，通过对更多资源的利用来达到快速完成任务&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A 先生自己发送完第一位员工的表格后，立即找到下一位员工并发送表格，直到表格发送完毕，然后等待等一位完成上交后，再去等待下一位完成并上交表格&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;此方案可以看作一个多线程的模式，在 IO 阻塞时（员工填写表格），我们并不是等待 IO 操作完成后才去执行后面的工作，而是继续执行计算任务 (发送表格)，完成所有计算任务后，再去等待 IO 并收集结果。&lt;/p&gt;
&lt;h3 id="场景二"&gt;场景二&lt;/h3&gt;
&lt;p&gt;接下来，我们把场景稍加改变：每位员工都在距离几公里到几十公里的办公室中&lt;/p&gt;

&lt;p&gt;改变后的场景中，从一位员工到另一位员工的这个路途是 A 先生要去做的事，也就还是 CPU 任务，在这个场景中，在路程中的时间可能是一位员工完成表格填写时间的数倍到数十倍。我们继续来分析上面三个方案&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A 先生自己每发送一份一份表格后，等待对方填写完成并上交后，再找到下一位填写完并上交，直到任务完成&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;一个完成上交了再找另一个，效率还是比较低的&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A 先生雇佣 4 个人来做这事件，每个人分别向不重复的 5 位员工做与方案一相同的流程，完成后将表格交给 A 先生&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;多进程处理，可以明显的看出，完成效率提升了数倍，本来要由一个人走很远的路途，分给 4 个人一起执行后，只需要花 1/4 的时间就可以走完所有路程&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A 先生自己发送完第一位员工的表格后，立即找到下一位员工并发送表格，直到表格发送完毕，然后等待等一位完成上交后，再去等待下一位完成并上交表格&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;多线程模式，上面的方案在计算机中执行一般会是这个情形：找到一位员工后，记录此员工办公室坐标，下次再来时，直接传送过来，不需要再去跑一段路程了。由于一次路程的时间（一个任务的 CPU 计算时间）就相当于几位或数十位员工的表格填写时间了，所以总体上来看，节约的时间几乎可以忽略不计了&lt;/p&gt;
&lt;h2 id="场景总结"&gt;场景总结&lt;/h2&gt;
&lt;p&gt;从上面的两个场景中可以看出：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;在 IO 等待比较多的场景中，应用多线程与多进程都能不同程度的提高效率&lt;/li&gt;
&lt;li&gt;在 CPU 计算比较多的场景中，多线程对程序的效率提升随着计算量的提升，几乎可以忽略不计&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在使用中，我们也可以把多进程与多线程结合起来使用，也就是第二方案中，每个雇佣来的人也使用第三方案的方式来执行任务，他们会在不同场景中得到不同的效果&lt;/p&gt;

&lt;p&gt;在实际应用中，有更多的因素需要考虑，也会不同程度的影响方案的选择：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;雇佣别人来完成（多个进程）需要考虑预算（CPU 运算核心的数量），如果只有一个人的预算雇来多个人，那另外几个肯定都是同一个假扮出来的，效率还是相当于一个人&lt;/li&gt;
&lt;li&gt;如在 20 位员工只有 5 支可使用的笔（IO 带宽或是共享资源），就算你给所有人发送了表格，但能同时填写的人最多不会超过 5 位&lt;/li&gt;
&lt;li&gt;比如要收集 100 位员工的信息时，A 先生最多一次记住 20 位员工，如果再多，到时候就可能忘了需要向谁去收取表格了&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="代码时间"&gt;代码时间&lt;/h2&gt;&lt;h3 id="Ruby 写一段简单的多线程"&gt;Ruby 写一段简单的多线程&lt;/h3&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a = [1, 2, 3, 4]
b = []
mutex = Mutex.new

a.length.times.map do |i|
  Thread.new do
    v = [i, i ** 2].join(' - ')
    mutex.synchronize { b &amp;lt;&amp;lt; v }
  end
end.map(&amp;amp;:join)

puts b
# =&amp;gt; 2 - 4
#    1 - 1
#    0 - 0
#    3 - 9
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;多线程操作时，要为共享资源加锁，同时不要把可以在锁外完成的操作放到锁中间去执行，长时间占用锁会降低处理能力&lt;/p&gt;
&lt;h3 id="Ruby 写一段多进程"&gt;Ruby 写一段多进程&lt;/h3&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require 'socket'

MAX_RECV = 100

sockets = 3.times.map do |i|
  parent_socket, child_socket = Socket.pair(:UNIX, :DGRAM, 0)
  fork do
    pid = Process.pid
    parent_socket.close
    number = child_socket.recv(MAX_RECV).to_i
    puts "#{Time.now} process #{pid}# receive #{number}"
    sleep 1
    child_socket.write("#{number} - #{number * 2}")
    child_socket.close
  end
  child_socket.close
  parent_socket
end

puts "send jobs"
sockets.each_with_index.each do |socket, index|
  socket.send((index + 1).to_s, 0)
end

puts "read result"
sockets.map do |socket|
  puts socket.recv(MAX_RECV)
  socket.close
end

# =&amp;gt; send jobs
#    read result
#    2016-04-03 11:30:34 +0800 process 21723# receive 12016-04-03 11:30:34 +0800 process 21724# receive 2
#    2016-04-03 11:30:34 +0800 process 21725# receive 3
#    1 - 2
#    2 - 4
#    3 - 6
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;由于进程间无法直接通信，也没有共享资源，所以我们不会做 &lt;code&gt;array &amp;lt;&amp;lt; result&lt;/code&gt; 的操作，上面使用 UnixSocket 进程通信&lt;/p&gt;
&lt;h3 id="更简单的使用多线程与多进程"&gt;更简单的使用多线程与多进程&lt;/h3&gt;
&lt;p&gt;使用 Parallel Gem 可以更简单的使用多进程与多线程&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require 'parallel'

list = 10.times.to_a
a = Proc.new { list.pop || Parallel::Stop }
result = Parallel.map(a, in_threads: 3) do |number|
  sleep 0.5
  puts "process #{Process.pid} receive #{number}\n"

  number = number.to_i
  number * 2
end

puts "result: #{result.join('-')}"

# =&amp;gt; process 21738 receive 9
#    process 21738 receive 7
#    process 21738 receive 8
#    process 21738 receive 5
#    process 21738 receive 6
#    process 21738 receive 4
#    process 21738 receive 1
#    process 21738 receive 2
#    process 21738 receive 3
#    process 21738 receive 0
#    result: 18-16-14-12-10-8-6-4-2-0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;更多详细的使用请自行围观使用文档&lt;/p&gt;
&lt;h2 id="结语"&gt;结语&lt;/h2&gt;
&lt;p&gt;本文中谈到的内容都比较基础的东西，但可能有不少的童鞋不了解，因为业务场景不同，可能一般不会涉及到这些，希望能帮助到大家&lt;/p&gt;

&lt;p&gt;抛开业务场景谈技术都是耍流氓！每种技术都有他适用的场景。&lt;/p&gt;

&lt;hr&gt;
&lt;h2 id="广告时间"&gt;广告时间&lt;/h2&gt;
&lt;p&gt;招 ruby 小伙伴，联系方式（Ruby China 专用通道）：jobs.rubychina###liulishuo.com（尽量用海外的邮箱给我们发邮件，理由你懂的）
更多信息参见 &lt;a href="https://www.liulishuo.com/hiringDetail?entity=engineer&amp;amp;job=Ruby%20%E5%B7%A5%E7%A8%8B%E5%B8%88&amp;amp;id=4" rel="nofollow" target="_blank" title=""&gt;这里&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/e96d504e42e73bdf25f14d5f8841964c.png" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>xjz19901211</author>
      <pubDate>Fri, 03 Jun 2016 19:53:00 +0800</pubDate>
      <link>https://ruby-china.org/topics/30208</link>
      <guid>https://ruby-china.org/topics/30208</guid>
    </item>
    <item>
      <title>一些 proc 的魔法</title>
      <description>&lt;p&gt;常常瞎折腾时遇到一个情况，让 proc 在一个 obj 的环境下执行&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;A&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Proc&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="vi"&gt;@val&lt;/span&gt;  &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="no"&gt;A&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;123&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;instance_eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; 246&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是，这样不能传参数啊！不能传参啊！！&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;才发现还有#instance_exec 方法，感谢 2、3、4 楼的同学：）&lt;/p&gt;</description>
      <author>xjz19901211</author>
      <pubDate>Wed, 03 Jun 2015 15:46:04 +0800</pubDate>
      <link>https://ruby-china.org/topics/25868</link>
      <guid>https://ruby-china.org/topics/25868</guid>
    </item>
    <item>
      <title>一个简单的 Ruby console</title>
      <description>&lt;p&gt;项目地址： &lt;a href="https://github.com/xjz19901211/console" rel="nofollow" target="_blank"&gt;https://github.com/xjz19901211/console&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;使用如下：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# my_console.rb
class MyConsole
  include Console

  define_cmd(:rand, "puts random number") do |max = 100|
    puts rand(max.to_i)
  end
end

MyConsole.new.start("my-console &amp;gt; ", "Use 'help' show all commands")
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ruby my_console.rb
Use 'help' show all commands
my-console &amp;gt; help
  help: show all commands
  exit: exit console
  rand: puts random number
my-console &amp;gt; rand
23
my-console &amp;gt; rand 10
4
my-console &amp;gt; rand 10100
2492
my-console &amp;gt; exit
$ 

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;非常简单快速就可以创建一个自己的简单 console
Console 这模块也非常简单，50 多行代码，大家可以吐槽下，哈哈&lt;/p&gt;

&lt;p&gt;是否有类似的 Gem 了？大家怎么看？&lt;/p&gt;</description>
      <author>xjz19901211</author>
      <pubDate>Sun, 17 May 2015 15:34:54 +0800</pubDate>
      <link>https://ruby-china.org/topics/25612</link>
      <guid>https://ruby-china.org/topics/25612</guid>
    </item>
    <item>
      <title>定义自己的 Migration method</title>
      <description>&lt;p&gt;文章转自我的 Blog &lt;a href="http://xjz.pw" rel="nofollow" target="_blank"&gt;http://xjz.pw&lt;/a&gt;，转载请注明出处&lt;/p&gt;

&lt;p&gt;PS: 文笔不好，大家见谅：）&lt;/p&gt;

&lt;hr&gt;
&lt;h3 id="瞎扯"&gt;瞎扯&lt;/h3&gt;
&lt;p&gt;在团队中，大家是否出现，DB 中的字段只有少数人知道这个字段是在做什么。或是折腾数据的同学，拿到我们的数据后，很多数据字段完全不知道是做啥的。&lt;/p&gt;

&lt;p&gt;为了让大家更好的理解数据库各字段的意义，通常的做法是为各个字段加上注释。而加注释的方式也很多，不同的方式可以达到不同的效果。比如：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;直接使用文档，这样加了字段还要去另一个地方修改文档，很可能造成不同步&lt;/li&gt;
&lt;li&gt;直接 migration 上加注释，这样只能方便自己团队内使用，如果其它不会 Ruby Rails 的同学来看，不够灵活&lt;/li&gt;
&lt;li&gt;直接把注释放到 DB 中去，在 rails 中，有一个很好的 gem &lt;a href="https://github.com/pinnymz/migration_comments" rel="nofollow" target="_blank" title=""&gt;MigrationComments&lt;/a&gt;，但是，新加的字段还好，如果是老的字段，要增加或是修改 comments 的话，会进行 alter table&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="准备折腾"&gt;准备折腾&lt;/h3&gt;
&lt;p&gt;我是喜欢能不修改或是影响原有功能就尽量不去动。所以我通过了创建一张表，专门来存放 comments，表的结构比较简单，如下&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;create_table(:database_comments) do |t|
  t.string :table_name, null: false
  t.string :column_name
  t.string :comment, null: false
  t.tags # split by ','
  t.index [:table_name, column_name], unique: true
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当 column_name 为空时，表示是一个 table 的 comment，否则就是对应 column 的 comment，顺便为每个列打上 tag，比如可以为 password/token 等标记为"secret"&lt;/p&gt;

&lt;p&gt;嗯，数据结构就这样定好了，接下来开始折腾了&lt;/p&gt;
&lt;h3 id="如何写comment"&gt;如何写 comment&lt;/h3&gt;
&lt;p&gt;既然有 MigrationComments 的例子在先，那么，我们也可以使用同样的方式，通过扩展 ActiveRecord 的 migration 来做这件事了。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def change
  set_table_coment :table_name, "A table comment"
  set_column_comment :table_name, :column_name, "A column comment"
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def change
  change_table :table_name do |t|
    t.comment "A table comment"
    t.change_comment :column_name, "A column comment"
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那么我们先来了解，ActiveRecord Migration 的实现，显然，我们通过现有的 MigrationComments + Google + ActiveRecord 源代码来了解是最快的&lt;/p&gt;

&lt;p&gt;通过 MigrationComment 代码和 Google 结果，了解到，想要扩展这些功能，需要修改 ActiveRecord 的代码的，那么接下来，就先了解下下 migration 是如何工作的吧&lt;/p&gt;
&lt;h3 id="ActiveRecord::Migration是如何工作的"&gt;ActiveRecord::Migration 是如何工作的&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;以 Rails 4.1.5 activerecord 为例&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id="up and down"&gt;up and down&lt;/h4&gt;
&lt;p&gt;大家应该都知道运行 migrate 的入口是&lt;code&gt;ActiveRecord::Migrator.migrate('./db/migrations')&lt;/code&gt;，如果不知道的话，现在也知道了：）&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/rails/rails/blob/v4.1.5/activerecord/lib/active_record/migration.rb#L782" rel="nofollow" target="_blank" title=""&gt;这里&lt;/a&gt;判断了是要 down 还是 up，也就是执行新的 migration 还是 rollback。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def migrate(migrations_paths, target_version = nil, &amp;amp;block)
  case
  when target_version.nil?
    up(migrations_paths, target_version, &amp;amp;block)
  when current_version == 0 &amp;amp;&amp;amp; target_version == 0
    []
  when current_version &amp;gt; target_version
    down(migrations_paths, target_version, &amp;amp;block)
  else
    up(migrations_paths, target_version, &amp;amp;block)
  end
end

# ...

def up(migrations_paths, target_version = nil)
  migrations = migrations(migrations_paths)
  migrations.select! { |m| yield m } if block_given?

  self.new(:up, migrations, target_version).migrate
end

def down(migrations_paths, target_version = nil, &amp;amp;block)
  migrations = migrations(migrations_paths)
  migrations.select! { |m| yield m } if block_given?

  self.new(:down, migrations, target_version).migrate
end

&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="Initialize migration table"&gt;Initialize migration table&lt;/h4&gt;
&lt;p&gt;而在&lt;a href="https://github.com/rails/rails/blob/v4.1.5/activerecord/lib/active_record/migration.rb#L909" rel="nofollow" target="_blank" title=""&gt;initialize&lt;/a&gt; 做了准备工作，初始了 schema_migrations 表&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def initialize(direction, migrations, target_version = nil)
  raise StandardError.new("This database does not yet support migrations") unless Base.connection.supports_migrations?

  @direction = direction
  @target_version = target_version
  @migrated_versions = nil
  @migrations = migrations

  validate(@migrations)

  Base.connection.initialize_schema_migrations_table
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="https://github.com/rails/rails/blob/v4.1.5/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb#L655" rel="nofollow" target="_blank" title=""&gt;initialize_schema_migrations_table&lt;/a&gt; 方法最终指向了 &lt;a href="https://github.com/rails/rails/blob/v4.1.5/activerecord/lib/active_record/schema_migration.rb#L21" rel="nofollow" target="_blank" title=""&gt;SchemaMigration.create_table&lt;/a&gt;&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def create_table(limit=nil)
  unless table_exists?
    version_options = {null: false}
    version_options[:limit] = limit if limit

    connection.create_table(table_name, id: false) do |t|
      t.column :version, :string, version_options
    end
    connection.add_index table_name, :version, unique: true, name: index_name
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="Run migrate"&gt;Run migrate&lt;/h4&gt;
&lt;p&gt;之后就是执行每一个&lt;a href="https://github.com/rails/rails/blob/v4.1.5/activerecord/lib/active_record/migration.rb#L568" rel="nofollow" target="_blank" title=""&gt;Migration#migrate&lt;/a&gt;，告诉它是要 up 还是 down&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def migrate(direction)
  return unless respond_to?(direction)

  case direction
  when :up then announce "migrating"
  when :down then announce "reverting"
  end

  time = nil
  ActiveRecord::Base.connection_pool.with_connection do |conn|
    time = Benchmark.measure do
      exec_migration(conn, direction)
    end
  end

  case direction
  when :up then announce "migrated (%.4fs)" % time.real; write
  when :down then announce "reverted (%.4fs)" % time.real; write
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="change? up? down?"&gt;change? up? down?&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://github.com/rails/rails/blob/v4.1.5/activerecord/lib/active_record/migration.rb#L589" rel="nofollow" target="_blank" title=""&gt;exec_migration&lt;/a&gt;&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def exec_migration(conn, direction)
  @connection = conn
  if respond_to?(:change)
    if direction == :down
      revert { change }
    else
      change
    end
  else
    send(direction)
  end
ensure
  @connection = nil
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们先来看&lt;code&gt;change&lt;/code&gt; method，它直接执行了，而在 Migration 类中并没有定义像&lt;code&gt;create_table&lt;/code&gt;/&lt;code&gt;change_table&lt;/code&gt;这里 method，继续可以看到 Migration 定义了 method_missing&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def method_missing(method, *arguments, &amp;amp;block)
  arg_list = arguments.map{ |a| a.inspect } * ', '

  say_with_time "#{method}(#{arg_list})" do
    unless @connection.respond_to? :revert
      unless arguments.empty? || [:execute, :enable_extension, :disable_extension].include?(method)
        arguments[0] = proper_table_name(arguments.first, table_name_options)
        arguments[1] = proper_table_name(arguments.second, table_name_options) if method == :rename_table
      end
    end
    return super unless connection.respond_to?(method)
    connection.send(method, *arguments, &amp;amp;block)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;都直接转发给 connettion(ActiveRecord::Base.connection) 了，而所有的 connection 都是由继承自&lt;a href="https://github.com/rails/rails/blob/v4.1.5/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb" rel="nofollow" target="_blank" title=""&gt;AbstractAdapter&lt;/a&gt;的类的实例&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ActiveRecord::Base.connection.method(:create_table).source_location
ActiveRecord::Base.connection.method(:change_table).source_location
ActiveRecord::Base.connection.method(:add_column).source_location
ActiveRecord::Base.connection.method(:change_column).source_location
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;由上可知道 migration 中的这些 method 都是来自&lt;a href="https://github.com/rails/rails/blob/v4.1.5/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb" rel="nofollow" target="_blank" title=""&gt;SchemaStatements&lt;/a&gt;，或是在各 db 的 adapter 中做了一些修改&lt;/p&gt;

&lt;p&gt;同样，由原代码可以看出，像&lt;code&gt;create_table&lt;/code&gt; / &lt;code&gt;change_table&lt;/code&gt; block 中的 method 都是由一个&lt;a href="https://github.com/rails/rails/blob/v4.1.5/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb#L48" rel="nofollow" target="_blank" title=""&gt;单独的类&lt;/a&gt;来定义的&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def create_table(table_name, options = {})
  td = create_table_definition table_name, options[:temporary], options[:options], options[:as]

  if !options[:as]
    unless options[:id] == false
      pk = options.fetch(:primary_key) {
        Base.get_primary_key table_name.to_s.singularize
      }

      td.primary_key pk, options.fetch(:id, :primary_key), options
    end

    yield td if block_given?
  end

  if options[:force] &amp;amp;&amp;amp; table_exists?(table_name)
    drop_table(table_name, options)
  end

  execute schema_creation.accept td
  td.indexes.each_pair { |c,o| add_index table_name, c, o }
end

# ...

private
def create_table_definition(name, temporary, options, as = nil)
  TableDefinition.new native_database_types, name, temporary, options, as
end
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="Migration revert"&gt;Migration revert&lt;/h4&gt;
&lt;p&gt;到这里我们已经了解到了，如何去写一个自己的 migration method 了，但是还没完呢，在 migrate 时可以看到，如果以 down 方式运行会 &lt;code&gt;revert { change }&lt;/code&gt;，那么这个 revert 是怎么实现的呢？继续看&lt;a href="https://github.com/rails/rails/blob/v4.1.5/activerecord/lib/active_record/migration.rb#L473" rel="nofollow" target="_blank" title=""&gt;代码&lt;/a&gt;&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def revert(*migration_classes)
  run(*migration_classes.reverse, revert: true) unless migration_classes.empty?
  if block_given?
    if @connection.respond_to? :revert
      @connection.revert { yield }
    else
      recorder = CommandRecorder.new(@connection)
      @connection = recorder
      suppress_messages do
        @connection.revert { yield }
      end
      @connection = recorder.delegate
      recorder.commands.each do |cmd, args, block|
        send(cmd, *args, &amp;amp;block)
      end
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里又出现新玩意了：&lt;a href="https://github.com/rails/rails/blob/v4.1.5/activerecord/lib/active_record/migration/command_recorder.rb" rel="nofollow" target="_blank" title=""&gt;CommandRecorder&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;这里比较迷惑的地方是&lt;code&gt;@connection.revert { yield }&lt;/code&gt;，外面传入的明明是&lt;code&gt;revert { change }&lt;/code&gt;，
change 中的 methods 作用域还是当前 Migration 实例，这样传进来可以执行？，看了好一会才回想起，
&lt;code&gt;method_missing&lt;/code&gt;时会 call &lt;code&gt;@connection.xxx&lt;/code&gt;，而上次的代码，在执行前做了&lt;code&gt;@connection = recorder&lt;/code&gt;了，写成一坨了。。。&lt;/p&gt;

&lt;p&gt;然后看了 CommandRecorder 中的代码，相信已经没人会对 change 可以同时支持 migrate 与 rollback 感到神奇了。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# record +command+. +command+ should be a method name and arguments.
# For example:
#
# recorder.record(:method_name, [:arg1, :arg2])
def record(*command, &amp;amp;block)
  if @reverting
    @commands &amp;lt;&amp;lt; inverse_of(*command, &amp;amp;block)
  else
    @commands &amp;lt;&amp;lt; (command &amp;lt;&amp;lt; block)
  end
end

# Returns the inverse of the given command. For example:
#
# recorder.inverse_of(:rename_table, [:old, :new])
# # =&amp;gt; [:rename_table, [:new, :old]]
#
# This method will raise an +IrreversibleMigration+ exception if it cannot
# invert the +command+.
def inverse_of(command, args, &amp;amp;block)
  method = :"invert_#{command}"
  raise IrreversibleMigration unless respond_to?(method, true)
  send(method, args, &amp;amp;block)
end


[:create_table, :create_join_table, :rename_table, :add_column, :remove_column,
  :rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps,
  :change_column_default, :add_reference, :remove_reference, :transaction,
  :drop_join_table, :drop_table, :execute_block, :enable_extension,
  :change_column, :execute, :remove_columns, :change_column_null # irreversible methods need to be here too
].each do |method|
  class_eval &amp;lt;&amp;lt;-EOV, __FILE__, __LINE__ + 1
    def #{method}(*args, &amp;amp;block) # def create_table(*args, &amp;amp;block)
      record(:"#{method}", args, &amp;amp;block) # record(:create_table, args, &amp;amp;block)
    end # end
  EOV
end

{ transaction: :transaction,
  execute_block: :execute_block,
  create_table: :drop_table,
  create_join_table: :drop_join_table,
  add_column: :remove_column,
  add_timestamps: :remove_timestamps,
  add_reference: :remove_reference,
  enable_extension: :disable_extension
}.each do |cmd, inv|
  [[inv, cmd], [cmd, inv]].uniq.each do |method, inverse|
    class_eval &amp;lt;&amp;lt;-EOV, __FILE__, __LINE__ + 1
      def invert_#{method}(args, &amp;amp;block) # def invert_create_table(args, &amp;amp;block)
        [:#{inverse}, args, block] # [:drop_table, args, block]
      end # end
    EOV
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;神秘的面纱揭开后，我想大家都不会对真像感到惊奇了...&lt;/p&gt;

&lt;p&gt;通过重定义所有的 migration 中的方法，这里方法执行的只是&lt;code&gt;record(:xxx, args, &amp;amp;block)&lt;/code&gt;,
再定义好每个 methods 对应的 invert 操作，在 invert 操作中可以根据正常执行的参数得出反向操作的参数，
最后在Migration#revert中再把这些command执行&lt;/p&gt;
&lt;h3 id="开始折腾"&gt;开始折腾&lt;/h3&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def change
  add_column_comment :users, :mobile, "user mobile'"
  add_table_comment :users, "user table'"

  add_comments :table_name do |c|
    c.table 'table comments'
    c.column :a, 'column comment'
    c.column :s, 'comment', sensitive: true
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;相信到这里时，上面的的实现方式大家应该已经会了，接下来，就可以随意折腾 ActiveRecord Migration&lt;/p&gt;

&lt;p&gt;本来只是想讲讲写一个 migratino commnet 的 gem，想到什么写什么，然后就把 Migration 的实现都讲了，
那么，折腾这事，就交给大家了，我折腾好的代码在公司项目上，就不拉出来了：）&lt;/p&gt;
&lt;h3 id="Reference"&gt;Reference&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://github.com/rails/rails/tree/v4.1.5/activerecord" rel="nofollow" target="_blank" title=""&gt;Rails ActiveRecord&lt;/a&gt;&lt;/p&gt;</description>
      <author>xjz19901211</author>
      <pubDate>Sun, 05 Apr 2015 15:45:32 +0800</pubDate>
      <link>https://ruby-china.org/topics/25004</link>
      <guid>https://ruby-china.org/topics/25004</guid>
    </item>
    <item>
      <title>Ruby 的 symbol, 感觉意义不大。。</title>
      <description>&lt;p&gt;虽然速度快了那么一点点，但却让我们陷入选择字符串还是 symbol 的蛋疼事中，更是出现像
&lt;code&gt;Hash#stringify_keys&lt;/code&gt;和&lt;code&gt;Hash#symbolize_keys&lt;/code&gt;这种方法，被调用者得到一个 hash 不得不去想一下传入的是 symbol 还是 string。&lt;/p&gt;

&lt;p&gt;而 hash 的写法也出现了&lt;code&gt;{:xx =&amp;gt; 1}&lt;/code&gt; 与 &lt;code&gt;{xx: 1}&lt;/code&gt;,  前者可以是任何对象，而后者的 key 就是一个 symbol。。&lt;/p&gt;

&lt;p&gt;今天闲的无聊测试了一下性能&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;sym_keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="n"&gt;str_keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;str_keys&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"number_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_asdf"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;sym_keys&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"number_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_asdf"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_sym&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'benchmark'&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000000&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;length&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;str_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt; &lt;span class="n"&gt;sym_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="no"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bm&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;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; 
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; string write"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;str_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;str_keys&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="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; 
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; symbol write"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;sym_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sym_keys&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="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; 

    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; string read"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;str_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;str_keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&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="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; symbol read"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;sym_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sym_keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&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;end&lt;/span&gt; 
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ruby 2.1.3 结果如下&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;       user     system      total        real
100000 string write  0.060000   0.000000   0.060000 (  0.064129)
100000 symbol write  0.040000   0.000000   0.040000 (  0.044240)
100000 string read  0.090000   0.000000   0.090000 (  0.098106)
100000 symbol read  0.050000   0.000000   0.050000 (  0.052136)
       user     system      total        real
1000000 string write  1.030000   0.040000   1.070000 (  1.066663)
1000000 symbol write  0.810000   0.020000   0.830000 (  0.833698)
1000000 string read  1.010000   0.010000   1.020000 (  1.021844)
1000000 symbol read  0.710000   0.000000   0.710000 (  0.713959)

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;从数据上看，symbol 相对于 string 的性能提升并没有多大，而我在 2.1.2 下测试得到的是，在 100W 数据下，symbol 反而变慢了，可能是 symbol 表太大了，查询也变慢了？这就不清楚了&lt;/p&gt;

&lt;p&gt;抛砖引玉，大家发表下看法？我测试的只是 hash, 在其它情况下是否会有更优异的表现？&lt;/p&gt;

&lt;p&gt;写着有点离题了，我疑惑的是 symbol 存在的意义，现在感觉他的出现，带来的麻烦比带来的好处更多。。&lt;/p&gt;</description>
      <author>xjz19901211</author>
      <pubDate>Sun, 16 Nov 2014 12:50:59 +0800</pubDate>
      <link>https://ruby-china.org/topics/22705</link>
      <guid>https://ruby-china.org/topics/22705</guid>
    </item>
    <item>
      <title>上次说的资源权限管理的功能，写成 Gem 包了</title>
      <description>&lt;p&gt;参见原贴： &lt;a href="http://ruby-china.org/topics/16284" rel="nofollow" target="_blank"&gt;http://ruby-china.org/topics/16284&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Gem 包： &lt;a href="https://github.com/xjz19901211/resource_authoriation" rel="nofollow" target="_blank"&gt;https://github.com/xjz19901211/resource_authoriation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;这 Gem 完全可以配合 CanCan 来使用，由于这种权限控制会产生大量数量，所以这个做的做精细的控制，而 CanCan 做在外层，比如不需要精细控制和定制的，些都由 CanCan 来控制&lt;/p&gt;

&lt;p&gt;把这 Gem 包放入了自己项目中，正在试用，哈哈&lt;/p&gt;

&lt;p&gt;由于英语非常烂，所以 rspec 说明的句子基本都是用会的几个单词拼出来了，基本上没管语法，或许只有自己能看懂？哈哈&lt;/p&gt;</description>
      <author>xjz19901211</author>
      <pubDate>Sun, 23 Feb 2014 13:03:22 +0800</pubDate>
      <link>https://ruby-china.org/topics/17429</link>
      <guid>https://ruby-china.org/topics/17429</guid>
    </item>
    <item>
      <title>又写了一个项目的权限系统，大家觉得怎么样？</title>
      <description>&lt;p&gt;没写成 Gem，放在项目里头，下面链接里说了这个系统的大概情况，大家觉得怎么样？&lt;/p&gt;

&lt;p&gt;&lt;a href="http://only-x.com/2013/12/16-rails-permission.html" rel="nofollow" target="_blank"&gt;http://only-x.com/2013/12/16-rails-permission.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;说下好与不好的地方，或是新的想法，大家一起学习学习&lt;/p&gt;</description>
      <author>xjz19901211</author>
      <pubDate>Wed, 18 Dec 2013 15:16:04 +0800</pubDate>
      <link>https://ruby-china.org/topics/16284</link>
      <guid>https://ruby-china.org/topics/16284</guid>
    </item>
    <item>
      <title>受不了 Emacs 的缩进了，求指点</title>
      <description>&lt;p&gt;缩进格式如下，没等宽有点变形。。：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a = if b
       2
     else
       3
     end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我喜欢的是：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a = if b
  2
else
  3
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;求解决，谢谢&lt;/p&gt;</description>
      <author>xjz19901211</author>
      <pubDate>Wed, 11 Dec 2013 14:07:07 +0800</pubDate>
      <link>https://ruby-china.org/topics/16136</link>
      <guid>https://ruby-china.org/topics/16136</guid>
    </item>
    <item>
      <title>写了一个 Chrome 密码生成扩展，求围观</title>
      <description>&lt;p&gt;&lt;strong&gt;地址&lt;/strong&gt;: &lt;a href="https://chrome.google.com/webstore/detail/pwd/ogehigndemcohkpoceaknomlnnpdclhh?hl=en-US" rel="nofollow" target="_blank"&gt;https://chrome.google.com/webstore/detail/pwd/ogehigndemcohkpoceaknomlnnpdclhh?hl=en-US&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;可以为你在不同的网站下生成不同的密码，让密码不再重复。
扩展的界面比较烂，懒的去美化。。&lt;/p&gt;

&lt;p&gt;原代码在这里：&lt;a href="https://github.com/xjz19901211/ChromePwdExt" rel="nofollow" target="_blank"&gt;https://github.com/xjz19901211/ChromePwdExt&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;第一次写扩展，了解了下 chrome 的扩展，比较有意思
想了解的同学可以看看我博客里的简单介绍 &lt;a href="http://only-x.com/2013/11/15-chrome-extensions.html" rel="nofollow" target="_blank"&gt;http://only-x.com/2013/11/15-chrome-extensions.html&lt;/a&gt;&lt;/p&gt;</description>
      <author>xjz19901211</author>
      <pubDate>Tue, 19 Nov 2013 11:51:15 +0800</pubDate>
      <link>https://ruby-china.org/topics/15645</link>
      <guid>https://ruby-china.org/topics/15645</guid>
    </item>
    <item>
      <title>发现 Puma 用 JRuby 来跑确实快了好多</title>
      <description>&lt;p&gt;拿 unicorn 和 puam 来测了下&lt;/p&gt;

&lt;p&gt;虚拟机，4 核 512M, ruby 2.0 
都使用了 4 worker,  puma 使用了 16 threads
测试 ab -c 100 -n 10000&lt;/p&gt;

&lt;p&gt;rails 3.2.13 下，puma 比 unicorn 差了点 几十到一百毫秒
rails 4.0.1 下，puma 比 unicorn 快了点也是几十到一百毫秒间&lt;/p&gt;

&lt;p&gt;在 4.0.1 jruby 下，puma 1 worker, 32 threads, 从原来平均每个请求 700 多变成了 530 左右，相差比较大&lt;/p&gt;

&lt;p&gt;而内存方面，感觉相差没个几十 M，新项目启动后也就 40M 的样子。。&lt;/p&gt;

&lt;p&gt;大家怎么看？&lt;/p&gt;</description>
      <author>xjz19901211</author>
      <pubDate>Wed, 06 Nov 2013 18:15:04 +0800</pubDate>
      <link>https://ruby-china.org/topics/15347</link>
      <guid>https://ruby-china.org/topics/15347</guid>
    </item>
    <item>
      <title>Rails 使用 rspec 测试 controller 的疑惑</title>
      <description>&lt;p&gt;有如下测试代码&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;
&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'xxx'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
   &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s1"&gt;'/aaa'&lt;/span&gt;
   &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s1"&gt;'/bbb'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;测试时发现，两次请求都使用的同一个 controller 实例，然后我在 filter 中有一段类似下面的代码&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;my_filter&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="vi"&gt;@a&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@a&lt;/span&gt;
   &lt;span class="vi"&gt;@a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'xxx'&lt;/span&gt;

   &lt;span class="c1"&gt;# @a ||= 'xxx'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在如上代码中，&lt;code&gt;my_filter&lt;/code&gt;中被注释的代码执行两次请求时没一点问题，每次&lt;a href="/a" class="user-mention" title="@a"&gt;&lt;i&gt;@&lt;/i&gt;a&lt;/a&gt;都是 nil
但是到&lt;code&gt;my_filter&lt;/code&gt;当前没被注释的代码中，除了第一次&lt;a href="/a" class="user-mention" title="@a"&gt;&lt;i&gt;@&lt;/i&gt;a&lt;/a&gt;是 nil, 后面再次 request 时还是上一次的值&lt;/p&gt;

&lt;p&gt;现在我想不明白，&lt;code&gt;my_filter&lt;/code&gt; 为那么会出现上会情况 &lt;code&gt;||=&lt;/code&gt;  不是 &lt;code&gt;@a = @a || 'xxx'&lt;/code&gt; 吗？&lt;/p&gt;</description>
      <author>xjz19901211</author>
      <pubDate>Wed, 30 Oct 2013 14:56:06 +0800</pubDate>
      <link>https://ruby-china.org/topics/15157</link>
      <guid>https://ruby-china.org/topics/15157</guid>
    </item>
    <item>
      <title>Linode 拿试用的进没用几分钟就被封了。。</title>
      <description>&lt;p&gt;登陆到服务器上安装个 git 再装个 rvm 完成后就挂了。 &lt;/p&gt;

&lt;p&gt;&lt;code&gt;This IP has been banned.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;为啥这么悲剧？&lt;/p&gt;

&lt;p&gt;会封多久？&lt;/p&gt;</description>
      <author>xjz19901211</author>
      <pubDate>Mon, 07 Oct 2013 12:23:02 +0800</pubDate>
      <link>https://ruby-china.org/topics/14563</link>
      <guid>https://ruby-china.org/topics/14563</guid>
    </item>
    <item>
      <title>看了别人做自己专用的个人主页我也给自己做了个</title>
      <description>&lt;p&gt;&lt;a href="http://only-y.com" rel="nofollow" target="_blank"&gt;http://only-y.com&lt;/a&gt;
完成一半了，哈哈&lt;/p&gt;</description>
      <author>xjz19901211</author>
      <pubDate>Fri, 13 Sep 2013 21:19:11 +0800</pubDate>
      <link>https://ruby-china.org/topics/14107</link>
      <guid>https://ruby-china.org/topics/14107</guid>
    </item>
    <item>
      <title>服务器方面的问题，求支招</title>
      <description>&lt;p&gt;自己写了一个项目部署管理的，目前遇到的问题：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;我用 Net::SSH 登陆到服务器后，用 sudo apt-get 安装软件要输密码，我在自己的虚拟机里可以正常安装的。怎么解决 sudo 操作要密码的问题？&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;服务器安装软件我都是通过 sudo apt-get 一个个的安装，有什么简单的庘，可以直接 ssh 到服务器上，然后自动安装指定软件的？&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;虽说现在已经可以在自己虚拟机里自动部署上一个项目并运行了，但功能还是弱爆了。。。&lt;/p&gt;</description>
      <author>xjz19901211</author>
      <pubDate>Thu, 08 Aug 2013 19:08:06 +0800</pubDate>
      <link>https://ruby-china.org/topics/13174</link>
      <guid>https://ruby-china.org/topics/13174</guid>
    </item>
    <item>
      <title>rails api 文档生成器</title>
      <description>&lt;p&gt;本来想用 rdoc, 但是总不能每个 action 上都写上 api 路径和地址什么的吧，又不知道别的工具，于是自己写了个，可以自动取得每个 Action 的地址请求方法，前些天写的，上两天才丢到 rubygems 里面。。&lt;/p&gt;

&lt;p&gt;源代码没丢 github 上，直接装了 gem 看吧。。。&lt;/p&gt;

&lt;p&gt;说明文章：&lt;a href="http://only-x.com/2013/07/12-rails-api-document.html" rel="nofollow" target="_blank"&gt;http://only-x.com/2013/07/12-rails-api-document.html&lt;/a&gt;&lt;/p&gt;</description>
      <author>xjz19901211</author>
      <pubDate>Thu, 11 Jul 2013 22:40:41 +0800</pubDate>
      <link>https://ruby-china.org/topics/12440</link>
      <guid>https://ruby-china.org/topics/12440</guid>
    </item>
    <item>
      <title>最近折腾一个用来部署的应用体会</title>
      <description>&lt;p&gt;项目地址： &lt;a href="https://github.com/xjz19901211/deploy_helper" rel="nofollow" target="_blank"&gt;https://github.com/xjz19901211/deploy_helper&lt;/a&gt;
目标：自动化配置服务器，部署应用，及服务器、应用，数据库的管理&lt;/p&gt;
&lt;h2 id="起源"&gt;起源&lt;/h2&gt;
&lt;p&gt;就以前被公司的应用部署折腾的蛋疼了，老是重复着这蛋疼的部署，于是想折腾一个自动  化的 web 管理应用，然后自己休息时间断断续续的码着&lt;/p&gt;
&lt;h2 id="目前"&gt;目前&lt;/h2&gt;
&lt;p&gt;使用 mina 脚本对一个可 ssh 上去的服务器将对应的应用弄上去，跑起来还没有。&lt;/p&gt;

&lt;p&gt;感觉略麻烦，感觉工作量略大，要弄好还得几个月，今天写的有些无聊了，就上来扯扯。&lt;/p&gt;

&lt;p&gt;上次刚通过 ssh 可以半自动的装一些应用要用到的包，本要想通过一个工具来自动安装需要的环境，还没找到。。&lt;/p&gt;

&lt;p&gt;然后数据库自动创建做了一些还没接上来，坚持不下来了。。&lt;/p&gt;

&lt;p&gt;今天想解决配置服务器及应用的 log 问题，log 放服务器上，要在网页上即时看到又是一个麻烦事，然后感觉好多知道差不多可以这样做，但又感觉太麻烦的事弄的蛋疼了。。&lt;/p&gt;
&lt;h2 id="其它"&gt;其它&lt;/h2&gt;
&lt;p&gt;然后前几天玩 html5 canvas 做动画又玩的比较 happily, 又想着去用用一些 canvas 游戏引擎来做点东西玩，顿时，手里敲着 ruby 代码，心里想着 JS。。。&lt;/p&gt;

&lt;p&gt;终于，我决定来论坛上来扯扯，然后先放下这个项目，以后兴趣再上来时接着弄，昨天用 Canvas 做了点小效果放到 blog 上，感觉还行，哈哈&lt;/p&gt;</description>
      <author>xjz19901211</author>
      <pubDate>Sat, 01 Jun 2013 13:33:44 +0800</pubDate>
      <link>https://ruby-china.org/topics/11400</link>
      <guid>https://ruby-china.org/topics/11400</guid>
    </item>
    <item>
      <title>项目隔个一两天或三五天就报 500 错</title>
      <description>&lt;p&gt;如题，不管进什么路径都会报 500 错误&lt;/p&gt;

&lt;p&gt;Rails 错误日志：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A SystemStackError occurred in controller_name#action:

 stack level too deep
 vendor/bundle/ruby/1.9.1/gems/activesupport-3.2.11/lib/active_support/notifications/instrumenter.rb:23
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后我把项目重启一下就好了。。。&lt;/p&gt;

&lt;p&gt;项目在服务器上是 Nginx+Unicorn，报错时看了 Nginx 日志，显示 time out，看 Unicorn 日志没有错误日志，真心不知道怎么解了&lt;/p&gt;

&lt;p&gt;请大家出出主意，谢谢&lt;/p&gt;

&lt;p&gt;现在正在把服务器的更新包都更新下，bundle update 以前也用过了，还是没用。。。&lt;/p&gt;</description>
      <author>xjz19901211</author>
      <pubDate>Thu, 25 Apr 2013 10:49:49 +0800</pubDate>
      <link>https://ruby-china.org/topics/10491</link>
      <guid>https://ruby-china.org/topics/10491</guid>
    </item>
  </channel>
</rss>
