<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>victor (Victor)</title>
    <link>https://ruby-china.org/victor</link>
    <description>弱智道士維克多</description>
    <language>en-us</language>
    <item>
      <title>TestProf</title>
      <description>&lt;p&gt;TestProf 收集了一堆 测试分析 和 性能优化 相关的工具。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://test-prof.evilmartians.io/#/?id=testprof" rel="nofollow" target="_blank"&gt;https://test-prof.evilmartians.io/#/?id=testprof&lt;/a&gt;&lt;/p&gt;</description>
      <author>victor</author>
      <pubDate>Fri, 06 Jul 2018 09:58:30 +0800</pubDate>
      <link>https://ruby-china.org/topics/37108</link>
      <guid>https://ruby-china.org/topics/37108</guid>
    </item>
    <item>
      <title>139 个关于 Rails 的小技巧</title>
      <description>&lt;p&gt;&lt;a href="https://til.hashrocket.com/rails" rel="nofollow" target="_blank"&gt;https://til.hashrocket.com/rails&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;每条仅需 30 秒，走过路过不要错过 &lt;img title=":kissing_cat:" alt="😽" src="https://twemoji.ruby-china.com/2/svg/1f63d.svg" class="twemoji"&gt;&lt;/p&gt;

&lt;p&gt;PS: 站点也有其他关于小技巧，比如：React, VIM 请自行去首页按需服用&lt;/p&gt;</description>
      <author>victor</author>
      <pubDate>Tue, 29 May 2018 09:44:33 +0800</pubDate>
      <link>https://ruby-china.org/topics/36853</link>
      <guid>https://ruby-china.org/topics/36853</guid>
    </item>
    <item>
      <title>Exploding Rails</title>
      <description>&lt;p&gt;Have you felt the pain of using Active Record on a large codebase? How about messy controllers? Read this short guide on how to improve a Rails application's structure by exploding its responsibilities into small classes with their own unique responsibilities. Starring rom-rb and dry-rb.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://leanpub.com/explodingrails" rel="nofollow" target="_blank"&gt;https://leanpub.com/explodingrails&lt;/a&gt;&lt;/p&gt;</description>
      <author>victor</author>
      <pubDate>Thu, 03 May 2018 10:33:42 +0800</pubDate>
      <link>https://ruby-china.org/topics/36670</link>
      <guid>https://ruby-china.org/topics/36670</guid>
    </item>
    <item>
      <title>Ruby 位运算符详解</title>
      <description>&lt;h2 id="字符编码"&gt;字符编码&lt;/h2&gt;&lt;h3 id="进化历史"&gt;进化历史&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;计算机只能处理数字，如果要处理文本等，那么必须要把文本转化为二进制的数字才可以处理。&lt;/li&gt;
&lt;li&gt;最开始只有 ASCII 编码，它包含大小写字母，符号，数字。1 个字符占用 1 个字节 (byte)，一个字节 8 位 (bits)。&lt;/li&gt;
&lt;li&gt;因为需要用其它语言，中间过渡了一下 GB2312 之类的编码，一个中文占用 2 个字节 (bytes)。&lt;/li&gt;
&lt;li&gt;后来开始使用 Unicode 编码，它包含所有国家语言，所以不会出乱码。ASCII 占用 1 个字节 (byte)，Unicode 通常 2 个字节 (bytes)，因为一些生僻字可能需要 3 或 4 个字节 (bytes)。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;关于 bit(位) 和 byte(字节) 的解释可以读一下 &lt;a href="http://wjp2013.github.io/ruby/Bits-and-Bytes-in-Ruby/" rel="nofollow" target="_blank" title=""&gt;Working with Bits and Bytes in Ruby&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.zhihu.com/question/21887246" rel="nofollow" target="_blank" title=""&gt;字符集和编码方式的区别&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="UTF-8"&gt;UTF-8&lt;/h3&gt;
&lt;p&gt;在 Unicode 编码下，使用中文的时候占用 2 个字节。但使用字母的时候，其实只是用了一个字节，就是后面的一个字节。比如 &lt;code&gt;00000000 01000000&lt;/code&gt;，大家会注意这里就造成了浪费，就是在原来的字节前面加 0 就可以了，这样就相当于多了一倍的储存空间，在存储和运输上不太划算。&lt;/p&gt;

&lt;p&gt;UTF-8 编码是基于 Unicode 编码的可变长度编码方式。它把 Unicode 编码根据不同的数字大小编码成 1-6 个字节，常用的字母就是 1 个字节，汉字等通常 2-3 个字节。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="s1"&gt;'hello world'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each_byte&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_a&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; [104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]&lt;/span&gt;
&lt;span class="s2"&gt;"s"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bytes&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; [115]&lt;/span&gt;
&lt;span class="s2"&gt;"中"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bytes&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; [228, 184, 173]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当你在看 &lt;code&gt;bytes&lt;/code&gt; 文档的时候，可能还会发现一些冷门知识，比如：代码点 codepoints，有兴趣自己去找资料学习吧。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="s1"&gt;'hello world'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;codepoints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_a&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; [104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]&lt;/span&gt;
&lt;span class="s2"&gt;"中"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;codepoints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_a&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; [20013]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="计算机通用的字符编码工作模式"&gt;计算机通用的字符编码工作模式&lt;/h3&gt;
&lt;p&gt;刚才我们知道在计算机内存中，用的是 Unicode 编码，当要存储在硬盘或者传输时，就转换为 UTF-8 模式，当我们在编辑一个文本文件时，还没保存的时候，我们用的就是 Unicode 编码，当我们点击保存时，这时候就转换为 UTF-8 编码了，当我们读取的时候，就又变成了 Unicode 编码，就是这样转换的。&lt;/p&gt;

&lt;p&gt;Ruby 默认是 UTF-8。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="s2"&gt;"some string"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encoding&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; #&amp;lt;Encoding:UTF-8&amp;gt;&lt;/span&gt;
&lt;span class="no"&gt;Encoding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;default_external&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; #&amp;lt;Encoding:UTF-8&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="什么是位运算"&gt;什么是位运算&lt;/h2&gt;
&lt;p&gt;在计算机中所有数据都是以二进制的形式储存的，位运算其实就是直接对在内存中的二进制数据进行操作，因此处理数据的速度非常快。&lt;/p&gt;

&lt;p&gt;位运算符作用于位，并逐位执行操作。&lt;/p&gt;
&lt;h3 id="Ruby 位运算符"&gt;Ruby 位运算符&lt;/h3&gt;
&lt;p&gt;假设如果 a = 60，且 b = 13，现在以二进制格式，它们如下所示：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mo"&gt;0011&lt;/span&gt; &lt;span class="mi"&gt;1100&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mo"&gt;0000&lt;/span&gt; &lt;span class="mi"&gt;1101&lt;/span&gt;

&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 0000 1100&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 0011 1101&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 0011 0001&lt;/span&gt;
&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;  &lt;span class="c1"&gt;#=&amp;gt; 1100 0011&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;下表列出了 Ruby 支持的位运算符 ( '或' 符号我用英文 I 代替的，尴尬)：&lt;/p&gt;
&lt;table class="table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th&gt;运算符&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;th&gt;运算规则&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;amp;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;与&lt;/td&gt;
&lt;td&gt;两个位都为 1 时，结果才为 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;I&lt;/td&gt;
&lt;td&gt;或&lt;/td&gt;
&lt;td&gt;两个位都为 0 时，结果才为 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;^&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;异或&lt;/td&gt;
&lt;td&gt;两个位相同为 0，相异为 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;~&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;取反&lt;/td&gt;
&lt;td&gt;0 变 1，1 变 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;左移&lt;/td&gt;
&lt;td&gt;各二进位全部左移若干位，高位丢弃，低位补 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;右移&lt;/td&gt;
&lt;td&gt;各二进位全部右移若干位&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;table class="table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th&gt;运算符&lt;/th&gt;
&lt;th&gt;实例&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;amp;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;(a &amp;amp; b)&lt;/code&gt; 将得到 12，即为 0000 1100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;I&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;(a I b)&lt;/code&gt; 将得到 61，即为 0011 1101&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;^&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;(a ^ b)&lt;/code&gt; 将得到 49，即为 0011 0001&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;~&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;(~a )&lt;/code&gt; 将得到 -61，即为 1100 0011，一个有符号二进制数的补码形式。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;a &amp;lt;&amp;lt; 2&lt;/code&gt; 将得到 240，即为 1111 0000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;a &amp;gt;&amp;gt; 2&lt;/code&gt; 将得到 15，即为 0000 1111&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;注意以下几点：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;只有 &lt;code&gt;~&lt;/code&gt; 取反是单目操作符，其它 5 种都是双目操作符。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;位操作只能用于整形数据，对 float 和 double 类型进行位操作会被编译器报错。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;位操作符的运算优先级比较低，因为尽量使用括号来确保运算顺序。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; 操作。对无符号数，高位补 0；有符号数，各编译器处理方法不一样，有的补符号位（算术右移），有的补 0（逻辑右移），见后面的代码示例。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 3&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; -4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因为 15 的二进制是 &lt;code&gt;0000 1111&lt;/code&gt;，右移二位成为 &lt;code&gt;__00 1111&lt;/code&gt;，最高位由符号位填充将得到 &lt;code&gt;0000 0011&lt;/code&gt; 即十进制的 3。-15 的二进制是 &lt;code&gt;1111 0001&lt;/code&gt;，右移二位成为 &lt;code&gt;__11 1100&lt;/code&gt;，最高位由符号位填充将得到 &lt;code&gt;1111 1100&lt;/code&gt; 即十进制的 -4。&lt;/p&gt;
&lt;h2 id="pack 和 unpack"&gt;pack 和 unpack&lt;/h2&gt;
&lt;p&gt;上面的例子这里涉及到两个知识点：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;怎么把 15 变成 2 进制，另外字母怎么转换成 2 进制，中文怎么转换成 2 进制？&lt;/li&gt;
&lt;li&gt;符号位是怎么回事？&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="不同编码之间的处理"&gt;不同编码之间的处理&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;第一个问题很简单，以字符串为例，先转换成 ascii 码再转换成 2 进制。&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="s1"&gt;'A'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bytes&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="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; "1000001"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;闹呢，当然不可能这么傻。C 语言允许开发人员直接访问存储变量的内存，而 Ruby 不行。当我们需要在 Ruby 中访问 字节 (byte) 和 位 (bits) 的时候，可以使用 &lt;code&gt;pack&lt;/code&gt; 和 &lt;code&gt;unpack&lt;/code&gt; 方法。&lt;/p&gt;

&lt;p&gt;一般来说，对于 &lt;code&gt;unpack&lt;/code&gt; 方法你只要记住两个参数 &lt;code&gt;b*&lt;/code&gt; 转换成 2 进制，和 &lt;code&gt;C*&lt;/code&gt; 转换成 ascii 码。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="s1"&gt;'A'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unpack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'b*'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; ["10000010"]&lt;/span&gt;
&lt;span class="s2"&gt;"hello world"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unpack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'C*'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; [104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]&lt;/span&gt;
&lt;span class="s2"&gt;"中"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unpack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'C*'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; [228, 184, 173]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;真的足够用了，再去研究 &lt;code&gt;B*&lt;/code&gt; 和 &lt;code&gt;b*&lt;/code&gt; 有什么不同，又会牵扯到 MSB/LSB 的问题，'H' 转换成 16 进制什么的，完全不用在意。&lt;/p&gt;

&lt;p&gt;懂了 &lt;code&gt;unpack&lt;/code&gt; 那 &lt;code&gt;pack&lt;/code&gt; 也就懂了，无非是逆向操作。&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;1000001&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;pack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'C'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; "A"&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;104&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;108&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;108&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;111&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;119&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;111&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;114&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;108&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;pack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'C*'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; "hello world"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="符号位"&gt;符号位&lt;/h3&gt;
&lt;p&gt;首先要弄懂 原码，反码和补码，而我不打算摘抄一大段东西，所以可以直接看&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.zhihu.com/question/23172611/answer/27248266" rel="nofollow" target="_blank" title=""&gt;原码，反码和补码的关系？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zhuanlan.zhihu.com/p/22718975" rel="nofollow" target="_blank" title=""&gt;原码，反码和补码&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;关于第 2 个问题你目前只需要了解到：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;符号位是第一个字节 8 位 (bits) 的第 1 位，1 为负，0 为正。&lt;/li&gt;
&lt;li&gt;ruby 的 &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; 操作是算术右移。低位溢出，符号位不变，&lt;strong&gt;并用符号位数补溢出的高位。&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="实际应用"&gt;实际应用&lt;/h2&gt;
&lt;p&gt;缘由出来了，今天遇到了这个函数。问这个函数有什么用。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# File activesupport/lib/active_support/security_utils.rb, line 11&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;secure_compare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bytesize&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bytesize&lt;/span&gt;
  &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unpack&lt;/span&gt; &lt;span class="s2"&gt;"C&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bytesize&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each_byte&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;|=&lt;/span&gt; &lt;span class="n"&gt;byte&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shift&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;看完了上面的文章，这函数应该能看懂了吧。当然，除了 &lt;code&gt;a.unpack "C#{a.bytesize}"&lt;/code&gt; 这句有点迷糊，刚才明明说好只要记住 &lt;code&gt;C*&lt;/code&gt; 和 &lt;code&gt;b*&lt;/code&gt; 两个参数足够了啊。&lt;/p&gt;

&lt;p&gt;没办法去看文档吧，然后，我懵了！&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="s2"&gt;"aaa"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unpack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'h2H2c'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; ["16", "61", 97]&lt;/span&gt;
&lt;span class="s2"&gt;"whole"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unpack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'xax2aX2aX1aX2a'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; ["h", "e", "l", "l", "o"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这什么鬼参数，好歹找到了一篇中文解释 &lt;a href="http://www.kuqin.com/rubycndocument/man/pack_template_string.html" rel="nofollow" target="_blank" title=""&gt;pack 模板字符串&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;翻译过来，这一句就跟 &lt;code&gt;a.unpack 'C*'&lt;/code&gt; 没区别。&lt;/p&gt;

&lt;p&gt;其它没什么好解释的了，按位 异或 比较传入的两个参数是否相等。&lt;/p&gt;

&lt;p&gt;我想了半天也没想到这么比较有什么好处，又不想靠 gg 搜答案，晚上跟老伙伴讨论了一波，他也没什么头绪，没办法只好 gg 了。&lt;/p&gt;

&lt;p&gt;转了一圈又回来了，原来论坛上有相关帖子，这函数主要是搞定 &lt;strong&gt;计时攻击&lt;/strong&gt; 的，具体可看 &lt;a href="https://ruby-china.org/topics/21380" title=""&gt;计时攻击原理以及 Rails 对应的防范&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id="后记"&gt;后记&lt;/h2&gt;
&lt;p&gt;延展开去还有关于 IO 打开文件时候的外部编码和内部编码的问题，可以直接看相关阅读中 &lt;em&gt;Ruby 对多语言的支持&lt;/em&gt; 一文，虽然文章有点老，还是针对 Ruby 1.9 版本阐述的问题，但是不影响你理解。&lt;/p&gt;

&lt;p&gt;再扯其它的就离主题太远了，初衷只是因为今天做笔试题时候发现自己对这块知识基本忘光了，赶快奶自己一波。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;如果你知道自己在某一领域上有所欠缺，就应该立刻开始学习相关知识。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="相关阅读"&gt;相关阅读&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.matrix67.com/blog/archives/263" rel="nofollow" target="_blank" title=""&gt;位运算简介及实用技巧（一）：基础篇&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.runoob.com/ruby/ruby-operator.html" rel="nofollow" target="_blank" title=""&gt;Ruby 运算符&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blog.csdn.net/jinguasu/article/details/74939678" rel="nofollow" target="_blank" title=""&gt;Issue-3 字符串和编码，了解 bytes str unicode 的区别&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.cnblogs.com/happyframework/p/3275367.html" rel="nofollow" target="_blank" title=""&gt;Ruby：字符集和编码学习总结&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zh.wikipedia.org/wiki/%E4%BD%8D%E6%93%8D%E4%BD%9C" rel="nofollow" target="_blank" title=""&gt;位操作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.yangx.site/2016/07/06/bit-operation-skills/" rel="nofollow" target="_blank" title=""&gt;位运算简介及基本技巧&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blog.bigbinary.com/2011/07/20/ruby-pack-unpack.html" rel="nofollow" target="_blank" title=""&gt;Ruby pack unpack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.cnblogs.com/rockchip/archive/2013/07/16/3192501.html" rel="nofollow" target="_blank" title=""&gt;Ruby 对多语言的支持&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://idiosyncratic-ruby.com/4-what-the-pack.html" rel="nofollow" target="_blank" title=""&gt;What the Pack&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="补充"&gt;补充&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;上文中应该把 ASCII 和 Unicode 称为字符集，UTF-8 称为编码规则更准确。&lt;/li&gt;
&lt;li&gt;广义的 Unicode 指一个标准，定义字符集及编码规则，即 Unicode 字符集和 UTF-8、UTF-16 编码等。&lt;/li&gt;
&lt;/ol&gt;</description>
      <author>victor</author>
      <pubDate>Fri, 16 Mar 2018 00:54:19 +0800</pubDate>
      <link>https://ruby-china.org/topics/35247</link>
      <guid>https://ruby-china.org/topics/35247</guid>
    </item>
    <item>
      <title>[北京][三元桥] W�E+ 诚聘 Ruby 初级后端工程师</title>
      <description>&lt;h2 id="公司介绍"&gt;公司介绍&lt;/h2&gt;
&lt;p&gt;WE+（上海帷迦科技有限公司）是由上实发展与美国柯罗尼资本（Colony Capital）在中国的新扬子基金于 2015 年 5 月共同创立。WE+&amp;nbsp;充分整合各方股东的优势资源，建立了互联网思维下的新型办公模式，采取创新与创业、线上与线下、孵化与投资相结合的方式，为中小微和初创企业提供全方位服务。以联合办公众创空间为载体，以人为本，共享经济为核心，定制服务和互联互信互助的社区文化相结合，打造办公生活一体化的生态圈。
创始团队和技术团队分别有海外留学经历及百度，腾讯，阿里巴巴等一流互联网公司工作经历，团队氛围活跃，扁平化管理方式。&lt;/p&gt;

&lt;p&gt;截止至 2016 年 6 月，WE+ 已有 10 个空间，20000 平米以上投入运营。位于北京、上海、杭州、广州、苏州等地。共有工位 3000 个，出租率 85%，已入驻团队上百个。
&lt;img src="https://l.ruby-china.com/photo/2016/d271e8a96cdd90cecce457b949519000.jpg" title="" alt=""&gt;
&lt;img src="https://l.ruby-china.com/photo/2016/fa15b741ff1a600c109378eba8698973.jpg" title="" alt=""&gt;
&lt;img src="https://l.ruby-china.com/photo/2016/f897c7a75f0aa65da52efd44d92aa626.jpg" title="" alt=""&gt;
&lt;img src="https://l.ruby-china.com/photo/2016/a0b7eef622289e7c49ac8bee8894abbd.jpg" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="技术团队目前的项目"&gt;技术团队目前的项目&lt;/h2&gt;
&lt;p&gt;凭借对联合办公行业和互联网的深刻理解，技术团队将联合办公行业需求与互联网应用相融合，为办公空间提供系统、全面、高效、实用的智慧管理解决方案，帮助空间实现低成本、高效率的信息化管理。&lt;/p&gt;
&lt;h3 id="职位职责"&gt;职位职责&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;服务端产品的开发与维护（Ruby on Rails/Web为主）；&lt;/li&gt;
&lt;li&gt;参与、协助产品的需求分析、设计、实现、测试、集成以及维护；&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="职位要求"&gt;职位要求&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;精通 Ruby，2 年以上 Ruby On Rails 经验；3 年以上互联网应用开发经验；&lt;/li&gt;
&lt;li&gt;熟练掌握 SQL、HTML、CSS、JavaScript，能够独立完成完整的 Web 应用；&lt;/li&gt;
&lt;li&gt;了解大型 Web 应用程序系统架构，对 Memcached、Redis、MySQL 负载均衡、服务器运维有实际经验；&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="加分"&gt;加分&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;有扎实的英语功底，并且能够轻松的阅读英文文档；&lt;/li&gt;
&lt;li&gt;对互联网行业与技术充满热忱，理解一个创业公司所面对的高速，多变，与未知；&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="福利待遇"&gt;福利待遇&lt;/h3&gt;
&lt;p&gt;上面的官话说完了，下面说点实在的。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;薪水 13K 起，根据能力可酌情上调（我们会合理避税，5 险 1 金均有，但你到手后的银子并不会差太多）；&lt;/li&gt;
&lt;li&gt;上班时间 10:00-19:00，双休（工作日肯定会偶尔加班，但周末肯定是双休）；&lt;/li&gt;
&lt;li&gt;带狗上班，中午有太空舱可以午睡，目前有 VR 可以玩，天天小团建，周周大团建，月月大保健；&lt;/li&gt;
&lt;li&gt;你可以选择 LOL 郊区钻石带你飞/Dota2 鱼塘队伍纯娱乐；&lt;/li&gt;
&lt;li&gt;资深老司机带你上车（懂的人自然懂）；&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="联系我"&gt;联系我&lt;/h3&gt;
&lt;p&gt;办公地址：北京朝阳区三元桥远洋新干线 D 座 1 层 WE+ Coffee
邮箱：victorwang@weplus.com&lt;/p&gt;

&lt;p&gt;下面是你将来的办公地点，和你的狗同事&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/0963465b441ab6d075897ae5b913841b.jpg" title="" alt=""&gt;
&lt;img src="https://l.ruby-china.com/photo/2016/fd76b64707ee0019b90e184b21423523.jpg" title="" alt=""&gt;
&lt;img src="https://l.ruby-china.com/photo/2016/589894bf99f749cbb7c4d133b6e0eb28.jpg" title="" alt=""&gt;
&lt;img src="https://l.ruby-china.com/photo/2016/982be39ff2525af9abbe4fc8877afff7.jpg" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>victor</author>
      <pubDate>Tue, 28 Jun 2016 12:28:44 +0800</pubDate>
      <link>https://ruby-china.org/topics/30386</link>
      <guid>https://ruby-china.org/topics/30386</guid>
    </item>
    <item>
      <title>How Ruby Uses Memory</title>
      <description>&lt;p&gt;每个开发者都想让自己编写的代码占用更少的内存并且运行的更快。在 Ruby 中内存是非常重要的，但是很少有开发者清楚地了解为啥在自己代码运行期间内存占用率会忽高忽低。本文会让你对 Ruby 中内存和对象的关系有一个初步的了解，介绍一些常见的技巧让你的代码减少内存占用并因此运行的更快。&lt;/p&gt;
&lt;h2 id="Object Retention"&gt;Object Retention&lt;/h2&gt;
&lt;p&gt;在 Ruby 中最常见的引起内存飙高的方法是保留对象。Ruby 中的常量是永远不会被垃圾回收的，所以如果常量引用了一个对象，那么这个对象也永远不会被垃圾回收。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;RETAINED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="mi"&gt;100_000&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;RETAINED&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"a string"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;执行这段代码，并用 &lt;code&gt;GC.stat(:total_freed_objects)&lt;/code&gt; 观察有多少个对象被释放。对比一下：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Ruby 2.2.0&lt;/span&gt;

&lt;span class="no"&gt;GC&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;
&lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;GC&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:total_freed_objects&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="no"&gt;RETAINED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="mi"&gt;100_000&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;RETAINED&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"a string"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;GC&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;
&lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;GC&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:total_freed_objects&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Objects Freed: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;before&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# =&amp;gt; "Objects Freed: 44&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里创建了 100000 个 &lt;code&gt;a string&lt;/code&gt; 的副本，但是由于咱将来 &lt;strong&gt;可能&lt;/strong&gt; 会使用它们，所以它们不会被垃圾回收。&lt;strong&gt;在 Ruby 中一个对象一旦被全局对象引用，它就不会被垃圾回收。&lt;/strong&gt; 这一原则也适用于常量，全局变量，模块 (modules) 和类 (class)。因此，在全局可访问的任何地方引用对象都要注意这一点。&lt;/p&gt;

&lt;p&gt;但是假如能在这个过程中不保留任何对象：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="mi"&gt;100_000&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"a string"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;被释放的对象会立刻增加到：&lt;code&gt;Objects Freed: 100038&lt;/code&gt;，内存占用率下降了。当保留对象引用的时候，内存占用从 6mb 增加到 12mb。你也可以使用 &lt;code&gt;get_process_mem&lt;/code&gt; gem 来监测内存变化。&lt;/p&gt;

&lt;p&gt;对象保留也可以使用 &lt;code&gt;GC.stat(:total_allocated_objects)&lt;/code&gt; 观测，被保留的对象等于 &lt;code&gt;total_allocated_objects - total_freed_objects&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id="Retention for Speed"&gt;Retention for Speed&lt;/h2&gt;
&lt;p&gt;Ruby 程序员都很熟悉 &lt;strong&gt;DRY&lt;/strong&gt;。这一原则也适用于代码中进行对象分配。有时我们期望保留对象以便重用，而不是一次又一次重新创建。Ruby 的字符串对象内置了这个方法。冻结一个字符串，解释器会认为你不会修改该字符串，并保留它以便重复使用。下面是一个例子：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;RETAINED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="mi"&gt;100_000&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;RETAINED&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"a string"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;执行这个代码，你发现被释放的对象是 &lt;code&gt;Objects Freed: 50&lt;/code&gt;，看起来没啥变化，但是我们的内存使用率确实降低了。你可以使用 &lt;code&gt;GC.stat(:total_allocated_objects)&lt;/code&gt; 来验证，我们为 &lt;code&gt;a string&lt;/code&gt; 分配了很少的对象，因为我们保留并重用了它。&lt;/p&gt;

&lt;p&gt;Ruby 只存储一个字符串并引用了 100000 次该对象，而不是创建 100000 个不同的对象。除了降低内存使用，我们还因此减少了运行时间，因为 Ruby 不会浪费时间去创建对象和分配内存。你可以使用 &lt;a href="https://github.com/evanphx/benchmark-ips" rel="nofollow" target="_blank" title=""&gt;benchmark-ips&lt;/a&gt; 来检查。&lt;/p&gt;

&lt;p&gt;这个去除重复对象的小技巧虽然常被用来处理字符串，但是当你要把其他对象分配给常量的时候也可以使用。事实上，储存外部连接（例如 Redis）的时候，这个技巧已经成了一种通用模式了。例如：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;RETAINED_REDIS_CONNECTION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因为常量引用了 Redis 的连接，所以它不会被垃圾回收。&lt;/p&gt;

&lt;p&gt;很有趣吧，有时我们很小心地保留住对象是可以降低内存占用的。&lt;/p&gt;
&lt;h2 id="Short Lived Objects"&gt;Short Lived Objects&lt;/h2&gt;
&lt;p&gt;大多数对象的生命周期都很短。短的意思是创建对象之后并没有引用它。例如下面的代码：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s2"&gt;"schneems"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;表面上看起来，这个语句仅需要很少的对象（一个 hash &lt;code&gt;{name: "schneems"}&lt;/code&gt;）。事实上，当你调用它的时候，会创建非常非常多的中间对象以便生成正确的 SQL 语句。这些对象中绝大部分的生命周期仅在这段代码的执行过程中。那么，我们为啥要关心这些不会被保留的对象被创建多少个呢？&lt;/p&gt;

&lt;p&gt;产生大量生命周期适中和较长的对象会引起内存在一段时间内持续增长。一旦在 GC 释放的瞬间这些对象仍在引用，可能引起 Ruby GC 需要更多的内存。&lt;/p&gt;
&lt;h2 id="Ruby Memory Goes Up"&gt;Ruby Memory Goes Up&lt;/h2&gt;
&lt;p&gt;当你有很多对象需要被使用，并且它们超过了 Ruby 当前内存中可放入对象的数量时，Ruby 需要分配更多的内存。从操作系统中请求内存分配的操作是很昂贵的，所以 Ruby 尽量减少这种操作的机会。Ruby 不会每次请求几 KB 的内存，而是请求一大块远超过当前需要的内存。你可以通过设置 &lt;code&gt;RUBY_GC_HEAP_GROWTH_FACTOR&lt;/code&gt; 环境变量来更改这个值。&lt;/p&gt;

&lt;p&gt;例如：Ruby 消耗了 100mb 内存，我们设置 &lt;code&gt;RUBY_GC_HEAP_GROWTH_FACTOR=1.1&lt;/code&gt;。Ruby 再次请求内存分配的时候，它会得到 110mb 内存。当 Ruby 应用程序启动的时候，它会按照同样的百分比增加内存，直到整个程序可以在这些已分配的内存中执行。这个环境变量值设置越低，意味着我们越要频繁的运行 GC 和请求分配内存。该数值越大，意味着更少的 GC，以及超过我们程序运行所需要的内存。&lt;/p&gt;

&lt;p&gt;基于优化网站性能的缘故，很多开发者以为 &lt;strong&gt;Ruby 永远不进行内存释放&lt;/strong&gt;。这不完全正确，事实上 Ruby 是会释放内存的，稍后我们会讨论这一点。&lt;/p&gt;

&lt;p&gt;如果把这些行为考虑在内，那么你会对于非保留的对象 (临时对象) 如何影响内存的使用有更清晰的认识。例如：&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;make_an_array&lt;/span&gt;
  &lt;span class="n"&gt;array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="mi"&gt;10_000_000&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;array&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;  &lt;span class="s2"&gt;"a string"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当我们调用这个方法，会创建 10000000 个字符串对象。当方法执行完毕退出后，这些字符串没有被引用，所以会垃圾回收。然后，当程序执行期间 Ruby 需要为这 10000000 个字符串分配足够的空间。这大概需要 500mb 的内存。&lt;/p&gt;

&lt;p&gt;也许你的应用仅需要 10mb 的空间，但是这个数组的创建却需要分配 500mb 的内存。一个简单的例子，假设这个过程是在一个大型 Rails 项目的页面请求中发生，它会耗尽你的内存。因为如果服务器没有足够的内存，GC 就需要不停地释放和分配内存。&lt;/p&gt;

&lt;p&gt;因为分配内存的操作开销很大，Ruby 会把这些分配的内存保持住一段时间。一旦进程将这些内存用尽，那么就再次申请内存。内存会逐渐释放，这一过程很慢。如果你在乎程序的效率，那就尽可能少的创建对象。&lt;/p&gt;
&lt;h2 id="In-Place Modification for Speed"&gt;In-Place Modification for Speed&lt;/h2&gt;
&lt;p&gt;有一个小技巧可以加快程序执行速度和减少对象分配：&lt;strong&gt;利用修改状态来替代创建新对象&lt;/strong&gt;。例如，这里有一些代码来自于 &lt;a href="https://github.com/halostatue/mime-types" rel="nofollow" target="_blank" title=""&gt;mime-types&lt;/a&gt; gem:&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;matchdata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;captures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&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;downcase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gsub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;%r{[Xx]-}o&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这段代码通过正则的 &lt;code&gt;match&lt;/code&gt; 方法返回了 &lt;a href="http://ruby-doc.org/core-2.2.1/MatchData.html" rel="nofollow" target="_blank" title=""&gt;matchdata object&lt;/a&gt;。然后，它将正则表达式捕获的元素组成了一个数组，并将其传递给代码块。代码块对字符串进行一些处理。这段代码看起来很合理。但是当它在 mime-types gem 中被上千次的调用时，每次调用 &lt;code&gt;downcase&lt;/code&gt; 和 &lt;code&gt;gsub&lt;/code&gt; 都会创建一个新字符串对象，及其耗时和浪费内存。为了避免这样，我们可以做一些修改：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;matchdata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;captures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&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;downcase!&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;gsub!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;%r{[Xx]-}o&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;freeze&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;虽然代码变得冗余一些，但是它运行起来更快。这招可以提高我们的效率，因为我们没有在代码块中引用原字符串对象，所以我们可以放心的修改已经存在的字符串而不是创建一个新的。&lt;/p&gt;

&lt;p&gt;注意：你不需要用一个常量来储存正则表达式，所有的正则表达式文本由 Ruby 解释器自动冻结（frozen）。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In-Place Modification&lt;/strong&gt; 也会给你带来麻烦。你很容易修改一个在其它地方会用到的变量，而你并没意识到这一点，因此造成的 bug 很难被找到。在使用这招进行性能优化之前，确保你已经有足够的测试。另外，仅对你仔细斟酌过并确认存在大量创建对象操作的代码进行优化。&lt;/p&gt;

&lt;p&gt;有一种错误的观点，认为 &lt;strong&gt;对象是很慢的&lt;/strong&gt;。事实上对象可以让程序容易理解和容易优化。即便是最快的工具和技术，当用法不对的时候一样会变得很慢。&lt;/p&gt;

&lt;p&gt;在应用级别捕捉不必要的分配可以使用 &lt;a href="https://github.com/schneems/derailed_benchmarks" rel="nofollow" target="_blank" title=""&gt;derailed_benchmarks&lt;/a&gt;。在更底层的级别，可以使用 &lt;a href="https://github.com/ko1/allocation_tracer" rel="nofollow" target="_blank" title=""&gt;allocation_tracer&lt;/a&gt; 或 &lt;a href="https://github.com/SamSaffron/memory_profiler" rel="nofollow" target="_blank" title=""&gt;memory_profiler&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;另外：本文的作者写了 &lt;a href="https://github.com/schneems/derailed_benchmarks" rel="nofollow" target="_blank" title=""&gt;derailed_benchmarks&lt;/a&gt;，可以利用 &lt;code&gt;rake perf:mem&lt;/code&gt; 来查看内存统计。&lt;/p&gt;
&lt;h2 id="Good to be Free"&gt;Good to be Free&lt;/h2&gt;
&lt;p&gt;正如前文所说，Ruby 会释放内存，虽然很慢。执行 &lt;code&gt;make_an_array&lt;/code&gt; 方法会引起内存飙高，你可以监控 Ruby 是如何释放内存的：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="no"&gt;GC&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;应用程序占用的内存减小的过程非常缓慢。当分配太多内存的时候，Ruby 释放少量空页（一组内存颗粒）。操作系统调用 &lt;code&gt;malloc&lt;/code&gt; 来进行内存分配，取决于操作系统对于 &lt;code&gt;malloc&lt;/code&gt; 库的不同实现，这些内存可能会被交还给系统。&lt;/p&gt;

&lt;p&gt;对于大多数应用，比如 web 应用来说，这一分配内存的动作都由客户端触发。当客户端频频触发这一动作时，我们无法依靠 Ruby 自身的能力去释放内存来保证我们的应用程序占用的空间足够小。另外，释放内存很耗时，最好还是避免创建对象。&lt;/p&gt;
&lt;h2 id="You’re Up"&gt;You’re Up&lt;/h2&gt;
&lt;p&gt;现在你已经对 Ruby 中对象和内存的关系有了基本的了解。当你想要对自己的程序进行内存方面的性能优化时，可以使用下面的工具：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/schneems/derailed_benchmarks" rel="nofollow" target="_blank" title=""&gt;derailed_benchmarks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ko1/allocation_tracer" rel="nofollow" target="_blank" title=""&gt;allocation_tracer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/SamSaffron/memory_profiler" rel="nofollow" target="_blank" title=""&gt;memory_profiler&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/evanphx/benchmark-ips" rel="nofollow" target="_blank" title=""&gt;benchmark-ips&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;遵循下面的模式：找到热点中容易引发问题的地方，优化性能，进行性能测试。&lt;/p&gt;
&lt;h2 id="相关链接"&gt;相关链接&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.sitepoint.com/ruby-uses-memory/" rel="nofollow" target="_blank" title=""&gt;How Ruby Uses Memory&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ruby-china.org/topics/25584" title=""&gt;Ruby 的内存陷阱&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="广告时间"&gt;广告时间&lt;/h2&gt;
&lt;p&gt;[远程办公] Tower 团队招募小伙伴，详情 &lt;a href="http://v2ex.com/t/192966" rel="nofollow" target="_blank"&gt;http://v2ex.com/t/192966&lt;/a&gt; 。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ruby / 10k-20k / 1-3 年 / 本科及以上&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;入职后，贫道带你一起飞。如果有幸可以在北京共事，我可以请吃西红柿刀削面，大家没事还能出来散散步，划划船。别犹豫了，快到碗里来吧！&lt;/p&gt;

&lt;p&gt;版主手下留情啊，&lt;a href="/zchar" class="user-mention" title="@zchar"&gt;&lt;i&gt;@&lt;/i&gt;zchar&lt;/a&gt; 让我发的。&lt;/p&gt;</description>
      <author>victor</author>
      <pubDate>Thu, 28 May 2015 17:37:13 +0800</pubDate>
      <link>https://ruby-china.org/topics/25790</link>
      <guid>https://ruby-china.org/topics/25790</guid>
    </item>
    <item>
      <title>利用 ActiveSupport::Notifications 在 Rails 中实现 PUB/SUB 模式</title>
      <description>&lt;h2 id="利用 ActiveSupport::Notifications 在 Rails 中实现 PUB/SUB 模式"&gt;利用 ActiveSupport::Notifications 在 Rails 中实现 PUB/SUB 模式&lt;/h2&gt;&lt;h2 id="前言"&gt;前言&lt;/h2&gt;
&lt;p&gt;自从 Rails 4 从核心中移除了 &lt;a href="https://github.com/rails/rails-observers" rel="nofollow" target="_blank" title=""&gt;Observers&lt;/a&gt; 之后，对于复杂的业务逻辑和依赖关系该放在哪，大家就开始各显神通了。&lt;/p&gt;

&lt;p&gt;有人建议利用 &lt;a href="https://signalvnoise.com/posts/3372-put-chubby-models-on-a-diet-with-concerns" rel="nofollow" target="_blank" title=""&gt;Concerns&lt;/a&gt; 和 callback 来搞定。&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;MyConcernModule&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;after_save&lt;/span&gt; &lt;span class="ss"&gt;:do_something&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;do_something&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;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyModel&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="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;MyConcernModule&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;更多的人认为，依据单一职责原则抽出一个 Service Object 才是王道。&lt;/p&gt;

&lt;p&gt;在此基础上，&lt;a href="https://github.com/krisleech/wisper" rel="nofollow" target="_blank" title=""&gt;Wisper&lt;/a&gt;确实是一个很好的解决方案。&lt;/p&gt;

&lt;p&gt;你可以这么玩：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostsController&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
    &lt;span class="vi"&gt;@post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:post&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="vi"&gt;@post.subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PusherListener&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;@post.subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ActivityListener&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;@post.subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;StatisticsListener&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;@post.on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:create_post_successful&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="vi"&gt;@post.on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:create_post_failed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;:action&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:new&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="vi"&gt;@post.create&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;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionController&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;around_filter&lt;/span&gt; &lt;span class="ss"&gt;:register_event_listeners&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;register_event_listeners&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;around_listener_block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;Wisper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with_listeners&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;UserListener&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="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;around_listener_block&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;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;User&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Wisper&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Publisher&lt;/span&gt;
  &lt;span class="n"&gt;after_create&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&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;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user_registered&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="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserListener&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;user_registered&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="no"&gt;Analytics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;track&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"user:registered"&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="nf"&gt;analytics&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;a href="/Rei" class="user-mention" title="@Rei"&gt;&lt;i&gt;@&lt;/i&gt;Rei&lt;/a&gt; 一句 &lt;strong&gt;Rails 有 ActiveSupport::Notifications&lt;/strong&gt; 竟让我无言以对。&lt;/p&gt;
&lt;h2 id="ActiveSupport::Notifications"&gt;ActiveSupport::Notifications&lt;/h2&gt;
&lt;p&gt;在阅读了相关阅读中给出的链接，以及 &lt;a href="http://railscasts.com/episodes/249-notifications-in-rails-3" rel="nofollow" target="_blank" title=""&gt;Notifications in Rails 3&lt;/a&gt; 和官方文档之后，感觉这东西设计出来完全是为了进行统计、日志、性能分析之类的事情啊。&lt;/p&gt;

&lt;p&gt;那么用它能不能实现一个 PUB/SUB 模式呢？先来回顾一下 PUB/SUB 模式核心的两个要点。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Publishers&lt;/strong&gt; 在对象状态改变且需要触发事件的时候发布事件。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscribers&lt;/strong&gt; 仅接收它们能响应的事件，并且在每个事件中可以接收到被监控的对象。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="The Basics of AS::Notifications"&gt;The Basics of AS::Notifications&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ActiveSupport::Notifications&lt;/code&gt; 主要核心就是两个方法：&lt;code&gt;instrument&lt;/code&gt; 和 &lt;code&gt;subscribe&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;你可以把 &lt;code&gt;instrument&lt;/code&gt; 理解为发布事件。&lt;code&gt;instrument&lt;/code&gt; 会在代码块执行完毕并返回结果之后，发布事件 &lt;code&gt;my.custom.event&lt;/code&gt;，同时会自动把相关的一组参数：开始时间、结束时间、每个事件的唯一 ID 等，放入 &lt;code&gt;payload&lt;/code&gt; 对象。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Notifications&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instrument&lt;/span&gt; &lt;span class="s2"&gt;"my.custom.event"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;this: :data&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# do your custom stuff here&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在你可以监听这个事件：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Notifications&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt; &lt;span class="s2"&gt;"my.custom.event"&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;started&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;finished&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unique_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt; &lt;span class="c1"&gt;# {:this=&amp;gt;:data}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;理解了这两个方法，我们可以试着实现一个 PUB/SUB 模式。&lt;/p&gt;
&lt;h4 id="Publisher"&gt;Publisher&lt;/h4&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/pub_sub/publisher.rb&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Publisher&lt;/span&gt;
  &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;

  &lt;span class="c1"&gt;# delegate to ActiveSupport::Notifications.instrument&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;broadcast_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&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="nb"&gt;block_given?&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;Notifications&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instrument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&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;yield&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;else&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;Notifications&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instrument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;首先我们定了 &lt;code&gt;Publisher&lt;/code&gt;。它把 &lt;code&gt;payload&lt;/code&gt; 夹在事件中广播出去，代码块也可以当做一个可选参数传递进来。&lt;/p&gt;

&lt;p&gt;我们可以在 model 或 controller 中发布具体事件。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;
  &lt;span class="c1"&gt;# publish event 'user.created', with payload {user: user}&lt;/span&gt;
  &lt;span class="no"&gt;Publisher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;broadcast_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'user.created'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="n"&gt;user&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;create_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&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="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;# publish event 'user.created', with payload {user: user}, using block syntax&lt;/span&gt;
  &lt;span class="c1"&gt;# now the event will have additional data about duration and exceptions&lt;/span&gt;
  &lt;span class="no"&gt;Publisher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;broadcast_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'user.created'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save!&lt;/span&gt;
    &lt;span class="c1"&gt;# do some more important stuff here&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;h4 id="Subscriber"&gt;Subscriber&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;Subscriber&lt;/code&gt; 可以订阅事件，并将代码块当做参数，传递给 &lt;code&gt;ActiveSupport::Notifications::Event&lt;/code&gt; 的实例。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/pub_sub/subscriber.rb&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Subscriber&lt;/span&gt;
  &lt;span class="c1"&gt;# delegate to ActiveSupport::Notifications.subscribe&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;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_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="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Notifications&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_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;event&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;Notifications&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Event&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;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&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="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;# subscriber example usage&lt;/span&gt;
&lt;span class="no"&gt;Subscriber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'user.created'&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;event&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Error: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:exception&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:exception&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transaction_id&lt;/span&gt;&lt;span class="si"&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;event&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; | &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="si"&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;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;duration&lt;/span&gt;&lt;span class="si"&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;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="si"&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;error&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;/code&gt;&lt;/pre&gt;&lt;h3 id="经典场景"&gt;经典场景&lt;/h3&gt;
&lt;p&gt;用户注册后，为该用户发送欢迎邮件。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/pub_sub/publisher.rb&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Publisher&lt;/span&gt;
  &lt;span class="kp"&gt;extend&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;Concern&lt;/span&gt;
  &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;

  &lt;span class="n"&gt;included&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# add support for namespace, one class - one namespace&lt;/span&gt;
    &lt;span class="n"&gt;class_attribute&lt;/span&gt; &lt;span class="ss"&gt;:pub_sub_namespace&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;pub_sub_namespace&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="c1"&gt;# delegate to class method&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;broadcast_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&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="nb"&gt;block_given?&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;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;broadcast_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&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;yield&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;broadcast_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&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;module&lt;/span&gt; &lt;span class="nn"&gt;ClassMethods&lt;/span&gt;
    &lt;span class="c1"&gt;# delegate to ASN&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;broadcast_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
      &lt;span class="n"&gt;event_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pub_sub_namespace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event_name&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;compact&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'.'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;block_given?&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;Notifications&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instrument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&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;yield&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;else&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;Notifications&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instrument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&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="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;# app/pub_sub/publishers/registration.rb&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Publishers&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Registration&lt;/span&gt;
    &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Publisher&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;pub_sub_namespace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'registration'&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;# broadcast event&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;
  &lt;span class="no"&gt;Publishers&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Registration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;broadcast_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'user_signed_up'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/pub_sub/subscribers/base.rb&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Subscribers&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Base&lt;/span&gt;
    &lt;span class="n"&gt;class_attribute&lt;/span&gt; &lt;span class="ss"&gt;:subscriptions_enabled&lt;/span&gt;
    &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:namespace&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;namespace&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="vi"&gt;@namespace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;namespace&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="c1"&gt;# attach public methods of subscriber with events in the namespace&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;attach_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;log_subscriber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;log_subscriber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;public_methods&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="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;event&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;Notifications&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&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;namespace&lt;/span&gt;&lt;span class="si"&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;event&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;log_subscriber&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="c1"&gt;# trigger methods when an even is captured&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;message&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="nb"&gt;method&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gsub&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;namespace&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="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;handler&lt;/span&gt; &lt;span class="o"&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;class&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;namespace&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;handler&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="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Notifications&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Event&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;message&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;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;# app/pub_sub/subscribers/registration_mailer.rb&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Subscribers&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RegistrationMailer&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Subscribers&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;user_signed_up&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="c1"&gt;# lets delay the delivery using delayed_job&lt;/span&gt;
      &lt;span class="no"&gt;RegistrationMailer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;priority: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;welcome_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:user&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="c1"&gt;# config/initializers/subscribers.rb&lt;/span&gt;
&lt;span class="no"&gt;Subscribers&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RegistrationMailer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attach_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'registration'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;醉了吗？详细的解释可以看相关阅读中的文章。如果你的队友没有把 ASN 吃透，肯定会掀桌子。&lt;/p&gt;

&lt;p&gt;所以是引入一个 gem 还是自己根据 ASN 来实现同样的功能，还是由你自己想吧。&lt;/p&gt;
&lt;h2 id="相关阅读"&gt;相关阅读&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=sKrrsF0MZ7Q" rel="nofollow" target="_blank" title=""&gt;Digging Deep with ActiveSupport::Notifications  视频&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://speakerdeck.com/nextmat/digging-deep-with-activesupportnotifications" rel="nofollow" target="_blank" title=""&gt;Digging Deep with ActiveSupport::Notifications PDF&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://edgeguides.rubyonrails.org/active_support_instrumentation.html" rel="nofollow" target="_blank" title=""&gt;Active Support Instrumentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://alma-connect.github.io/techblog/2014/03/rails-pub-sub.html#.VRjq9JNWeZk" rel="nofollow" target="_blank" title=""&gt;Implementing PUB/SUB in Rails; using ActiveSupport::Notifications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://metaskills.net/2013/12/15/instrumenting-your-code-with-activesupport-notifications/" rel="nofollow" target="_blank" title=""&gt;Instrumenting Your Code With ActiveSupport Notifications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://lostechies.com/derickbailey/2011/06/09/using-activesupportnotifications-and-activesupportconcern-to-create-an-audit-trail/" rel="nofollow" target="_blank" title=""&gt;Using ActiveSupport::Notifications and ActiveSupport::Concern To Create An Audit Trail&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr&gt;
&lt;h2 id="4.22 补充"&gt;4.22 补充&lt;/h2&gt;
&lt;p&gt;22 楼的 &lt;a href="/satzcoal" class="user-mention" title="@satzcoal"&gt;&lt;i&gt;@&lt;/i&gt;satzcoal&lt;/a&gt; 提了如下几个问题，我也答应 &lt;a href="/billy" class="user-mention" title="@billy"&gt;&lt;i&gt;@&lt;/i&gt;billy&lt;/a&gt; 在踩坑之后过来补充一下此文。&lt;a href="/satzcoal" class="user-mention" title="@satzcoal"&gt;&lt;i&gt;@&lt;/i&gt;satzcoal&lt;/a&gt; 面对的问题有下面这些：&lt;/p&gt;
&lt;h3 id="ActiveSupport::Notifications 在 sub 的管理上非常困难"&gt;ActiveSupport::Notifications 在 sub 的管理上非常困难&lt;/h3&gt;
&lt;p&gt;ASN 对于 sub 的管理和 Wisper 其实并无太大差异。仍然可以进行全局订阅和临时订阅。如果你觉得难以管理，那我建议你跟我一样，在 initializes 文件夹下面建立一个 subscribers.rb 用来统一进行订阅。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;subsribers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="ss"&gt;todos: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s1"&gt;'started'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'paused'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'completed'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'deleted'&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="ss"&gt;topics: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'created'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'deleted'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'sticked'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'unsticked'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'commented'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="ss"&gt;documents: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'created'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'deleted'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'updated'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'commented'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'recovered'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;subsribers&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;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;value&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;action&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;Notifications&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&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;key&lt;/span&gt;&lt;span class="si"&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;action&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="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;event&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;Notifications&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Event&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;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="no"&gt;WebhookService&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;event&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="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transaction_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;payload&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;/code&gt;&lt;/pre&gt;&lt;h3 id="很难验证 sub 的重复性"&gt;很难验证 sub 的重复性&lt;/h3&gt;
&lt;p&gt;如果你使用了上面那种集中式管理 sub 的方法，这个问题就略过吧。&lt;/p&gt;
&lt;h3 id="很难进行 unsub"&gt;很难进行 unsub&lt;/h3&gt;
&lt;p&gt;可以利用 &lt;a href="https://github.com/rails/rails/blob/30af171af13293aa37bee612ae7b976d3ede0439/activesupport/lib/active_support/notifications.rb#L181" rel="nofollow" target="_blank" title=""&gt;unsubscribe&lt;/a&gt; 来取消订阅，但是我在使用中，没有遇到这个场景。&lt;/p&gt;
&lt;h3 id="很难管理 sub 的顺序"&gt;很难管理 sub 的顺序&lt;/h3&gt;
&lt;p&gt;我猜你问得是很难定义 instrument 的顺序吧。从别人那里摘抄一个代码，你看看是不是你需要的？&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Notifications&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instrument&lt;/span&gt; &lt;span class="s1"&gt;'user.signup'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# create user record&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;Notifications&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instrument&lt;/span&gt; &lt;span class="s1"&gt;'twitter.location'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Twitter&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;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;twitter_username&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;location&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="c1"&gt;# do more work&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="很难对 sub 中的异常进行处理"&gt;很难对 sub 中的异常进行处理&lt;/h3&gt;
&lt;p&gt;同上直接上代码了&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Notifications&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instrument&lt;/span&gt; &lt;span class="s1"&gt;'twitter.location'&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="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Twitter&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;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;twitter_username&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;location&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# instrument message data - name, start, end, id, payload&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'twitter.location'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2015&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;04&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2015&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;04&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'LAiKEjiMCy8XYY9Y'&lt;/span&gt;&lt;span class="err"&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="mi"&gt;1023&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:exception&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Twitter::Error:NotFound"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"not found"&lt;/span&gt;&lt;span class="p"&gt;]}]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ASN 会在 payload 中返回 Exception Type 和 Exception Message 的。&lt;/p&gt;
&lt;h3 id="小结"&gt;小结&lt;/h3&gt;
&lt;p&gt;向 &lt;a href="/billy" class="user-mention" title="@billy"&gt;&lt;i&gt;@&lt;/i&gt;billy&lt;/a&gt; 的报告。过去几周我使用 ASN 为我司的项目添加了 Webhook 功能。还是原来的答案，核心逻辑不该用 ASN。&lt;/p&gt;

&lt;p&gt;ASN 可以使用在下列场景：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Custom Logging&lt;/li&gt;
&lt;li&gt;New Relic Tracking&lt;/li&gt;
&lt;li&gt;Google Analytics events&lt;/li&gt;
&lt;li&gt;Gampfire / HipChat alerts&lt;/li&gt;
&lt;li&gt;Schedule background jobs&lt;/li&gt;
&lt;li&gt;Fire web hooks&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>victor</author>
      <pubDate>Mon, 30 Mar 2015 19:34:10 +0800</pubDate>
      <link>https://ruby-china.org/topics/24914</link>
      <guid>https://ruby-china.org/topics/24914</guid>
    </item>
    <item>
      <title>发布 / 订阅模式</title>
      <description>&lt;h2 id="使用场景"&gt;使用场景&lt;/h2&gt;
&lt;p&gt;很多项目中都有消息分发或者事件通知机制，尤其是模块化程度高的项目。&lt;/p&gt;

&lt;p&gt;比如：在你的系统中，很多模块都对 &lt;strong&gt;新建用户&lt;/strong&gt; 感兴趣。权限模块希望给新用户设置默认权限，报表模块希望重新生成当月的报表，邮件系统希望给用户发送激活邮件...诸如此类的代码都写到新建用户的业务逻辑后面，会加大耦合度，降低可维护性，并且对于每个模块都是一个独立系统的情况，这种方式更是不可取。&lt;/p&gt;

&lt;p&gt;对于简单的情形，观察者模式 &lt;code&gt;The Observer Pattern&lt;/code&gt; 就足够了。如果系统中有很多地方都需要收发消息，那么它就不适用了。否则会造成类数量的膨胀，增加类的复杂性，这时候就需要一种更集中的机制来处理这些业务逻辑。&lt;/p&gt;
&lt;h2 id="什么是发布/订阅模式(PUB-SUB)"&gt;什么是发布/订阅模式 (PUB-SUB)&lt;/h2&gt;
&lt;p&gt;现实中，并不是所有请求都期待答复，而不期待答复，自然就没有了状态。广播听过吧？收音机用过吧？就这个意思。&lt;/p&gt;

&lt;p&gt;发布/订阅模式定义了一种一对多的依赖关系，让多个订阅者对象同时监听某一个主题对象。这个主题对象在自身状态变化时，会通知所有订阅者对象，使它们能够自动更新自己的状态。&lt;/p&gt;
&lt;h2 id="特点"&gt;特点&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;一个订阅者可以订阅多个发布者&lt;/li&gt;
&lt;li&gt;消息是会到达所有订阅者处，订阅者根据 &lt;code&gt;filter&lt;/code&gt; 丢掉自己不需要的消息 (&lt;code&gt;filter&lt;/code&gt; 是在订阅端起作用的)&lt;/li&gt;
&lt;li&gt;每个订阅者都会接收到每条消息的一个副本&lt;/li&gt;
&lt;li&gt;基于推送 &lt;code&gt;push&lt;/code&gt;，其中消息自动地向订阅者广播，它们无须请求或轮询主题来获得新消息&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;发布/订阅模式内部，有多种不同类型的订阅者。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;非持久订阅者是临时订阅类型，它们只是在主动侦听主题时才接收消息。&lt;/li&gt;
&lt;li&gt;持久订阅者将接收到发布的每条消息的一个副本，即便在发布消息，它们处于"离线"状态时也是如此。&lt;/li&gt;
&lt;li&gt;另外还有动态持久订阅者和受管的持久订阅者等类型。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="优势"&gt;优势&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;降低了模块间的耦合度：发布者与订阅者松散地耦合，并且不需要知道对方的存在。相关操作都集中在 &lt;code&gt;Publisher&lt;/code&gt; 中。&lt;/li&gt;
&lt;li&gt;可扩展性强：系统复杂后，可以把消息订阅和分发机制单独作为一个模块来实现，增加新特性以满足需求&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="缺陷"&gt;缺陷&lt;/h2&gt;
&lt;p&gt;与其说缺陷，不如说它设计本身就有如下特点。但不管怎么说，这种模式在逻辑上不可靠的。主要体现在：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;发布者不知道订阅者是否收到发布的消息&lt;/li&gt;
&lt;li&gt;订阅者不知道自己是否收到了发布者发出的所有消息&lt;/li&gt;
&lt;li&gt;发送者不能获知订阅者的执行情况&lt;/li&gt;
&lt;li&gt;没人知道订阅者何时开始收到消息&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="Wisper"&gt;Wisper&lt;/h2&gt;
&lt;p&gt;你可能早就看过 RailsCasts 上的 &lt;a href="http://railscasts.com/episodes/260-messaging-with-faye" rel="nofollow" target="_blank" title=""&gt;#260 Messaging with Faye&lt;/a&gt; 和 &lt;a href="http://railscasts.com/episodes/316-private-pub" rel="nofollow" target="_blank" title=""&gt;#316 Private Pub&lt;/a&gt;。但今天要来介绍的是另外一个 gem。&lt;/p&gt;
&lt;h3 id="首先看看传统的利用 callback 的实现"&gt;首先看看传统的利用 &lt;code&gt;callback&lt;/code&gt; 的实现&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Post&lt;/code&gt; 模型通过回调，与 &lt;code&gt;Feed&lt;/code&gt; 模型和 &lt;code&gt;User::NotifyFollowers&lt;/code&gt; 服务紧密的耦合在一起。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/post.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;
  &lt;span class="n"&gt;after_create&lt;/span&gt; &lt;span class="ss"&gt;:create_feed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:notify_followers&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_feed&lt;/span&gt;
    &lt;span class="no"&gt;Feed&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="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;notify_followers&lt;/span&gt;
    &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;NotifyFollowers&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="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;end&lt;/span&gt;

&lt;span class="c1"&gt;# app/controllers/api/v1/posts_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Api::V1::PostsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Api&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;V1&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ApiController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
    &lt;span class="vi"&gt;@post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;posts&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;post_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@post.save&lt;/span&gt;
      &lt;span class="n"&gt;render_created&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;render_unprocessable_entity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@post.errors&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;/code&gt;&lt;/pre&gt;&lt;h3 id="利用 Wisper 的 PUB-SUB 模式"&gt;利用 &lt;code&gt;Wisper&lt;/code&gt; 的 PUB-SUB 模式&lt;/h3&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/post.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;
  &lt;span class="c1"&gt;# no callbacks in the models!&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Publishers&lt;/strong&gt; 在对象状态改变且需要触发事件的时候发布事件。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/controllers/api/v1/posts_controller.rb&lt;/span&gt;
&lt;span class="c1"&gt;# corresponds to the publisher in the previous figure&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Api::V1::PostsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Api&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;V1&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ApiController&lt;/span&gt;

  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Wisper&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Publisher&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
    &lt;span class="vi"&gt;@post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;posts&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;post_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@post.save&lt;/span&gt;
      &lt;span class="c1"&gt;# Publish event about post creation for any interested listeners&lt;/span&gt;
      &lt;span class="n"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:post_create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;render_created&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="c1"&gt;# Publish event about post error for any interested listeners&lt;/span&gt;
      &lt;span class="n"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:post_errors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;render_unprocessable_entity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@post.errors&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Subscribers&lt;/strong&gt; 仅接收它们能响应的事件。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/listener/feed_listener.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FeedListener&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;post_create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;Feed&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;post&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="c1"&gt;# app/listener/user_listener.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserListener&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;post_create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;NotifyFollowers&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="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;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Event Bus&lt;/strong&gt; 用来管理系统中订阅者都订阅哪些频道。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/initializers/wisper.rb&lt;/span&gt;

&lt;span class="no"&gt;Wisper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;FeedListener&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="no"&gt;Wisper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;UserListener&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;/code&gt;&lt;/pre&gt;&lt;h3 id="进一步，根据单一职责原则把 PUB-SUB 模式和 Service Objects 联合起来"&gt;进一步，根据单一职责原则把 PUB-SUB 模式和 Service Objects 联合起来&lt;/h3&gt;
&lt;p&gt;Publisher&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/service/financial/order_review.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Financial::OrderReview&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Wisper&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Publisher&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;call&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;approved?&lt;/span&gt;
      &lt;span class="n"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:order_create&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="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:order_decline&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="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;Subscribers&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/listener/client_listener.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ClientListener&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;order_create&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="c1"&gt;# can implement transaction using different service objects&lt;/span&gt;
    &lt;span class="no"&gt;Client&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Charge&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;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;Inventory&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;UpdateStock&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;order&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;order_decline&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="no"&gt;Client&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;NotifyDeclinedOrder&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="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;a href="https://github.com/krisleech/wisper/wiki" rel="nofollow" target="_blank" title=""&gt;Wisper wiki&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id="后记"&gt;后记&lt;/h2&gt;
&lt;p&gt;本文的唯一作用可能就是，能让大家在设计 &lt;code&gt;Service Objects&lt;/code&gt; 时候再多思考一点。虽然到底要不要抽出 &lt;code&gt;Service Objects&lt;/code&gt; 也是一个见仁见智的问题。在相关连接中我有贴出 Gourmet Service Objects 一文，另外本站的 &lt;a href="https://ruby-china.org/topics/24780" title=""&gt;Service Object: What? Why? and How?&lt;/a&gt; 也请抽空读一下。&lt;/p&gt;

&lt;p&gt;这里没有交代如何在发布/订阅模式中引入队列系统的方法，所以上面的代码你是绝对没办法直接拿去用的。&lt;/p&gt;
&lt;h2 id="相关链接"&gt;相关链接&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern" rel="nofollow" target="_blank" title=""&gt;Publish–subscribe pattern&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zh.wikipedia.org/wiki/%E5%8F%91%E5%B8%83/%E8%AE%A2%E9%98%85" rel="nofollow" target="_blank" title=""&gt;发布/订阅&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blog.csdn.net/tjvictor/article/details/5223309" rel="nofollow" target="_blank" title=""&gt;设计模式---订阅发布模式 (Subscribe/Publish)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.toptal.com/ruby-on-rails/the-publish-subscribe-pattern-on-rails" rel="nofollow" target="_blank" title=""&gt;The Publish-Subscribe Pattern on Rails: An Implementation Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://brewhouse.io/blog/2014/04/30/gourmet-service-objects.html" rel="nofollow" target="_blank" title=""&gt;Gourmet Service Objects&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://shcatula.wordpress.com/2013/06/02/whisper-ruby/" rel="nofollow" target="_blank" title=""&gt;WHISPER RUBY&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://devblog.reverb.com/post/57704562313/getting-hexagonal-with-wisper-a-listener" rel="nofollow" target="_blank" title=""&gt;Getting Hexagonal with Wisper, a listener Framework for Ruby&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.sitepoint.com/using-wisper-to-decompose-applications/" rel="nofollow" target="_blank" title=""&gt;Using Wisper to Decompose Applications&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>victor</author>
      <pubDate>Mon, 30 Mar 2015 13:48:47 +0800</pubDate>
      <link>https://ruby-china.org/topics/24908</link>
      <guid>https://ruby-china.org/topics/24908</guid>
    </item>
    <item>
      <title>Gulp on Rails: Replacing the Asset Pipeline</title>
      <description>&lt;p&gt;&lt;a href="http://viget.com/extend/gulp-rails-asset-pipeline" rel="nofollow" target="_blank"&gt;http://viget.com/extend/gulp-rails-asset-pipeline&lt;/a&gt;&lt;/p&gt;</description>
      <author>victor</author>
      <pubDate>Thu, 19 Feb 2015 22:31:35 +0800</pubDate>
      <link>https://ruby-china.org/topics/24307</link>
      <guid>https://ruby-china.org/topics/24307</guid>
    </item>
    <item>
      <title>Learn the Ruby Programming Language</title>
      <description>&lt;p&gt;&lt;a href="http://ruby.about.com/" rel="nofollow" target="_blank"&gt;http://ruby.about.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This site will teach you the Ruby programming language. We'll teach you with simple, step-by-step tutorials so you can follow along.&lt;/p&gt;</description>
      <author>victor</author>
      <pubDate>Wed, 31 Dec 2014 12:13:18 +0800</pubDate>
      <link>https://ruby-china.org/topics/23489</link>
      <guid>https://ruby-china.org/topics/23489</guid>
    </item>
    <item>
      <title>个推服务端 Ruby SDK</title>
      <description>&lt;p&gt;百度推送实在太坑，在小米手机上也经常出现丢失或者无法唤醒程序的 bug。所以老大决定换个推。&lt;/p&gt;

&lt;p&gt;如果也有用 &lt;strong&gt;个推&lt;/strong&gt; 进行推送的兄弟们，可以省点时间了。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/wjp2013/igetui-ruby" rel="nofollow" target="_blank"&gt;https://github.com/wjp2013/igetui-ruby&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;不谢，请叫我雷锋侠。&lt;/p&gt;</description>
      <author>victor</author>
      <pubDate>Fri, 08 Aug 2014 18:04:44 +0800</pubDate>
      <link>https://ruby-china.org/topics/20919</link>
      <guid>https://ruby-china.org/topics/20919</guid>
    </item>
    <item>
      <title>railstips 的 alfred workflow</title>
      <description>&lt;p&gt;起因，不知道 &lt;code&gt;reorder&lt;/code&gt; 这个 rails build-in 的方法。想了半天怎么把 &lt;code&gt;default_scope&lt;/code&gt; 的结果重新排序，因为不能用 &lt;code&gt;unscoped&lt;/code&gt; 粗暴的搞定，会打破关联。google 了一下，结果发现了 &lt;code&gt;reorder&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;干脆做个 workflow 每天早上先读 1，2 个方法在开工吧&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;功能：学习 rails 的小花招 &lt;/li&gt;
&lt;li&gt;用法：唤醒 alfred 输入 railstips 回车，看到内容之后再按一下回车 跳转到 &lt;a href="http://api.rubyonrails.org/" rel="nofollow" target="_blank"&gt;http://api.rubyonrails.org/&lt;/a&gt; 查看详情&lt;/li&gt;
&lt;li&gt;数据地址：&lt;a href="https://github.com/wjp2013/railstips" rel="nofollow" target="_blank"&gt;https://github.com/wjp2013/railstips&lt;/a&gt; 欢迎帮忙添加内容&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src="https://github.com/wjp2013/alfred2-workflows/raw/master/screenshots/railstips2.png" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>victor</author>
      <pubDate>Mon, 05 May 2014 15:04:25 +0800</pubDate>
      <link>https://ruby-china.org/topics/19024</link>
      <guid>https://ruby-china.org/topics/19024</guid>
    </item>
    <item>
      <title>RubyChina Workflow For Alfred 2.0 by ruby</title>
      <description>&lt;p&gt;&lt;a href="https://github.com/wjp2013/alfred2-workflows" rel="nofollow" target="_blank"&gt;https://github.com/wjp2013/alfred2-workflows&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;没啥新鲜的，和 &lt;a href="http://ruby-china.org/topics/9730" rel="nofollow" target="_blank"&gt;http://ruby-china.org/topics/9730&lt;/a&gt; 的功能完全一样，只不过用 ruby 实现了一次而已。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://github.com/wjp2013/alfred2-workflows/raw/master/screenshots/rubychina.png" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>victor</author>
      <pubDate>Wed, 26 Mar 2014 12:18:15 +0800</pubDate>
      <link>https://ruby-china.org/topics/18174</link>
      <guid>https://ruby-china.org/topics/18174</guid>
    </item>
    <item>
      <title>Hybrid 技术调研</title>
      <description>&lt;p&gt;&lt;strong&gt;此文是我们团队内部选型时候我的一点笔记，主要是给同事看的，也许对大家有点帮助，没什么新东西，主要是整理人家的文章和内容为主。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="移动 App 开发技术分类"&gt;移动 App 开发技术分类&lt;/h2&gt;&lt;h3 id="Web App"&gt;Web App&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://www.cocoachina.com/applenews/devnews/2012/0709/4428_2.html" rel="nofollow" target="_blank" title=""&gt;介绍&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;成本低，快速开发&lt;/li&gt;
&lt;li&gt;响应式设计&lt;/li&gt;
&lt;li&gt;浏览器运行很多手机特性无法访问。例如联系人，推送等&lt;/li&gt;
&lt;li&gt;single page app&lt;/li&gt;
&lt;li&gt;销售渠道限制于浏览器&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="Native App"&gt;Native App&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://www.cocoachina.com/applenews/devnews/2012/0709/4428.html" rel="nofollow" target="_blank" title=""&gt;介绍&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;学习成本高，开发成本高，开发效率低&lt;/li&gt;
&lt;li&gt;不同平台一道坎&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="Hybrid App"&gt;Hybrid App&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://www.cocoachina.com/applenews/devnews/2012/0709/4428_3.html" rel="nofollow" target="_blank" title=""&gt;介绍&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="Hybrid App 优点"&gt;Hybrid App 优点&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;兼具 Native App 良好用户体验的优势和 Web App 跨平台开发的优势&lt;/li&gt;
&lt;li&gt;开发成本和难度比 Native App 小&lt;/li&gt;
&lt;li&gt;使得开发和日常维护过程变得集中式、更简短、更经济高效&lt;/li&gt;
&lt;li&gt;既可以通过 Native 分发渠道推广产品，也可同时在微信，百度轻，UC 等移动平台推广&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="现状"&gt;现状&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;国外的 Facebook，App Store&lt;/li&gt;
&lt;li&gt;国内的百度搜索，淘宝客户端 Android 版。其中百度封装的不是 WebView 而是自己的浏览内核，体验更像原生客户端，更高效。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="Hybrid 含义"&gt;Hybrid 含义&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;mobile application: 就是一个移动应用&lt;/li&gt;
&lt;li&gt;both browser-supported language and computer language: 同时使用网页语言和程序语言编写&lt;/li&gt;
&lt;li&gt;avaiable through application distribution platforms: 通过应用商店进行分发&lt;/li&gt;
&lt;li&gt;a target device: 区分目标平台&lt;/li&gt;
&lt;li&gt;install to run: 用户需要安装使用&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="Hybrid 类型"&gt;Hybrid 类型&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;多 View 混合型&lt;/li&gt;
&lt;li&gt;单 View 混合型&lt;/li&gt;
&lt;li&gt;Web 主体型&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="多 View 混合型(中)"&gt;多 View 混合型 (中)&lt;/h3&gt;
&lt;p&gt;即 Native View 和 Web View 独立展示，交替出现。例如我们第一版本的安卓 App。&lt;/p&gt;
&lt;h3 id="单 View 混合型(好)"&gt;单 View 混合型 (好)&lt;/h3&gt;
&lt;p&gt;即在同一个 View 内，同时包括 Native View 和 Web View。互相之间是覆盖（层叠）的关系。&lt;/p&gt;

&lt;p&gt;这种 Hybrid App 的开发成本较高，开发难度较大，但是体验较好。&lt;/p&gt;

&lt;p&gt;例如百度搜索为代表的单 View 混合型移动应用，既可以实现充分的灵活性，又能实现较好的用户体验。 &lt;/p&gt;
&lt;h3 id="Web 主体型(差)"&gt;Web 主体型 (差)&lt;/h3&gt;
&lt;p&gt;即移动应用的主体是 Web View，主要以网页语言编写，穿插 Native 功能的 Hybrid App 开发类型。&lt;/p&gt;

&lt;p&gt;这种类型开发的移动应用体验相对而言存在缺陷，但整体开发难度大幅降低，并且基本可以实现跨平台。&lt;/p&gt;

&lt;p&gt;Web 主体型的移动应用用户体验的好坏，主要取决于底层中间件的交互与跨平台的能力。&lt;/p&gt;

&lt;p&gt;国外的 appMobi、PhoneGap 国内的 AppCan 和 Rexsee 都属于 Web 主体型移动应用中间件。其中 Rexsee 不支持跨平台开发。appMobi 和 PhoneGap 除基础的底层能力更多是通过插件（Plugins）扩展的机制实现 Hybrid。而 AppCan 除了插件机制，还提供了大量的单 View 混合型的接口来完善和弥补 Web 主体型 Hybrid App 体验差的问题，接近 Native App 的体验。 &lt;/p&gt;
&lt;h2 id="中间件"&gt;中间件&lt;/h2&gt;
&lt;p&gt;App 的 Native 代码部分使用操作系统的 API 来创建嵌入式 HTML 渲染引擎，该引擎在浏览器和设备的 API 之间充当了桥梁。这座桥梁让 Hybrid App 得以充分利用现代设备所提供的全部特性。&lt;/p&gt;

&lt;p&gt;App 开发者可以选择编写自己的桥梁，或者充分利用现成的解决方案，比如 PhoneGap。&lt;/p&gt;

&lt;p&gt;App 的 Web 部分可能是驻留在服务器上的网页，也可能是一组 HTML、JavaScript、CSS 和媒体文件，封装到 App 代码中，存储在设备本地。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;放置在服务器上的 HTML 代码让开发者不必经历提交和批准过程——有些 App 商店要求这个过程，就可以对 App 进行小幅更新。遗憾的是，这个方法摈弃了任何离线可用性，因为设备与网络没有连接时，无法访问设备。&lt;/li&gt;
&lt;li&gt;把 Web 代码封装到 App 里面可以提高性能和可访问性，但是不允许远程更新。&lt;/li&gt;
&lt;li&gt;推荐的方案是：结合使用两者。这种系统采用的架构可以把 HTML 资源放置在 Web 服务器上，以获得灵活性，但是又把它们本地缓存在移动设备上，以获得高性能。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;根据我们目前的技术实力和业务需求，内部时间人力资源预算，不建议使用中间件，以下是市面主流中间件的对比。&lt;/p&gt;

&lt;p&gt;&lt;img src="http://dl.iteye.com/upload/attachment/0070/0982/8b90b009-6cb5-39a7-bf98-ed497674fb76.jpg" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="最后选择的架构"&gt;最后选择的架构&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Ember 用于双向绑定，网络请求，视图管理等工作&lt;/li&gt;
&lt;li&gt;Require.js JavaScript 模块化工具，能够帮助你把整体的代码管理的井井有条（这块还没决定好）&lt;/li&gt;
&lt;li&gt;开发阶段使用 Haml + Sass + Coffee&lt;/li&gt;
&lt;li&gt;API server 使用 Ruby Rack + Grape + Puma&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="题外话"&gt;题外话&lt;/h2&gt;
&lt;p&gt;为毛不用 Angular，为毛不用 jade，为毛不用 BackBone，不用 jQuery Mobile，不用 PhoneGap。。。。&lt;/p&gt;

&lt;p&gt;我也不知道为啥不用。可能都是命吧。另外朋友们提到的一些技术，例如 jade，我是确实不了解。可能在具体的开发中，如果发现特别合适的话，就会果断使用的。另外一些，比如 jQuery Mobile，PhoneGap 和 BackBone 确实是各有缺点，不适合我们目前的需求。并不是它们不好，我相信它们都有自己适用的地方。&lt;/p&gt;
&lt;h2 id="后记"&gt;后记&lt;/h2&gt;
&lt;p&gt;在用 Ember 开发了 2 周之后，发现确实可以满足我们目前阶段的需求。还在疑虑的同学，可以小范围的开始上了。&lt;/p&gt;
&lt;h2 id="参考资源"&gt;参考资源&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.iteye.com/news/25442" rel="nofollow" target="_blank" title=""&gt;Web App 或夭折，Hybrid App 才是新世界的王&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.cocoachina.com/applenews/devnews/2012/0709/4428_3.html" rel="nofollow" target="_blank" title=""&gt;HTML5、Native 或 Hybrid App 开发全接触&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.infoq.com/cn/articles/hybrid-app-development-combat" rel="nofollow" target="_blank" title=""&gt;Hybrid App 开发实战&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="延伸阅读"&gt;延伸阅读&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.aliued.cn/2014/03/02/hybrid-%E6%9E%B6%E6%9E%84%E4%B8%8B%E7%9A%84-h5-%E5%BA%94%E7%94%A8%E5%8A%A0%E9%80%9F%E6%96%B9%E6%A1%88.html?utm_source=tuicool" rel="nofollow" target="_blank" title=""&gt;Hybrid 架构下的 H5 应用加速方案&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.alloyteam.com/2014/03/frontend-workflow/?utm_source=tuicool" rel="nofollow" target="_blank" title=""&gt;停不下来的前端，自动化流程&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>victor</author>
      <pubDate>Tue, 18 Mar 2014 12:49:06 +0800</pubDate>
      <link>https://ruby-china.org/topics/17964</link>
      <guid>https://ruby-china.org/topics/17964</guid>
    </item>
    <item>
      <title>[北京][电商] 招 Ruby 开发初级工程师 [结贴]</title>
      <description>&lt;h2 id="公司介绍："&gt;公司介绍：&lt;/h2&gt;
&lt;p&gt;团队主营项目是垂直电商，盈利稳定，发展迅速。同时和阿里系的公司有深度合作。&lt;/p&gt;

&lt;p&gt;我们不需要业内的见证，不想当明星团队，只需要一个踏实，勤奋的你来补充我们的队伍。&lt;/p&gt;
&lt;h3 id="公司地址"&gt;公司地址&lt;/h3&gt;
&lt;p&gt;13 号线望京西站附近南湖西园，步行道望京西站大概 8 分钟&lt;/p&gt;
&lt;h2 id="职位描述"&gt;职位描述&lt;/h2&gt;
&lt;p&gt;参与设计、开发、调试 Ruby and Rails 编写的程序&lt;/p&gt;
&lt;h2 id="任职要求"&gt;任职要求&lt;/h2&gt;&lt;h3 id="基本要求"&gt;基本要求&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;2 本以上计算机相关专业毕业&lt;/li&gt;
&lt;li&gt;1 年工作经验，对学校里面学习的知识没有忘光&lt;/li&gt;
&lt;li&gt;能看懂 Ruby 代码&lt;/li&gt;
&lt;li&gt;使用 Rails 开发过项目&lt;/li&gt;
&lt;li&gt;使用 Mac&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="加分"&gt;加分&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;有自己的 github&lt;/li&gt;
&lt;li&gt;使用 vim 或者 sublime text&lt;/li&gt;
&lt;li&gt;写技术 blog&lt;/li&gt;
&lt;li&gt;善于学习技术，有持续学习技术的激情&lt;/li&gt;
&lt;li&gt;能阅读英文技术文档&lt;/li&gt;
&lt;li&gt;会科学上网，遇到问题会 google，知道 stackoverflow&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="薪资"&gt;薪资&lt;/h2&gt;
&lt;p&gt;6 - 8K 如果觉得过低，先别忙着关闭本页，可以抬头看看题目，然后再看看最我们的基本要求。&lt;/p&gt;

&lt;p&gt;我在发帖之前，充分参考了其他家的薪资水平，对于这个看起来低于市场价的薪水，我们相应的放低了对应聘者的要求。&lt;/p&gt;

&lt;p&gt;我们更注重你的人品和学习能力，而不是现阶段的工作能力。&lt;/p&gt;
&lt;h2 id="我们不能提供"&gt;我们不能提供&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;我们目前还在民宅中办公，所以没有那种奢华高调的办公室，但也不会是格子间。&lt;/li&gt;
&lt;li&gt;我们是一帮笨人，信奉勤能补拙，偶尔会加班几小时。我们没有加班费，但能提供一份晚餐。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="我们能提供"&gt;我们能提供&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;5 险，定期体检&lt;/li&gt;
&lt;li&gt;不打卡，工作时间 9 点半 - 6 点半，周末双休&lt;/li&gt;
&lt;li&gt;奖金妥妥的，工作满 1 年配发期权&lt;/li&gt;
&lt;li&gt;零食饮料，不定期组织聚会&lt;/li&gt;
&lt;li&gt;没节操的同事和妹子&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;友情提示：我们的期权不是某些拿投资的公司那种，上市之后才能兑现的。我们直接按照期权年底分红，给现金。&lt;/p&gt;
&lt;h2 id="我们还能提供什么"&gt;我们还能提供什么&lt;/h2&gt;
&lt;p&gt;我们没有什么办公室政治，没有经理，老板。只有哥哥，姐姐。我们这里永远称呼 X 哥，X 姐。如果有一天你发现我们有 X 经理、X 总、X 董，那意味着我们要完蛋了，你可以收拾东西准备跑路了。&lt;/p&gt;

&lt;p&gt;你想买任何计算机技术相关的书籍都报销，我们也提供 tutsplus.com 的付费账号。买美金付费的电子书，也没问题。&lt;/p&gt;

&lt;p&gt;我们目前在民宅也要马上搬家到更大的民宅，搬家后你可以根据个人喜欢，布置自己期望的工作环境。公司提供 Mac Mini 并且公司内部有 2T 的时间胶囊供大家备份资料。&lt;/p&gt;

&lt;p&gt;对于你个人技术上的成长。老道虽然是老菜鸟，但也是从 07 年开始接触 Rails 开发的。6 年来，多多少少累积了一点点心得和开发经验。&lt;/p&gt;

&lt;p&gt;你可以学习到：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;如何进行产品的开发，测试，调研，需求分析&lt;/li&gt;
&lt;li&gt;常见的 gem 使用技巧，rvm、bunlder、brew 等的基本使用&lt;/li&gt;
&lt;li&gt;Ruby and Rails 的编程规范&lt;/li&gt;
&lt;li&gt;MVC 的设计思想&lt;/li&gt;
&lt;li&gt;RESTful API 设计，如何开发一套供移动应用使用的 API 服务&lt;/li&gt;
&lt;li&gt;Ruby/Rails 项目部署&lt;/li&gt;
&lt;li&gt;Git 的使用，如何利用 git flow 帮我们进行团队开发&lt;/li&gt;
&lt;li&gt;日访问量百万级别项目的缓存和优化&lt;/li&gt;
&lt;li&gt;如果你有兴趣，对于 HTML5，CSS3，jQuery，响应式布局，移动 Web 开发，我都可以提供一点心得和经验&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;将来我们可以一起：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;学习 rspec 进行 BDD 或者 TDD &lt;/li&gt;
&lt;li&gt;学习 Mysql 的优化&lt;/li&gt;
&lt;li&gt;学习 Memcache，Redis 为将来的项目的优化提供技术选型&lt;/li&gt;
&lt;li&gt;学习 BDD，OOD，Refactoring Patterns 等等&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;因为我也是一枚老菜鸟，所以在这些方面上我能提供的经验和心得有限，只能我们一起学习，共同进步了。&lt;/p&gt;
&lt;h2 id="联系方式"&gt;联系方式&lt;/h2&gt;
&lt;p&gt;QQ: 22674812 先简单聊聊，聊的好就来公司见见。不会让你先投简历，之后石沉大海。或者完全不了解你，就让你往公司跑耽误你的宝贵时间。&lt;/p&gt;
&lt;h2 id="写在最后"&gt;写在最后&lt;/h2&gt;
&lt;p&gt;我们期望寻找到一枚靠谱，稳重的小伙。和我们一起成长，咱俩一起撑起整个公司的后台项目。我会尽力帮你迅速成长为熟练开发者。&lt;/p&gt;

&lt;p&gt;PS：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;老道的熏香会一直在工作时间滋养你的身体，绝对让你生龙活虎，晚上撸再多也不累。&lt;/li&gt;
&lt;li&gt;贫道在打坐，念经，驱鬼，风水，国学，茶，香，剑道方面也可提供一定指导。&lt;/li&gt;
&lt;/ol&gt;</description>
      <author>victor</author>
      <pubDate>Mon, 17 Feb 2014 14:30:15 +0800</pubDate>
      <link>https://ruby-china.org/topics/17305</link>
      <guid>https://ruby-china.org/topics/17305</guid>
    </item>
    <item>
      <title>71 篇 Practicing Ruby 系列文章</title>
      <description>&lt;p&gt;免费开放，篇篇精彩。&lt;/p&gt;

&lt;p&gt;地址：&lt;a href="https://practicingruby.com/#public-archives" rel="nofollow" target="_blank"&gt;https://practicingruby.com/#public-archives&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;发现自最新一期的《码农周刊》&lt;/p&gt;</description>
      <author>victor</author>
      <pubDate>Mon, 10 Feb 2014 10:21:41 +0800</pubDate>
      <link>https://ruby-china.org/topics/17140</link>
      <guid>https://ruby-china.org/topics/17140</guid>
    </item>
    <item>
      <title>Issuepost 限免</title>
      <description>&lt;p&gt;Issuepost lets you submit GitHub issues from your desktop quickly—much faster than what you're doing now.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://itunes.apple.com/cn/app/issuepost/id673907630?l=en&amp;amp;mt=12" rel="nofollow" target="_blank"&gt;https://itunes.apple.com/cn/app/issuepost/id673907630?l=en&amp;amp;mt=12&lt;/a&gt;&lt;/p&gt;</description>
      <author>victor</author>
      <pubDate>Tue, 28 Jan 2014 14:30:31 +0800</pubDate>
      <link>https://ruby-china.org/topics/17039</link>
      <guid>https://ruby-china.org/topics/17039</guid>
    </item>
    <item>
      <title>有人了解 ruby 2.0 的 collect! 和 map! 的区别么？</title>
      <description>&lt;p&gt;从 Ruby 2 的文档看两个方法是一样的。但是实际使用中不是这样&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;OptionValue&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:option_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;touch: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;inverse_of: :option_values&lt;/span&gt;
  &lt;span class="n"&gt;has_and_belongs_to_many&lt;/span&gt; &lt;span class="ss"&gt;:variants&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;class&lt;/span&gt; &lt;span class="nc"&gt;Variant&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;has_and_belongs_to_many&lt;/span&gt; &lt;span class="ss"&gt;:option_values&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;options_text&lt;/span&gt;
    &lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;option_values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;joins&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:option_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collect!&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;ov&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;ov&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;option_type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;presentation&lt;/span&gt;&lt;span class="si"&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;ov&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;presentation&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;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_sentence&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="ss"&gt;words_connector: &lt;/span&gt;&lt;span class="s2"&gt;", "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;two_words_connector: &lt;/span&gt;&lt;span class="s2"&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;这里如果把 values.collect! 替换成 values.map! 就报错&lt;/p&gt;

&lt;p&gt;&lt;code&gt;undefined method `map!' for #&amp;lt;OptionValue::ActiveRecord_AssociationRelation:0x007fd5175ba650&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;我也查看了 Rails4 的 API。并没有扩展 map! 或者 collect! 方法。&lt;/p&gt;

&lt;p&gt;环境：Ruby 2.0.0, Rails 4.1.0.beta1&lt;/p&gt;

&lt;p&gt;PS: 我的 Dash 安装的是 Ruby 2.1 的文档，所以看的是 Ruby2.1 的文档对 map! 和 collect! 的解释。&lt;/p&gt;</description>
      <author>victor</author>
      <pubDate>Sun, 19 Jan 2014 17:57:47 +0800</pubDate>
      <link>https://ruby-china.org/topics/16869</link>
      <guid>https://ruby-china.org/topics/16869</guid>
    </item>
    <item>
      <title>Ruby Code Smells</title>
      <description>&lt;p&gt;写在前面，因为是摘抄和自己混写。所以出现简体繁体参杂的情况，请大家体谅，勿喷。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;强烈推荐 &lt;a href="https://github.com/tutsplus/ruby-refactoring" rel="nofollow" target="_blank"&gt;https://github.com/tutsplus/ruby-refactoring&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="1. Duplicate code 重复的代码"&gt;1. Duplicate code 重复的代码&lt;/h3&gt;
&lt;p&gt;如果你在一个以上的地点看到相同的程序结构，那么当可肯定：设法将它们合而为一，程序会变得更好。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;同一个 class 内的两个函数中含有重复的代码段&lt;/li&gt;
&lt;li&gt;两个兄弟 class 的成员函数中含有重复的代码段&lt;/li&gt;
&lt;li&gt;两个毫不相关的 class 内出现重复的代码段&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;注意：重复的代码是多数潜在 BUG 的温床！&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="2. Long method 过长的方法"&gt;2. Long method 过长的方法&lt;/h3&gt;
&lt;p&gt;拥有短函数的对象会活的比较好、比较长。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;程序愈长就愈难理解&lt;/li&gt;
&lt;li&gt;函数过长阅读起来也不方便&lt;/li&gt;
&lt;li&gt;小函数的价值：解释能力、共享能力、选择能力&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;原则：每当感觉需要以注释来说明点什么的时候，我们就把需要说明的东西写进一个独立的函数中。记着，起个好名字！&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="3. Feature envy 依恋情节"&gt;3. Feature envy 依恋情节&lt;/h3&gt;
&lt;p&gt;函数对某个类的兴趣高过对自己所处类的兴趣，就会产生对这个类的依恋情节，造成紧耦合。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;原则：判断哪个类拥有最多被此函数使用的数据，然后将这个函数和那些数据摆在一起。&lt;/strong&gt;
&lt;strong&gt;原则：将总是变化的东西放在一块。&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# BAD&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;A&lt;/span&gt;
  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:z&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;
    &lt;span class="vi"&gt;@x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
    &lt;span class="vi"&gt;@y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
    &lt;span class="vi"&gt;@z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z&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;some_function&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;z&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;B&lt;/span&gt;
  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:a&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
    &lt;span class="vi"&gt;@a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&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;my_method&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;y&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;z&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some_function&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;# GOOD&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;A&lt;/span&gt;
  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:z&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;
    &lt;span class="vi"&gt;@x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
    &lt;span class="vi"&gt;@y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
    &lt;span class="vi"&gt;@z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z&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;some_function&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;z&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;special_method&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="n"&gt;some_function&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;B&lt;/span&gt;
  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:a&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
    &lt;span class="vi"&gt;@a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&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;my_method&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;special_method&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="4. Data clumps 数据泥团"&gt;4. Data clumps 数据泥团&lt;/h3&gt;
&lt;p&gt;有些数据项，喜欢成群结队地待在一块。那就把它们绑起来放在一个新的类里面。这样就可以：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;缩短参数列表&lt;/li&gt;
&lt;li&gt;简化函数调用&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Point&lt;/span&gt;
  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:z&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;
    &lt;span class="vi"&gt;@x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
    &lt;span class="vi"&gt;@y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
    &lt;span class="vi"&gt;@z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;#BAD&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;some_function&lt;/span&gt; &lt;span class="n"&gt;_x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_z&lt;/span&gt;
    &lt;span class="c1"&gt;# Do something with it&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;#GOOD&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;some_function&lt;/span&gt; &lt;span class="n"&gt;point&lt;/span&gt;
    &lt;span class="c1"&gt;# Do something with it&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="5. Comments 过多的注释"&gt;5. Comments 过多的注释&lt;/h3&gt;
&lt;p&gt;过多注释的代码段，往往都是因为那段代码比较糟糕，散发着一股恶臭。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;原则：当你感觉需要写注释时，请尝试重构，试着让所有注释都变得多余。&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="6. Divergent Change 发散式变化"&gt;6. Divergent Change 发散式变化&lt;/h3&gt;
&lt;p&gt;我们希望软件能够更容易被修改。一旦需要修改，我们希望能够跳到系统的某一点，只在该处做修改。如果不能做到这点，你就踩入了 &lt;strong&gt;发散式变化&lt;/strong&gt; 或 &lt;strong&gt;霰弹式修改&lt;/strong&gt; 的坑。&lt;/p&gt;

&lt;p&gt;发散式变化：一个类受多种变化的影响&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;数据库新加一个字段，同时修改三个函数：Load、Insert、Update&lt;/li&gt;
&lt;li&gt;新加一个角色二进制，同时修改四处&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;原则：针对某一外界变化的所有相应修改，都只应该发生在单一类中&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="7. Primitive Obsession 基本类型偏执"&gt;7. Primitive Obsession 基本类型偏执&lt;/h3&gt;
&lt;p&gt;代码中有很多基本数据类型的数据。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;原则：如果看到一些基本类型数据，尝试定义一种新的数据类型，符合它当前所代表的对象类型。&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;
  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:price&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&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;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;
    &lt;span class="vi"&gt;@name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;
    &lt;span class="vi"&gt;@color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;
    &lt;span class="vi"&gt;@price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;price&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;# BAD&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"Shirt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Pink"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# GOOD&lt;/span&gt;
&lt;span class="no"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="s2"&gt;"Shirt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Pink"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id="我所参考的视频，仅总结了如上7点，我自己又从其它文章中补了下面几个"&gt;我所参考的视频，仅总结了如上 7 点，我自己又从其它文章中补了下面几个&lt;/h2&gt;&lt;h3 id="8. Large Class 过大类"&gt;8. Large Class 过大类&lt;/h3&gt;
&lt;p&gt;如果想利用单一类做太多事情，其内往往就会出现太多的成员变量。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;提取完成同一任务的相关变量到一个新的类&lt;/li&gt;
&lt;li&gt;干太多事情的类，可以考虑把责任委托给其他类&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;注意：一个类如果拥有太多的代码，也是代码重复、混乱、死亡的绝佳滋生地点。&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="9. Long Parameters List 过长的参数列表"&gt;9. Long Parameters List 过长的参数列表&lt;/h3&gt;
&lt;p&gt;太长的参数列表难以理解，太多参数会造成前后不一致、不易使用，而且你需要更多数据时，就不得不修改它。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;原则：参数不超过 3 个！&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="10. Shotgun Surgery 霰弹式修改"&gt;10. Shotgun Surgery 霰弹式修改&lt;/h3&gt;
&lt;p&gt;如果每遇到某种变化，你都必须在许多不同的类内做出许多小修改以响应之。如果需要修改的代码散布四处，你不但难以找到它们，也很容易忘记某个重要的修改。&lt;/p&gt;

&lt;p&gt;霰弹式修改：一种变化引起多个类相应的修改&lt;/p&gt;
&lt;h3 id="11. Switch Statement Switch 语句"&gt;11. Switch Statement Switch 语句&lt;/h3&gt;
&lt;p&gt;面向对象程序的一个最明显特征就是：少用 switch 语句。从本质上说 switch 语句的问题在于重复。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;原则：看到 switch 你就应该考虑使用多态来替换它。&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="12. Temporary Field 暂时成员变量"&gt;12. Temporary Field 暂时成员变量&lt;/h3&gt;
&lt;p&gt;有时你会看到这样的对象：其内某个成员变量仅为某种特定的情形而设。这样的代码容易让人不解，因为你通常认为对象在所有时候都需要它的所有变量。&lt;/p&gt;

&lt;p&gt;在变量未被使用的情况下猜测当初设置目的，会让你发疯。&lt;/p&gt;

&lt;p&gt;一个对象的属性可能只在某些情况下才有意义。这样的代码将难以理解。专门建立一个对象来持有这样的孤儿属性，把只和他相关的行为移到该类。最常见的是一个特定的算法需要某些只有该算法才有用的变量。&lt;/p&gt;
&lt;h3 id="最后"&gt;最后&lt;/h3&gt;
&lt;p&gt;尚有如下情况未列入，欢迎大家补充。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parallel Inheritance Hierarchies&lt;/li&gt;
&lt;li&gt;Speculative Generality&lt;/li&gt;
&lt;li&gt;Message Chain&lt;/li&gt;
&lt;li&gt;Middle Man&lt;/li&gt;
&lt;li&gt;Inappropriate Intimacy&lt;/li&gt;
&lt;li&gt;Alternative Classes with Different Interfaces&lt;/li&gt;
&lt;li&gt;Incomplete Library Class&lt;/li&gt;
&lt;li&gt;Data Class&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="相关阅读和参考"&gt;相关阅读和参考&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.youtube.com/watch?v=q_qdWuCAkd8" rel="nofollow" target="_blank" title=""&gt;MountainWest RubyConf 2013 Code Smells: Your Refactoring Cheat Codes by John Pignata&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://rubylearning.com/blog/2011/03/01/how-do-i-smell-ruby-code/" rel="nofollow" target="_blank" title=""&gt;http://rubylearning.com/blog/2011/03/01/how-do-i-smell-ruby-code/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/" rel="nofollow" target="_blank" title=""&gt;7 Patterns to Refactor Fat ActiveRecord Models&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.cnblogs.com/mywolrd/archive/2012/04/24/2467395.html" rel="nofollow" target="_blank" title=""&gt;代码的坏味道&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>victor</author>
      <pubDate>Tue, 14 Jan 2014 20:08:11 +0800</pubDate>
      <link>https://ruby-china.org/topics/16805</link>
      <guid>https://ruby-china.org/topics/16805</guid>
    </item>
    <item>
      <title>为 Grape 和 Jbuilder 的非 Rails 应用增加缓存的 Gem</title>
      <description>&lt;p&gt;使用 Grape 做 API 的兄弟们如果使用 Jbuilder 来构建模板。想用 HTTP caching 的话可以考虑一下这个 Gem&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/wjp2013/grape-shaman_cache" rel="nofollow" target="_blank"&gt;https://github.com/wjp2013/grape-shaman_cache&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;仅在非 Rails 应用上，测试通过。&lt;/p&gt;</description>
      <author>victor</author>
      <pubDate>Thu, 19 Dec 2013 17:33:26 +0800</pubDate>
      <link>https://ruby-china.org/topics/16316</link>
      <guid>https://ruby-china.org/topics/16316</guid>
    </item>
  </channel>
</rss>
