<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>adamshen (adam.shen)</title>
    <link>https://ruby-china.org/adamshen</link>
    <description>争取成为ruby圈一流段子手</description>
    <language>en-us</language>
    <item>
      <title>感觉 UI 风格比以前好看了耶</title>
      <description>&lt;p&gt;&lt;img title=":sunglasses:" alt="😎" src="https://twemoji.ruby-china.com/2/svg/1f60e.svg" class="twemoji"&gt; &lt;/p&gt;</description>
      <author>adamshen</author>
      <pubDate>Thu, 19 Apr 2018 09:27:39 +0800</pubDate>
      <link>https://ruby-china.org/topics/35485</link>
      <guid>https://ruby-china.org/topics/35485</guid>
    </item>
    <item>
      <title>论坛新人值得一看的，Ruby China 上的经典讨论贴子集合 (2016 年 以前)</title>
      <description>&lt;h2 id="Asset Pipe"&gt;Asset Pipe&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://ruby-china.org/topics/9664" title=""&gt;Rails Assets Pipeline 的价值&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ruby-china.org/topics/16172" title=""&gt;Assets management solved: we released Rails Assets&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="Front"&gt;Front&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://ruby-china.org/topics/27293" title=""&gt;前后分离架构的探索之路&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ruby-china.org/topics/27265" title=""&gt;2015 最新调查：现在的前端工程师都用什么&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ruby-china.org/topics/26944" title=""&gt;还在纠结 Flux 或 Relay，或许 Redux 更适合你&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ruby-china.org/topics/24646" title=""&gt;求 Ember.js 开发经验的分享&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ruby-china.org/topics/16915" title=""&gt;你选择 Angular 还是 Ember?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ruby-china.org/topics/16132" title=""&gt;DHH 再次重申，Rails 项目应该拥抱 SJR，别去整啥 JSON-Client render 方案&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="Turbolink"&gt;Turbolink&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://ruby-china.org/topics/13543" title=""&gt;我会说 Turbolink 的体验就是一坨屎吗&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="Api"&gt;Api&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://ruby-china.org/topics/25822" title=""&gt;使用 Rails 构建 API 实践&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="ActiveRecord"&gt;ActiveRecord&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://ruby-china.org/topics/17612" title=""&gt;请教如何让 ActiveRecord 的查询按照某个虚拟字段排序&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="Cache"&gt;Cache&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://ruby-china.org/topics/21488" title=""&gt;说说 Rails 的套娃缓存机制&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ruby-china.org/topics/19389" title=""&gt;总结 Web 应用中常用的各种 Cache&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="Log"&gt;Log&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://ruby-china.org/topics/3704" title=""&gt;Rails log 自动分割该怎么配置才正确&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="并发"&gt;并发&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://ruby-china.org/topics/10932" title=""&gt;Ruby on Rails 线程安全代码&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ruby-china.org/topics/5199" title=""&gt;大家是如何解决并发问题的&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://ruby-china.org/topics/11248" title=""&gt;实例说明 Ruby 多线程的潜力和弱点&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ruby-china.org/topics/13565" title=""&gt;EventMachine 和 Fiber&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="测试"&gt;测试&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://ruby-china.org/topics/143" title=""&gt;关于测试，有什么好的建议吗？&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ruby-china.org/topics/18809" title=""&gt;DHH: TDD is dead. Long live testing.&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="rspec"&gt;rspec&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://ruby-china.org/topics/9271" title=""&gt;RSpec 中 let 和 subject 的区别是什么&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="Ruby"&gt;Ruby&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://ruby-china.org/topics/10353" title=""&gt;cruby 在什么情况下比 C++, JavaScript, Go, Java, Haskell 更效率...&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ruby-china.org/topics/10414" title=""&gt;聊聊 Ruby 中的 block, proc 和 lambda&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="重构"&gt;重构&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://ruby-china.org/topics/24780" title=""&gt;Service Object: What? Why? and How?&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="Rails"&gt;Rails&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://ruby-china.org/topics/24742" title=""&gt;分享 Rails 开发：那些年，我们一起踩过的坑 (剧终)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ruby-china.org/topics/26120" title=""&gt;Rails 项目里的三大毒瘤&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="性能"&gt;性能&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://ruby-china.org/topics/11270" title=""&gt;服务器性能讨论...&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ruby-china.org/topics/12008" title=""&gt;如何降低页面 render 时的耗时以及 CPU 资源&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ruby-china.org/topics/13442" title=""&gt;Ruby 性能真的比 Node 差这么多么？&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="db"&gt;db&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://ruby-china.org/topics/19499" title=""&gt;坑爹的 MySQL 事务&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ruby-china.org/topics/20598" title=""&gt;为什么我从 MongoDB 迁移到 PostgreSQL&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ruby-china.org/topics/20128" title=""&gt;MongoDB 那些坑&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ruby-china.org/topics/25908" title=""&gt;Redis 实战之薄荷 timeline 的优化&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="部署"&gt;部署&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://ruby-china.org/topics/12033" title=""&gt;蝉游记网站的部署 Nginx,Unicorn,Capistrano,OOB,Graceful Restart&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ruby-china.org/topics/19520" title=""&gt;多台机器上 Unicorn 共享 Session&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ruby-china.org/topics/18616" title=""&gt;Capistrano 3 实现 Rails 自动化部署&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ruby-china.org/topics/20385" title=""&gt;Install Ruby The "Postmodern" Way&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ruby-china.org/topics/25529" title=""&gt;Rails 中自动布署工具 mina 的经验谈&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="算法"&gt;算法&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://ruby-china.org/topics/16750" title=""&gt;关于算法，我们该学什么？&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;广告：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/adamshen/kawayi" rel="nofollow" target="_blank" title=""&gt;最近写的任务管理小工具&lt;/a&gt;，欢迎给点意见。&lt;/p&gt;</description>
      <author>adamshen</author>
      <pubDate>Thu, 27 Jul 2017 10:21:17 +0800</pubDate>
      <link>https://ruby-china.org/topics/33641</link>
      <guid>https://ruby-china.org/topics/33641</guid>
    </item>
    <item>
      <title>使用 Ruby 进行混合云开发的小伙伴，一起来交流一下吧</title>
      <description>&lt;p&gt;一直以来我觉得这一块可能 python 更多一点，但是最近通过 ruby-china 认识了几个同样从事这个开发的朋友。&lt;/p&gt;

&lt;p&gt;的确，用 ruby 来进行这方面的开发是比较方便的。&lt;/p&gt;

&lt;p&gt;比如客户的需求不复杂的，用 sinatra 起一个后端，然后用 fog 调 api，一天就可以做出一个简单的 demo 给客户展示。&lt;/p&gt;

&lt;p&gt;这块的产品在互联网公司比较鸡肋，但是在运维人员素质没那么高的传统企业却还有一点市场。&lt;/p&gt;

&lt;p&gt;我在微信上把最近认识的几个朋友拉了一个群，现在有 6 个人。&lt;/p&gt;

&lt;p&gt;如果你也从事这方面的开发，欢迎加我微信 adam5681，我帮你拉进去。&lt;/p&gt;</description>
      <author>adamshen</author>
      <pubDate>Thu, 08 Jun 2017 18:53:07 +0800</pubDate>
      <link>https://ruby-china.org/topics/33186</link>
      <guid>https://ruby-china.org/topics/33186</guid>
    </item>
    <item>
      <title>Warden 的代码学习</title>
      <description>&lt;p&gt;warden 是 devise 依赖的一个 Authentication Framework，作为一个框架，warden 解决了下面这些问题&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;auth result handle&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;要能够处理验证的结果，验证通过就跳到下面的 action 执行，不然就返回错误或者重定向到登陆界面&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;scope&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;比如说要能同时实现 admin 和 user 的验证，一个用户可以同时登陆 admin 账户和 user 账户&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;strategies&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;比如先从 session 里找 user_id，然后到 header 里找 token 等等，支持多种命中策略&lt;/p&gt;
&lt;h2 id="Warden"&gt;Warden&lt;/h2&gt;
&lt;p&gt;接下来看看 warden 如何实现这些功能，以及 devise 是如何调用这些功能的&lt;/p&gt;
&lt;h3 id="auth result handle"&gt;auth result handle&lt;/h3&gt;
&lt;p&gt;warden 解决这个问题办法是将自己作为一个 rack middleware 注册在 rails 之前，然后使用 catch 和 throw 在验证未通过时，接管响应流程&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In Warden::Manager&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# :nodoc:&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="vi"&gt;@app.call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'warden'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'warden'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;manager&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;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'warden'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Proxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:warden&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="vi"&gt;@app.call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;Array&lt;/span&gt;
    &lt;span class="n"&gt;handle_chain_result&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;Hash&lt;/span&gt;
    &lt;span class="n"&gt;process_unauthenticated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Response&lt;/span&gt;
    &lt;span class="n"&gt;handle_chain_result&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&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;这样一来，不管在 controller 里的任何地方 throw(:warden)，该请求就会立即被 warden 接管&lt;/p&gt;

&lt;p&gt;warden 在 rack env 里增加了一个 warden 属性，使用 lazy load 的模式给下面一层调用。在 controller 里如果需要用到验证就去 get 这个对象，不然直接不用就行了&lt;/p&gt;

&lt;p&gt;devise 会注册一个 helper 方法，然后把 authentication_xxxxx! 方法委托到 env['warden'] 对象上来执行&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In Devise::Controller::Helpers&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;warden&lt;/span&gt;
  &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'warden'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;MissingWarden&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define_helpers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#:nodoc:&lt;/span&gt;
  &lt;span class="n"&gt;mapping&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;

  &lt;span class="nb"&gt;class_eval&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;METHODS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;__LINE__&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="sh"&gt;
    def authenticate_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;!(opts={})
      opts[:scope] = :&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
      warden.authenticate!(opts) if !devise_controller? || opts.delete(:force)
    end

    def &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;_signed_in?
      !!current_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
    end

    def current_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
      @current_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt; ||= warden.authenticate(scope: :&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;)
    end

    def &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;_session
      current_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt; &amp;amp;&amp;amp; warden.session(:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;)
    end
&lt;/span&gt;&lt;span class="no"&gt;  METHODS&lt;/span&gt;

  &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on_load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:action_controller&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;respond_to?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:helper_method&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;helper_method&lt;/span&gt; &lt;span class="s2"&gt;"current_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;mapping&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="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_signed_in?"&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;mapping&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_session"&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;p&gt;这个对象的 authenticate! 方法会做身份确认，如果没有通过就 throw(:warden)&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In Warden::Proxy&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;authenticate!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_perform_authentication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kp"&gt;throw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:warden&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;对于不同类型的认证失败，需要配置一个 failure_app 来处理后续的流程&lt;/p&gt;

&lt;p&gt;对于 devise 来说，这个 failure_app 是 Devise::FailureApp，devise 会把它写入到 warden 的配置里&lt;/p&gt;

&lt;p&gt;所以使用用户名密码的策略认证失败（关于策略下面再说）会返回通知，而没有登陆则会重定向到 sign_in 页面&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In Warden::Manager&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call_failure_app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;failure_app&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:attempted_path&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fullpath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"PATH_INFO"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&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;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:action&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"warden.options"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;

    &lt;span class="n"&gt;_run_callbacks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:before_failure&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;failure_app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_a&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s2"&gt;"No Failure App provided"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="c1"&gt;# call_failure_app&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="scope"&gt;scope&lt;/h3&gt;
&lt;p&gt;warden 实现 scope 的方式是用一个 hash 来记录不同 scope 的 user 对象，把 scope 的名字用作 key，然后大量的 api 里把 scope 作为可选参数&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In Warden::Proxy&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;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;manager&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#:nodoc:&lt;/span&gt;
  &lt;span class="vi"&gt;@env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@winning_strategies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@locked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&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="kp"&gt;false&lt;/span&gt;
  &lt;span class="vi"&gt;@manager&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;manager&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dup&lt;/span&gt;
  &lt;span class="vi"&gt;@strategies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Hash&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="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_run_callbacks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:on_request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;self&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;def&lt;/span&gt; &lt;span class="nf"&gt;session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@config.default_scope&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;NotAuthenticated&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;scope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; user is not logged in"&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;authenticated?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;raw_session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"warden.user.&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.session"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;authenticated?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@config.default_scope&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;block_given?&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
  &lt;span class="n"&gt;result&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Same API as authenticated?, but returns false when authenticated.&lt;/span&gt;
&lt;span class="c1"&gt;# :api: public&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;unauthenticated?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@config.default_scope&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;authenticated?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;block_given?&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
  &lt;span class="n"&gt;result&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面&lt;a href="/users" class="user-mention" title="@users"&gt;&lt;i&gt;@&lt;/i&gt;users&lt;/a&gt;就是那个可以包含不同 scope 的 user 对象的 hash，从 session 方法可以看到，不同的 scope 在 session 中是有 namespace 作为区分的，这样就可以从 session 中读出不同 scope 的 user_id&lt;/p&gt;

&lt;p&gt;devise 里有 map 的概念，一个 map 对应一个 scope&lt;/p&gt;

&lt;p&gt;map 的配置用 Devise::Mapping 来记录，这里不继续展开，devise 会为每个 scope 注册不同的 helper 方法，这样在调用 warden api 的时候，这个 map 对应的 scope 就自动作为参数传给了 warden&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Devise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mappings&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="c1"&gt;#&amp;lt;Devise::Mapping:0x007f9e0d5eded8&lt;/span&gt;
   &lt;span class="vi"&gt;@class_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"User"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="vi"&gt;@controllers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:sessions&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"devise/sessions"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:passwords&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"devise/passwords"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:registrations&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"devise/registrations"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="vi"&gt;@failure_app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="no"&gt;Devise&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;FailureApp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="vi"&gt;@format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="vi"&gt;@klass&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c1"&gt;#&amp;lt;Devise::Getter:0x007f9e0d5eda00 @name="User"&amp;gt;,&lt;/span&gt;
   &lt;span class="vi"&gt;@modules&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:database_authenticatable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:rememberable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:recoverable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:registerable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:validatable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:trackable&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="vi"&gt;@path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="vi"&gt;@path_names&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:registration&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:new&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"new"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:edit&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"edit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:sign_in&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"sign_in"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:sign_out&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"sign_out"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:password&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:sign_up&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"sign_up"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:cancel&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"cancel"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="vi"&gt;@path_prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="vi"&gt;@router_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="vi"&gt;@routes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:registration&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="vi"&gt;@scoped_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="vi"&gt;@sign_out_via&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="ss"&gt;:delete&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="vi"&gt;@singular&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="vi"&gt;@strategies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:rememberable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:database_authenticatable&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="vi"&gt;@used_helpers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:registration&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="vi"&gt;@used_routes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:registration&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="strategies"&gt;strategies&lt;/h3&gt;
&lt;p&gt;回到 authenticate! 方法里做检查的部分，看看 warden 如何做身份验证&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In Warden::Proxy&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_perform_authentication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_retrieve_scope_and_opts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;

  &lt;span class="c1"&gt;# Look for an existing user in the session for this scope.&lt;/span&gt;
  &lt;span class="c1"&gt;# If there was no user in the session. See if we can get one from the request.&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:scope&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="n"&gt;_run_strategies_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;winning_strategy&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;winning_strategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;successful?&lt;/span&gt;
    &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:store&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;winning_strategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;store?&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;set_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;winning_strategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:event&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:authentication&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="vi"&gt;@users&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;opts&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;这个方法大致的作用是从 session 里找出某个 scope 的 key，如果有的话就根据提前定义的规则根据 key 来找到 user 对象。如果 session 里不存在 key，那么就根据这个 scope 的 strategies 列表来进行 auth，如果有策略命中的话就会返回 user 对象&lt;/p&gt;

&lt;p&gt;strategy 存在类变量里&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In Warden::Proxy&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_fetch_strategy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="vi"&gt;@strategies&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;klass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Warden&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Strategies&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="vi"&gt;@config.silence_missing_strategies&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
    &lt;span class="kp"&gt;nil&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s2"&gt;"Invalid strategy &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&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="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In Warden::Strategies&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;[]&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;_strategies&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;label&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;# :api: private&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_strategies&lt;/span&gt;
  &lt;span class="vi"&gt;@strategies&lt;/span&gt; &lt;span class="o"&gt;||=&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;strategy 必须继承自 Warden::Strategies::Base，含有 authenticate! 方法，在 devise 下 2 个默认的 strategy 在 lib/devise/strategies 目录下&lt;/p&gt;

&lt;p&gt;这是根据用户名密码校验用户的 strategy 的 auth 方法，检查密码的时候有用到 BCrypt&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In Devise::Strategies::DatabaseAuthenticatable&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;authenticate!&lt;/span&gt;
    &lt;span class="n"&gt;resource&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_for_database_authentication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;authentication_hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;hashed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt; &lt;span class="n"&gt;hashed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;valid_password?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="n"&gt;remember_me&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;after_database_authentication&lt;/span&gt;
      &lt;span class="n"&gt;success!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to&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="nf"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;hashed&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="no"&gt;Devise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;paranoid&lt;/span&gt;
    &lt;span class="nb"&gt;fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:not_found_in_database&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;devise 会给每个 scope 注册好 strategies，这样 warden 就会按照顺序去做检查了&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In Devise&lt;/span&gt;

&lt;span class="no"&gt;Devise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mappings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each_value&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;mapping&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
   &lt;span class="n"&gt;warden_config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scope_defaults&lt;/span&gt; &lt;span class="n"&gt;mapping&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="ss"&gt;strategies: &lt;/span&gt;&lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strategies&lt;/span&gt;

   &lt;span class="n"&gt;warden_config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;serialize_into_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mapping&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="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
     &lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;serialize_into_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;

   &lt;span class="n"&gt;warden_config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;serialize_from_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mapping&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="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="n"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;serialize_from_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&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;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;devise 依赖于 warden，要看 devise 的代码，最好先把 warden 代码过一遍&lt;/p&gt;

&lt;p&gt;warden 是一个 rack app，会生成 env['warden'] 对象，供 Rails Controller 调用&lt;/p&gt;

&lt;p&gt;warden 对不同的 scope 可以配置不同的 strategy。在进行 authenticate 的时候，要把 scope 传给 warden(或默认 default_scope)。warden 先查找 session 里的 user_key，并根据 session 里的 user_key 找到 user 对象，如果失败就会取出 scope 的 strategies 列表，挨个命中。&lt;/p&gt;

&lt;p&gt;使用 catch 和 throw 可以跳出多层调用栈，有点像 goto 语句。如果未通过身份认证，通过 catch(:warden)，warden 会控制响应的后续过程。&lt;/p&gt;

&lt;p&gt;warden 的设计很值得学习，作为一个框架，它将 authenticate 的部分问题解决了，并提供了简洁的 api 供上层调用。要完成 authenticate 验证，只需要写 strategy 和 failure_app 类，并将配置写入 warden 的类变量即可。&lt;/p&gt;</description>
      <author>adamshen</author>
      <pubDate>Fri, 21 Apr 2017 13:34:54 +0800</pubDate>
      <link>https://ruby-china.org/topics/32842</link>
      <guid>https://ruby-china.org/topics/32842</guid>
    </item>
    <item>
      <title>普通码农的思维总结</title>
      <description>&lt;p&gt;看到这个标题，你一定会有疑问，什么叫普通码农。&lt;/p&gt;

&lt;p&gt;相对普通码农，自然会有超级码农。记得刚参加工作的时候，我被分到一个路由器的组，专门干移植 bootloader 的工作。同组还有一个同事干同样的工作，只是我们会跟进不同项目。有一天早上，当我还在研究初始化代码的时候，我看到他正在用 GDB 工具看反汇编出来的代码。我问他这是在干嘛，他说他已经完成了这几个项目的移植，现在正在考虑是否可以对初始化代码用汇编来写。我当时在想，Are you kidding me？不过过一段时间他居然真的用汇编把这些东西给折腾出来了。&lt;/p&gt;

&lt;p&gt;似乎有些人，他们更擅长高强度的脑力劳动。当时我们常常上完一天班已经很累了，他还能精力十足地跑两公里回家。果然几年以后，他就成为了别人眼中的大牛。这是超级码农，当时同组还有另一位同事，他只是编写简单的 html 代码，但干了两个月就辞职了。辞职的时候兴冲冲地和我说，终于不用再看这些烦人的代码啦。码农有各种各样的，其中也有一大堆和我一样的人。我们对于编程，即没有那么绝对的擅长，也没有那么厌倦。这样的人，姑且称之为普通码农吧。&lt;/p&gt;

&lt;p&gt;作为一个普通码农，这些年我在编码的时候走了不少弯路。所以我想总结一下其中的几个经验教训，顺便和大家交流一下。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;经验一、只在同一个抽象层次进行思考&lt;/strong&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;p&gt;很多 Git Repo 在 commit 的时候要求改动接口和具体实现要分开提交，就是遵循上述的原则。这么多的语言、框架、范例就是为了让我们在面对复杂问题的时候，有一个相对标准的解耦的思路，节省我们的脑力。&lt;/p&gt;

&lt;p&gt;过去在看大量代码的时候，我常常很焦虑，甚至怀疑我自己的职业生涯。然而只要学会循序渐进，不要在某个单点上透支注意力，解决一个复杂问题的时候可能就没有那么吃力了。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;经验二、定期整理自己的知识结构&lt;/strong&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;你可以把自己想象成一个人工智能，虽然运算能力没那么强大，但是框架却要比 tensorflow 好上一万倍。你不需要太多的数据，也可以优化自己的模型。&lt;/p&gt;

&lt;p&gt;其实仔细一想，很多知识是完全相通的。我在学习 YARV 的时候，发现它的指令集和一些 cpu 指令集比如 x86 或者 mips 的有很多共通之处。而 js 实现异步的原理，和实时性操作系统 ucosii 实现多进程的方式如出一辙。&lt;/p&gt;

&lt;p&gt;过去我在学习的时候，总是想方设法让自己强记某些知识，最后发现这样不仅低效且很容易因为经常忘记而让自己失去信心。所以，学习新的知识时，要尽量把它和老的知识做比较，建立知识点之间的联系，才能事半功倍。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;经验三、Plan yourself&lt;/strong&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;p&gt;&lt;strong&gt;总结&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;编码和其他很多事情一样，都不是一件容易的事情。如果你想节约时间，可以快乐编码，上完班可以找三四个好友，聚在大排档，喝点啤酒，谈谈人生。那么可以学习一门为优雅而生的语言——ruby。&lt;/p&gt;

&lt;p&gt;入坑以后，你就会发现，这 TM 还是要有这么多东西要学，一点也不轻松。于是你就会抛弃轻松编码的幻想，继续努力工作。&lt;/p&gt;

&lt;p&gt;完。&lt;/p&gt;</description>
      <author>adamshen</author>
      <pubDate>Sat, 01 Apr 2017 11:43:50 +0800</pubDate>
      <link>https://ruby-china.org/topics/32679</link>
      <guid>https://ruby-china.org/topics/32679</guid>
    </item>
    <item>
      <title>从 Rails 中学习元编程的使用场景</title>
      <description>&lt;p&gt;Ruby 有许多元编程的特性，到底什么时候该用呢？这是个值得思考的问题&lt;/p&gt;

&lt;p&gt;在不该用的地方使用这些特性，只能影响程序的可维护性，违反了写代码的蠢萌原则 (Kiss)&lt;/p&gt;

&lt;p&gt;其实 Rails 里，就有大量使用元编程的例子。总结下来，主要目的有这么几种：&lt;/p&gt;
&lt;h2 id="一、Rails中元编程的使用场景"&gt;一、Rails 中元编程的使用场景&lt;/h2&gt;&lt;h3 id="1.1 简化接口"&gt;1.1 简化接口&lt;/h3&gt;
&lt;p&gt;在刚开始学习 Rails 的时候，我常常被 Rails dsl 的表达能力给震惊到&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;before_action&lt;/span&gt; &lt;span class="ss"&gt;:require_no_sso!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;only: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:new&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:create&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;through: :team_users&lt;/span&gt;
&lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="ss"&gt;:teams&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;短短几个单词能够干的事情很多，而为了支持这样的语法，就要在 dsl 的原方法里使用元编程来动态定义和调用一些方法&lt;/p&gt;

&lt;p&gt;这种极简化接口的特点就是，只用最少数的语句描述关键行为，将实现的细节完全隐藏，达到代码的高度简洁&lt;/p&gt;
&lt;h3 id="1.2 让代码更加语义化"&gt;1.2 让代码更加语义化&lt;/h3&gt;
&lt;p&gt;比如 1.day.ago 这种完全可以读出来的代码，要用 money patch 来实现。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;months&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;years&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;from_now&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;各种 hack scope，动态扩展类，使原本需要用函数调用实现的功能，还是依然可以用优雅的链式调用来进行表达&lt;/p&gt;

&lt;p&gt;比如 ActiveRecord 里的 wherechain，写起来十分的优雅，令人感到舒服&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Book&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;'title LIKE ?'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'%Rails%'&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="ss"&gt;:created_at&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;5&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;author: &lt;/span&gt;&lt;span class="s1"&gt;'David'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="1.3 Warning &amp;amp; Patch"&gt;1.3 Warning &amp;amp; Patch&lt;/h3&gt;
&lt;p&gt;比如 Api 变了，可以用 alias_method 来做个兼容&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# *_action is the same as append_*_action&lt;/span&gt;
 &lt;span class="kp"&gt;alias_method&lt;/span&gt; &lt;span class="ss"&gt;:"append_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="ss"&gt;_action"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="ss"&gt;_action"&lt;/span&gt;

 &lt;span class="n"&gt;define_method&lt;/span&gt; &lt;span class="s2"&gt;"append_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_filter"&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;names&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;blk&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
   &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Deprecation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"append_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_filter is deprecated and will be removed in Rails 5.1. Use append_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_action instead."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="nb"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"append_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_action"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;blk&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;ruby 的动态特性使得在进行版本兼容的时候可以更加随心所欲&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;RUBY_VERSION&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"..."&lt;/span&gt;
    &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="n"&gt;something&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="1.4 DRY，减少重复性代码"&gt;1.4 DRY，减少重复性代码&lt;/h3&gt;
&lt;p&gt;如果要用相同的逻辑 wrapper 一些方法，或者注册一些逻辑相同的 helper 方法时，可以用元编程来减少代码量&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="sx"&gt;%w(info debug warn error fatal unknown)&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;level&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="nb"&gt;class_eval&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;METHOD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;__LINE__&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="sh"&gt;
        def &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;(progname = nil, &amp;amp;block)
          logger.&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;(progname, &amp;amp;block) if logger
        end
&lt;/span&gt;&lt;span class="no"&gt;  METHOD&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="1.5 实现功能性需求"&gt;1.5 实现功能性需求&lt;/h3&gt;
&lt;p&gt;有些 api 本身就需要利用语言的动态特性，这时就不得不用了&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;helper_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;meths&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;meths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flatten!&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;_helper_methods&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;meths&lt;/span&gt;

  &lt;span class="n"&gt;meths&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;meth&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;_helpers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class_eval&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;ruby_eval&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;__LINE__&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="sh"&gt;
            def &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;meth&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;(*args, &amp;amp;blk)                             
              controller.send(%(&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;meth&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;), *args, &amp;amp;blk) 
            end                                                    
&lt;/span&gt;&lt;span class="no"&gt;    ruby_eval&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 语言的特性，就可以用到元编程&lt;/p&gt;

&lt;p&gt;例如 autoload 和 ActiveSupport::Concern 或者 mattr_accessor 和 class_attribute 这样的功能，原生的 ruby 是不支持的，但是可以通过元编程来进行支持&lt;/p&gt;
&lt;h2 id="二、总结"&gt;二、总结&lt;/h2&gt;
&lt;p&gt;当你想实现的功能本来就依赖 ruby 的动态特性时，就必须要使用元编程。&lt;/p&gt;

&lt;p&gt;另外元编程可以为模块化、语义化和 Dsl 提供支撑，让我们可以写更少的代码，写更明白的代码。&lt;/p&gt;</description>
      <author>adamshen</author>
      <pubDate>Mon, 27 Mar 2017 16:53:17 +0800</pubDate>
      <link>https://ruby-china.org/topics/32645</link>
      <guid>https://ruby-china.org/topics/32645</guid>
    </item>
    <item>
      <title>在使用 Ruby 或者 Rails 的过程中，你遇到过哪些令你印象深刻的坑？</title>
      <description>&lt;p&gt;如题，请教各位，有没有哪些坑令你至今依然难以忘记&lt;/p&gt;</description>
      <author>adamshen</author>
      <pubDate>Fri, 03 Mar 2017 07:51:22 +0800</pubDate>
      <link>https://ruby-china.org/topics/32425</link>
      <guid>https://ruby-china.org/topics/32425</guid>
    </item>
    <item>
      <title>详解 has_many 方法在 Active Record 中的运行过程</title>
      <description>&lt;h2 id="一、Reflection"&gt;一、Reflection&lt;/h2&gt;
&lt;p&gt;Reflection 是 ar 用来记录 model 的 associations &amp;amp; aggregations 配置的一个类，不同的方法会创建不同的 Reflection 类型&lt;/p&gt;

&lt;p&gt;composed_of 方法会创建一个 AggregateReflection 实例，而 has_many 方法会创建一个 AssociationReflection 实例&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Holds all the methods that are shared between MacroReflection and ThroughReflection.&lt;/span&gt;
&lt;span class="c1"&gt;#                                                                                     &lt;/span&gt;
&lt;span class="c1"&gt;#   AbstractReflection                                                                &lt;/span&gt;
&lt;span class="c1"&gt;#     MacroReflection                                                                 &lt;/span&gt;
&lt;span class="c1"&gt;#       AggregateReflection                                                           &lt;/span&gt;
&lt;span class="c1"&gt;#       AssociationReflection                                                         &lt;/span&gt;
&lt;span class="c1"&gt;#         HasManyReflection                                                           &lt;/span&gt;
&lt;span class="c1"&gt;#         HasOneReflection                                                            &lt;/span&gt;
&lt;span class="c1"&gt;#         BelongsToReflection                                                         &lt;/span&gt;
&lt;span class="c1"&gt;#         HasAndBelongsToManyReflection                                               &lt;/span&gt;
&lt;span class="c1"&gt;#     ThroughReflection                                                               &lt;/span&gt;
&lt;span class="c1"&gt;#       PolymorphicReflection                                                         &lt;/span&gt;
&lt;span class="c1"&gt;#         RuntimeReflection                                                           &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reflection 由统一的一个 builder 来进行实例化，下面的 create 方法创建一个 Reflection，它会把一条 dsl 语句 tokenize 成各种属性集，用来对 association 进行实际的配置&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;ActiveRecord&lt;/span&gt;
  &lt;span class="c1"&gt;# = Active Record Reflection&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Reflection&lt;/span&gt; &lt;span class="c1"&gt;# :nodoc:&lt;/span&gt;
    &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Concern&lt;/span&gt;

    &lt;span class="n"&gt;included&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;class_attribute&lt;/span&gt; &lt;span class="ss"&gt;:_reflections&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;instance_writer: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
      &lt;span class="n"&gt;class_attribute&lt;/span&gt; &lt;span class="ss"&gt;:aggregate_reflections&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;instance_writer: &lt;/span&gt;&lt;span class="kp"&gt;false&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;_reflections&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&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;aggregate_reflections&lt;/span&gt; &lt;span class="o"&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;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;macro&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;klass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;macro&lt;/span&gt;
              &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="ss"&gt;:composed_of&lt;/span&gt;
                &lt;span class="no"&gt;AggregateReflection&lt;/span&gt;
              &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="ss"&gt;:has_many&lt;/span&gt;
                &lt;span class="no"&gt;HasManyReflection&lt;/span&gt;
              &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="ss"&gt;:has_one&lt;/span&gt;
                &lt;span class="no"&gt;HasOneReflection&lt;/span&gt;
              &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="ss"&gt;:belongs_to&lt;/span&gt;
                &lt;span class="no"&gt;BelongsToReflection&lt;/span&gt;
              &lt;span class="k"&gt;else&lt;/span&gt;
                &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s2"&gt;"Unsupported Macro: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;macro&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;reflection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:through&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="no"&gt;ThroughReflection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reflection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;reflection&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;比如下面这条语句会生成的 Reflection 如下，然后在创建 association 方法的时候，会调取这个 Reflection 来查询配置&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:tracks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="ss"&gt;dependent: :destroy&lt;/span&gt;

 &lt;span class="c1"&gt;#&amp;lt;ActiveRecord::Reflection::HasManyReflection:0x007fc4ccd7e310&lt;/span&gt;
  &lt;span class="vi"&gt;@active_record&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
   &lt;span class="no"&gt;CheckCard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="n"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;title: &lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;level: &lt;/span&gt;&lt;span class="n"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;submit_times: &lt;/span&gt;&lt;span class="n"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;finish_times: &lt;/span&gt;&lt;span class="n"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;created_at: &lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;updated_at: &lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;user_id: &lt;/span&gt;&lt;span class="n"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;period: &lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="vi"&gt;@association_scope_cache&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="vi"&gt;@automatic_inverse_of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="vi"&gt;@constructable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="vi"&gt;@foreign_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"tracks_type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="vi"&gt;@klass&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="vi"&gt;@name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="ss"&gt;:tracks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="vi"&gt;@options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:dependent&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;:destroy&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="vi"&gt;@plural_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"tracks"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="vi"&gt;@scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c1"&gt;#&amp;lt;Proc:0x007fc4ccd7e338@/usr/local/lib/ruby/gems/2.4.0/gems/activerecord-5.0.1/lib/active_record/associations/builder/association.rb:55&amp;gt;,&lt;/span&gt;
  &lt;span class="vi"&gt;@scope_lock&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c1"&gt;#&amp;lt;Thread::Mutex:0x007fc4ccd7e158&amp;gt;,&lt;/span&gt;
  &lt;span class="vi"&gt;@type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 association 设置完毕后，这个 Reflection 会被加入 model 的 Reflections 列表里，可以用 reflect_on_all_associations 方法打印出这个表，有什么用呢？文档上是这么说的&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This information, for example, can be used in a form builder that takes an Active Record object and creates input fields for all of the attributes depending on their type and displays the associations to other objects.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Reflection 的实例除了保存配置以外，还存在一个 cache 和一个互斥锁。当进行一次 association 查询以后，相应的 statement 就会存到这个 cache 里，所以重新进行相同的 association 查询时要进行 reload 操作&lt;/p&gt;
&lt;h2 id="二、has_many的运行过程"&gt;二、has_many 的运行过程&lt;/h2&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Associations&lt;/span&gt; &lt;span class="c1"&gt;# :nodoc&lt;/span&gt;
    &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Builder&lt;/span&gt; &lt;span class="c1"&gt;#:nodoc:&lt;/span&gt;
      &lt;span class="nb"&gt;autoload&lt;/span&gt; &lt;span class="ss"&gt;:Association&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;           &lt;span class="s1"&gt;'active_record/associations/builder/association'&lt;/span&gt;
      &lt;span class="nb"&gt;autoload&lt;/span&gt; &lt;span class="ss"&gt;:SingularAssociation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="s1"&gt;'active_record/associations/builder/singular_association'&lt;/span&gt;
      &lt;span class="nb"&gt;autoload&lt;/span&gt; &lt;span class="ss"&gt;:CollectionAssociation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'active_record/associations/builder/collection_association'&lt;/span&gt;

      &lt;span class="nb"&gt;autoload&lt;/span&gt; &lt;span class="ss"&gt;:BelongsTo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;           &lt;span class="s1"&gt;'active_record/associations/builder/belongs_to'&lt;/span&gt;
      &lt;span class="nb"&gt;autoload&lt;/span&gt; &lt;span class="ss"&gt;:HasOne&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;              &lt;span class="s1"&gt;'active_record/associations/builder/has_one'&lt;/span&gt;
      &lt;span class="nb"&gt;autoload&lt;/span&gt; &lt;span class="ss"&gt;:HasMany&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;             &lt;span class="s1"&gt;'active_record/associations/builder/has_many'&lt;/span&gt;
      &lt;span class="nb"&gt;autoload&lt;/span&gt; &lt;span class="ss"&gt;:HasAndBelongsToMany&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'active_record/associations/builder/has_and_belongs_to_many'&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;ClassMethod&lt;/span&gt;
     &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;has_many&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&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="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;reflection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Builder&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HasMany&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="no"&gt;Reflection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_reflection&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reflection&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;p&gt;has_many 方法只有两行，它的实现方式全部都被封装到了 ActiveRecord::Associations::Builder::HasMany 类里&lt;/p&gt;

&lt;p&gt;Builder::HasMany.build 方法不仅创建了 reflection，还同时对 association 进行了配置&lt;/p&gt;

&lt;p&gt;这里类的继承关系是 Builder::HasMany &amp;lt; Builder::CollectionAssociation &amp;lt; Builder::Association&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# The hierarchy is defined as follows:&lt;/span&gt;
&lt;span class="c1"&gt;#  Association&lt;/span&gt;
&lt;span class="c1"&gt;#    - SingularAssociation&lt;/span&gt;
&lt;span class="c1"&gt;#      - BelongsToAssociation&lt;/span&gt;
&lt;span class="c1"&gt;#      - HasOneAssociation&lt;/span&gt;
&lt;span class="c1"&gt;#    - CollectionAssociation&lt;/span&gt;
&lt;span class="c1"&gt;#      - HasManyAssociation&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Builder::HasMany.build 方法由父类 Builder::Association 类来实现，它创建了一个 Reflection，定义了各种 accessors 方法，以及 callback 和 validations&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;ActiveRecord::Associations::Builder&lt;/span&gt; &lt;span class="c1"&gt;# :nodoc:&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Association&lt;/span&gt; &lt;span class="c1"&gt;#:nodoc:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dangerous_attribute_method?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ArgumentError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"You tried to define an association named &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; on the model &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, but "&lt;/span&gt; &lt;span class="p"&gt;\&lt;/span&gt;
                             &lt;span class="s2"&gt;"this will conflict with a method &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; already defined by Active Record. "&lt;/span&gt; &lt;span class="p"&gt;\&lt;/span&gt;
                             &lt;span class="s2"&gt;"Please choose a different association name."&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="n"&gt;extension&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;define_extensions&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;
      &lt;span class="n"&gt;reflection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;create_reflection&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;extension&lt;/span&gt;
      &lt;span class="n"&gt;define_accessors&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reflection&lt;/span&gt;
      &lt;span class="n"&gt;define_callbacks&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reflection&lt;/span&gt;
      &lt;span class="n"&gt;define_validations&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reflection&lt;/span&gt;
      &lt;span class="n"&gt;reflection&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接下来逐步看一下它的运行流程，首先检查一下方法名称是否已经被定义，然后生成一个 extension。define_extensions 方法在 CollectionAssociation 类里&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;In&lt;/span&gt; &lt;span class="no"&gt;Model&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Author&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:books&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;find_by_book_prefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;find_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;category_id: &lt;/span&gt;&lt;span class="n"&gt;book_number&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="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="nb"&gt;method&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define_extensions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;block_given?&lt;/span&gt;
        &lt;span class="n"&gt;extension_module_name&lt;/span&gt; &lt;span class="o"&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;model&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;demodulize&lt;/span&gt;&lt;span class="si"&gt;}#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;camelize&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;AssociationExtension"&lt;/span&gt;
        &lt;span class="n"&gt;extension&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Module&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="o"&gt;&amp;amp;&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="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;const_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;extension_module_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;extension&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;老实说这个方法，呃，十分有趣&lt;/p&gt;

&lt;p&gt;首先假如 has_many 方法有 block，这个 block 会被传递给 Proc.new，然后再用&amp;amp;符号转成 block。也就是说，原来的 block 被通过一个 Proc 的中转交给了新建的 Module&lt;/p&gt;

&lt;p&gt;这个新建的 Module 会立即在他的 scope 下运行这个 block。所以上面定义的这个 find_by_book_prefix 方法在新建的这个 Module 上被定义了&lt;/p&gt;

&lt;p&gt;最后把新建的这个 Module 绑定在一个常量上，这个常量属于 model 的 parent 类，总之看起来既让人迷惑又觉得很酷&lt;/p&gt;

&lt;p&gt;这个新建 Module 有什么用呢？其实和用 lambda 定义的 scope 作用是一样的，如果你同时用了 lambda 定义 scope，那么它们会呈链式调用&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap_scope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arity&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
      &lt;span class="nb"&gt;proc&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;instance_exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;extending&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="nb"&gt;proc&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;instance_exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;extending&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="nb"&gt;proc&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;extending&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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;接下来是创建 Reflection 的方法&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_reflection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;extension&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ArgumentError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"association names must be a Symbol"&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;kind_of?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Symbol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_a?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Hash&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="n"&gt;scope&lt;/span&gt;
    &lt;span class="n"&gt;scope&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;validate_options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;build_scope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;)&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;Reflection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;macro&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&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;首先检查参数，允许没有 lambda 的情况，options 提前。然后校验 options 的 keys 是否合法。合并 lambda 和 extension，最后创建 reflection&lt;/p&gt;

&lt;p&gt;接下来是生成 accessor 方法&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define_accessors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reflection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;mixin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generated_association_methods&lt;/span&gt;
  &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reflection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;
  &lt;span class="n"&gt;define_readers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mixin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;define_writers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mixin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&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;def&lt;/span&gt; &lt;span class="nf"&gt;generated_association_methods&lt;/span&gt;
  &lt;span class="vi"&gt;@generated_association_methods&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="n"&gt;mod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;const_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:GeneratedAssociationMethods&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Module&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="kp"&gt;include&lt;/span&gt; &lt;span class="n"&gt;mod&lt;/span&gt;
    &lt;span class="n"&gt;mod&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;这里生成的 accessor 方法并不是直接定义在 model 上，而是先新建一个 Module，include 到 model 里，然后再把各种方法定义到这个 Module 上。我猜这样做是为了可以让你自己在 model 定义同名方法，并使用 default_scope 调用原方法&lt;/p&gt;

&lt;p&gt;Builder::Association 会定义类似 student.books 以及 student.books=这两个方法，Builder:: CollectionAssociation 扩展定义了_ids 方法&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;In&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define_readers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mixin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;mixin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class_eval&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;CODE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;__LINE__&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="sh"&gt;
        def &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;(*args)
          association(:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;).reader(*args)
        end
&lt;/span&gt;&lt;span class="no"&gt;      CODE&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define_writers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mixin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;mixin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class_eval&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;CODE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;__LINE__&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="sh"&gt;
        def &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;=(value)
          association(:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;).writer(value)
        end
&lt;/span&gt;&lt;span class="no"&gt;      CODE&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;In&lt;/span&gt; &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="c1"&gt;# Defines the setter and getter methods for the collection_singular_ids.&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define_readers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mixin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;super&lt;/span&gt;

      &lt;span class="n"&gt;mixin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class_eval&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;CODE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;__LINE__&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="sh"&gt;
        def &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;singularize&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;_ids
          association(:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;).ids_reader
        end
&lt;/span&gt;&lt;span class="no"&gt;      CODE&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define_writers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mixin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;super&lt;/span&gt;

      &lt;span class="n"&gt;mixin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class_eval&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;CODE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;__LINE__&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="sh"&gt;
        def &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;singularize&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;_ids=(ids)
          association(:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;).ids_writer(ids)
        end
&lt;/span&gt;&lt;span class="no"&gt;      CODE&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;再接下来是定义 callbacks，关于 callbacks 链可以见上一篇文章&lt;/p&gt;

&lt;p&gt;这里没有做什么特别的事情，只是从 Reflection 中提取 callback 的配置，然后写到相对应的 callbacks 链中&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;CALLBACKS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:before_add&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:after_add&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:before_remove&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:after_remove&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define_callbacks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reflection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;super&lt;/span&gt;
  &lt;span class="nb"&gt;name&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reflection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;
  &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reflection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;options&lt;/span&gt;
  &lt;span class="no"&gt;CALLBACKS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;callback_name&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;define_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;callback_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&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;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;callback_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;full_callback_name&lt;/span&gt; &lt;span class="o"&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;callback_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_for_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

  &lt;span class="c1"&gt;# TODO : why do i need method_defined? I think its because of the inheritance chain&lt;/span&gt;
  &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class_attribute&lt;/span&gt; &lt;span class="n"&gt;full_callback_name&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;method_defined?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;full_callback_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;callbacks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;callback_name&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="nf"&gt;map&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;callback&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;
    &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;Symbol&lt;/span&gt;
      &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;Proc&lt;/span&gt;
      &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;record&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;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;full_callback_name&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="n"&gt;callbacks&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最后 define_validations，Builder::HasMany 并没有覆写这个方法，所以这里什么也没干&lt;/p&gt;
&lt;h3 id="三、总结"&gt;三、总结&lt;/h3&gt;
&lt;p&gt;声明 associations 的各个方法 belongs_to、has_many 的运行过程分两步。一是创建 Reflection，二是把 Reflection 当参数丢过去来进行实际配置。&lt;/p&gt;

&lt;p&gt;运行过程由 Builder 控制，因为 associations 的 dsl 基本都需要创建读写命令和 callbacks，流程差不多。所以它们的配置方法由父类来定义，然后具体的实现细节由不同的子类来实现。&lt;/p&gt;</description>
      <author>adamshen</author>
      <pubDate>Thu, 02 Mar 2017 11:14:05 +0800</pubDate>
      <link>https://ruby-china.org/topics/32419</link>
      <guid>https://ruby-china.org/topics/32419</guid>
    </item>
    <item>
      <title>详解 Rails Controller 中的 Callback</title>
      <description>&lt;h2 id="一、ActiveSupport::Callbacks"&gt;一、ActiveSupport::Callbacks&lt;/h2&gt;&lt;h3 id="1.1 例子"&gt;1.1 例子&lt;/h3&gt;
&lt;p&gt;rails 中的 callback 使用 ActiveSupport::Callbacks 模块来实现，Api 文档里有一个相应的例子&lt;/p&gt;

&lt;p&gt;使用的方法是先创建一个空的 callback 链表，给这个 callback 链表 push 一些方法，然后在需要运行 callback 的地方调用该队列&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;Record&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Callbacks&lt;/span&gt;
  &lt;span class="n"&gt;define_callbacks&lt;/span&gt; &lt;span class="ss"&gt;:save&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save&lt;/span&gt;
    &lt;span class="n"&gt;run_callbacks&lt;/span&gt; &lt;span class="ss"&gt;:save&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"- save"&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;class&lt;/span&gt; &lt;span class="nc"&gt;PersonRecord&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Record&lt;/span&gt;
  &lt;span class="n"&gt;set_callback&lt;/span&gt; &lt;span class="ss"&gt;:save&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:before&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:saving_message&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;saving_message&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"saving..."&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;set_callback&lt;/span&gt; &lt;span class="ss"&gt;:save&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:after&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;object&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"saved"&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;person&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;PersonRecord&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;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Output:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;saving...
-save
saved&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="1.2 初始化callback链"&gt;1.2 初始化 callback 链&lt;/h3&gt;
&lt;p&gt;define_callbacks 方法会定义一个类变量，然后把一个空的 callback 链赋值给这个类变量。这个变量的值可以被子类继承，一旦该类被继承，子类也会拥有这个 callback 链&lt;/p&gt;

&lt;p&gt;同时在这个方法里会根据 name 来创建一个 run 方法以供调用&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;define_callbacks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;names&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="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extract_options!&lt;/span&gt;

  &lt;span class="n"&gt;names&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="nb"&gt;name&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;class_attribute&lt;/span&gt; &lt;span class="s2"&gt;"_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_callbacks"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;instance_writer: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
    &lt;span class="n"&gt;set_callbacks&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CallbackChain&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="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nb"&gt;module_eval&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;RUBY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;__LINE__&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="sh"&gt;
      def _run_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;_callbacks(&amp;amp;block)
        __run_callbacks__(_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;_callbacks, &amp;amp;block)
      end
&lt;/span&gt;&lt;span class="no"&gt;    RUBY&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;def&lt;/span&gt; &lt;span class="nf"&gt;set_callbacks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;callbacks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# :nodoc:&lt;/span&gt;
  &lt;span class="nb"&gt;send&lt;/span&gt; &lt;span class="s2"&gt;"_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_callbacks="&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;callbacks&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="1.3 注册hook_method"&gt;1.3 注册 hook_method&lt;/h3&gt;
&lt;p&gt;set_callback 方法会将一个 hook_method 注入 Callback 链&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;set_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;filter_list&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filters&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="n"&gt;normalize_callback_params&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filter_list&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;self_chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_callbacks&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;
  &lt;span class="n"&gt;mapped&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&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;filter&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="no"&gt;Callback&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self_chain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;__update_callbacks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&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;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:prepend&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mapped&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mapped&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_callbacks&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chain&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;h3 id="1.4 调用callback链"&gt;1.4 调用 callback 链&lt;/h3&gt;
&lt;p&gt;run_callbacks 方法会调用前面 define_callbacks 方法里定义的 run 方法&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;run_callbacks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;send&lt;/span&gt; &lt;span class="s2"&gt;"_run_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_callbacks"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;run 方法内部根据 callback 链生成一个 callback 队列，然后直接运行这个 callback 队列&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;__run_callbacks__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;callbacks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;callbacks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;block_given?&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="n"&gt;runner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;callbacks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Filters&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Environment&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="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;value&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;h2 id="二、Controller中的callback"&gt;二、Controller 中的 callback&lt;/h2&gt;&lt;h3 id="2.1 定义callback链"&gt;2.1 定义 callback 链&lt;/h3&gt;
&lt;p&gt;contoller 中的 callback 链在 AbstractController::Callbacks 中定义&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;AbstractController&lt;/span&gt;
  &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Autoload&lt;/span&gt;

  &lt;span class="o"&gt;...&lt;/span&gt;
  &lt;span class="nb"&gt;autoload&lt;/span&gt; &lt;span class="ss"&gt;:Callbacks&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;AbstractController&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Callbacks&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="n"&gt;included&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;define_callbacks&lt;/span&gt; &lt;span class="ss"&gt;:process_action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                       &lt;span class="ss"&gt;terminator: &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result_lambda&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;result_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_a?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Proc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;performed?&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                       &lt;span class="ss"&gt;skip_after_callbacks_if_terminated: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="2.2 注册hook_method"&gt;2.2 注册 hook_method&lt;/h3&gt;
&lt;p&gt;在 controller 里我们不使用 set_callback 来定义 callback 函数，因为 rails wrapper 了一层 help 方法，可以省略一些参数&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:before&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:after&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:around&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;callback&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;define_method&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_action"&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;names&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;blk&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;_insert_callbacks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blk&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="nb"&gt;name&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="n"&gt;set_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:process_action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&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;span class="n"&gt;define_method&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_filter"&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;names&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;blk&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Deprecation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&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;callback&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_filter is deprecated and will be removed in Rails 5.1. Use &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_action instead."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;send&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;callback&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_action"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;blk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;define_method&lt;/span&gt; &lt;span class="s2"&gt;"prepend_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_action"&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;names&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;blk&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;_insert_callbacks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blk&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="nb"&gt;name&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="n"&gt;set_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:process_action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:prepend&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&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;span class="n"&gt;define_method&lt;/span&gt; &lt;span class="s2"&gt;"prepend_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_filter"&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;names&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;blk&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Deprecation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"prepend_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_filter is deprecated and will be removed in Rails 5.1. Use prepend_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_action instead."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"prepend_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_action"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;blk&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;# Skip a before, after or around callback. See _insert_callbacks&lt;/span&gt;
  &lt;span class="c1"&gt;# for details on the allowed parameters.&lt;/span&gt;
  &lt;span class="n"&gt;define_method&lt;/span&gt; &lt;span class="s2"&gt;"skip_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_action"&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;names&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;_insert_callbacks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;names&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="nb"&gt;name&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="n"&gt;skip_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:process_action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&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;span class="n"&gt;define_method&lt;/span&gt; &lt;span class="s2"&gt;"skip_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_filter"&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;names&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;blk&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Deprecation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"skip_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_filter is deprecated and will be removed in Rails 5.1. Use skip_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_action instead."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"skip_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_action"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;blk&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;# *_action is the same as append_*_action&lt;/span&gt;
  &lt;span class="kp"&gt;alias_method&lt;/span&gt; &lt;span class="ss"&gt;:"append_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="ss"&gt;_action"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="ss"&gt;_action"&lt;/span&gt;

  &lt;span class="n"&gt;define_method&lt;/span&gt; &lt;span class="s2"&gt;"append_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_filter"&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;names&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;blk&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Deprecation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"append_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_filter is deprecated and will be removed in Rails 5.1. Use append_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_action instead."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"append_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_action"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;blk&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;根据代码，在 rails5.1 里调用各种 xxxxx_filter 的结果就是直接转发给 xxxxx_action，同时给 xxxxx_action 取了个别名叫 append_xxxxx_action&lt;/p&gt;

&lt;p&gt;既然知道了 callbacks 链的 name 是:process_action，我们就可以用 define_callbacks 生成的方法打印出 callbacks 链&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;Api::Test&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;_process_action_callbacks&lt;/span&gt;
  &lt;span class="n"&gt;before_action&lt;/span&gt; &lt;span class="ss"&gt;:authenticate_api_user!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;only: :create&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;_process_action_callbacks&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;before_action 前，callbacks 链里只有两个 callback&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#&amp;lt;ActiveSupport::Callbacks::CallbackChain:0x0055aba4e7c278&lt;/span&gt;
 &lt;span class="vi"&gt;@callbacks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="vi"&gt;@chain&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="c1"&gt;#&amp;lt;ActiveSupport::Callbacks::Callback:0x0055aba4e7c980&lt;/span&gt;
    &lt;span class="vi"&gt;@chain_config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:scope&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:kind&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="ss"&gt;:terminator&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="c1"&gt;#&amp;lt;Proc:0x0055aba4e2fd88@/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/actionpack-5.0.1/lib/abstract_controller/callbacks.rb:12 (lambda)&amp;gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;:skip_after_callbacks_if_terminated&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="vi"&gt;@filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="ss"&gt;:set_request_start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="vi"&gt;@if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="vi"&gt;@key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="ss"&gt;:set_request_start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="vi"&gt;@kind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="ss"&gt;:before&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="vi"&gt;@name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="ss"&gt;:process_action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="vi"&gt;@unless&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="c1"&gt;#&amp;lt;ActiveSupport::Callbacks::Callback:0x0055aba4e7c3b8&lt;/span&gt;
    &lt;span class="vi"&gt;@chain_config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:scope&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:kind&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="ss"&gt;:terminator&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="c1"&gt;#&amp;lt;Proc:0x0055aba4e2fd88@/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/actionpack-5.0.1/lib/abstract_controller/callbacks.rb:12 (lambda)&amp;gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;:skip_after_callbacks_if_terminated&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="vi"&gt;@filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="ss"&gt;:update_auth_header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="vi"&gt;@if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="vi"&gt;@key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="ss"&gt;:update_auth_header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="vi"&gt;@kind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="ss"&gt;:after&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="vi"&gt;@name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="ss"&gt;:process_action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="vi"&gt;@unless&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="vi"&gt;@config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:scope&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:kind&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="ss"&gt;:terminator&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="c1"&gt;#&amp;lt;Proc:0x0055aba4e2fd88@/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/actionpack-5.0.1/lib/abstract_controller/callbacks.rb:12 (lambda)&amp;gt;,&lt;/span&gt;
   &lt;span class="ss"&gt;:skip_after_callbacks_if_terminated&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
 &lt;span class="vi"&gt;@mutex&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c1"&gt;#&amp;lt;Thread::Mutex:0x0055aba4e7c228&amp;gt;,&lt;/span&gt;
 &lt;span class="vi"&gt;@name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="ss"&gt;:process_action&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;执行了 before_action 后，callback 链里增加了一个 callback&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#&amp;lt;ActiveSupport::Callbacks::CallbackChain:0x0055aba4bcd9f0&lt;/span&gt;
 &lt;span class="vi"&gt;@callbacks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="vi"&gt;@chain&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="n"&gt;同上省略&lt;/span&gt;
   &lt;span class="c1"&gt;#&amp;lt;ActiveSupport::Callbacks::Callback:0x0055aba4c14968&lt;/span&gt;
    &lt;span class="vi"&gt;@chain_config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:scope&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:kind&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="ss"&gt;:terminator&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="c1"&gt;#&amp;lt;Proc:0x0055aba4e2fd88@/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/actionpack-5.0.1/lib/abstract_controller/callbacks.rb:12 (lambda)&amp;gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;:skip_after_callbacks_if_terminated&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="vi"&gt;@filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="ss"&gt;:authenticate_api_user!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="vi"&gt;@if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="c1"&gt;#&amp;lt;Proc:0x0055aba4c4c340@/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/actionpack-5.0.1/lib/abstract_controller/callbacks.rb:52&amp;gt;],&lt;/span&gt;
    &lt;span class="vi"&gt;@key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="ss"&gt;:authenticate_api_user!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="vi"&gt;@kind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="ss"&gt;:before&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="vi"&gt;@name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="ss"&gt;:process_action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="vi"&gt;@unless&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="vi"&gt;@config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:scope&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:kind&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="ss"&gt;:terminator&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="c1"&gt;#&amp;lt;Proc:0x0055aba4e2fd88@/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/actionpack-5.0.1/lib/abstract_controller/callbacks.rb:12 (lambda)&amp;gt;,&lt;/span&gt;
   &lt;span class="ss"&gt;:skip_after_callbacks_if_terminated&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
 &lt;span class="vi"&gt;@mutex&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c1"&gt;#&amp;lt;Thread::Mutex:0x0055aba4bb1ea8&amp;gt;,&lt;/span&gt;
 &lt;span class="vi"&gt;@name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="ss"&gt;:process_action&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;试着打印生成的 callback 队列&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;_process_action_callbacks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;#&amp;lt;ActiveSupport::Callbacks::CallbackSequence:0x0055aba3454738&lt;/span&gt;
 &lt;span class="vi"&gt;@after&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="c1"&gt;#&amp;lt;Proc:0x0055aba34541c0@/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:216&amp;gt;],&lt;/span&gt;
 &lt;span class="vi"&gt;@before&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="c1"&gt;#&amp;lt;Proc:0x0055aba3454080@/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:163&amp;gt;,&lt;/span&gt;
   &lt;span class="c1"&gt;#&amp;lt;Proc:0x0055aba34543a0@/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:144&amp;gt;],&lt;/span&gt;
 &lt;span class="vi"&gt;@call&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c1"&gt;#&amp;lt;Proc:0x0055aba3454698@/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:506&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="2.3 调用callback链"&gt;2.3 调用 callback 链&lt;/h3&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Override AbstractController::Base's process_action to run the&lt;/span&gt;
&lt;span class="c1"&gt;# process_action callbacks around the normal behavior.&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;run_callbacks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:process_action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;super&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;前面的 set 和 define 都发生在类源文件被 load 的过程中，但是 run_callbacks 在何时被调用却并不清晰，因此在 controller 中直接打印调用栈来查看&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:101:in `__run_callbacks__'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:750:in `_run_process_action_callbacks'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:90:in `run_callbacks'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/actionpack-5.0.1/lib/abstract_controller/callbacks.rb:19:in `process_action'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/actionpack-5.0.1/lib/action_controller/metal/rescue.rb:20:in `process_action'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/actionpack-5.0.1/lib/action_controller/metal/instrumentation.rb:32:in `block in process_action'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/activesupport-5.0.1/lib/active_support/notifications.rb:164:in `block in instrument'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/activesupport-5.0.1/lib/active_support/notifications/instrumenter.rb:21:in `instrument'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/activesupport-5.0.1/lib/active_support/notifications.rb:164:in `instrument'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/actionpack-5.0.1/lib/action_controller/metal/instrumentation.rb:30:in `process_action'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/actionpack-5.0.1/lib/action_controller/metal/params_wrapper.rb:248:in `process_action'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/activerecord-5.0.1/lib/active_record/railties/controller_runtime.rb:18:in `process_action'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/actionpack-5.0.1/lib/abstract_controller/base.rb:126:in `process'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/actionpack-5.0.1/lib/action_controller/metal.rb:190:in `dispatch'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/actionpack-5.0.1/lib/action_controller/metal.rb:262:in `dispatch'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/actionpack-5.0.1/lib/action_dispatch/routing/route_set.rb:50:in `dispatch'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/actionpack-5.0.1/lib/action_dispatch/routing/route_set.rb:32:in `serve'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/actionpack-5.0.1/lib/action_dispatch/journey/router.rb:39:in `block in serve'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/actionpack-5.0.1/lib/action_dispatch/journey/router.rb:26:in `each'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/actionpack-5.0.1/lib/action_dispatch/journey/router.rb:26:in `serve'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/actionpack-5.0.1/lib/action_dispatch/routing/route_set.rb:725:in `call'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/warden-1.2.7/lib/warden/manager.rb:36:in `block in call'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/warden-1.2.7/lib/warden/manager.rb:35:in `catch'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/warden-1.2.7/lib/warden/manager.rb:35:in `call'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/rack-2.0.1/lib/rack/etag.rb:25:in `call'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/rack-2.0.1/lib/rack/conditional_get.rb:25:in `call'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/rack-2.0.1/lib/rack/head.rb:12:in `call'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/activerecord-5.0.1/lib/active_record/migration.rb:553:in `call'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/actionpack-5.0.1/lib/action_dispatch/middleware/callbacks.rb:38:in `block in call'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:97:in `__run_callbacks__'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:750:in `_run_call_callbacks'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:90:in `run_callbacks'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/actionpack-5.0.1/lib/action_dispatch/middleware/callbacks.rb:36:in `call'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/actionpack-5.0.1/lib/action_dispatch/middleware/executor.rb:12:in `call'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/actionpack-5.0.1/lib/action_dispatch/middleware/remote_ip.rb:79:in `call'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/actionpack-5.0.1/lib/action_dispatch/middleware/debug_exceptions.rb:49:in `call'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/actionpack-5.0.1/lib/action_dispatch/middleware/show_exceptions.rb:31:in `call'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/railties-5.0.1/lib/rails/rack/logger.rb:36:in `call_app'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/railties-5.0.1/lib/rails/rack/logger.rb:24:in `block in call'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/activesupport-5.0.1/lib/active_support/tagged_logging.rb:69:in `block in tagged'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/activesupport-5.0.1/lib/active_support/tagged_logging.rb:26:in `tagged'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/activesupport-5.0.1/lib/active_support/tagged_logging.rb:69:in `tagged'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/railties-5.0.1/lib/rails/rack/logger.rb:24:in `call'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/actionpack-5.0.1/lib/action_dispatch/middleware/request_id.rb:24:in `call'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/rack-2.0.1/lib/rack/runtime.rb:22:in `call'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/activesupport-5.0.1/lib/active_support/cache/strategy/local_cache_middleware.rb:28:in `call'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/actionpack-5.0.1/lib/action_dispatch/middleware/executor.rb:12:in `call'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/actionpack-5.0.1/lib/action_dispatch/middleware/static.rb:136:in `call'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/rack-2.0.1/lib/rack/sendfile.rb:111:in `call'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/rack-cors-0.4.1/lib/rack/cors.rb:81:in `call'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/railties-5.0.1/lib/rails/engine.rb:522:in `call'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/puma-3.7.0/lib/puma/configuration.rb:226:in `call'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/puma-3.7.0/lib/puma/server.rb:578:in `handle_request'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/puma-3.7.0/lib/puma/server.rb:415:in `process_client'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/puma-3.7.0/lib/puma/server.rb:275:in `block in run'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"/home/adam/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/puma-3.7.0/lib/puma/thread_pool.rb:120:in `block in spawn_thread'"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;大致上一次请求从 puma 侦听到 IO 并交给 rack，经过路由找到对应的 controller 之后，就会进到 process_action&lt;/p&gt;

&lt;p&gt;很多 module 都有这个方法，利用 super 根据 include 进来的顺序倒顺执行，控制响应请求的过程，callback 是其中一环&lt;/p&gt;</description>
      <author>adamshen</author>
      <pubDate>Wed, 22 Feb 2017 11:15:54 +0800</pubDate>
      <link>https://ruby-china.org/topics/32357</link>
      <guid>https://ruby-china.org/topics/32357</guid>
    </item>
    <item>
      <title>推荐一下编辑器 nuclide 的 remote 功能，可以方便地修改远程代码</title>
      <description>&lt;p&gt;&lt;a href="https://nuclide.io/docs/quick-start/getting-started/#remote-connection" rel="nofollow" target="_blank"&gt;https://nuclide.io/docs/quick-start/getting-started/#remote-connection&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;碰到有修改远程代码的需求，代码量小的项目可以用 sshfs 等协议把目录 mount 到本地。&lt;/p&gt;

&lt;p&gt;但在文件数量众多，或者网络连接不稳定的情况下就不适合这么干了。&lt;/p&gt;

&lt;p&gt;也可以用 rsync 来进行同步，不过配置起来有点复杂。&lt;/p&gt;

&lt;p&gt;最近同事推荐了 nuclide 的远程同步功能，感觉不错。&lt;/p&gt;

&lt;p&gt;nuclide 是 atom 的一个扩展包，由 facebook 开发并开源了（&lt;a href="https://github.com/facebook/nuclide" rel="nofollow" target="_blank"&gt;https://github.com/facebook/nuclide&lt;/a&gt;），其中一个功能就是远程同步。&lt;/p&gt;

&lt;p&gt;用起来很方便，client 端装好 nuclide，server 端装好一个 npm 包，通过 ssh 凭据就可以直接用了。&lt;/p&gt;

&lt;p&gt;我测试了一下，基本都很流畅，偶尔 save 文件进行同步的时候会稍微卡一下，影响不大。&lt;/p&gt;</description>
      <author>adamshen</author>
      <pubDate>Mon, 23 Jan 2017 21:35:32 +0800</pubDate>
      <link>https://ruby-china.org/topics/32201</link>
      <guid>https://ruby-china.org/topics/32201</guid>
    </item>
    <item>
      <title>使用 serialize 在 db 中储存 hash 对象时遇到的一个小问题</title>
      <description>&lt;p&gt;更改 attibutes 方法生成出来的 hash 对象再 save 以后，会改变数据库里实际所存储的值&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;Request&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;serialize&lt;/span&gt; &lt;span class="ss"&gt;:options&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;options: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;level: &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;request_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attributes&lt;/span&gt;
&lt;span class="n"&gt;request_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'options'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;level: &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;
&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reload&lt;/span&gt;

&lt;span class="n"&gt;request&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;options: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;level: &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;造成这个问题的原因是 attributes 方法得到的 hash 里，key options 所指向的 obj 和 ActiveRecord::Attribute::FromDatabase 实例里 value 变量所指向的 obj 是同一个 obj&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'options'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;AD 的 instance 里用 attributes 变量暂存 record 的值&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# From: /opt/rubies/ruby-2.3.1/lib/ruby/gems/2.3.0/gems/activerecord-5.0.0.1/lib/active_record/attribute_methods.rb @ line 275:&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;attributes&lt;/span&gt;
  &lt;span class="vi"&gt;@attributes.to_hash&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;它是一个 ActiveRecord::AttributeSet 的实例&lt;/p&gt;

&lt;p&gt;每一个 column 的 value 用 ActiveRecord::Attribute::FromDatabase 的实例来存储&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;request&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="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:@attributes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;#&amp;lt;ActiveRecord::AttributeSet:0x00000008d8e930&lt;/span&gt;
 &lt;span class="vi"&gt;@attributes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="c1"&gt;#&amp;lt;ActiveRecord::LazyAttributeHash:0x00000008d8e980&lt;/span&gt;
   &lt;span class="vi"&gt;@additional_types&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
   &lt;span class="vi"&gt;@delegate_hash&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"options"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="c1"&gt;#&amp;lt;ActiveRecord::Attribute::FromDatabase:0x00000008d86de8&lt;/span&gt;
       &lt;span class="vi"&gt;@name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"options"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="vi"&gt;@original_attribute&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="vi"&gt;@type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c1"&gt;#&amp;lt;ActiveModel::Type::Text:0x00000000f73668 @limit=nil, @precision=nil, @scale=nil&amp;gt;,&lt;/span&gt;
       &lt;span class="vi"&gt;@value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:level&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&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;当使用 attributes 方法时，只是取出 ActiveRecord::Attribute::FromDatabase 实例的 value 变量指向的对象，没有进行 dup，所以产生了上述问题&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# From: /opt/rubies/ruby-2.3.1/lib/ruby/gems/2.3.0/gems/activerecord-5.0.0.1/lib/active_record/attribute_set.rb @ line 21:&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;to_hash&lt;/span&gt;
  &lt;span class="n"&gt;initialized_attributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transform_values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:value&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;</description>
      <author>adamshen</author>
      <pubDate>Mon, 12 Dec 2016 11:11:04 +0800</pubDate>
      <link>https://ruby-china.org/topics/31881</link>
      <guid>https://ruby-china.org/topics/31881</guid>
    </item>
    <item>
      <title>[上海] VaneCloud 中科氢氧 Ruby 高级工程师</title>
      <description>&lt;h3 id="我们是谁？"&gt;我们是谁？&lt;/h3&gt;
&lt;p&gt;中科氢氧云计算（上海）有限公司（以下简称“中科氢氧”）于 2015 年成立，是《中国云体系产业创新战略联盟》的理事单位。公司在北京、广州、合肥等地设有多个分支机构，业务范围覆盖华北、华东、华中和华南等大部分地区。&lt;/p&gt;

&lt;p&gt;中科氢氧的核心技术团队，由中科院的骨干技术人员以及原 IBM 等前全球 50 强的研发团队共同组成，公司致力于开发企业私有云平台 (vaneCloud)、大数据解决方案和私有云盘（VaneDisk）的提供商。创新、服务激情和责任是我们公司创立至今一直秉承的服务理念，以此来为我们的客户提供赋有前瞻性和可持续性的优质的产品和服务。&lt;/p&gt;
&lt;h3 id="招聘职位"&gt;招聘职位&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;rails 高级工程师&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="岗位职责"&gt;岗位职责&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;基于 Ruby on Rails 框架进行云计算管理平台的设计和开发&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="职位要求"&gt;职位要求&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;熟悉 Ruby, Ruby on Rails，有两年及以上 Ruby on Rails 项目开发经验；&lt;/li&gt;
&lt;li&gt;熟悉 HTML, CSS, Javascript，jQuery，JSON 等 Web 相关技术；&lt;/li&gt;
&lt;li&gt;熟悉 Git 等版本控制工具，Linux 操作系统；&lt;/li&gt;
&lt;li&gt;有良好的代码书写和编程习惯、非常强的学习能力，解决问题的能力；&lt;/li&gt;
&lt;li&gt;加分条件：云计算等相关工作经验者优先；有大中型 WEB 网站开发经验者优先; 有自己的 blog 或 github 链接；&lt;/li&gt;
&lt;li&gt;具有团队合作精神，有责任感，能承受一定的工作压力、保持工作效率&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="福利待遇"&gt;福利待遇&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;我们不想说弹（jia）性（ban）工作，如果你产出足够高效，时间自己安排，做五休二&lt;/li&gt;
&lt;li&gt;10K~20K，一年至少一次薪水调整&lt;/li&gt;
&lt;li&gt;四险一金 带薪年假 &lt;/li&gt;
&lt;li&gt;云计算先关，高挑战、快成长环境&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="舒适的工作环境"&gt;舒适的工作环境&lt;/h3&gt;
&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/3bb6cf9acefdfc560170365bb5730058.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/3cd177a2714d99ad08fe089cc24e305c.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/a3347fcd60f4cbf3c81833a2840c45f9.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/a460a8268cfb6688841b56200e41f82f.jpg!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h3 id="工作地点"&gt;工作地点&lt;/h3&gt;
&lt;p&gt;上海市徐汇区漕河泾桂平路 471 号 3 号楼 3 楼&lt;/p&gt;
&lt;h3 id="简历投递"&gt;简历投递&lt;/h3&gt;
&lt;p&gt;wangxiangyu@vanecloud.com&lt;/p&gt;</description>
      <author>adamshen</author>
      <pubDate>Thu, 08 Sep 2016 15:56:53 +0800</pubDate>
      <link>https://ruby-china.org/topics/31019</link>
      <guid>https://ruby-china.org/topics/31019</guid>
    </item>
    <item>
      <title>介绍一个好玩的工具给大家，可以生成类关联网状图</title>
      <description>&lt;p&gt;&lt;a href="https://github.com/blazeeboy/rubrowser" rel="nofollow" target="_blank"&gt;https://github.com/blazeeboy/rubrowser&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;我玩了下 puma 的&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/411add4dbd7c6b333350f63e6e9b3100.png!large" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>adamshen</author>
      <pubDate>Mon, 29 Aug 2016 12:53:55 +0800</pubDate>
      <link>https://ruby-china.org/topics/30924</link>
      <guid>https://ruby-china.org/topics/30924</guid>
    </item>
    <item>
      <title>有没有人实践过用 Docker 部署 Rails 集群，效果如何？</title>
      <description>&lt;p&gt;对 Rails 来说容器级别的隔离应该满足部署集群的需要了吧？&lt;/p&gt;</description>
      <author>adamshen</author>
      <pubDate>Wed, 27 Jul 2016 11:47:40 +0800</pubDate>
      <link>https://ruby-china.org/topics/30635</link>
      <guid>https://ruby-china.org/topics/30635</guid>
    </item>
    <item>
      <title>[上海] VaneCloud 中科氢氧 Rails 高级工程师</title>
      <description>&lt;h3 id="我们是谁？"&gt;我们是谁？&lt;/h3&gt;
&lt;p&gt;中科氢氧云计算（上海）有限公司（以下简称“中科氢氧”）于 2015 年成立，是《中国云体系产业创新战略联盟》的理事单位。公司在北京、广州、合肥等地设有多个分支机构，业务范围覆盖华北、华东、华中和华南等大部分地区。&lt;/p&gt;

&lt;p&gt;中科氢氧的核心技术团队，由中科院的骨干技术人员以及原 IBM 等前全球 50 强的研发团队共同组成，公司致力于开发企业私有云平台 (vaneCloud)、大数据解决方案和私有云盘（VaneDisk）的提供商。创新、服务激情和责任是我们公司创立至今一直秉承的服务理念，以此来为我们的客户提供赋有前瞻性和可持续性的优质的产品和服务。&lt;/p&gt;
&lt;h3 id="招聘职位"&gt;招聘职位&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;rails 高级工程师&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="岗位职责"&gt;岗位职责&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;基于 Ruby on Rails 框架进行云计算管理平台的设计和开发&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="职位要求"&gt;职位要求&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;熟悉 Ruby, Ruby on Rails，有两年及以上 Ruby on Rails 项目开发经验；&lt;/li&gt;
&lt;li&gt;熟悉 HTML, CSS, Javascript，jQuery，JSON 等 Web 相关技术；&lt;/li&gt;
&lt;li&gt;熟悉 Git 等版本控制工具，Linux 操作系统；&lt;/li&gt;
&lt;li&gt;有良好的代码书写和编程习惯、非常强的学习能力，解决问题的能力；&lt;/li&gt;
&lt;li&gt;加分条件：云计算等相关工作经验者优先；有大中型 WEB 网站开发经验者优先; 有自己的 blog 或 github 链接；&lt;/li&gt;
&lt;li&gt;具有团队合作精神，有责任感，能承受一定的工作压力、保持工作效率&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="福利待遇"&gt;福利待遇&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;我们不想说弹（jia）性（ban）工作，如果你产出足够高效，时间自己安排，做五休二&lt;/li&gt;
&lt;li&gt;10K~20K，一年至少一次薪水调整&lt;/li&gt;
&lt;li&gt;四险一金 带薪年假 &lt;/li&gt;
&lt;li&gt;云计算先关，高挑战、快成长环境&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="舒适的工作环境"&gt;舒适的工作环境&lt;/h3&gt;
&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/3bb6cf9acefdfc560170365bb5730058.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/3cd177a2714d99ad08fe089cc24e305c.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/a3347fcd60f4cbf3c81833a2840c45f9.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/a460a8268cfb6688841b56200e41f82f.jpg!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h3 id="工作地点"&gt;工作地点&lt;/h3&gt;
&lt;p&gt;上海市徐汇区漕河泾桂平路 471 号 3 号楼 3 楼&lt;/p&gt;
&lt;h3 id="简历投递"&gt;简历投递&lt;/h3&gt;
&lt;p&gt;wangxiangyu@vanecloud.com&lt;/p&gt;</description>
      <author>adamshen</author>
      <pubDate>Tue, 12 Jul 2016 13:52:54 +0800</pubDate>
      <link>https://ruby-china.org/topics/30505</link>
      <guid>https://ruby-china.org/topics/30505</guid>
    </item>
    <item>
      <title>在 Rails 项目里，redis 一般用来存哪些数据？</title>
      <description>&lt;p&gt;上次听演讲，薄荷说薄荷网的 Redis 服务器里数据量最大的都存了十几个 G 的数据了，larry 说简书有许多 redis 服务器，不同的服务器里存了不同的东西。很好奇这些 redis 服务器里都存了什么，如果我自己做项目，要如何设计哪些数据存在 mysql 里，哪些存在 redis 里？&lt;/p&gt;</description>
      <author>adamshen</author>
      <pubDate>Wed, 01 Jun 2016 21:20:56 +0800</pubDate>
      <link>https://ruby-china.org/topics/30185</link>
      <guid>https://ruby-china.org/topics/30185</guid>
    </item>
    <item>
      <title>无聊爬了下 GitHub 上的开源 Rails 项目</title>
      <description>&lt;p&gt;按照 star 数量排序，本站排名 11。如果不算工具类的，仅次于 discourse。&lt;/p&gt;
&lt;table class="table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/discourse/discourse" rel="nofollow" target="_blank" title=""&gt;discourse/discourse&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A platform for community discussion. Free, open, simple.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/gitlabhq/gitlabhq" rel="nofollow" target="_blank" title=""&gt;gitlabhq/gitlabhq&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;GitLab is version control for your server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/cantino/huginn" rel="nofollow" target="_blank" title=""&gt;cantino/huginn&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Build agents that monitor and act on your behalf. Your agents are standing by!&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/diaspora/diaspora" rel="nofollow" target="_blank" title=""&gt;diaspora/diaspora&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A privacy-aware, distributed, open source social network.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/errbit/errbit" rel="nofollow" target="_blank" title=""&gt;errbit/errbit&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;The open source error catcher that's Airbrake API compliant&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/lockitron/selfstarter" rel="nofollow" target="_blank" title=""&gt;lockitron/selfstarter&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Roll your own crowdfunding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/fatfreecrm/fat_free_crm" rel="nofollow" target="_blank" title=""&gt;fatfreecrm/fat_free_crm&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Ruby on Rails CRM platform for Web 2.0 and beyond&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/edavis10/redmine" rel="nofollow" target="_blank" title=""&gt;edavis10/redmine&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Redmine is a flexible project management web application written using Ruby on Rails framework. &lt;a href="http://github.com/edavis10/redmine" rel="nofollow" target="_blank"&gt;http://github.com/edavis10/redmine&lt;/a&gt; is the official git mirror of the svn repository&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/comfy/comfortable-mexican-sofa" rel="nofollow" target="_blank" title=""&gt;comfy/comfortable-mexican-sofa&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;ComfortableMexicanSofa is a powerful Rails 4 CMS Engine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/ruby-china/ruby-china" rel="nofollow" target="_blank" title=""&gt;ruby-china/ruby-china&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Ruby China website source code.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/teambox/teambox" rel="nofollow" target="_blank" title=""&gt;teambox/teambox&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;This is the legacy version of Teambox - the award-winning collaboration solution, inspired by Basecamp, Yammer and Twitter.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/redmine/redmine" rel="nofollow" target="_blank" title=""&gt;redmine/redmine&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Mirror of redmine code source - Official SVN repository is at &lt;a href="https://svn.redmine.org/redmine" rel="nofollow" target="_blank"&gt;https://svn.redmine.org/redmine&lt;/a&gt; - contact: &lt;a href="/jbbarth" class="user-mention" title="@jbbarth"&gt;&lt;i&gt;@&lt;/i&gt;jbbarth&lt;/a&gt; or jeanbaptiste.barth (at) gmail (dot) com&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/instructure/canvas-lms" rel="nofollow" target="_blank" title=""&gt;instructure/canvas-lms&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;The open LMS by Instructure, Inc.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/hummingbird-me/hummingbird" rel="nofollow" target="_blank" title=""&gt;hummingbird-me/hummingbird&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Probably the coolest anime discovery platform around.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/feedbin/feedbin" rel="nofollow" target="_blank" title=""&gt;feedbin/feedbin&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;RSS Reader&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/publify/publify" rel="nofollow" target="_blank" title=""&gt;publify/publify&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A self hosted Web publishing platform on Rails.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/tenex/rails-assets" rel="nofollow" target="_blank" title=""&gt;tenex/rails-assets&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;The solution to assets management in Rails&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/natew/obtvse" rel="nofollow" target="_blank" title=""&gt;natew/obtvse&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Deprecated: See natew/obtvse2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/gitlabhq/gitlab-ci" rel="nofollow" target="_blank" title=""&gt;gitlabhq/gitlab-ci&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Mirror of GitLab CI, please use GitLab.com repo for issues&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/rubygems/rubygems.org" rel="nofollow" target="_blank" title=""&gt;rubygems/rubygems.org&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;The Ruby community's gem hosting service.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/eliotsykes/rspec-rails-examples" rel="nofollow" target="_blank" title=""&gt;eliotsykes/rspec-rails-examples&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;RSpec cheatsheet &amp;amp; Rails app: Learn how to expertly test Rails apps from a model codebase&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/churchio/onebody" rel="nofollow" target="_blank" title=""&gt;churchio/onebody&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;private member portal for churches, built with Ruby on Rails&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/houndci/hound" rel="nofollow" target="_blank" title=""&gt;houndci/hound&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Automated review for code style&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/loomio/loomio" rel="nofollow" target="_blank" title=""&gt;loomio/loomio&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Loomio is an open-source web application that helps groups make better decisions together.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/ivaldi/brimir" rel="nofollow" target="_blank" title=""&gt;ivaldi/brimir&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Email helpdesk built using Ruby on Rails and Zurb Foundation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/jcs/lobsters" rel="nofollow" target="_blank" title=""&gt;jcs/lobsters&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Rails code running the Lobsters link aggregation site&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/RailsApps/rails-stripe-membership-saas" rel="nofollow" target="_blank" title=""&gt;RailsApps/rails-stripe-membership-saas&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;An example Rails 4.2 app with Stripe and the Payola gem for a membership or subscription site.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/drhenner/ror_ecommerce" rel="nofollow" target="_blank" title=""&gt;drhenner/ror_ecommerce&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Ruby on Rails Ecommerce platform, perfect for your small business solution.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/phusion/juvia" rel="nofollow" target="_blank" title=""&gt;phusion/juvia&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A commenting server similar to Disqus and IntenseDebate. CURRENTLY UNMAINTAINED&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/chloerei/writings" rel="nofollow" target="_blank" title=""&gt;chloerei/writings&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Closed] Source code of writings.io&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/SquareSquash/web" rel="nofollow" target="_blank" title=""&gt;SquareSquash/web&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Squash’s front-end and API host.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/iridakos/duckrails" rel="nofollow" target="_blank" title=""&gt;iridakos/duckrails&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A development tool to quickly &amp;amp; dynamically mock API endpoints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/asciinema/asciinema.org" rel="nofollow" target="_blank" title=""&gt;asciinema/asciinema.org&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;asciinema.org web app&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/peritor/webistrano" rel="nofollow" target="_blank" title=""&gt;peritor/webistrano&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Webistrano is a Web UI for managing Capistrano deployments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/24pullrequests/24pullrequests" rel="nofollow" target="_blank" title=""&gt;24pullrequests/24pullrequests&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Giving back little gifts of code for Christmas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/fdietz/team_dashboard" rel="nofollow" target="_blank" title=""&gt;fdietz/team_dashboard&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Visualize your team's metrics all in one place.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/shakacode/react-webpack-rails-tutorial" rel="nofollow" target="_blank" title=""&gt;shakacode/react-webpack-rails-tutorial&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Example of integration of Rails, react, redux, using the react_on_rails gem, webpack, enabling the es7 and jsx transpilers, and node integration. Live Demo:&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/coderwall/coderwall-legacy" rel="nofollow" target="_blank" title=""&gt;coderwall/coderwall-legacy&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Professional network for software engineers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/xaviershay/enki" rel="nofollow" target="_blank" title=""&gt;xaviershay/enki&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A Ruby on Rails blogging app for the fashionable developer. It's better than Mephisto or SimpleLog&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/jeromegn/DocumentUp" rel="nofollow" target="_blank" title=""&gt;jeromegn/DocumentUp&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Pretty documentation generator for Github projects with proper Readme.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/github-archive/swordfish" rel="nofollow" target="_blank" title=""&gt;github-archive/swordfish&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;EXPERIMENTAL password management app. Don't use this.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/peatio/peatio" rel="nofollow" target="_blank" title=""&gt;peatio/peatio&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;An open-source assets exchange.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/SUSE/Portus" rel="nofollow" target="_blank" title=""&gt;SUSE/Portus&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Authorization service and frontend for Docker registry (v2)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/TracksApp/tracks" rel="nofollow" target="_blank" title=""&gt;TracksApp/tracks&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Tracks is a GTD™ web application, built with Ruby on Rails&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/fredwu/angel_nest" rel="nofollow" target="_blank" title=""&gt;fredwu/angel_nest&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Project code name: Angel Nest. :)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/ryanb/railscasts" rel="nofollow" target="_blank" title=""&gt;ryanb/railscasts&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;railscasts.com in open source (outdated).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/hotsh/rstat.us" rel="nofollow" target="_blank" title=""&gt;hotsh/rstat.us&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Simple microblogging network based on the ostatus protocol.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/vdaubry/github-awards" rel="nofollow" target="_blank" title=""&gt;github-awards&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Discover your ranking on github&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/spark/thermostat" rel="nofollow" target="_blank" title=""&gt;spark/thermostat&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A place for all things related to ye olde Spark Thermostat Hackathon&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/copycopter/copycopter-server" rel="nofollow" target="_blank" title=""&gt;copycopter/copycopter-server&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Copycopter Server is open source. Run it as a web service.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/DefactoSoftware/Hours" rel="nofollow" target="_blank" title=""&gt;DefactoSoftware/Hours&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Time registration that doesn't suck&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/saberma/shopqi" rel="nofollow" target="_blank" title=""&gt;saberma/shopqi&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;An open source clone of Shopify.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/robotmay/photographer-io" rel="nofollow" target="_blank" title=""&gt;robotmay/photographer-io&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;An open source photography community. No longer in production but still open source.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/zendesk/samson" rel="nofollow" target="_blank" title=""&gt;zendesk/samson&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Web interface for deployments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/iobridge/thingspeak" rel="nofollow" target="_blank" title=""&gt;iobridge/thingspeak&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;An open source “Internet of Things” application&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/thoughtworks/cruisecontrol.rb" rel="nofollow" target="_blank" title=""&gt;thoughtworks/cruisecontrol.rb&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;CruiseControl for Ruby. Keep it simple.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/daqing/rabel" rel="nofollow" target="_blank" title=""&gt;daqing/rabel&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A powerful web community software to manage people, relationship and information&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/masuidrive/open-wripe" rel="nofollow" target="_blank" title=""&gt;masuidrive/open-wripe&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://wri.pe" rel="nofollow" target="_blank"&gt;https://wri.pe&lt;/a&gt; source code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/chiliproject/chiliproject" rel="nofollow" target="_blank" title=""&gt;chiliproject/chiliproject&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;ChiliProject is a web based project management system built on Ruby on Rails&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/rafaelfranca/simple_form-bootstrap" rel="nofollow" target="_blank" title=""&gt;rafaelfranca/simple_form-bootstrap&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Example application with SimpleForm and Twitter Bootstrap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/joelmoss/strano" rel="nofollow" target="_blank" title=""&gt;joelmoss/strano&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Capistrano and Github sittin' in a tree...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/binarylogic/authlogic_example" rel="nofollow" target="_blank" title=""&gt;binarylogic/authlogic_example&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;An example rails app using the Authlogic authentication library&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/harrystech/prelaunchr" rel="nofollow" target="_blank" title=""&gt;harrystech/prelaunchr&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A small and simple Rails 4 project that is ready to be used as a prelaunch site. It includes all the necessary requirements like prize groups, open/closed states, and simple social sharing.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/Crowdtilt/CrowdtiltOpen" rel="nofollow" target="_blank" title=""&gt;Crowdtilt/CrowdtiltOpen&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Open source crowdfunding platform -&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/19wu/19wu" rel="nofollow" target="_blank" title=""&gt;19wu/19wu&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;19 屋 - 专注于技术社区的活动平台，Based on Rails, AngularJS and Bootstrap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/ari/jobsworth" rel="nofollow" target="_blank" title=""&gt;ari/jobsworth&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Project Management, Collaboration and Time Tracking.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/courtenay/altered_beast" rel="nofollow" target="_blank" title=""&gt;courtenay/altered_beast&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Ground-up rewrite of Beast, a Ruby on Rails forum.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/RailsApps/rails3-bootstrap-devise-cancan" rel="nofollow" target="_blank" title=""&gt;RailsApps/rails3-bootstrap-devise-cancan&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Outdated. See the rails-devise-pundit example app for Rails 4.1.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/atmos/heaven" rel="nofollow" target="_blank" title=""&gt;atmos/heaven&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Rails app for GitHub Flow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/documentcloud/documentcloud" rel="nofollow" target="_blank" title=""&gt;documentcloud/documentcloud&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;The DocumentCloud platform&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;带 demo 的看这里
&lt;a href="https://github.com/adamshen/rails_source_colletion/blob/master/README.md" rel="nofollow" target="_blank"&gt;https://github.com/adamshen/rails_source_colletion/blob/master/README.md&lt;/a&gt;&lt;/p&gt;</description>
      <author>adamshen</author>
      <pubDate>Wed, 25 May 2016 12:29:56 +0800</pubDate>
      <link>https://ruby-china.org/topics/30118</link>
      <guid>https://ruby-china.org/topics/30118</guid>
    </item>
    <item>
      <title>del</title>
      <description>&lt;p&gt;.......&lt;/p&gt;</description>
      <author>adamshen</author>
      <pubDate>Mon, 23 May 2016 23:27:46 +0800</pubDate>
      <link>https://ruby-china.org/topics/30101</link>
      <guid>https://ruby-china.org/topics/30101</guid>
    </item>
    <item>
      <title>最近 electron 发布了 v1.0 之后，超级火爆啊</title>
      <description>&lt;p&gt;会不会迎来新一轮爆炸？&lt;/p&gt;</description>
      <author>adamshen</author>
      <pubDate>Fri, 13 May 2016 11:31:52 +0800</pubDate>
      <link>https://ruby-china.org/topics/30007</link>
      <guid>https://ruby-china.org/topics/30007</guid>
    </item>
    <item>
      <title>发现一个还不错的 Ruby C API Guide</title>
      <description>&lt;p&gt;&lt;a href="https://silverhammermba.github.io/emberb/c/" rel="nofollow" target="_blank"&gt;https://silverhammermba.github.io/emberb/c/&lt;/a&gt;&lt;/p&gt;</description>
      <author>adamshen</author>
      <pubDate>Wed, 20 Apr 2016 09:51:35 +0800</pubDate>
      <link>https://ruby-china.org/topics/29786</link>
      <guid>https://ruby-china.org/topics/29786</guid>
    </item>
  </channel>
</rss>
