<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>IChou (小虫)</title>
    <link>https://ruby-china.org/IChou</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>[RubyConfChina2017 话题分享] 从零开始，打造 Ruby 项目的容器化集成工具</title>
      <description>&lt;p&gt;&lt;a href="https://speakerdeck.com/ivanchou/cong-ling-kai-shi-da-zao-ruby-xiang-mu-de-rong-qi-hua-ji-cheng-gong-ju" rel="nofollow" target="_blank" title=""&gt;slides&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;关于分享的主题其实之前有犹豫很久，本来是想分享我们的工具，但是又觉得我们做得不够好，不足以拿出来见人。&lt;/p&gt;

&lt;p&gt;于是就改为分享我们这一路的经历，把我们怎么一步步建立这个体系的过程讲出来，希望能帮到一些打算要做容器化迁移的同学们，告诉他们需要考虑哪些问题，经过哪些步骤，少走弯路。&lt;/p&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;如何将打包内容模块化&lt;/li&gt;
&lt;li&gt;如何通过自己编写工具简化工作内容&lt;/li&gt;
&lt;li&gt;如何进一步偷懒&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;在这次演讲之后的讨论中，我们也收获了很多，发现了一些之前没有太注意的问题。&lt;/p&gt;

&lt;p&gt;正如大家所讨论的，演讲中给大家展示的方案中有不少 tricks，也没有保证各个环境下镜像的一致性，这些都是速递易以后需要去改进的方向。&lt;/p&gt;

&lt;p&gt;感谢大家，长期处于一种状态下，视野容易被限制，所以也希望大家能分享出自己的 docker 部署方案，互相交流，互相学习，探索出一种能媲美 capistrano 的模式&lt;/p&gt;

&lt;p&gt;参考资料：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://github.com/discourse/discourse_docker" rel="nofollow" target="_blank"&gt;https://github.com/discourse/discourse_docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rancher/rancher" rel="nofollow" target="_blank"&gt;https://github.com/rancher/rancher&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/SamSaffron/pups" rel="nofollow" target="_blank"&gt;https://github.com/SamSaffron/pups&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description>
      <author>IChou</author>
      <pubDate>Tue, 19 Sep 2017 12:46:29 +0800</pubDate>
      <link>https://ruby-china.org/topics/34177</link>
      <guid>https://ruby-china.org/topics/34177</guid>
    </item>
    <item>
      <title>用 method chain 方式来包装 HTTP API 调用</title>
      <description>&lt;p&gt;最近项目组有一个关于人脸识别相关的需求，需要用到 FacePP 的服务，而官方提供的 Ruby SDK 自 2013 年提交以来，从未更新过，Issue 和 PR 也无人处理。So, 我被安排去『重新』实现 FacePP 的 SDK。&lt;/p&gt;

&lt;p&gt;官方 SDK 地址：&lt;a href="https://github.com/FacePlusPlus/facepp-ruby-sdk" rel="nofollow" target="_blank"&gt;https://github.com/FacePlusPlus/facepp-ruby-sdk&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;本以为这是个苦差事，难鹅，当我拉下官方的 SDK 源码时，却被华丽丽的惊艳到了 w(°o°)w&lt;/p&gt;

&lt;p&gt;通常来说我们写 SDK 的常规思路：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;实现一个通用的请求处理，包括 url 拼装，参数处理，签名等&lt;/li&gt;
&lt;li&gt;根据各个接口去实现一个方法（method）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;而这个 SDK 的实现，直接使用了 Ruby 里的大杀器 —— &lt;strong&gt;元编程&lt;/strong&gt;，进而毫不费力的实现了链式调用&lt;/p&gt;
&lt;h2 id="链式调用"&gt;链式调用&lt;/h2&gt;
&lt;p&gt;关于链式调用相信大家都不陌生，最常见的就是 ActiveRecord 的查询方法&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Article&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="s1"&gt;'id &amp;gt; 10'&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;20&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'id desc'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;only&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:where&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;个人认为，链式调用最大的优点就是优雅，相比起把所有参数放在一个 options(hash) 里喂给一个方法的调用方式，链式调用的可读性明显更好，参数组合也更自由。&lt;/p&gt;

&lt;p&gt;FacePP 的 SDK 里面实现的链式调用&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;FacePP&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="s1"&gt;'YOUR_API_KEY'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'YOUR_API_SECRET'&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detect&lt;/span&gt; &lt;span class="ss"&gt;url: &lt;/span&gt;&lt;span class="s1"&gt;'/tmp/0.jpg'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="实现原理"&gt;实现原理&lt;/h2&gt;
&lt;p&gt;链式调用的实现原理其实很好理解，每当你调用一个链式对象的某个方法时，返回一个该对象所属类的新实例即可&lt;/p&gt;

&lt;p&gt;比如在 ActiveRecord 中，当你调用 Model &lt;code&gt;Article&lt;/code&gt; 的 &lt;code&gt;where&lt;/code&gt; 方法时，它返回了一个 &lt;code&gt;ActiveRecord::Relation&lt;/code&gt; 实例，假定它叫 &lt;code&gt;relation_1&lt;/code&gt;。&lt;code&gt;.limit(20)&lt;/code&gt; 其实调用的是&lt;code&gt;relation_1&lt;/code&gt; 的 &lt;code&gt;limit&lt;/code&gt; 方法，然后返回一个新的 &lt;code&gt;ActiveRecord::Relation&lt;/code&gt; 实例 &lt;code&gt;relation_2&lt;/code&gt;，以此类推。&lt;/p&gt;

&lt;p&gt;所以当你只是调用 &lt;code&gt;ActiveRecord::Relation&lt;/code&gt; 的各种查询方法时，并没有真的触发查询，而是不停的返回新的 &lt;code&gt;ActiveRecord::Relation&lt;/code&gt; 实例，直到遇到第一个需要取值的调用，才会触发查询，并返回数据。&lt;/p&gt;

&lt;p&gt;以上只是简单的描述，实际上 &lt;code&gt;ActiveRecord::Relation&lt;/code&gt; 的实现还挺复杂的，有兴趣可以去看看源码：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/rails/rails/blob/5-1-stable/activerecord/lib/active_record/relation.rb" rel="nofollow" target="_blank"&gt;https://github.com/rails/rails/blob/5-1-stable/activerecord/lib/active_record/relation.rb&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;相比起数据库查询的复杂性，http api 的复杂度就算很低了，因此在 http 接口上实现链式调用，其实可以很容易。&lt;/p&gt;
&lt;h2 id="简单实现"&gt;简单实现&lt;/h2&gt;
&lt;p&gt;这个部分我就直接贴 FacePP SDK 的源码了：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# https://github.com/FacePlusPlus/facepp-ruby-sdk/blob/master/lib/facepp/client.rb&lt;/span&gt;
&lt;span class="c1"&gt;# 代码略有删减&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FacePP&lt;/span&gt;
  &lt;span class="no"&gt;APIS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s1"&gt;'/detection/detect'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s1"&gt;'/info/get_image'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;# ...&lt;/span&gt;
    &lt;span class="p"&gt;]&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;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="no"&gt;APIS&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;api&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;
      &lt;span class="n"&gt;breadcrumbs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&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="n"&gt;breadcrumbs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..-&lt;/span&gt;&lt;span class="mi"&gt;2&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;breadcrumb&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance_variable_defined?&lt;/span&gt; &lt;span class="s2"&gt;"@&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;breadcrumb&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
          &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance_variable_set&lt;/span&gt; &lt;span class="s2"&gt;"@&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;breadcrumb&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
          &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;singleton_class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class_eval&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="n"&gt;breadcrumb&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;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance_variable_get&lt;/span&gt; &lt;span class="s2"&gt;"@&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;breadcrumb&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define_singleton_method&lt;/span&gt; &lt;span class="n"&gt;breadcrumbs&lt;/span&gt;&lt;span class="p"&gt;[&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="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="c1"&gt;# send a request to #{api} with #{args}&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&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;ol&gt;
&lt;li&gt;先预置了一个 api path 的列表，相当于一个路由表。&lt;/li&gt;
&lt;li&gt;当 FacePP 被 new 的时候，会逐条解析这个路由表，把每条路由以 &lt;code&gt;/&lt;/code&gt; 作分割符解析为数组。&lt;/li&gt;
&lt;li&gt;遍历数组至倒数第二个元素，把每个元素变成上层对象的一个同名实例变量，其值是一个新的 &lt;code&gt;Object&lt;/code&gt; 实例，并通过 &lt;code&gt;attr_reader&lt;/code&gt; 为该实例变量添加访问方法&lt;/li&gt;
&lt;li&gt;将数组的最后一个元素变成上层对象的一个 &lt;code&gt;singleton_method&lt;/code&gt;, 里面包含了真正的请求代码。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;其成果就是，我们可以以&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detect&lt;/span&gt; &lt;span class="ss"&gt;url: &lt;/span&gt;&lt;span class="s1"&gt;'/tmp/0.jpg'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样的方式，『形象的』调用 FacePP 的各个接口。当有新增接口的时候，也只需要添加一条路由即可。&lt;/p&gt;

&lt;p&gt;作者 &lt;a href="https://github.com/MaskRay" rel="nofollow" target="_blank" title=""&gt;&lt;/a&gt;&lt;a href="/MaskRay" class="user-mention" title="@MaskRay"&gt;&lt;i&gt;@&lt;/i&gt;MaskRay&lt;/a&gt; 用一个普通的 &lt;code&gt;Object&lt;/code&gt; 替代了 &lt;code&gt;ActiveRecord::Relation&lt;/code&gt; 的功能，我觉得是一种灰常 geek 的方式。因为这个东西足够简单，我们并没有必要去造一个自己的 &lt;code&gt;Relation&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="改进空间"&gt;改进空间&lt;/h2&gt;
&lt;p&gt;假定我们的需求场景再复杂一点&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;包含的项目多，接口数量庞大，接口变动相对频繁&lt;/li&gt;
&lt;li&gt;常用的 4 种 http 请求方式都需要被支持（FacePP 所有接口都是 POST）&lt;/li&gt;
&lt;li&gt;被调用的路由很长，但前面有一大段是几乎不会变的前缀&lt;/li&gt;
&lt;li&gt;各个接口的的请求实现方式可能不完全一样&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;由此，我想到了一些改进思路&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;抛弃预置路由表，通过覆写 method_missing 方法，在被调用的时候才去生成链式对象&lt;/li&gt;
&lt;li&gt;以 &lt;code&gt;get|post|put|delete&lt;/code&gt; 或 &lt;code&gt;index|show|create|update|destroy|save&lt;/code&gt; 作为最后一层发起请求的方法来结束一串调用&lt;/li&gt;
&lt;li&gt;为链式对象 &lt;code&gt;Object.new&lt;/code&gt; 增加一些实例变量，比如 &lt;code&gt;@host&lt;/code&gt;， &lt;code&gt;@path&lt;/code&gt; 等，初始化时可以通过附加参数指定前缀等参数&lt;/li&gt;
&lt;li&gt;允许传入一个 block&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;在我所在的公司，有一个内部 gem 叫 &lt;code&gt;services_support&lt;/code&gt;, 专门用来处理系统间的 api 调用。这个 gem 实现了两种接口调用方式：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;一种是诸如 &lt;code&gt;ServicesSupport::BMS.post 'api/orders', args&lt;/code&gt; 这样将 path 作为参数传入&lt;/li&gt;
&lt;li&gt;一种是预定义一个 &lt;code&gt;ServicesSupport::BMS.create_order(args)&lt;/code&gt; 方法来调用&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;实际使用中，几乎所有同事都倾向于使用后面这种方式来书写代码，有定义好的要用，没有定义好的自己去加上也要用。不知道这是不是 Rubyists 们追求代码优雅的一个常态。&lt;/p&gt;

&lt;p&gt;Anyway，等我用链式调用重写了这个 gem 后，他们就再也不用纠结怎么调了，也不用在新增接口时一个个的去新增调用方法了。想一想那酸爽，鸡肉味，嘎嘣脆~~~&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;PS：最后还是贴一下我更新之后的 FacePP SDK 吧，没准有人需要 &lt;img title=":stuck_out_tongue_closed_eyes:" alt="😝" src="https://twemoji.ruby-china.com/2/svg/1f61d.svg" class="twemoji"&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/IvanChou/facepp-ruby-sdk" rel="nofollow" target="_blank"&gt;https://github.com/IvanChou/facepp-ruby-sdk&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <author>IChou</author>
      <pubDate>Sun, 07 May 2017 21:12:50 +0800</pubDate>
      <link>https://ruby-china.org/topics/32944</link>
      <guid>https://ruby-china.org/topics/32944</guid>
    </item>
    <item>
      <title>Rails + Puma 线程死掉了没有报错么？</title>
      <description>&lt;h4 id="先容我描述一下这个场景："&gt;先容我描述一下这个场景：&lt;/h4&gt;
&lt;p&gt;这是一个微信公众号项目，大部分时间，它还是讲道理的，看上去老老实实，勤勤恳恳的。但是，冷不防你准备下班或者睡觉的时候，微信报警群里就会跳出来一条报警：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;XXX 公众号在微信服务器请求后 5s 内没有响应&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;然后你屁颠屁颠的去查 Rails 日志，擦咧，没有报错，也基本没有超过 5s 的请求。去 OneApm 上去看，报错率 0%，顿时感觉日了🐶了&lt;/p&gt;

&lt;p&gt;就在你还没找到问题是什么的时候，这个时候，第二条报警又来了。。吓得我立马重启服务，终于世界安静了&lt;/p&gt;

&lt;p&gt;但是哪儿出错了依旧没找到。&lt;/p&gt;

&lt;hr&gt;
&lt;h4 id="背景交代结束，下面是正文"&gt;背景交代结束，下面是正文&lt;/h4&gt;
&lt;p&gt;是问题终归要解决，Rails 里查不出问题，就往下一层走，先去看看 nginx，果然有收获&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2016/07/04 22:05:58 [error] 32398#0: *18194461 upstream prematurely closed connection while reading response header from upstream, client: ***.207.54.75, server: **.cn, request: "POST /robot?signature=1dd244608fe34b**650c3497185d33d60cd12cfe&amp;amp;timestamp=1467641156&amp;amp;nonce=443071359&amp;amp;openid=***FPt3ibC_ujlPjUF5Y79SwRkEw HTTP/1.0", upstream: "http://**.168.***.86:10081/robot?signature=1dd244608fe34b**650c3497185d33d60cd12cfe&amp;amp;timestamp=1467641156&amp;amp;nonce=443071359&amp;amp;openid=***FPt3ibC_ujlPjUF5Y79SwRkEw", host: "**.cn"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;upstream 非正常提前退出，好，现在拿着这个请求去 Rails 的日志中查&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# grep 64d3bb21-af3b-46c3-af98-5056a64e7088 log/production.log

I, [2016-07-04T22:05:56.714306 #11883]  INFO -- : [64d3bb21-af3b-46c3-af98-5056a64e7088] Started POST "/robot?signature=1dd244608fe34b**650c3497185d33d60cd12cfe&amp;amp;timestamp=1467641156&amp;amp;nonce=443071359&amp;amp;openid=***FPt3ibC_ujlPjUF5Y79SwRkEw" for 140.207.54.75 at 2016-07-04 22:05:56 +0800
I, [2016-07-04T22:05:56.718092 #11883]  INFO -- : [64d3bb21-af3b-46c3-af98-5056a64e7088] Processing by RespondersController#create as XML
I, [2016-07-04T22:05:56.718229 #11883]  INFO -- : [64d3bb21-af3b-46c3-af98-5056a64e7088]   Parameters: {"signature"=&amp;gt;"1dd244608fe34b**650c3497185d33d60cd12cfe", "timestamp"=&amp;gt;"1467641156", "nonce"=&amp;gt;"443071359", "openid"=&amp;gt;"***FPt3ibC_ujlPjUF5Y79SwRkEw"}
W, [2016-07-04T22:05:56.721179 #11883]  WARN -- : [64d3bb21-af3b-46c3-af98-5056a64e7088] Request Info:  event   click btn_search    By User:    ***FPt3ibC_ujlPjUF5Y79SwRkEw
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;发现这个请求跟平时的日志比，少了类似这样的一行&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I, [2016-07-05T10:59:45.049711 #9389]  INFO -- : [e026d1c6-bb33-48ff-ad76-fac910f2bbdb] Completed 200 OK in 4ms (Views: 0.1ms | ActiveRecord: 0.0ms)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也就是说，这个请求没有被执行完，就中断了，nginx 收到后端处理异常退出的信号，就抛出了上面的错误&lt;/p&gt;

&lt;p&gt;由于应用是用 puma 在跑，rails 某个请求没执行完突然中断了，应该是由于某些原因导致这个线程死掉了吧，打开 puma 的日志一看，哭了&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[8553] ! Terminating timed out worker: 22146
[8553] - Worker 3 (pid: 7459) booted, phase: 0
[8553] ! Terminating timed out worker: 22181
[8553] - Worker 0 (pid: 7457) booted, phase: 0
[8553] - Worker 1 (pid: 7495) booted, phase: 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;连个时间戳都没有，也没有报错（这个可能是我配置的问题，请先忽略）&lt;/p&gt;
&lt;h3 id="下面，就是想要请教各位的问题了"&gt;下面，就是想要请教各位的问题了&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;puma 中某个请求死掉，在 Rails 和 puma 的日志里面都不会有标志性的体现，如果不去对比着 nginx 的日志查，根本就不知道这种情况的存在。是否有什么方法配置 puma，让这种情况发生的时候，记录在日志中，以方便观测或预警&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;这种情况下希望 Rails 自己去输出错误日志，我觉得已经不太可能了，毕竟线程已死。那么，我要如何去定位问题究竟是在哪一个环节上触发的呢？在代码里加入大量的 debug logger 可行，但是有点 low，这种情况的线上触发概率还是很低的&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;通过排查请求处理时间 大于 5s 的 nginx 日志，我还发现一种更奇怪的现象：&lt;strong&gt;请求没执行完，死掉了，但是连 nginx 都没有收到信号，也没有错误日志&lt;/strong&gt;。由于微信自己对于 5s 没有回应的请求会主动发送 499，中断 Nginx 的处理，不然估计 nginx 会对这个请求挂起到超时，大面积出现的话，那不是连 nginx 都被拖跨了&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;已经开始去查找相关资料，感觉我是不是碰到个疑难杂症，如果哪位 大大 知道什么思路，还望提点&lt;/p&gt;</description>
      <author>IChou</author>
      <pubDate>Tue, 05 Jul 2016 11:57:21 +0800</pubDate>
      <link>https://ruby-china.org/topics/30449</link>
      <guid>https://ruby-china.org/topics/30449</guid>
    </item>
    <item>
      <title>关于在 Rails Model 中使用 Enum (枚举) 的若干总结</title>
      <description>&lt;blockquote&gt;
&lt;p&gt;题记：本文最初针对 Rails 4 编写，内容过时，会对当前 Rails 5 的使用者造成干扰，故已更新此文到适合 Rails 5 的版本&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;在 Rails 的 ActiveRecord 中，有一个 ActiveRecord::Enum 的 Module，即枚举对象。&lt;/p&gt;

&lt;p&gt;官方说明：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Declare an enum attribute where the values map to integers in the database, but can be queried by name.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;给数据库中的整型字段声明一个一一对应的枚举属性值，这个值可以以字面量用于查询。&lt;/p&gt;

&lt;p&gt;拿到具体的运用场景中去考虑，Enum 的主要用于数据库中类似于 状态 (status) 的字段，这类字段用不同的 整数 (Integer) 来表示不用的状态。如果不使用 Enum，那就意味着我们代码中会出现很多表示状态的数字，他们可能会出现在查询条件里，也可能会出现在判断条件里，除非你记得或者拿着数据字典去看，否则你很难理解这段代码的含义。&lt;/p&gt;

&lt;p&gt;代码中，以数字方式去表示数据状态，导致代码可读性被破坏，这样的数字被称为『魔鬼数字』。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enum 就是 Rails 用来消灭魔鬼数字的工具。&lt;/strong&gt;&lt;/p&gt;

&lt;hr&gt;
&lt;h3 id="ActiveRecord::Enum 与 Mysql 的 Enum 有何不同"&gt;ActiveRecord::Enum 与 Mysql 的 Enum 有何不同&lt;/h3&gt;
&lt;p&gt;枚举的功能，是为了解决数据库相关的问题，那么当然的，数据库本身大多都含有枚举的功能。&lt;/p&gt;

&lt;p&gt;以 Mysql 为例，mysql 的字段类型中有一个 ENUM 的类型：&lt;/p&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;gender&lt;/span&gt; &lt;span class="nb"&gt;ENUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Male'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Female'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样就设置了一个叫 gender 的 ENUM 字段，其值为： &lt;code&gt;{NULL: NULL, 1: 'Male', 2: 'Female'}&lt;/code&gt;, 在使用 SQL 的时候，数字键值 (index of the value) 和定义的字面量（actual constant literal）是通用的。&lt;/p&gt;

&lt;p&gt;既然数据库的 ENUM 已是如此的方便，为什么我们不直接使用它呢？最大的问题在于 ENUM 的属性值在建表的时候就已经固定了下来，一旦到了后期需要加一个状态，那么就意味着需要改字段。而且目前各种数据库对于 ENUM 的处理方式也并非是完全一致的，给 ORM 的实现也带来的不少的问题。&lt;/p&gt;

&lt;p&gt;ActiveRecord::Enum 在实现上，和对外键的处理方式一样，并不直接使用数据库自身的 ENUM，仅用普通的 Integer 来做存储，以此避免了 Enum 属性变动时需要修改数据库结构的问题。&lt;/p&gt;
&lt;h3 id="ActiveRecord::Enum 的使用"&gt;ActiveRecord::Enum 的使用&lt;/h3&gt;
&lt;p&gt;具体使用请参见&lt;a href="http://api.rubyonrails.org/classes/ActiveRecord/Enum.html" rel="nofollow" target="_blank" title=""&gt;官方文档&lt;/a&gt;&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Migration&lt;/span&gt;
&lt;span class="n"&gt;create_table&lt;/span&gt; &lt;span class="ss"&gt;:conversations&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;t&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="nf"&gt;integer&lt;/span&gt; &lt;span class="ss"&gt;:status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;default: &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Model&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Conversation&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="n"&gt;enum&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;active: &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;waiting: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;archived: &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;# or, but not recommended&lt;/span&gt;
  &lt;span class="n"&gt;enum&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="ss"&gt;:active&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:waiting&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:archived&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;p&gt;声明之后，会多出以下一些辅助方法：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;conversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;active!&lt;/span&gt; &lt;span class="c1"&gt;# 改写状态为 active&lt;/span&gt;
&lt;span class="n"&gt;conversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;active?&lt;/span&gt; &lt;span class="c1"&gt;# 检查状态是否为 active&lt;/span&gt;

&lt;span class="n"&gt;conversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;     &lt;span class="c1"&gt;# =&amp;gt; "active" 输出为字面量&lt;/span&gt;
&lt;span class="n"&gt;conversation&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:status&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;   &lt;span class="c1"&gt;# =&amp;gt; 0 输出为数字键值（仅 Rails 4.x的版本）&lt;/span&gt;


&lt;span class="n"&gt;conversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;            &lt;span class="c1"&gt;# =&amp;gt; "archived"&lt;/span&gt;
&lt;span class="n"&gt;conversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"archived"&lt;/span&gt;   &lt;span class="c1"&gt;# =&amp;gt; "archived"&lt;/span&gt;
&lt;span class="n"&gt;conversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:archived&lt;/span&gt;    &lt;span class="c1"&gt;# =&amp;gt; "archived" 赋值时，三者等价&lt;/span&gt;

&lt;span class="c1"&gt;# 自动添加 Scope&lt;/span&gt;
&lt;span class="no"&gt;Conversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;active&lt;/span&gt;    &lt;span class="c1"&gt;# 等价于 Conversation.where(status: 0)&lt;/span&gt;

&lt;span class="c1"&gt;# 获得一个名为 statuses 的 HashWithIndifferentAccess&lt;/span&gt;
&lt;span class="no"&gt;Conversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;statuses&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; { "active" =&amp;gt; 0, "waiting" =&amp;gt; 1, "archived" =&amp;gt; 2 }&lt;/span&gt;
&lt;span class="no"&gt;Conversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;statuses&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:active&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;    &lt;span class="c1"&gt;# =&amp;gt; 0&lt;/span&gt;
&lt;span class="no"&gt;Conversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;statuses&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"archived"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; 2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="ActiveRecord::Enum 在 Rails 5.0+ 中的新特性"&gt;ActiveRecord::Enum 在 Rails 5.0+ 中的新特性&lt;/h3&gt;
&lt;p&gt;首先需要提一下，ActiveRecord::Enum 是在 Rails 4.1 加入的功能，所以 4.1 以下的版本是没有原生 Enum 支持的，可以使用 &lt;a href="https://rubygems.org/gems/simple_enum" rel="nofollow" target="_blank" title=""&gt;simple_enum&lt;/a&gt; 这个 gem 来实现类似需求&lt;/p&gt;

&lt;p&gt;然后在 Rails 5.0 中，又对 ActiveRecord::Enum 做了一些扩展，使得它现在用起来更为顺手&lt;/p&gt;

&lt;p&gt;主要的改进有：&lt;/p&gt;
&lt;h4 id="1. where 查询支持直接使用字面量做查询条件"&gt;1. where 查询支持直接使用字面量做查询条件&lt;/h4&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 在 Rails 4.1+ 中&lt;/span&gt;
&lt;span class="no"&gt;Conversation&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;status: &lt;/span&gt;&lt;span class="sx"&gt;%i[active waiting]&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_sql&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; "SELECT `conversations`.* FROM `conversations` WHERE `conversations`.`status` IN (NULL, NULL)"&lt;/span&gt;

&lt;span class="c1"&gt;# 惊不惊喜，意不意外，这也是 4.x 版本 Enum 比较鸡肋的原因，正确的你应这样写&lt;/span&gt;
&lt;span class="no"&gt;Conversation&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;status: &lt;/span&gt;&lt;span class="sx"&gt;%i[active waiting]&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&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="o"&gt;|&lt;/span&gt; &lt;span class="no"&gt;Conversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;statuses&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="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;to_sql&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; "SELECT `conversations`.* FROM `conversations` WHERE `conversations`.`status` IN (0, 1)"&lt;/span&gt;

&lt;span class="c1"&gt;# 好在，Rails 5.0 之后就可以这么用了&lt;/span&gt;
&lt;span class="no"&gt;Conversation&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;status: &lt;/span&gt;&lt;span class="sx"&gt;%i[active waiting]&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_sql&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; "SELECT \"conversations\".* FROM \"conversations\" WHERE \"conversations\".\"status\" IN (0, 1)"&lt;/span&gt;

&lt;span class="c1"&gt;# BUT，当你选择手写 SQL 查询条件的时候，仍是需要自己转义的&lt;/span&gt;
&lt;span class="no"&gt;Conversation&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;"status &amp;lt;&amp;gt; ?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Conversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;statuses&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:archived&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="2. conversation[:status] 与 conversation.status 返回值一致，都是字面量"&gt;2. conversation[:status] 与 conversation.status 返回值一致，都是字面量&lt;/h4&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Rails 4.1+&lt;/span&gt;
&lt;span class="n"&gt;conversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; "active"&lt;/span&gt;
&lt;span class="n"&gt;conversation&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:status&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; 0&lt;/span&gt;
&lt;span class="no"&gt;Conversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; [0, 1, 2, 1, ...]&lt;/span&gt;

&lt;span class="c1"&gt;# Rails 5.0+&lt;/span&gt;
&lt;span class="n"&gt;conversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; "active"&lt;/span&gt;
&lt;span class="n"&gt;conversation&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:status&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; "active"&lt;/span&gt;
&lt;span class="no"&gt;Conversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; ["active", "waiting", "archived", "waiting", ...]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="3. 增加了两个可选参数 _prefix 和 _suffix"&gt;3. 增加了两个可选参数 &lt;code&gt;_prefix&lt;/code&gt; 和 &lt;code&gt;_suffix&lt;/code&gt;
&lt;/h4&gt;
&lt;p&gt;在 Rails 4.1+ 的版本中，即使是不同的 enum 字段也不能有同名的值&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# user.rb&lt;/span&gt;
  &lt;span class="n"&gt;enum&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:temporary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:active&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:deleted&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;enum&lt;/span&gt; &lt;span class="ss"&gt;admin_status: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:active&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:super_admin&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# rails console&lt;/span&gt;
&lt;span class="n"&gt;irb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="mo"&gt;001&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="no"&gt;ArgumentError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;You&lt;/span&gt; &lt;span class="n"&gt;tried&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;define&lt;/span&gt; &lt;span class="n"&gt;an&lt;/span&gt; &lt;span class="n"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;named&lt;/span&gt; &lt;span class="s2"&gt;"admin_status"&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="s2"&gt;"User"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;but&lt;/span&gt; &lt;span class="n"&gt;this&lt;/span&gt; &lt;span class="n"&gt;will&lt;/span&gt; &lt;span class="n"&gt;generate&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="nb"&gt;method&lt;/span&gt; &lt;span class="s2"&gt;"active?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;which&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;already&lt;/span&gt; &lt;span class="n"&gt;defined&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="n"&gt;another&lt;/span&gt; &lt;span class="n"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="nf"&gt;.&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;于是在 Rails 5 中，引入了 &lt;code&gt;_prefix&lt;/code&gt; 和 &lt;code&gt;_suffix&lt;/code&gt; 两个选项来解决这个问题，它会给对应的 &lt;code&gt;!&lt;/code&gt;、&lt;code&gt;?&lt;/code&gt; 以及 scope 方法加上前/后缀以示区分&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# user.rb&lt;/span&gt;
  &lt;span class="n"&gt;enum&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:temporary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:active&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:deleted&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;_suffix: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="n"&gt;enum&lt;/span&gt; &lt;span class="ss"&gt;admin_status: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:active&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:super_admin&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# rails console&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;active_status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;active_status?&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deleted_status!&lt;/span&gt;

&lt;span class="c1"&gt;# user.rb&lt;/span&gt;
  &lt;span class="n"&gt;enum&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:temporary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:active&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:deleted&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;_suffix: :stat&lt;/span&gt;
  &lt;span class="n"&gt;enum&lt;/span&gt; &lt;span class="ss"&gt;admin_status: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:active&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:super_admin&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# rails console&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;active_stat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;active_stat?&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deleted_stat!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="实际使用中的一些经验总结"&gt;实际使用中的一些经验总结&lt;/h3&gt;&lt;h4 id="1. 不要使用数据库的 enum ！！！"&gt;1. 不要使用数据库的 enum！！！&lt;/h4&gt;
&lt;p&gt;除非你们的 DBA 同意你这么做，并且以后的迁移由他负责，否则真心不建议使用&lt;/p&gt;
&lt;h4 id="2. 尽量升级到 Rails 5 以上的版本"&gt;2. 尽量升级到 Rails 5 以上的版本&lt;/h4&gt;
&lt;p&gt;Rails 4.x 的 Enum 有点鸡肋的感觉，看上去感觉很爽，实际上用着蛋疼。&lt;/p&gt;
&lt;h4 id="3. 对 enum 字段赋值时，已经隐含了数据验证"&gt;3. 对 enum 字段赋值时，已经隐含了数据验证&lt;/h4&gt;
&lt;p&gt;在对一个 enum 字段赋值时，值必须是该字段字面量（symbol/string 皆可）或数字键值中的一个，否则会直接抛出一个 &lt;code&gt;ArgumentError&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;以上文例子来说，给 &lt;code&gt;conversation.status&lt;/code&gt; 赋值时，必须是 &lt;code&gt;[:active, :waiting, :archived, "active", "waiting", "archived", 0, 1, 2]&lt;/code&gt; 中的一个，否则就会报错&lt;/p&gt;
&lt;h4 id="4. 尽量不要使用数组来定义 enum"&gt;4. 尽量不要使用数组来定义 enum&lt;/h4&gt;
&lt;p&gt;就是不要使用下面这种形式&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;enum&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="ss"&gt;:active&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:waiting&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:archived&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;enum&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="sx"&gt;%i[active waiting archived]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;相比起使用 Hash，数组相当于隐式指定了数字键值，字面量的顺序就很关键。你无法保证每个可能改这处代码的人都深知这一点，而一旦被插值或者打乱顺序，可能会导致几个通宵的加班。&lt;/p&gt;
&lt;h4 id="5. 手动设置了 table_name 时，需要警惕关联查询的陷阱"&gt;5. 手动设置了 table_name 时，需要警惕关联查询的陷阱&lt;/h4&gt;
&lt;p&gt;注：这条是 Rails 5.x 的专属烦恼&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# post.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;table_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:articles&lt;/span&gt;

  &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:comments&lt;/span&gt;

  &lt;span class="n"&gt;enum&lt;/span&gt; &lt;span class="ss"&gt;category: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;it: &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;law: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;medical: &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# comment.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Comment&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
    &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;foreign_key: :article_id&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# 正常查询是 OK 的&lt;/span&gt;
&lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;law&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_sql&lt;/span&gt; 
&lt;span class="c1"&gt;#=&amp;gt; "SELECT \"articles\".* FROM \"articles\" WHERE \"articles\".\"category\" = 1"&lt;/span&gt;

&lt;span class="c1"&gt;# 作为关联表的查询条件，enum 字段就无法转义了，查询会报错&lt;/span&gt;
&lt;span class="no"&gt;Comment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;joins&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:post&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;articles: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;category: :law&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;to_sql&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; "SELECT \"comments\".* FROM \"comments\" INNER JOIN \"articles\" ON \"articles\".\"id\" = \"comments\".\"article_id\" WHERE \"articles\".\"category\" = 'law'"&lt;/span&gt;

&lt;span class="c1"&gt;# 故意写个错的查询，看看错出在哪儿&lt;/span&gt;
&lt;span class="no"&gt;Comment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;joins&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:post&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;post: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;category: :law&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;to_sql&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; "SELECT \"comments\".* FROM \"comments\" INNER JOIN \"articles\" ON \"articles\".\"id\" = \"comments\".\"article_id\" WHERE \"post\".\"category\" = 1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过这个对比可以发现，因为手动设置了 table_name 时，关联表查询需要指定真实的表名，这会导致 enum 字段无法被正确转义&lt;/p&gt;

&lt;p&gt;&lt;img title=":vertical_traffic_light:" alt="🚦" src="https://twemoji.ruby-china.com/2/svg/1f6a6.svg" class="twemoji"&gt; &lt;strong&gt;前方高能预警，神坑要来了！！！&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;猜猜这个查询会不会报错？能不能查出数据？&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Comment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;joins&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:post&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;articles: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;category: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:law&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;答案是：不会报错，会查到数据，但绝不是你想要的&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Comment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;joins&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:post&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;articles: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;category: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:law&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;to_sql&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; "SELECT \"comments\".* FROM \"comments\" INNER JOIN \"articles\" ON \"articles\".\"id\" = \"comments\".\"article_id\" WHERE \"articles\".\"category\" = 0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;where&lt;/code&gt; 条件里面变成了 &lt;code&gt;"articles"."category" = 0&lt;/code&gt;, 也就是查出了条件为 &lt;code&gt;{ category: :it }&lt;/code&gt; 的数据，够惊悚吧&lt;/p&gt;

&lt;p&gt;所以，遇到这种情况，最好自己做转义！自己做转义！自己做转义！&lt;/p&gt;
&lt;h4 id="6. 给 enum 字段添加默认值是一个好习惯"&gt;6. 给 enum 字段添加默认值是一个好习惯&lt;/h4&gt;
&lt;p&gt;默认值最好还是定义的属性值里的第一个，通常来说是『0』&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;create_table&lt;/span&gt; &lt;span class="ss"&gt;:conversations&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;t&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="nf"&gt;integer&lt;/span&gt; &lt;span class="ss"&gt;:status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;limit: &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;default: &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;null: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;特别是对于 4.x 版本而言，如果字段允许为 null，那当你按 5.x 的习惯写 where 查询的时候，可能会返回些让你一脸懵逼的结果。&lt;/p&gt;
&lt;h4 id="7. 添加新属性时，最好写一个迁移"&gt;7. 添加新属性时，最好写一个迁移&lt;/h4&gt;
&lt;p&gt;加属性值的时候，并不涉及到数据库变动，为什么要写迁移呢？当然这不是必须的，建议写主要出于两项考虑&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;检测当前数据库中新加的值是否已经被占用&lt;/li&gt;
&lt;li&gt;更新数据库字段的 comment&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;当多个系统使用同一张数据表的时候，可能会出现 A 系统加了一个新的状态 &lt;code&gt;{ deleted: 3 }&lt;/code&gt;，B 系统不知道，也添加了一个 &lt;code&gt;{ reactive: 3 }&lt;/code&gt; 的新状态，等到某天其中一方发现问题时，线上的数据早已经是一团浆糊了。&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;AddDeletedStatusToConversations&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;5.1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;up&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s2"&gt;"The value of deleted status has already been taken."&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;Conversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deleted&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;positive?&lt;/span&gt;
    &lt;span class="n"&gt;change_column&lt;/span&gt; &lt;span class="ss"&gt;:conversations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;default: &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;null: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;comment: &lt;/span&gt;&lt;span class="s2"&gt;"0 - active, 1 - waiting, 2 - archived, 3 - deleted"&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;虽然这并不能保证万无一失，但及时的修改 comment 至少还是一个好习惯，毕竟使用数据库的可能不止是写 Rails 的人，还有 DBA，还有数据分析师，好的 comment 给他们带来很多便利&lt;/p&gt;
&lt;h4 id="8. 数据库字段不一定非得是 Integer"&gt;8. 数据库字段不一定非得是 Integer&lt;/h4&gt;
&lt;p&gt;可以是 boolean（个人觉得 boolean 字段已经没有必要使用 enum 了，毕竟语意已经很明确了）&lt;/p&gt;

&lt;p&gt;还可以是 string，在对一些老代码做重构的时候，这个特性可能会很实用&lt;/p&gt;
&lt;h4 id="9. 结合 I18n 食用，风味更佳"&gt;9. 结合 I18n 食用，风味更佳&lt;/h4&gt;
&lt;p&gt;针对 enum 的 i18n 方案有很多，比如 &lt;/p&gt;

&lt;p&gt;&lt;a href="https://rubygems.org/gems/enum_i18n" rel="nofollow" target="_blank" title=""&gt;enum_i18n&lt;/a&gt; / &lt;a href="https://rubygems.org/gems/human_enum" rel="nofollow" target="_blank" title=""&gt;human_enum&lt;/a&gt; / &lt;a href="https://github.com/dhyegofernando/active_record-humanized_enum" rel="nofollow" target="_blank" title=""&gt;active_record-humanized_enum&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;这三个很相似，都是把翻译写在 &lt;code&gt;zh-CN.activerecord.attributes.conversation.statuses&lt;/code&gt; 下面，只是调用方式略微有点不同&lt;/p&gt;

&lt;p&gt;另外还有 &lt;a href="/zmbacker" class="user-mention" title="@zmbacker"&gt;&lt;i&gt;@&lt;/i&gt;zmbacker&lt;/a&gt; 写的 &lt;a href="https://github.com/zmbacker/enum_help" rel="nofollow" target="_blank" title=""&gt;enum_help&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;它的翻译写在 &lt;code&gt;zh-CN.enums.conversation.status&lt;/code&gt; 下面，相比起来更直观一点，而且它支持 simple_form，个人比较推荐&lt;/p&gt;

&lt;p&gt;如果你不怕麻烦，也不想引入任何 gem，还可以利用 &lt;code&gt;human_attribute_name&lt;/code&gt; 来实现：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# zh-CN.yml&lt;/span&gt;
&lt;span class="c1"&gt;# zh-CN:&lt;/span&gt;
&lt;span class="c1"&gt;#   activerecord:&lt;/span&gt;
&lt;span class="c1"&gt;#     attributes:&lt;/span&gt;
&lt;span class="c1"&gt;#       conversation/status:&lt;/span&gt;
&lt;span class="c1"&gt;#         active: 当前激活&lt;/span&gt;
&lt;span class="c1"&gt;#         waiting: 等待中&lt;/span&gt;
&lt;span class="c1"&gt;#         archived: 已归档&lt;/span&gt;

&lt;span class="n"&gt;conversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; "active"&lt;/span&gt;
&lt;span class="no"&gt;Conversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;human_attribute_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"status.&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;conversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; "当前激活"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="10. enum 的字面量要注意避开 model 已有的 method_names/attribute_names"&gt;10. enum 的字面量要注意避开 model 已有的 method_names/attribute_names&lt;/h4&gt;
&lt;p&gt;这一点就不用啰嗦了，除非是表字段特别复杂，正常情况下，应该是不太会在这个问题上犯错的。&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;参考文章：&lt;a href="http://www.cubrid.org/blog/cubrid-life/when-you-should-and-should-not-use-enum-data-type/" rel="nofollow" target="_blank" title=""&gt;When you should and should NOT use ENUM data type&lt;/a&gt;&lt;/p&gt;</description>
      <author>IChou</author>
      <pubDate>Fri, 08 Jan 2016 05:09:15 +0800</pubDate>
      <link>https://ruby-china.org/topics/28654</link>
      <guid>https://ruby-china.org/topics/28654</guid>
    </item>
    <item>
      <title>关于 inverse_of 的困惑与探究</title>
      <description>&lt;p&gt;&lt;strong&gt;🚨 Rails5 以后，&lt;code&gt;inverse_of&lt;/code&gt; 已经做了不少变更，这篇帖子的内容可能已经不在符合现在的情况&lt;/strong&gt;&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;最近在使用 关联 的时候，由于一点手误遇到了些问题，于是花了一下午时间来仔细读了 Guide 中关于 Active Record Associations 的部分。在看到 &lt;code&gt;inverse_of&lt;/code&gt; 时，感觉自己突然一下就懵了。&lt;/p&gt;
&lt;h3 id="问题缘由"&gt;问题缘由&lt;/h3&gt;
&lt;p&gt;我对 &lt;code&gt;inverse_of&lt;/code&gt; 的困惑并不是在实际使用中产生的，即使不了解它也能在项目中愉快的玩耍，这似乎又旁证了 Rails 是一个很智能的框架。&lt;/p&gt;

&lt;p&gt;不过它的 Guide 文档里这一部分就有些描述不清了（或说自相矛盾？）。关于 &lt;code&gt;inverse_of&lt;/code&gt; 的功用倒是没有什么疑惑，只是被这混乱的文档弄得不知道哪些情况下需要去显式的声明 &lt;code&gt;inverse_of&lt;/code&gt; , 哪些情况下 &lt;code&gt;inverse_of&lt;/code&gt; 又是无效果的。&lt;/p&gt;
&lt;h3 id="问题描述"&gt;问题描述&lt;/h3&gt;
&lt;p&gt;Guide 中关于 &lt;code&gt;inverse_of&lt;/code&gt; 的解释：&lt;a href="http://guides.rubyonrails.org/association_basics.html#bi-directional-associations" rel="nofollow" target="_blank"&gt;http://guides.rubyonrails.org/association_basics.html#bi-directional-associations&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;其中有两处说明让我很费解：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;其一：&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There are a few limitations to inverse_of support:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;They do not work with :through associations.&lt;/li&gt;
&lt;li&gt;They do not work with :polymorphic associations.&lt;/li&gt;
&lt;li&gt;They do not work with :as associations.
&lt;strong&gt;4. For belongs_to associations, has_many inverse associations are ignored.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;del&gt;按第四条所说的，has_many 的关联是无效的，但是 Guide 中的栗子便是使用的 has_many, 而且很好的证明了 inverse_of 的效果。&lt;/del&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;【2016.6.17】这个其实没有疑问，这是指在 has_many / belongs_to 关联的两端都声明 inverse_of，只会按 belongs_to 一方的声明处理&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;其二：&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Every association will attempt to automatically find the inverse association and set the :inverse_of option heuristically (based on the association name). Most associations with standard names will be supported. However, associations that contain the following options will not have their inverses set automatically:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;:conditions &lt;/li&gt;
&lt;li&gt;:through&lt;/li&gt;
&lt;li&gt;:polymorphic &lt;/li&gt;
&lt;li&gt;:foreign_key &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;del&gt;按这个说法，只要是按约定命名的 关联 会自动加上 inverse_of, 那么，演示用例是按约定命名的吧，也不属于下面声明的四种情况，为什么加与不加是有差别的？&lt;/del&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;【2016.6.17】这个其实也没有疑问，这是因为 Rails 版本变迁，我当时看得文档没来得及更新导致的。文章后面部分已经解释了这个问题，这里提前把结论贴一下。&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="动手验证"&gt;动手验证&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;验证环境 Rails 版本：4.2.1&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;定义模型：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# a.rb
class A &amp;lt; ActiveRecord::Base
    has_many :b
end

# b.rb
class B &amp;lt; ActiveRecord::Base
    belongs_to :a
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;测试结果：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="mf"&gt;2.2&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="mo"&gt;001&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&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;create&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'ichou'&lt;/span&gt;
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;#&amp;lt;A id: 2, name: "ichou", created_at: "2015-04-04 06:41:41", updated_at: "2015-04-04 06:41:41"&amp;gt;&lt;/span&gt;

&lt;span class="mf"&gt;2.2&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="mo"&gt;002&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;B&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'kindle'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:a&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;#&amp;lt;B id: 3, name: "kindle", a_id: 2, created_at: "2015-04-04 06:42:45", updated_at: "2015-04-04 06:42:45"&amp;gt;&lt;/span&gt;

&lt;span class="mf"&gt;2.2&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="mo"&gt;003&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;B&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Air'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:a&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;#&amp;lt;B id: 4, name: "Air", a_id: 2, created_at: "2015-04-04 06:43:10", updated_at: "2015-04-04 06:43:10"&amp;gt;&lt;/span&gt;

&lt;span class="mf"&gt;2.2&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="mo"&gt;004&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object_id&lt;/span&gt;
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;70264637654120&lt;/span&gt;

&lt;span class="mf"&gt;2.2&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="mo"&gt;005&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object_id&lt;/span&gt;
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;70264637654120&lt;/span&gt;

&lt;span class="mf"&gt;2.2&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="mo"&gt;006&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object_id&lt;/span&gt;
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;70264637654120&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;object_id 全都一样，说明 &lt;code&gt;inverse_of&lt;/code&gt; 已经被启用了。事实上，即使严格按照 Guide 的案例来做，你也会发现结果全是 True，而不是 Guide 所说的结果。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;结论：在 4.1+ 的 Rails 中，即使不手动声明 &lt;code&gt;inverse_of&lt;/code&gt; ，has_many 关联也会自动创建，而且是有效的！&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="总结与使用"&gt;总结与使用&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;inverse_of&lt;/code&gt; 的作用在于关联模型间共用实例，而不是让不同的查询在内存中存在多份 Copies. 
实际运用中可以带来两个好处，一是减少数据库查询；二是在对 关联对象 修改数据后写入数据前，保证从任何一方取得的值都是最新的。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;从 4.1 开始，基本的关联类型（has_many, has_one, belongs_to），若按约定命名，不需要再手动设定 &lt;code&gt;inverse_of&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;(&lt;strong&gt;当时的&lt;/strong&gt;)Guide 的相关用例是有问题的，或者说是适用于老版本的 Rails，而不是当前版本。
&lt;strong&gt;因为给 &lt;code&gt;basic associations\*&lt;/code&gt; 自动添加 &lt;code&gt;inverse_of&lt;/code&gt; 是在 Rails 4.1 加入的特性。&lt;/strong&gt;
事实上，关于 has_many 的那个例子，在 4.1 及以后的版本中已经不能复现了。 &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;inverse_of&lt;/code&gt; 已经支持 has_many 是从 3.2.1 开始的，4.1 开始支持自动添加 &lt;code&gt;inverse_of&lt;/code&gt;, 但是 has_many :through 仍然需要显式声明 &lt;code&gt;inverse_of&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;使用没有持久化的关联对象时，根据需要使用 inverse_of，否则反向调用会得到 nil 或者还未更新的 对象。详见 &lt;a href="https://ruby-china.org/topics/6426" title=""&gt;topics/6426&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;hr&gt;

&lt;p&gt;感谢 社区 和 stackoverflow 上的大大们，感谢微信群 成都 Ruby 群 里的星哥。&lt;/p&gt;

&lt;p&gt;比较新手向的帖子，若有纰漏，烦请指正&lt;/p&gt;
&lt;h5 id="参考文章"&gt;参考文章&lt;/h5&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://ruby-china.org/topics/8560" rel="nofollow" target="_blank"&gt;https://ruby-china.org/topics/8560&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ruby-china.org/topics/6426" rel="nofollow" target="_blank"&gt;https://ruby-china.org/topics/6426&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://stackoverflow.com/questions/9296694/what-does-inverse-of-do-what-sql-does-it-generate" rel="nofollow" target="_blank"&gt;http://stackoverflow.com/questions/9296694/what-does-inverse-of-do-what-sql-does-it-generate&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://stackoverflow.com/questions/14927952/why-would-i-not-want-to-use-inverse-of-everywhere" rel="nofollow" target="_blank"&gt;http://stackoverflow.com/questions/14927952/why-would-i-not-want-to-use-inverse-of-everywhere&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://stackoverflow.com/questions/7654184/does-inverse-of-works-with-has-many" rel="nofollow" target="_blank"&gt;http://stackoverflow.com/questions/7654184/does-inverse-of-works-with-has-many&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://stackoverflow.com/questions/7436173/activerecord-inverse-of-does-not-work-on-has-many-through-on-the-join-model-on" rel="nofollow" target="_blank"&gt;http://stackoverflow.com/questions/7436173/activerecord-inverse-of-does-not-work-on-has-many-through-on-the-join-model-on&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description>
      <author>IChou</author>
      <pubDate>Sat, 04 Apr 2015 15:11:29 +0800</pubDate>
      <link>https://ruby-china.org/topics/24998</link>
      <guid>https://ruby-china.org/topics/24998</guid>
    </item>
    <item>
      <title>关于 mpg123 获取音频文件比特率的问题</title>
      <description>&lt;p&gt;啊啦 作为小白 实在扛不住了 还是决定到论坛上来咨询一下了&lt;/p&gt;

&lt;p&gt;涉及 gem：&lt;code&gt;audite&lt;/code&gt; , &lt;code&gt;mpg123&lt;/code&gt; , &lt;code&gt;portaudio&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;事件描述：我最近在练习 ruby，决定把虾米猜电台写个 ruby 版。我想把 下载进度 和
 播放进度 以进度条的形式展现出来，所以我至少需要获取 mp3 的时长，问题就出在这里：&lt;/p&gt;

&lt;p&gt;1.虾米的 mp3 文件既有 CBR 又有 VBR&lt;/p&gt;

&lt;p&gt;2.audite 这个 gem 计算时间采用的是  &lt;code&gt;Duration = Samples / Samples_per_Frame * time_per_frame&lt;/code&gt;
这个公式对于 没有在第一桢后面用 header 记录信息的 CBR 来说，在文件没下载完整之前取到的 Samples 是错误的，于是也就没有办法获得时长&lt;/p&gt;

&lt;p&gt;3.当然如果能对 CBR 编码的 mp3 取到 比特率（Bitrate），根据 http header 里面提供的文件大小也是能算出音频时长的，但是各种找资料，还是不知道 mpg123 该如何取得 比特率&lt;/p&gt;

&lt;p&gt;由于对音频文件的知识十分有限（都是现学的 ~~o(&amp;gt;_&amp;lt;)o ~~） ，所以也不知道上边的理解对不对
主要是实在找不到思路了 所以拿出来给大家看看，看还能不能再抢救一下，没有要做伸手党的意思哈~&lt;/p&gt;

&lt;p&gt;PS：不知道有人发现没，通过 gem 安装的 audite 好像是有点小问题的，不过 github 上边的源码是 ok 的，也不知是不是我打开的方式不对 ~&lt;/p&gt;

&lt;p&gt;等回复的时间，我还是去研究下虾米的手机版吧，看看它是怎么处理的，阿门&lt;/p&gt;</description>
      <author>IChou</author>
      <pubDate>Thu, 01 May 2014 12:03:53 +0800</pubDate>
      <link>https://ruby-china.org/topics/18949</link>
      <guid>https://ruby-china.org/topics/18949</guid>
    </item>
    <item>
      <title>The Great Wall 没有墙掉 Ruby 包地址了？！</title>
      <description>&lt;p&gt;今天在阿里云的服务器上部署 Rails 环境，由于是第一次使用 Centos，发现它有点不同
比如：它没有在用户根目录下创建.rvm 文件夹（这好像是真 rvm 文件夹的替身，求解）
于是我按论坛 Wiki 里的帖子安装时，这一步&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# 替换 Ruby 下载地址到国内淘宝镜像服务器
$ sed -i 's/ftp\.ruby-lang\.org\/pub\/ruby/ruby\.taobao\.org\/mirrors\/ruby/g' ~/.rvm/config/db
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;失败了&lt;/p&gt;

&lt;p&gt;接着我硬着头皮继续安装，发现&lt;strong&gt;从原始位置也可以下载！！&lt;/strong&gt;
难道伟大的 Wall 已经不再屏蔽这个地址了？&lt;/p&gt;

&lt;p&gt;&lt;img src="//l.ruby-china.com/photo/b370e70c7955605f9978e8f6d204b16f.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;找到&lt;code&gt;rvm/config/db&lt;/code&gt;后看到的下载地址&lt;/p&gt;</description>
      <author>IChou</author>
      <pubDate>Sat, 15 Sep 2012 14:39:59 +0800</pubDate>
      <link>https://ruby-china.org/topics/5555</link>
      <guid>https://ruby-china.org/topics/5555</guid>
    </item>
    <item>
      <title>Mac 下 Rails 连接 Mysql 的一点点心得</title>
      <description>&lt;p&gt;首先附上我的工作环境&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;系统：Mac OS X 10.7.4

Ruby：ruby 1.9.3p194

Rails：Rails 3.2.6

Mysql：5.5.27 MySQL Community Server
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;由于我手上的书籍都是从图书馆借来的，他们的 rails 版本普遍比较早，虽然都是默认 mysql 数据库，但都没提到 rails 与 mysql 的连接问题，在他们看来这似乎不需要任何说明的。&lt;/p&gt;

&lt;p&gt;但事实是，这是个灰常严肃的问题！至少在我这个版本下是的。&lt;/p&gt;

&lt;p&gt;按书上操作，只会得到一个结果&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ActiveRecord::ConnectionNotEstablished&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;目前为止，这个问题百度无法解决，我是在一个视频中无意看到的&lt;/p&gt;

&lt;p&gt;原来在这个版本里，gem 里面内含了 sqlite3 的驱动包，但是没有 mysql 的，需要自己安装&lt;/p&gt;

&lt;p&gt;安装方法：&lt;/p&gt;

&lt;p&gt;1.cd 到你的项目目录 比如我的是&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cd ruby/demo&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;2.安装 mqsql 驱动包（插件）&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gem install mysql&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;3.在项目的 Gemfile 文件中包含 mysql 驱动包&lt;/p&gt;

&lt;p&gt;&lt;code&gt;vi Gemfile&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;新加一行&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gem 'mysql'&lt;/code&gt;&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;**感谢2L @ywjno 提醒 用Mysql2才是王道**

gem install mysql2

gem 'mysql2'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;好，到这儿就安装完成了，重新启动 WEBrick 服务。这时你有可能得到两种结果：&lt;/p&gt;

&lt;p&gt;成功了，或者报错：&lt;/p&gt;

&lt;p&gt;&lt;code&gt;undefined method 'init' for Mysql:Class&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;这又是怎么回事儿呢？&lt;/p&gt;

&lt;p&gt;研究过 Mac 下 Mysql 与 Apche 连接的同学应该明白的，没错，因为 Mac 下 Mysql 的安装目录与 Liunx 等使用的默认安装路径是不一样的，rails 去找默认路径当然找不到，这个时候只要只要导入 mysql lib 环境变量即可。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;export DYLD_LIBRARY_PATH=/usr/local/mysql/lib/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;现在在运行，没问题了是不是？
文章大部分解说都是自己参详的，看我还在研究这个就能明白我也才入门的小白，若有错误之处，请各位大神指正！&lt;/p&gt;</description>
      <author>IChou</author>
      <pubDate>Sat, 08 Sep 2012 04:04:15 +0800</pubDate>
      <link>https://ruby-china.org/topics/5419</link>
      <guid>https://ruby-china.org/topics/5419</guid>
    </item>
  </channel>
</rss>
