<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>wosuopu (龙昌)</title>
    <link>https://ruby-china.org/wosuopu</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>在 Ruby 中使用 DATA 和 __END__ 将代码和数据混合</title>
      <description>&lt;p&gt;之前一直不理解 &lt;code&gt;__END__&lt;/code&gt; 的用法，现在看了这篇文章后才算是了解了，于是便翻译之。&lt;br&gt;
《Mixing code and data in Ruby with DATA and __END__》: &lt;a href="http://blog.honeybadger.io/data-and-end-in-ruby/" rel="nofollow" target="_blank"&gt;http://blog.honeybadger.io/data-and-end-in-ruby/&lt;/a&gt;&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;你知道 Ruby 提供了一种方法在你的脚本中可以将源文件作为数据源来使用吗？当你在写一些一次性的脚本用于验证概念时这个小技巧会为你节约一些时间。让我们来看看吧。&lt;/p&gt;
&lt;h2 id="DATA 和 __END__"&gt;DATA 和 __END__&lt;/h2&gt;
&lt;p&gt;在下面这个例子中，我使用了一个有趣的关键字 &lt;code&gt;__END__&lt;/code&gt;。所有在 &lt;code&gt;__END__&lt;/code&gt; 下面的内容将会被 Ruby 解释器所忽略。但是有趣的是 ruby 为你提供了一个称为 &lt;code&gt;DATA&lt;/code&gt; 的 IO 对象，就像你可以读取其他任何文件一样，它能让你读取到 &lt;code&gt;__END__&lt;/code&gt; 以下的所有内容。&lt;/p&gt;

&lt;p&gt;下面这个例子中，我们遍历每一行并进行输出。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;DATA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each_line&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;line&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;line&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="cp"&gt;__END__
Doom
Quake
Diablo
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;关于这个技术我最喜欢的实例是使用 &lt;code&gt;DATA&lt;/code&gt; 来包含一个 ERB 模板。它同样也可用于 YAML、CSV 等等。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'erb'&lt;/span&gt;

&lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;
&lt;span class="n"&gt;renderer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ERB&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;DATA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;result&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="cp"&gt;__END__
The current time is &amp;lt;%= time %&amp;gt;.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;实际上你也可以使用 &lt;code&gt;DATA&lt;/code&gt; 来读取 &lt;code&gt;__END__&lt;/code&gt; 关键字以上的内容。那是因为 &lt;code&gt;DATA&lt;/code&gt; 实际上是一个指向了整个源文件，并定位到 &lt;code&gt;__END__&lt;/code&gt; 关键字的位置。你可以试试看在输出之前将 IO 对象反转。下面这个例子将会输出整个源文件。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;DATA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rewind&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="no"&gt;DATA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt; &lt;span class="c1"&gt;# prints the entire source file&lt;/span&gt;

&lt;span class="cp"&gt;__END__
meh
&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;code&gt;__END__&lt;/code&gt; 部分。然而却只有一个全局 &lt;code&gt;DATA&lt;/code&gt; 对象。因此第二个文件的 &lt;code&gt;__END__&lt;/code&gt; 部分刚访问不到了。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# first.rb&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"./second"&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"First file&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;----------------------"&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="no"&gt;DATA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;

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

&lt;span class="cp"&gt;__END__
First end clause
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# second.rb&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;print_second_data&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Second file&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;----------------------"&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="no"&gt;DATA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt; &lt;span class="c1"&gt;# Won't output anything, since first.rb read the entire file&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="cp"&gt;__END__

Second end clause
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;snhorne ~/tmp $ ruby first.rb
First file
----------------------
First end clause

Second file
----------------------
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="对于多文件的一个解决方案"&gt;对于多文件的一个解决方案&lt;/h2&gt;
&lt;p&gt;在 Sinatra 中有一个很酷的特性是它允许你在你应用的 &lt;code&gt;__END__&lt;/code&gt; 部分添加多个内联模板。它看起来像下面这样：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# This code is from the Sinatra docs at http://www.sinatrarb.com/intro.html&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'sinatra'&lt;/span&gt;

&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s1"&gt;'/'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;haml&lt;/span&gt; &lt;span class="ss"&gt;:index&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="cp"&gt;__END__

@@ layout
%html

  = yield

@@ index
%div.title Hello world.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;sinatra 是如何实现的呢？毕竟你的应用可能是运行在 rack 上。在生产环境中你不能再通过 &lt;code&gt;ruby myapp.rb&lt;/code&gt; 来运行！他们必须有一种在多文件中使用 &lt;code&gt;DATA&lt;/code&gt; 的解决方案。&lt;/p&gt;

&lt;p&gt;因此如果你稍微深入一下 Sinatra 的源代码，你会发现它们并没有使用 &lt;code&gt;DATA&lt;/code&gt;。而是使用了跟下面这段代码类似的方案。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# I'm paraphrasing. See the original at https://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb#L1284&lt;/span&gt;
&lt;span class="n"&gt;app&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="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^__END__$/&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;实际上它比这个要更复杂一些，因为它们不想读取 &lt;code&gt;__FILE__&lt;/code&gt;，它只是 &lt;code&gt;sinatra/base.rb&lt;/code&gt; 文件。它们其实是需要获取调用了该方法的文件的内容。它们通过解析 &lt;code&gt;caller&lt;/code&gt; 的结果来获取。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;caller&lt;/code&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;some_method&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="nb"&gt;caller&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;some_method&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; caller.rb:5:in `&amp;lt;main&amp;gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在可以简单地获取到文件名了，然后从该文件中再提取出与 &lt;code&gt;DATA&lt;/code&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;get_caller_data&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;caller&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;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;":"&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;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"__END__"&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="nf"&gt;last&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="请善用它，不要作恶"&gt;请善用它，不要作恶&lt;/h2&gt;
&lt;p&gt;希望对于这些技巧你不要经常使用。它们不会使得代码干净、可维护。&lt;/p&gt;

&lt;p&gt;然后，你偶尔需要一些又快又脏的实现一个一次性的脚本或者验证一些概念。此时 &lt;code&gt;DATA&lt;/code&gt; 和 &lt;code&gt;__END__&lt;/code&gt; 就非常有用了。&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;我的博客： &lt;a href="http://www.xefan.com/archives/84154.html" rel="nofollow" target="_blank"&gt;http://www.xefan.com/archives/84154.html&lt;/a&gt;&lt;/p&gt;</description>
      <author>wosuopu</author>
      <pubDate>Sun, 23 Aug 2015 12:03:36 +0800</pubDate>
      <link>https://ruby-china.org/topics/27040</link>
      <guid>https://ruby-china.org/topics/27040</guid>
    </item>
    <item>
      <title>使用 Rails + Angular 开发的一个记账程序</title>
      <description>&lt;p&gt;最初就是想写个小程序来方便自己日常记账，顺便再练习一下 angular。整个程序是一个 SPA。
好了直接看效果图：&lt;/p&gt;

&lt;p&gt;&lt;img src="https://raw.githubusercontent.com/wusuopu/rails-billing/master/doc/001.jpg" title="" alt="001"&gt;
&lt;img src="https://raw.githubusercontent.com/wusuopu/rails-billing/master/doc/002.jpg" title="" alt="002"&gt;
&lt;img src="https://raw.githubusercontent.com/wusuopu/rails-billing/master/doc/003.jpg" title="" alt="003"&gt;&lt;/p&gt;

&lt;p&gt;开发完成之后也算是对 angular 有些熟悉了吧。
最近听说 angular 2 也快要发布了，于是去了解了一下。瞬间崩溃了，尼玛这与 angular 1.x 完全不兼容啊，又得重新学习了。不过反而觉得 angular 2 跟 react 之类的比较相似。&lt;/p&gt;

&lt;p&gt;程序源代码：&lt;a href="https://github.com/wusuopu/rails-billing" rel="nofollow" target="_blank"&gt;https://github.com/wusuopu/rails-billing&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;如果有需要学习 angular 的同学，这个应该可以提供一些参考吧。&lt;/p&gt;</description>
      <author>wosuopu</author>
      <pubDate>Wed, 19 Aug 2015 17:42:38 +0800</pubDate>
      <link>https://ruby-china.org/topics/26986</link>
      <guid>https://ruby-china.org/topics/26986</guid>
    </item>
    <item>
      <title>Unicorn 是如何与 Nginx 通讯的——介绍 Ruby 中的 Unix Socket</title>
      <description>&lt;p&gt;Ruby 应用服务典型地是与一个 web 服务一同使用的，如 nginx。当用户请求你的 Rails 应用中的页面时，nginx 将请求指派给应用服务。
然而这个过程是如何完成的呢？nginx 与 unicorn 是如何通讯的呢？&lt;/p&gt;

&lt;p&gt;最有效的一种选择是使用 unix 套接字 (sockets)。让我们来看看它们是如何工作的！
在这篇文章中我们将从一个基本的套接字 (sockets) 开始，最后将创建一个使用 nginx 代理的简单应用服务。&lt;/p&gt;

&lt;p&gt;&lt;img src="http://www.xefan.com/wp-content/uploads/2015/07/socket.png" title="" alt="socket"&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;套接字 (sockets) 允许进程之间通过对一个文件读或者写进行相互通讯。&lt;/em&gt;
&lt;em&gt;在这个例子中 Unicorn 创建 socket 并监听它的连接。然后 Nginx 就可以连接到这个 socket 并与 Unicorn 通讯了。&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="什么是 unix socket？"&gt;什么是 unix socket？&lt;/h2&gt;
&lt;p&gt;Unix socket 使得一个进程通过类似文件的方式与另一个进程进行通讯。它是 IPC(Interprocess Communication) 的一种。&lt;/p&gt;

&lt;p&gt;要使得可以通过 socket 访问进程，首先需要创建一个 socket 并作为一个文件保存在磁盘中。
然后监听这个 socket 的连入连接。当接收到一个连接时，就可以使用&lt;a href="http://ruby-doc.org/core-2.2.2/IO.html#method-i-readline" rel="nofollow" target="_blank" title=""&gt;标准 IO 方法&lt;/a&gt;进行读写数据。&lt;/p&gt;

&lt;p&gt;Ruby 通过以下一组类提供了 unix socket 所需的所有内容：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UNIXServer - 创建 socket 并保存到磁盘中，并且让你可以监听新连接。&lt;/li&gt;
&lt;li&gt;UNIXSocket - 打开已存在的套接字 (sockets)。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;注意：&lt;/strong&gt;还存在着其它类型的 socket，最突出的是 TCP socket。不过这篇文章只处理 unix socket。那么它们之间的区别是什么呢？unix  socket 具有文件名。&lt;/p&gt;
&lt;h2 id="最简单的 Socket"&gt;最简单的 Socket&lt;/h2&gt;
&lt;p&gt;我们接下来看两个小程序。&lt;/p&gt;

&lt;p&gt;第一个是服务端，它创建一个 &lt;code&gt;UnixServer&lt;/code&gt; 实例，然后使用 &lt;code&gt;server.accept&lt;/code&gt; 等待连接。当收到连接后则相互问候。&lt;/p&gt;

&lt;p&gt;需要说明一下，&lt;code&gt;accept&lt;/code&gt; 和 &lt;code&gt;readline&lt;/code&gt; 方法都会阻塞程序的执行，直到收到内容。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"socket"&lt;/span&gt;

&lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;UNIXServer&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="s1"&gt;'/tmp/simple.sock'&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;"==== Waiting for connection"&lt;/span&gt;
&lt;span class="n"&gt;socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accept&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"==== Got Request:"&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readline&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"==== Sending Response"&lt;/span&gt;
&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"I read you loud and clear, good buddy!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里我们有了服务端，现在还需要客户端。&lt;/p&gt;

&lt;p&gt;在下面这个例子中，我们打开由服务端创建的 socket，然后使用普通的 IO 方法进行发送和接收问候。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"socket"&lt;/span&gt;

&lt;span class="n"&gt;socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;UNIXSocket&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="s1"&gt;'/tmp/simple.sock'&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;"==== Sending"&lt;/span&gt;
&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Hello server, can you hear me?&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"==== Getting Response"&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readline&lt;/span&gt;

&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;演示一下程序，先运行服务端，然后再运行客户端。你可以看到以下结果：&lt;/p&gt;

&lt;p&gt;&lt;img src="http://www.xefan.com/wp-content/uploads/2015/07/simple_ruby_socket_example.png" title="" alt="simple_ruby_socket_example"&gt;
&lt;em&gt;简单的 Unix socket 服务端/客户端交互的例子。左边是客户端，右边是服务端。&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="与 nginx 接合"&gt;与 nginx 接合&lt;/h2&gt;
&lt;p&gt;现在我们知道如何创建一个 unix socket 的服务端了，我们可以很容易地与 nginx 接合。&lt;/p&gt;

&lt;p&gt;不相信我？让我们来做一个快速的概念验证吧。我修改上面的代码使其输出从 socket 接收到的所有内容。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"socket"&lt;/span&gt;

&lt;span class="c1"&gt;# Create the socket and "save it" to the file system&lt;/span&gt;
&lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;UNIXServer&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="s1"&gt;'/tmp/socktest.sock'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Wait until for a connection (by nginx)&lt;/span&gt;
&lt;span class="n"&gt;socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accept&lt;/span&gt;

&lt;span class="c1"&gt;# Read everything from the socket&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readline&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在如果我修改 nginx 配置，将请求转发到 &lt;code&gt;/tmp/socktest.sock&lt;/code&gt; socket 上。
我就能看到 nginx 发送来的数据了。(别担心，我们稍后会讨论它的配置的)&lt;/p&gt;

&lt;p&gt;当我发起一个 web 请求时，nginx 将如下数据发送到我的服务端上：&lt;/p&gt;

&lt;p&gt;&lt;img src="http://www.xefan.com/wp-content/uploads/2015/07/request_http.png" title="" alt="request_http"&gt;&lt;/p&gt;

&lt;p&gt;太酷了！这就是一个包含了额外头信息的 HTTP 请求。现在我们准备来构建一个真正的应用服务。
但是，首先让我们讨论一个 nginx 的配置吧。&lt;/p&gt;
&lt;h2 id="安装配置 Nginx"&gt;安装配置 Nginx&lt;/h2&gt;
&lt;p&gt;如果你还没有安装 nginx 的话，请先安装。对于 OSX 可以 homebrew 简单完成：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在我们配置 nginx 将 localhost:2048 的请求通过名为 &lt;code&gt;/tmp/socktest.sock&lt;/code&gt; 的 socket 转发到上游服务端。
名字可以是任意的，它仅需要与我们 web 服务的 socket 名字匹配即可。&lt;/p&gt;

&lt;p&gt;你可以将其保存至 &lt;code&gt;/tmp/nginx.conf&lt;/code&gt; 并通过命令 &lt;code&gt;nginx -c /tmp/nginx.conf&lt;/code&gt; 运行 nginx。&lt;/p&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run nginx as a normal console program, not as a daemon
&lt;/span&gt;&lt;span class="n"&gt;daemon&lt;/span&gt; &lt;span class="n"&gt;off&lt;/span&gt;;

&lt;span class="c"&gt;# Log errors to stdout
&lt;/span&gt;&lt;span class="n"&gt;error_log&lt;/span&gt; /&lt;span class="n"&gt;dev&lt;/span&gt;/&lt;span class="n"&gt;stdout&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;;

&lt;span class="n"&gt;events&lt;/span&gt; {} &lt;span class="c"&gt;# Boilerplate
&lt;/span&gt;
&lt;span class="n"&gt;http&lt;/span&gt; {

  &lt;span class="c"&gt;# Print the access log to stdout
&lt;/span&gt;  &lt;span class="n"&gt;access_log&lt;/span&gt; /&lt;span class="n"&gt;dev&lt;/span&gt;/&lt;span class="n"&gt;stdout&lt;/span&gt;;

  &lt;span class="c"&gt;# Tell nginx that there's an external server called @app living at our socket
&lt;/span&gt;  &lt;span class="n"&gt;upstream&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; {
    &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="n"&gt;unix&lt;/span&gt;:/&lt;span class="n"&gt;tmp&lt;/span&gt;/&lt;span class="n"&gt;socktest&lt;/span&gt;.&lt;span class="n"&gt;sock&lt;/span&gt; &lt;span class="n"&gt;fail_timeout&lt;/span&gt;=&lt;span class="m"&gt;0&lt;/span&gt;;
  }

  &lt;span class="n"&gt;server&lt;/span&gt; {

    &lt;span class="c"&gt;# Accept connections on localhost:2048
&lt;/span&gt;    &lt;span class="n"&gt;listen&lt;/span&gt; &lt;span class="m"&gt;2048&lt;/span&gt;;
    &lt;span class="n"&gt;server_name&lt;/span&gt; &lt;span class="n"&gt;localhost&lt;/span&gt;;

    &lt;span class="c"&gt;# Application root
&lt;/span&gt;    &lt;span class="n"&gt;root&lt;/span&gt; /&lt;span class="n"&gt;tmp&lt;/span&gt;;

    &lt;span class="c"&gt;# If a path doesn't exist on disk, forward the request to @app
&lt;/span&gt;    &lt;span class="n"&gt;try_files&lt;/span&gt; $&lt;span class="n"&gt;uri&lt;/span&gt;/&lt;span class="n"&gt;index&lt;/span&gt;.&lt;span class="n"&gt;html&lt;/span&gt; $&lt;span class="n"&gt;uri&lt;/span&gt; @&lt;span class="n"&gt;user3&lt;/span&gt;;

    &lt;span class="c"&gt;# Set some configuration options on requests forwarded to @app
&lt;/span&gt;    &lt;span class="n"&gt;location&lt;/span&gt; @&lt;span class="n"&gt;user5&lt;/span&gt; {
      &lt;span class="n"&gt;proxy_set_header&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;-&lt;span class="n"&gt;Forwarded&lt;/span&gt;-&lt;span class="n"&gt;For&lt;/span&gt; $&lt;span class="n"&gt;proxy_add_x_forwarded_for&lt;/span&gt;;
      &lt;span class="n"&gt;proxy_set_header&lt;/span&gt; &lt;span class="n"&gt;Host&lt;/span&gt; $&lt;span class="n"&gt;http_host&lt;/span&gt;;
      &lt;span class="n"&gt;proxy_redirect&lt;/span&gt; &lt;span class="n"&gt;off&lt;/span&gt;;
      &lt;span class="n"&gt;proxy_pass&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;://&lt;span class="n"&gt;app&lt;/span&gt;;
    }

  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以非后台模式运行 nginx。当你运行 nginx 时它应该看起来像如下这样：&lt;/p&gt;

&lt;p&gt;&lt;img src="http://www.xefan.com/wp-content/uploads/2015/07/nginx_non_daemon.png" title="" alt="nginx_non_daemon"&gt;
&lt;em&gt;Nginx 以非后台模式运行&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="自定义应用服务"&gt;自定义应用服务&lt;/h2&gt;
&lt;p&gt;既然我们已经知道了如何将 nginx 与我们程序进行连接，那么我们就来构建一个简单的应用服务。
当 nginx 将请求转发到我们的 socket 时，它是一个标准的 HTTP 请求。
经过一些处理后我可以决定 socket 是否会返回一个有效的 HTTP 响应，它会在浏览器中显示。&lt;/p&gt;

&lt;p&gt;下面这个应用接受任何请求并显示时间戳。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"socket"&lt;/span&gt;

&lt;span class="c1"&gt;# Connection creates the socket and accepts new connections&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Connection&lt;/span&gt;

  &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:path&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;path&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
    &lt;span class="vi"&gt;@path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;
    &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unlink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&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;server&lt;/span&gt;
    &lt;span class="vi"&gt;@server&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="no"&gt;UNIXServer&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;@path&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;on_request&lt;/span&gt;
    &lt;span class="n"&gt;socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accept&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;socket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&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;# AppServer logs incoming requests and renders a view in response&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppServer&lt;/span&gt;

  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:connection&lt;/span&gt;
  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:view&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;connection&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
    &lt;span class="vi"&gt;@connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;
    &lt;span class="vi"&gt;@view&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;view&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;run&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
      &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on_request&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;socket&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readline&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
          &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&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;span class="c1"&gt;# TimeView simply provides the HTTP response&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TimeView&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;
&lt;span class="sx"&gt;%[HTTP/1.1 200 OK


The current timestamp is: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt; &lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sx"&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="no"&gt;AppServer&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="ss"&gt;connection: &lt;/span&gt;&lt;span class="no"&gt;Connection&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="ss"&gt;path: &lt;/span&gt;&lt;span class="s1"&gt;'/tmp/socktest.sock'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="ss"&gt;view: &lt;/span&gt;&lt;span class="no"&gt;TimeView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在运行 nginx 和脚本，然后访问 localhost:2048。请求会发送到我的应用上，然后响应被浏览器渲染。太酷了！&lt;/p&gt;

&lt;p&gt;&lt;img src="http://www.xefan.com/wp-content/uploads/2015/07/appserver.png" title="" alt="appserver"&gt;
&lt;em&gt;HTTP 请求信息由我们的应用服务输出到 STDOUT&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;以下就是我们的劳动成果。&lt;/p&gt;

&lt;p&gt;&lt;img src="http://www.xefan.com/wp-content/uploads/2015/07/timestamp.png" title="" alt="timestamp"&gt;
&lt;em&gt;浏览器中显示服务端返回的时间戳&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;我的博客：&lt;a href="http://www.xefan.com/archives/84146.html" rel="nofollow" target="_blank"&gt;http://www.xefan.com/archives/84146.html&lt;/a&gt;
原文：&lt;a href="http://blog.honeybadger.io/how-unicorn-talks-to-nginx-an-introduction-to-unix-sockets-in-ruby/" rel="nofollow" target="_blank"&gt;http://blog.honeybadger.io/how-unicorn-talks-to-nginx-an-introduction-to-unix-sockets-in-ruby/&lt;/a&gt;&lt;/p&gt;</description>
      <author>wosuopu</author>
      <pubDate>Mon, 03 Aug 2015 23:04:52 +0800</pubDate>
      <link>https://ruby-china.org/topics/26758</link>
      <guid>https://ruby-china.org/topics/26758</guid>
    </item>
    <item>
      <title>Ruby 的内存陷阱</title>
      <description>&lt;p&gt;Ruby 有一套自动的内存管理机制。这在大多数情况下是不错的，但是有时它却是个麻烦。&lt;/p&gt;

&lt;p&gt;Ruby 的内存管理既简洁又笨重。它将对象（名为 &lt;code&gt;RVALUE&lt;/code&gt;）存储在大约有 16KB 大小的堆中。
从底层上，&lt;code&gt;RVALUE&lt;/code&gt; 是一个 &lt;code&gt;C&lt;/code&gt; 的结构体，它包含了一个共同体表示不同的标准 ruby 对象。&lt;/p&gt;

&lt;p&gt;因此在堆中存储着大小不超过 40 字节的 &lt;code&gt;RVALUE&lt;/code&gt; 对象，如 &lt;code&gt;String&lt;/code&gt;、&lt;code&gt;Array&lt;/code&gt;、&lt;code&gt;Hash&lt;/code&gt;等。
这意味着小的对象在堆中很合适，但是一旦它们达到到阈值，那么就需要在 Ruby 的堆之外再分配一片额外的内存。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;这块额外的内存空间是灵活的。一旦对象被垃圾回收了它就会被释放掉。但是堆本身的内存是不会被释放给操作系统的。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;让我们来看一个简单的例子：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;report&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'Memory '&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sb"&gt;`ps ax -o pid,rss | grep -E "^[[:space:]]*&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vg"&gt;$$&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;"`&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:to_i&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;'KB'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;report&lt;/span&gt;
&lt;span class="n"&gt;big_var&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;" "&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10_000_000&lt;/span&gt;
&lt;span class="n"&gt;report&lt;/span&gt;
&lt;span class="n"&gt;big_var&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
&lt;span class="n"&gt;report&lt;/span&gt;
&lt;span class="no"&gt;ObjectSpace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;garbage_collect&lt;/span&gt;
&lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;report&lt;/span&gt;

&lt;span class="c1"&gt;# ⇒ Memory 11788KB&lt;/span&gt;
&lt;span class="c1"&gt;# ⇒ Memory 65188KB&lt;/span&gt;
&lt;span class="c1"&gt;# ⇒ Memory 65188KB&lt;/span&gt;
&lt;span class="c1"&gt;# ⇒ Memory 11788KB&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="o"&gt;-&lt;/span&gt;  &lt;span class="n"&gt;big_var&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;" "&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10_000_000&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;  &lt;span class="n"&gt;big_var&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1_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="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;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:to_s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这只是一个简单的修改，不是吗。但是结果：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# ⇒ Memory 11788KB
# ⇒ Memory 65188KB
# ⇒ Memory 65188KB
# ⇒ Memory 57448KB
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;怎么回事？内存没有释放归还给操作系统。这是因为数组中的每个元素符合 &lt;code&gt;RVALUE&lt;/code&gt; 的大小并存储在 ruby 的堆中。&lt;/p&gt;

&lt;p&gt;在大多情况下这是正常的。现在 ruby 堆中多了许多空的位置，再次运行代码将不会再消耗额外的内存了。
每次我们处理 &lt;code&gt;big_var&lt;/code&gt; 和一些空的堆时， &lt;code&gt;GC[:heap_used]&lt;/code&gt;的值果然减小了。
对于这些操作 Ruby 是早有准备，注意这里是 Ruby 而不是操作系统。&lt;/p&gt;

&lt;p&gt;因此，对于创建大量的符合 40 个字节的临时变量就要注意了：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;big_var = " " * 10_000_000
big_var.gsub(/\s/) { |c| '-' }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;结果同样是 Ruby 的内存疯狂增长，并且这部分内存在程序运行期间是不会归还给操作系统的：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# ⇒ Memory 10156KB
# ⇒ Memory 13788KB
# ⇒ Memory 13788KB
# ⇒ Memory 12808KB
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个问题不是太重要，稍微注意一下即可。&lt;/p&gt;

&lt;p&gt;我的博客：&lt;a href="http://www.xefan.com/archives/84145.html" rel="nofollow" target="_blank"&gt;http://www.xefan.com/archives/84145.html&lt;/a&gt;&lt;/p&gt;</description>
      <author>wosuopu</author>
      <pubDate>Thu, 14 May 2015 23:18:19 +0800</pubDate>
      <link>https://ruby-china.org/topics/25584</link>
      <guid>https://ruby-china.org/topics/25584</guid>
    </item>
    <item>
      <title>Ruby 3.0 的未来</title>
      <description>&lt;p&gt;原文地址：&lt;a href="http://hrnabi.com/2015/05/12/7035/" rel="nofollow" target="_blank"&gt;http://hrnabi.com/2015/05/12/7035/&lt;/a&gt;&lt;br&gt;
谁日语好的为大家做下贡献，把原文翻译一下吧。&lt;/p&gt;

&lt;p&gt;反正我不会日语，根据文中的一些汉字连蒙带猜的，大致看了一遍，现在简单总结一下。
如果有总结得不对的，本人不负责哦。&lt;/p&gt;

&lt;p&gt;看来要玩好 Ruby 不仅需要学英语，也还得学日语啊。&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;在 2014 年 9 月举行的 RubyKaigi 2014 大会上，Matz 在演讲过程中首次提到了 Ruby 3.0。&lt;br&gt;
文中说的 Ruby 3.0 的三个工作方向：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Concurrency (并行性)&lt;/li&gt;
&lt;li&gt;JIT (即时编译)&lt;/li&gt;
&lt;li&gt;Static typing (静态类型)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ruby 要引入静态类型检查？&lt;br&gt;
Matz 说，在 20 世纪出生的语言大多是脚本语言，如：Ruby、PHP 和 Perl、JavaScript，这些都不是静态类型的。
另一方面，最近推出的如 Scala 和 Dart、Go 是属于静态类型的。
在 Ruby 中可以考虑引入 Python 这种通过注释来进行检查的方法。&lt;/p&gt;

&lt;p&gt;Ruby 要引入并行计算？&lt;br&gt;
Matz 详细讨论了静态类型，但没有提到并行计算。详细的内容是由笹田氏来说的。&lt;br&gt;
（这部分内容我没看懂，只是大概知道这位笹田氏的博士论文与这个相关。）&lt;br&gt;
关于并行计算，Matz 提到了 Erlang 和 Scala 的 actor 模型。&lt;/p&gt;

&lt;p&gt;最后期待下一个 Ruby 开发者大会。&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;好了，我只看懂了这么多。其余的各位感兴趣的自己去看原文吧。
(怎么感觉好坑啊，这总结得跟没总结一样啊。没办法了我的日语水平有限。)&lt;/p&gt;

&lt;p&gt;我的博客地址： &lt;a href="http://www.xefan.com/archives/84144.html" rel="nofollow" target="_blank"&gt;http://www.xefan.com/archives/84144.html&lt;/a&gt;&lt;/p&gt;</description>
      <author>wosuopu</author>
      <pubDate>Wed, 13 May 2015 23:21:54 +0800</pubDate>
      <link>https://ruby-china.org/topics/25567</link>
      <guid>https://ruby-china.org/topics/25567</guid>
    </item>
    <item>
      <title>Ruby 中一些重要的钩子方法</title>
      <description>&lt;p&gt;原文地址： &lt;a href="http://www.sitepoint.com/rubys-important-hook-methods/" rel="nofollow" target="_blank"&gt;http://www.sitepoint.com/rubys-important-hook-methods/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ruby 的哲学理念是基于一个基本的要素，那就是让程序员快乐。Ruby 非常注重程序员的快乐，并且也提供了许多不同的方法来实现它。
它的元编程能力能够让程序员编写在运行时动态生成的代码。它的线程功能使得程序员有一种优雅的的方式编写多线程代码。
它的钩子方法能让程序员在程序运行时扩展它的行为。&lt;/p&gt;

&lt;p&gt;上述的这些特性，以及一些其他很酷的语言方面，使得 Ruby 成为编写代码的优先选择之一。
本文将探讨 Ruby 中的一些重要的钩子方法。我们将从不同方面讨论钩子方法，如它们是什么，它们用于什么，以及我们如何使用它们来解决不同的问题。
我们同时也了解一下一些流行的 Ruby 框架/Gem 包/库是如何使用它们来提供非常酷的特性的。&lt;/p&gt;

&lt;p&gt;我们开始吧。&lt;/p&gt;
&lt;h2 id="什么是钩子方法？"&gt;什么是钩子方法？&lt;/h2&gt;
&lt;p&gt;钩子方法提供了一种方式用于在程序运行时扩展程序的行为。
假设有这样的功能，可以在无论何时一个子类继承了一些特定的父类时收到通知，
或者是比较优雅地处理一个对象上的不可调用的方法而不是让编译器抛出异常。
这些情况就是使用钩子方法，但是它们的用法并不仅限于此。
不同的框架/库使用了不同的钩子方法来实现它们的功能。&lt;/p&gt;

&lt;p&gt;在本文中我们将会讨论如下几个钩子方法：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;included&lt;/li&gt;
&lt;li&gt;extended&lt;/li&gt;
&lt;li&gt;prepended&lt;/li&gt;
&lt;li&gt;inherited&lt;/li&gt;
&lt;li&gt;method_missing&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="included"&gt;included&lt;/h2&gt;
&lt;p&gt;Ruby 给我们提供了一种方式使用 &lt;code&gt;模块(modules)&lt;/code&gt; （在其他语言中被称作 &lt;code&gt;混入类(mixins)&lt;/code&gt;）来编写模块化的代码供其他的 &lt;code&gt;模块&lt;/code&gt;/&lt;code&gt;类&lt;/code&gt; 使用。
&lt;code&gt;模块&lt;/code&gt; 的概念很简单，它就是一个可以在其他地方使用的独立代码块。&lt;/p&gt;

&lt;p&gt;例如，如果我们想要编写一些代码在任何时候调用特定的方法都会返回一个静态字符串。
我们姑且将这个方法称作 &lt;code&gt;name&lt;/code&gt;。你可能在其他地方也会想使用同一块代码。
这样最好是新建一个模块。让我们来创建一个：&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;Person&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"My name is Person"&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;name&lt;/code&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;User&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Person&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;模块&lt;/code&gt;。&lt;code&gt;include&lt;/code&gt; 是其中之一。&lt;code&gt;include&lt;/code&gt; 所做的就是将在 &lt;code&gt;module&lt;/code&gt; 内定义的方法在一个 &lt;code&gt;class&lt;/code&gt; 的实例变量上可用。
在我们的例子中，是将 &lt;code&gt;Person&lt;/code&gt; 模块中定义的方法变为一个 &lt;code&gt;User&lt;/code&gt; 类实例对象的方法。
这就相当于我们是将 &lt;code&gt;name&lt;/code&gt; 方法写在 &lt;code&gt;User&lt;/code&gt; 类里一样，但是定义在 &lt;code&gt;module&lt;/code&gt; 里的好处是可复用。
要调用 &lt;code&gt;name&lt;/code&gt; 方法我们需要创建一个 &lt;code&gt;User&lt;/code&gt; 的实例对象，然后再在这个对象上调用 &lt;code&gt;name&lt;/code&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;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;My&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="no"&gt;Person&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;让我们看看基于 &lt;code&gt;include&lt;/code&gt; 的钩子方法。&lt;code&gt;included&lt;/code&gt; 是 Ruby 提供的一个钩子方法，当你在一些 &lt;code&gt;module&lt;/code&gt; 或者 &lt;code&gt;class&lt;/code&gt; 中 &lt;code&gt;include&lt;/code&gt; 了一个 &lt;code&gt;module&lt;/code&gt; 时它会被调用。
更新 &lt;code&gt;Person&lt;/code&gt; 模块：&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;Person&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;included&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&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;base&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; included &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt;
    &lt;span class="s2"&gt;"My name is Person"&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;included&lt;/code&gt; 被定义为 &lt;code&gt;Person&lt;/code&gt; 模块的类方法。当你在其他的模块或者类中执行 &lt;code&gt;include Person&lt;/code&gt; 时，这个 &lt;code&gt;included&lt;/code&gt; 方法会被调用。
该方法接收的一个参数是对包含该模块的类的引用。试试运行 &lt;code&gt;User.new.name&lt;/code&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="n"&gt;included&lt;/span&gt; &lt;span class="no"&gt;Person&lt;/span&gt;
&lt;span class="no"&gt;My&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="no"&gt;Person&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;正如你所见，&lt;code&gt;base&lt;/code&gt; 返回的是包含该模块的类名。现在我们有了一个包含 &lt;code&gt;Person&lt;/code&gt; 模块的类的引用，我们可以通过元编程来实现我们想要的功能。
让我们来看看 &lt;strong&gt;Devise&lt;/strong&gt;是如何使用 &lt;code&gt;included&lt;/code&gt; 钩子的。&lt;/p&gt;
&lt;h3 id="Devise中的 included"&gt;Devise 中的 &lt;code&gt;included&lt;/code&gt;
&lt;/h3&gt;
&lt;p&gt;Devise 是 Ruby 中使用最广泛的身份验证 gem 包之一。它主要是由我喜欢的程序员 &lt;a href="https://twitter.com/josevalim" rel="nofollow" target="_blank" title=""&gt;José Valim&lt;/a&gt; 开发的，现在是由一些了不起的贡献者在维护。
Devise 为我们提供了从注册到登录，从忘记密码到找回密码等等完善的功能。它可以让我们在用户模型中使用简单的语法来配置各种模块：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;devise&lt;/span&gt; &lt;span class="ss"&gt;:database_authenticatable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:registerable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:validatable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在我们模型中使用的 &lt;code&gt;devise&lt;/code&gt; 方法在&lt;a href="https://github.com/plataformatec/devise/blob/v3.4.1/lib/devise/models.rb#L77" rel="nofollow" target="_blank" title=""&gt;这里&lt;/a&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;devise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;modules&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;modules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extract_options!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dup&lt;/span&gt;

  &lt;span class="n"&gt;selected_modules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;modules&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;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:to_sym&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;uniq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort_by&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;s&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="no"&gt;Devise&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ALL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;  &lt;span class="c1"&gt;# follow Devise::ALL order&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;devise_modules_hook!&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Devise&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Authenticatable&lt;/span&gt;

    &lt;span class="n"&gt;selected_modules&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;m&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;mod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Devise&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;const_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;classify&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;mod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;const_defined?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"ClassMethods"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;class_mod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;const_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"ClassMethods"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="n"&gt;class_mod&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;class_mod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond_to?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:available_configs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="n"&gt;available_configs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;class_mod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;available_configs&lt;/span&gt;
          &lt;span class="n"&gt;available_configs&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;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
            &lt;span class="k"&gt;next&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;key?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nb"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="ss"&gt;="&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
          &lt;span class="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="kp"&gt;include&lt;/span&gt; &lt;span class="n"&gt;mod&lt;/span&gt;
    &lt;span class="k"&gt;end&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;devise_modules&lt;/span&gt; &lt;span class="o"&gt;|=&lt;/span&gt; &lt;span class="n"&gt;selected_modules&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;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="nb"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="ss"&gt;="&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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;code&gt;devise&lt;/code&gt; 方法的模块名将会作为一个数组保存在 &lt;code&gt;*modules&lt;/code&gt; 中。
对于传入的模块调用 &lt;code&gt;extract_options!&lt;/code&gt; 方法提取可能传入的选项。
在 11 行中调用 &lt;code&gt;each&lt;/code&gt; 方法，并且每个模块在代码块中用 &lt;code&gt;m&lt;/code&gt; 表示。
在 12 行中 &lt;code&gt;m&lt;/code&gt; 将会转化为一个常量（类名），因此使用 &lt;code&gt;m.to.classify&lt;/code&gt; 一个例如 &lt;code&gt;:validatable&lt;/code&gt; 这样的符号会变为 &lt;code&gt;Validatable&lt;/code&gt; 。
随便说一下 &lt;code&gt;classify&lt;/code&gt; 是 ActiveSupport 的方法。&lt;br&gt;
&lt;code&gt;Devise::Models.const_get(m.to_classify)&lt;/code&gt; 会获取该模块的引用，并赋值给 &lt;code&gt;mod&lt;/code&gt;。
在 27 行使用 &lt;code&gt;include mod&lt;/code&gt; 包含该模块。
例子中的 &lt;code&gt;Validatable&lt;/code&gt; 模块是定义在&lt;a href="https://github.com/plataformatec/devise/blob/v3.4.1/lib/devise/models/validatable.rb" rel="nofollow" target="_blank" title=""&gt;这里&lt;/a&gt;。
&lt;code&gt;Validatable&lt;/code&gt; 的 &lt;code&gt;included&lt;/code&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="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;included&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;ClassMethods&lt;/span&gt;
  &lt;span class="n"&gt;assert_validations_api!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class_eval&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;validates_presence_of&lt;/span&gt;   &lt;span class="ss"&gt;:email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;if: :email_required?&lt;/span&gt;
    &lt;span class="n"&gt;validates_uniqueness_of&lt;/span&gt; &lt;span class="ss"&gt;:email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;allow_blank: &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;if: :email_changed?&lt;/span&gt;
    &lt;span class="n"&gt;validates_format_of&lt;/span&gt;     &lt;span class="ss"&gt;:email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;with: &lt;/span&gt;&lt;span class="n"&gt;email_regexp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;allow_blank: &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;if: :email_changed?&lt;/span&gt;

    &lt;span class="n"&gt;validates_presence_of&lt;/span&gt;     &lt;span class="ss"&gt;:password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;if: :password_required?&lt;/span&gt;
    &lt;span class="n"&gt;validates_confirmation_of&lt;/span&gt; &lt;span class="ss"&gt;:password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;if: :password_required?&lt;/span&gt;
    &lt;span class="n"&gt;validates_length_of&lt;/span&gt;       &lt;span class="ss"&gt;:password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;within: &lt;/span&gt;&lt;span class="n"&gt;password_length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;allow_blank: &lt;/span&gt;&lt;span class="kp"&gt;true&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;base&lt;/code&gt;。在第 5 行的 &lt;code&gt;class_eval&lt;/code&gt; 代码块会以该类作为上下文进行求值运算。
通过 &lt;code&gt;class_eval&lt;/code&gt; 编写的代码与直接打开该类的文件将代码粘贴进去效果是一样的。
Devise 是通过 &lt;code&gt;class_eval&lt;/code&gt; 将验证包含到我们的用户模型中的。&lt;/p&gt;

&lt;p&gt;当我们试着使用 Devise 注册或者登录时，我们会看到这些验证，但是我们并没有编写这些验证代码。
Devise 是利用了 &lt;code&gt;included&lt;/code&gt; 钩子来实现这些的。非常的优雅吧。&lt;/p&gt;
&lt;h2 id="extended"&gt;extended&lt;/h2&gt;
&lt;p&gt;Ruby 也允许开发者 &lt;code&gt;扩展(extend)&lt;/code&gt; 一个模块，这与 &lt;code&gt;包含(include)&lt;/code&gt; 有点不同。
&lt;code&gt;extend&lt;/code&gt; 是将定义在 &lt;code&gt;模块(module)&lt;/code&gt; 内的方法应用为类的方法，而不是实例的方法。
让我们来看一个简单的例子：&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;Person&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt;
    &lt;span class="s2"&gt;"My name is Person"&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;extend&lt;/span&gt; &lt;span class="no"&gt;Person&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;puts&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;name&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; My name is Person&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;正如你所看到的，我们将 &lt;code&gt;Person&lt;/code&gt; 模块内定义的 &lt;code&gt;name&lt;/code&gt; 方法作为了 &lt;code&gt;User&lt;/code&gt; 的类方法调用。
&lt;code&gt;extend&lt;/code&gt; 将 &lt;code&gt;Person&lt;/code&gt; 模块内的方法添加到了 &lt;code&gt;User&lt;/code&gt; 类中。&lt;code&gt;extend&lt;/code&gt; 同样也可以用于将模块内的方法作为单例方法 (singleton methods)。
让我们再来看另外一个例子：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# We are using same Person module and User class from previous example.     &lt;/span&gt;

&lt;span class="n"&gt;u1&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="n"&gt;u2&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="n"&gt;u1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;Person&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;u1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; My name is Person&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;u2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; undefined method `name' for #&amp;lt;User:0x007fb8aaa2ab38&amp;gt; (NoMethodError)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们创建了两个 &lt;code&gt;User&lt;/code&gt; 的实例对象，并将 &lt;code&gt;Person&lt;/code&gt; 作为参数在 &lt;code&gt;u1&lt;/code&gt; 上调用 &lt;code&gt;extend&lt;/code&gt; 方法。
使用这种调用方式，&lt;code&gt;Person&lt;/code&gt; 的 &lt;code&gt;name&lt;/code&gt; 方法仅对 &lt;code&gt;u1&lt;/code&gt; 有效，对于其他实例是无效的。&lt;/p&gt;

&lt;p&gt;正如 &lt;code&gt;included&lt;/code&gt; 一样，与 &lt;code&gt;extend&lt;/code&gt; 相对应的钩子方法是 &lt;code&gt;extended&lt;/code&gt;。
当一个模块被其他模块或者类执行了 &lt;code&gt;extend&lt;/code&gt; 操作时，该方法将会被调用。
让我们来看一个例子：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Modified version of Person module&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Person&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;extended&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&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;base&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; extended &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt;
    &lt;span class="s2"&gt;"My name is Person"&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;extend&lt;/span&gt; &lt;span class="no"&gt;Person&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;该代码的运行结果是输出 &lt;code&gt;User extended Person&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;关于 &lt;code&gt;extended&lt;/code&gt; 的介绍已经完了，让我们来看看 &lt;code&gt;ActiveRecord&lt;/code&gt; 是如何使用它的。&lt;/p&gt;
&lt;h3 id="ActiveRecord中的 extended"&gt;ActiveRecord 中的 &lt;code&gt;extended&lt;/code&gt;
&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ActiveRecord&lt;/code&gt; 是在 Ruby 以及 Rails 中广泛使用的 ORM 框架。它具有许多酷的特性，
因此使用它在很多情况下成为了 ORM 的首选。让我们进入 &lt;code&gt;ActiveRecord&lt;/code&gt; 内部看看 &lt;code&gt;ActiveRecord&lt;/code&gt; 是如何使用回调的。
(我们使用的是 Rails v3.2.21)&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ActiveRecord&lt;/code&gt; 在&lt;a href="https://github.com/rails/rails/blob/v3.2.21/activerecord/lib/active_record/callbacks.rb#L246" rel="nofollow" target="_blank" title=""&gt;这里&lt;/a&gt; &lt;code&gt;extend&lt;/code&gt; 了 &lt;code&gt;ActiveRecord::Models&lt;/code&gt; 模块。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;ActiveModel&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Callbacks&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;ActiveModel&lt;/code&gt; 提供了一套在模型类中使用的接口。它们允许 ActionPack 与不是 ActiveRecord 的模型进行交互。
在&lt;a href="https://github.com/rails/rails/blob/v3.2.21/activemodel/lib/active_model/callbacks.rb#L49-L53" rel="nofollow" target="_blank" title=""&gt;这里&lt;/a&gt;， &lt;code&gt;ActiveModel::Callbacks&lt;/code&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="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extended&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class_eval&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Callbacks&lt;/span&gt;
  &lt;span class="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;ActiveModel::Callbacks&lt;/code&gt; 对 &lt;code&gt;base&lt;/code&gt; 即就是 &lt;code&gt;ActiveRecord::Callbacks&lt;/code&gt; 调用了 &lt;code&gt;class_eval&lt;/code&gt; 方法，
并包含了 &lt;code&gt;ActiveSupport::Callbacks&lt;/code&gt; 模块。我们前面已经提到过了，对一个类调用 &lt;code&gt;class_eval&lt;/code&gt; 与手动地将代码写在这个类里是一样的。
&lt;code&gt;ActiveSupport::Callbacks&lt;/code&gt; 为 &lt;code&gt;ActiveRecord::Callbacks&lt;/code&gt; 提供了 Rails 中的回调方法。&lt;/p&gt;

&lt;p&gt;这里我们讨论了 &lt;code&gt;extend&lt;/code&gt; 方法，以及与之对应的钩子 &lt;code&gt;extended&lt;/code&gt;。并且也了解了 &lt;code&gt;ActiveRecord&lt;/code&gt; / &lt;code&gt;ActiveModel&lt;/code&gt; 
是如何使用上述方法为我们提供可用功能的。&lt;/p&gt;
&lt;h2 id="prepended"&gt;prepended&lt;/h2&gt;
&lt;p&gt;另一个使用定义在模块内部方法的方式称为 &lt;code&gt;prepend&lt;/code&gt;。&lt;code&gt;prepend&lt;/code&gt; 是在 Ruby 2.0 中引入的，并且与 &lt;code&gt;include&lt;/code&gt; 和 &lt;code&gt;extend&lt;/code&gt; 很不一样。
使用 &lt;code&gt;include&lt;/code&gt; 和 &lt;code&gt;extend&lt;/code&gt; 引入的方法可以被目标模块/类重新定义覆盖。
例如，如果我们在某个模块中定义了一个名为 &lt;code&gt;name&lt;/code&gt; 的方法，并且在目标模块/类中也定义同名的方法。
那么这个在我们类在定义的 &lt;code&gt;name&lt;/code&gt; 方法将会覆盖模块中的。而 &lt;code&gt;prepend&lt;/code&gt; 是不一样的，它会将 &lt;code&gt;prepend&lt;/code&gt; 引入的模块
中的方法覆盖掉我们模块/类中定义的方法。让我们来看一个简单的例子：&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;Person&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt;
    &lt;span class="s2"&gt;"My name belongs to Person"&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;Person&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt;
    &lt;span class="s2"&gt;"My name belongs to User"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;puts&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="nf"&gt;name&lt;/span&gt; 
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;My&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="n"&gt;belongs&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在再来看看 &lt;code&gt;prepend&lt;/code&gt; 的情况：&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;Person&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt;
    &lt;span class="s2"&gt;"My name belongs to Person"&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="n"&gt;prepend&lt;/span&gt; &lt;span class="no"&gt;Person&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt;
    &lt;span class="s2"&gt;"My name belongs to User"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;puts&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="nf"&gt;name&lt;/span&gt; 
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;My&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="n"&gt;belongs&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="no"&gt;Person&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用 &lt;code&gt;prepend Person&lt;/code&gt; 会将 &lt;code&gt;User&lt;/code&gt; 中的同名方法给覆盖掉，因此在终端输出的结果为 &lt;code&gt;My name belongs to Person&lt;/code&gt;。
&lt;code&gt;prepend&lt;/code&gt; 实际上是将方法添加到方法链的前端。在调用 &lt;code&gt;User&lt;/code&gt; 类内定义的 &lt;code&gt;name&lt;/code&gt; 方法时，会调用 &lt;code&gt;super&lt;/code&gt; 从而调用 &lt;code&gt;Person&lt;/code&gt; 模块的 &lt;code&gt;name&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;与 &lt;code&gt;prepend&lt;/code&gt; 对应的回调名为（你应该猜到了） &lt;code&gt;prepended&lt;/code&gt;。当一个模块被预置到另一个模块/类中时它会被调用。
我们来看下效果。更新 &lt;code&gt;Person&lt;/code&gt; 模块的定义：&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;Person&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;prepended&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&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="nb"&gt;self&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; prepended to &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt;
    &lt;span class="s2"&gt;"My name belongs to Person"&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 plaintext"&gt;&lt;code&gt;Person prepended to User
My name belongs to Person
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;prepend&lt;/code&gt; 的引入是为了去除 &lt;code&gt;alias_method_chain&lt;/code&gt; hack 的丑陋，它曾被 Rails 以及其他库广泛地使用以达到与 &lt;code&gt;prepend&lt;/code&gt; 相同的功能。
因为 &lt;code&gt;prepend&lt;/code&gt; 只有在 Ruby &amp;gt;= 2.0 的版本中才能使用，因此如果你打算使用 &lt;code&gt;prepend&lt;/code&gt; 的话，那么你就应该升级你的 Ruby 版本。&lt;/p&gt;
&lt;h2 id="inherited"&gt;inherited&lt;/h2&gt;
&lt;p&gt;继承是面向对象中一个最重要的概念。Ruby 是一门面向对象的编程语言，并且提供了从基/父类继承一个子类的功能。
我们来看一个简单的例子：&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;Person&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt;
     &lt;span class="s2"&gt;"My name is Person"&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="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Person&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;puts&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="nf"&gt;name&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; My name is Person&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们创建了一个 &lt;code&gt;Person&lt;/code&gt; 类和一个子类 &lt;code&gt;User&lt;/code&gt;。在 &lt;code&gt;Person&lt;/code&gt; 中定义的方法也成为了 &lt;code&gt;User&lt;/code&gt; 的一部分。
这是非常简单的继承。你可能会好奇，是否有什么方法可以在一个类被其他类继承时收到通知呢？
是的，Ruby 有一个名为 &lt;code&gt;inherited&lt;/code&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;Person&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;inherited&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;child_class&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;child_class&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; inherits &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt;
    &lt;span class="s2"&gt;"My name is Person"&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="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Person&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;puts&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="nf"&gt;name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;正如你所见，当 &lt;code&gt;Person&lt;/code&gt; 类被其他子类继承时 &lt;code&gt;inherited&lt;/code&gt; 类方法将会被调用。
运行以上代码结果如下：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User inherits Person
My name is Person
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;让我们看看 &lt;code&gt;Rails&lt;/code&gt; 在它的代码中是如何使用 &lt;code&gt;inherited&lt;/code&gt; 的。&lt;/p&gt;
&lt;h3 id="Rails中的 inherited"&gt;Rails 中的 &lt;code&gt;inherited&lt;/code&gt;
&lt;/h3&gt;
&lt;p&gt;Rails 应用中有一个重要的类名为 &lt;code&gt;Application&lt;/code&gt; ，定义中 &lt;strong&gt;config/application.rb&lt;/strong&gt; 文件内。
这个类执行了许多不同的任务，如运行所有的 Railties，引擎以及插件的初始化。
关于 &lt;code&gt;Application&lt;/code&gt; 类的一个有趣的事件是，在同一个进程中不能运行两个实例。
如果我们尝试修改这个行为，Rails 将会抛出一个异常。让我们来看看 Rails 是如何实现这个特性的。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Application&lt;/code&gt; 类继承自 &lt;code&gt;Rails::Application&lt;/code&gt;，它是在&lt;a href="https://github.com/rails/rails/blob/v3.2.21/railties/lib/rails/application.rb" rel="nofollow" target="_blank" title=""&gt;这里&lt;/a&gt;定义的。
在 62 行定义了 &lt;code&gt;inherited&lt;/code&gt; 钩子，它会在我们的 Rails 应用 &lt;code&gt;Application&lt;/code&gt; 类继承 &lt;code&gt;Rails::Application&lt;/code&gt; 时被调用。
&lt;code&gt;inherited&lt;/code&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="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;inherited&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s2"&gt;"You cannot have more than one Rails::Application"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;
    &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance&lt;/span&gt;
    &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_lib_to_load_path!&lt;/span&gt;
    &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_load_hooks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:before_configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance&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;code&gt;class &amp;lt;&amp;lt; self&lt;/code&gt; 是 Ruby 中的另一个定义类方法的方式。在 &lt;code&gt;inherited&lt;/code&gt; 中的第 1 行是检查 &lt;code&gt;Rails.application&lt;/code&gt; 是否已存在。
如果存在则抛出异常。第一次运行这段代码时 &lt;code&gt;Rails.application&lt;/code&gt; 会返回 false 然后调用 &lt;code&gt;super&lt;/code&gt;。
在这里 &lt;code&gt;super&lt;/code&gt; 即是 &lt;code&gt;Rails::Engine&lt;/code&gt; 的 &lt;code&gt;inherited&lt;/code&gt; 钩子，因为 &lt;code&gt;Rails::Application&lt;/code&gt; 继承自 &lt;code&gt;Rails::Engine&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;在下一行，你会看到 &lt;code&gt;Rails.application&lt;/code&gt; 被赋值为 &lt;code&gt;base.instance&lt;/code&gt; 。其余就是设置 Rails 应用了。&lt;/p&gt;

&lt;p&gt;这就是 Rails 如何巧妙地使用 &lt;code&gt;inherited&lt;/code&gt; 钩子来实现我们的 Rails &lt;code&gt;Application&lt;/code&gt; 类的单实例。&lt;/p&gt;
&lt;h2 id="method_missing"&gt;method_missing&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;method_missing&lt;/code&gt; 可能是 Ruby 中使用最广的钩子。在许多流行的 Ruby 框架/gem 包/库中都有使用它。
当我们试图访问一个对象上不存在的方法时则会调用这个钩子方法。
让我们来看一个简单的例子：&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;Person&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt;
    &lt;span class="s2"&gt;"My name is Person"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="nb"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;     &lt;span class="c1"&gt;# =&amp;gt; My name is Person &lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="nb"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;address&lt;/span&gt;  &lt;span class="c1"&gt;# =&amp;gt; undefined method `address' for #&amp;lt;Person:0x007fb730a2b450&amp;gt; (NoMethodError)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们定义了一个简单的 &lt;code&gt;Person&lt;/code&gt; 类，它只有一个 &lt;code&gt;name&lt;/code&gt; 方法。然后创建一个 &lt;code&gt;Person&lt;/code&gt; 的实例对象，
并分别调用 &lt;code&gt;name&lt;/code&gt; 和 &lt;code&gt;address&lt;/code&gt; 两个方法。因为 &lt;code&gt;Person&lt;/code&gt; 中定义了 &lt;code&gt;name&lt;/code&gt;，因此这个运行没问题。
然而 &lt;code&gt;Person&lt;/code&gt; 并没有定义 &lt;code&gt;address&lt;/code&gt;，这将会抛出一个异常。
&lt;code&gt;method_missing&lt;/code&gt; 钩子可以优雅地捕捉到这些未定义的方法，避免此类异常。
让我们修改一下 &lt;code&gt;Person&lt;/code&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;Person&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;method_missing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sym&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="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;sym&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; not defined on &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt;
    &lt;span class="s2"&gt;"My name is Person"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="nb"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;     &lt;span class="c1"&gt;# =&amp;gt; My name is Person&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="nb"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;address&lt;/span&gt;  &lt;span class="c1"&gt;# =&amp;gt; address not defined on #&amp;lt;Person:0x007fb2bb022fe0&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;method_missing&lt;/code&gt; 接收两个参数：被调用的方法名和传递给该方法的参数。
首先 Ruby 会寻找我们试图调用的方法，如果方法没找到则会寻找 &lt;code&gt;method_missing&lt;/code&gt; 方法。
现在我们重载了 &lt;code&gt;Person&lt;/code&gt; 中的 &lt;code&gt;method_missing&lt;/code&gt;，因此 Ruby 将会调用它而不是抛出异常。&lt;/p&gt;

&lt;p&gt;让我们来看看 &lt;code&gt;Rake&lt;/code&gt; 是如何使用 &lt;code&gt;method_missing&lt;/code&gt; 的。&lt;/p&gt;
&lt;h3 id="Rake中的 method_missing"&gt;Rake 中的 &lt;code&gt;method_missing&lt;/code&gt;
&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Rake&lt;/code&gt; 是 Ruby 中使用最广泛的 gem 包之一。&lt;code&gt;Rake&lt;/code&gt; 使用 &lt;code&gt;method_missing&lt;/code&gt; 来提供访问传递给 Rake 任务的参数。
首先创建一个简单的 rake 任务：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;:hello&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Hello"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你通过调用 &lt;code&gt;rake hello&lt;/code&gt; 来执行这个任务，你会看到输出 &lt;code&gt;Hello&lt;/code&gt;。
让我们扩展这个 rake 任务，以便接收一个参数（一个人名）并向他打招呼：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;:hello&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Hello &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;args&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="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;t&lt;/code&gt; 是任务名，&lt;code&gt;args&lt;/code&gt; 保存了传递过来的参数。正如你所见，我们调用 &lt;code&gt;args.name&lt;/code&gt; 来获取传递给 &lt;code&gt;hello&lt;/code&gt; 任务的 &lt;code&gt;name&lt;/code&gt; 参数。
运行该任务，并传递一个参数：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;rake&lt;/span&gt; &lt;span class="n"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Imran Latif"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Hello&lt;/span&gt; &lt;span class="no"&gt;Imran&lt;/span&gt; &lt;span class="no"&gt;Latif&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;让我们来看看 &lt;code&gt;Rake&lt;/code&gt; 是如何使用 &lt;code&gt;method_missing&lt;/code&gt; 为我们提供了传递给任务的参数的。&lt;/p&gt;

&lt;p&gt;在上面任务中的 &lt;code&gt;args&lt;/code&gt; 对象是一个 &lt;code&gt;Rake::TaskArguments&lt;/code&gt; 实例，它是在&lt;a href="https://github.com/ruby/rake/blob/v10.4.2/lib/rake/task_arguments.rb" rel="nofollow" target="_blank" title=""&gt;这里&lt;/a&gt;所定义。
这个类负责管理传递给 Rake 任务的参数。查看 &lt;code&gt;Rake::TaskArguments&lt;/code&gt; 的代码，你会发现并没有定义相关的方法将参数传给任务。
那么 &lt;code&gt;Rake&lt;/code&gt; 是如何将参数提供给任务的呢？答案是 &lt;code&gt;Rake&lt;/code&gt; 是使用了 &lt;code&gt;method_missing&lt;/code&gt; 巧妙地实现了这个功能。
看看第 64 行 &lt;code&gt;method_missing&lt;/code&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;method_missing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_sym&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在这个类中定义 &lt;code&gt;method_missing&lt;/code&gt; 是为了保证能够访问到那些未定义的方法，而不是由 Ruby 抛出异常。
在 &lt;code&gt;method_missing&lt;/code&gt; 中它调用了 &lt;code&gt;lookup&lt;/code&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;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@hash.has_key&lt;/span&gt;&lt;span class="p"&gt;?(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="vi"&gt;@hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="vi"&gt;@parent&lt;/span&gt;
    &lt;span class="vi"&gt;@parent.lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;method_missing&lt;/code&gt; 调用 &lt;code&gt;lookup&lt;/code&gt;，并将方法名以 &lt;code&gt;Symbol(符号)&lt;/code&gt; 的形式传递给它。
&lt;code&gt;lookup&lt;/code&gt; 方法将会在 &lt;code&gt;@hash&lt;/code&gt; 中进行查找，它是在 &lt;code&gt;Rake::TaskArguments&lt;/code&gt; 的构造函数中创建的。
如果 &lt;code&gt;@hash&lt;/code&gt; 中包含该参数则返回，如果在 &lt;code&gt;@hash&lt;/code&gt; 中没有则 &lt;code&gt;Rake&lt;/code&gt; 会尝试调用 &lt;code&gt;@parent&lt;/code&gt; 的 &lt;code&gt;lookup&lt;/code&gt;。
如果该参数没有找到，则什么都不返回。&lt;/p&gt;

&lt;p&gt;这就是 &lt;code&gt;Rake&lt;/code&gt; 如何巧妙地使用 &lt;code&gt;method_missing&lt;/code&gt; 提供了访问传递给 Rake 任务的参数的。
感谢&lt;a href="https://twitter.com/jimweirich" rel="nofollow" target="_blank" title=""&gt;Jim Weirich&lt;/a&gt;编写了 Rake。&lt;/p&gt;
&lt;h2 id="结束语"&gt;结束语&lt;/h2&gt;
&lt;p&gt;我们讨论了 5 个重要的 Ruby 钩子方法，探索了它们是如何工作的，以及一些流行的框架/gem 包是如何使用它们来提供一些优雅的功能。
我希望你能喜欢这篇文章。请在评论中告诉我们你所喜欢的 Ruby 钩子，以及你使用它们所解决的问题。&lt;/p&gt;</description>
      <author>wosuopu</author>
      <pubDate>Fri, 01 May 2015 12:03:30 +0800</pubDate>
      <link>https://ruby-china.org/topics/25397</link>
      <guid>https://ruby-china.org/topics/25397</guid>
    </item>
    <item>
      <title>编写 Ruby 的 C 扩展</title>
      <description>&lt;p&gt;最近在折腾用 C 语言编写 Ruby 扩展时发现相关资料很少，于是乎在折腾完了之后我把收集到的资料整理成了一本电子书。但愿对各位需要写扩展的有所帮忙。&lt;/p&gt;

&lt;p&gt;电子书地址： &lt;a href="https://www.gitbook.io/book/wusuopu/write-ruby-extension-with-c" rel="nofollow" target="_blank"&gt;https://www.gitbook.io/book/wusuopu/write-ruby-extension-with-c&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;不足之处欢迎指正。&lt;/p&gt;</description>
      <author>wosuopu</author>
      <pubDate>Mon, 22 Sep 2014 21:16:22 +0800</pubDate>
      <link>https://ruby-china.org/topics/21667</link>
      <guid>https://ruby-china.org/topics/21667</guid>
    </item>
    <item>
      <title>Ruby 元编程第二版发布了</title>
      <description>&lt;p&gt;之前本来说是 6 月份发布的，然后又延期到了 8 月 20 号。但是今天再上去看的时候发现已经发布了。&lt;/p&gt;

&lt;p&gt;电子版的是$25，感觉略贵了点。有没有人一起合买啊？&lt;/p&gt;

&lt;p&gt;&lt;a href="http://pragprog.com/book/ppmetr2/metaprogramming-ruby-2" rel="nofollow" target="_blank"&gt;http://pragprog.com/book/ppmetr2/metaprogramming-ruby-2&lt;/a&gt;&lt;/p&gt;</description>
      <author>wosuopu</author>
      <pubDate>Sun, 17 Aug 2014 10:31:25 +0800</pubDate>
      <link>https://ruby-china.org/topics/21060</link>
      <guid>https://ruby-china.org/topics/21060</guid>
    </item>
    <item>
      <title>使用 ruby 开发 vim 插件</title>
      <description>&lt;p&gt;作为一个 Vimmer 和 Pythoner，之前折腾过用 python 编写 vim 插件。现在作为半个 Rubist，又开始继续折腾。&lt;/p&gt;

&lt;p&gt;在开始编写插件之前，你需要确认 Vim 是否支持 Ruby，通过以下命令来判别：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ vim --version | grep +ruby
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果输出为空，则表示你当前的 vim 不支持 Ruby，需要重新编译一下，并启用对 Ruby 的支持。&lt;/p&gt;

&lt;p&gt;顺便说下我当前的环境是：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;vim 7.4&lt;br&gt;
&lt;/li&gt;
&lt;li&gt;ruby 2.1.0&lt;br&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;环境检查没有问题那么就开始吧。&lt;br&gt;
在~/.vim/plugin 目录下创建一个 demo.vim 文件。&lt;/p&gt;

&lt;p&gt;在开头写上以下代码：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if !has('ruby')
    echo "Error: Required vim compiled with +ruby"
    finish
endif
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这段代码就是用 VimL 编写的，它将检查 Vim 是否支持 Ruby。&lt;/p&gt;

&lt;p&gt;接下来再判断该插件是否已经加载过了，以免重复加载：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if exists('g:loaded_ruby_demo_plugin')
    finish
endif
let g:loaded_ruby_demo_plugin = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所有的检查都没有问题，则开始插件的正文了。先定义一个函数。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function! DemoFun1()
ruby&amp;lt;&amp;lt;EOF
buf = VIM::Buffer.current
puts "current buffer name: #{buf.name} number: #{buf.number} length: #{buf.length}"
EOF
endfunction
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;function 与 endfunction 是 vim 中用于定义函数的，在”ruby&amp;lt;&amp;lt;EOF”和”EOF”之间部分的是 Ruby 代码。这个例子是输出当前缓冲区的名字、编号以及总行数。&lt;br&gt;
执行命令:call DemoFun1()，应该就可以看到输出结果了。&lt;/p&gt;

&lt;p&gt;然后再举个例子说下函数的参数处理。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function! DemoFun2(arg1)
ruby&amp;lt;&amp;lt; EOF
puts "you input: #{VIM.evaluate('a:arg1')}"
EOF
endfunction 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里定义了一个函数接收一个参数，然后将其输出。使用 VIM.evaluate 将 vim 的变量转化为 Ruby 的变量。&lt;/p&gt;

&lt;p&gt;为了方便我们再定义两个命令，以简化对这两个函数的调用。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;command! -nargs=0 DemoFun1 call DemoFun1()  
command! -nargs=1 -rang DemoFun2 call DemoFun2(&amp;lt;f-args&amp;gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;要获取完整的代码可以访问： &lt;a href="https://gist.github.com/wusuopu/c1182efefa85d4f6839b" rel="nofollow" target="_blank"&gt;https://gist.github.com/wusuopu/c1182efefa85d4f6839b&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;接下来再简单说下 vim 中 Ruby 的使用。&lt;/p&gt;

&lt;p&gt;vim 为 Ruby 提供了一个 VIM 模块，通过它可以在 Ruby 中访问 vim 的接口。同时还提供了两个全局变量：$curwin、$curbuf，它们分别代表了当前窗口对象以及当前缓冲区对象。&lt;/p&gt;

&lt;p&gt;VIM 模块中有 Buffer 和 Window 两个对象，分别是用来对缓冲区和窗口进行操作的。同时 VIM 模块还提供了 message、set_option、command 和 evaluate 四个函数。&lt;/p&gt;

&lt;p&gt;想要查看更多的帮忙信息，可以在 vim 中执行如下命令：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:help ruby
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;好了，先写这么多吧，其余的自己去尝试吧。&lt;/p&gt;

&lt;p&gt;博客原文地址： &lt;a href="http://www.xefan.com/archives/84117.html" rel="nofollow" target="_blank"&gt;http://www.xefan.com/archives/84117.html&lt;/a&gt; &lt;/p&gt;</description>
      <author>wosuopu</author>
      <pubDate>Thu, 03 Jul 2014 23:19:24 +0800</pubDate>
      <link>https://ruby-china.org/topics/20304</link>
      <guid>https://ruby-china.org/topics/20304</guid>
    </item>
    <item>
      <title>使用 css 绘制 lxde</title>
      <description>&lt;p&gt;最近在学习 CSS，本来是打算写点小东西练习练习。正好自己用的是 linux 系统的 lxde 桌面，于是就用 CSS 来绘制一个 lxde。
效果如图：
&lt;img src="//l.ruby-china.com/photo/2014/e3f79a93195e604994790b039ab61d34.png" title="" alt=""&gt;
&lt;img src="//l.ruby-china.com/photo/2014/0c777ee0f35074a770dfee92158815f9.png" title="" alt=""&gt;
&lt;img src="//l.ruby-china.com/photo/2014/05ffd35c136734a8a708131c314a41fc.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;源代码： &lt;a href="https://github.com/wusuopu/css3-lxde" rel="nofollow" target="_blank"&gt;https://github.com/wusuopu/css3-lxde&lt;/a&gt;&lt;/p&gt;</description>
      <author>wosuopu</author>
      <pubDate>Wed, 15 Jan 2014 21:51:13 +0800</pubDate>
      <link>https://ruby-china.org/topics/16822</link>
      <guid>https://ruby-china.org/topics/16822</guid>
    </item>
    <item>
      <title>刚学了两个多星期的 Ruby，动手写一个简单的有道词典</title>
      <description>&lt;p&gt;程序是在命令行下运行的，打算之后有时间用 GTK 写个前端界面。&lt;/p&gt;

&lt;p&gt;目前程序可以进行联网查单词，并把查询结果存到本地 sqlite 数据库中，这样下次再查同一个词时就不用再联网了。&lt;/p&gt;

&lt;p&gt;源代码：&lt;a href="https://github.com/wusuopu/youdao-dict-rb" rel="nofollow" target="_blank"&gt;https://github.com/wusuopu/youdao-dict-rb&lt;/a&gt;&lt;/p&gt;</description>
      <author>wosuopu</author>
      <pubDate>Wed, 20 Nov 2013 21:54:22 +0800</pubDate>
      <link>https://ruby-china.org/topics/15684</link>
      <guid>https://ruby-china.org/topics/15684</guid>
    </item>
  </channel>
</rss>
