<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>lvjian700 (吕健)</title>
    <link>https://ruby-china.org/lvjian700</link>
    <description>移居墨尔本的程序员。前 ThoughtWorks 高级咨询师，现 Cash App Snr Engineer。《程序员在澳洲》Podcast 主播</description>
    <language>en-us</language>
    <item>
      <title>Vim 中快速移动行文本</title>
      <description>&lt;p&gt;在使用 &lt;a href="https://www.jetbrains.com/" rel="nofollow" target="_blank" title=""&gt;IDEA&lt;/a&gt; 时，可以用 Alt + Command Up/Down 可以对代码进行上移/下移。个人非常喜欢这个功能，本文介绍如何在 Vim 中实现这个功能：  &lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/c19d6c82971f8fb691910eb966c274f5.gif" title="" alt="move"&gt;&lt;/p&gt;
&lt;h2 id="在 vimrc 中加入如下配置"&gt;在 vimrc 中加入如下配置&lt;/h2&gt;&lt;h3 id="For windows"&gt;For windows&lt;/h3&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nnoremap &amp;lt;A-j&amp;gt; :m .+1&amp;lt;CR&amp;gt;==
nnoremap &amp;lt;A-k&amp;gt; :m .-2&amp;lt;CR&amp;gt;==
inoremap &amp;lt;A-j&amp;gt; &amp;lt;Esc&amp;gt;:m .+1&amp;lt;CR&amp;gt;==gi
inoremap &amp;lt;A-k&amp;gt; &amp;lt;Esc&amp;gt;:m .-2&amp;lt;CR&amp;gt;==gi
vnoremap &amp;lt;A-j&amp;gt; :m '&amp;gt;+1&amp;lt;CR&amp;gt;gv=gv
vnoremap &amp;lt;A-k&amp;gt; :m '&amp;lt;-2&amp;lt;CR&amp;gt;gv=gv
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="For Mac OS"&gt;For Mac OS&lt;/h3&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nnoremap ∆ :m .+1&amp;lt;CR&amp;gt;==
nnoremap ˚ :m .-2&amp;lt;CR&amp;gt;==
inoremap ∆ &amp;lt;Esc&amp;gt;:m .+1&amp;lt;CR&amp;gt;==gi
inoremap ˚ &amp;lt;Esc&amp;gt;:m .-2&amp;lt;CR&amp;gt;==gi
vnoremap ∆ :m '&amp;gt;+1&amp;lt;CR&amp;gt;gv=gv
vnoremap ˚ :m '&amp;lt;-2&amp;lt;CR&amp;gt;gv=gv
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="normal &amp;amp; insert mode"&gt;normal &amp;amp; insert mode&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Alt + k：上移当前行&lt;/li&gt;
&lt;li&gt;Alt + j：下移当前行&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="visual mode"&gt;visual mode&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Alt + k：上移当前行或者选中行&lt;/li&gt;
&lt;li&gt;Alt + j：下移当前行或者选中行&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="参考"&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://vim.wikia.com/wiki/Moving_lines_up_or_down" rel="nofollow" target="_blank"&gt;http://vim.wikia.com/wiki/Moving_lines_up_or_down&lt;/a&gt;&lt;/p&gt;</description>
      <author>lvjian700</author>
      <pubDate>Tue, 19 Jan 2016 12:37:11 +0800</pubDate>
      <link>https://ruby-china.org/topics/28777</link>
      <guid>https://ruby-china.org/topics/28777</guid>
    </item>
    <item>
      <title>在 Vim 中运行 RSpec 测试</title>
      <description>&lt;p&gt;自从转到 Ruby，Vim 已成为平时主要的开发工具。Vim 配合 &lt;a href="https://www.iterm2.com/" rel="nofollow" target="_blank" title=""&gt;iTerm 2&lt;/a&gt; 能很好的满足平时的开发需求。但是在运行 RSpec 的时候总是需要切换到 iTerm，尤其是在运行单个测试时，总是感觉不方便。    &lt;/p&gt;

&lt;p&gt;本文介绍插件 &lt;a href="https://github.com/thoughtbot/vim-rspec" rel="nofollow" target="_blank" title=""&gt;vim-rspec&lt;/a&gt; 插件，它可以直接从 Vim 中直接运行 RSpec：  &lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/1713453e13dfdd0a33bfeb9132716545.gif" title="" alt="vim-rspec"&gt;&lt;/p&gt;
&lt;h2 id="安装"&gt;安装&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;vim-rspec&lt;/code&gt; 需要使用 &lt;a href="https://github.com/VundleVim/Vundle.vim" rel="nofollow" target="_blank" title=""&gt;vundle&lt;/a&gt;：   &lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Plugin 'thoughtbot/vim-rspec'
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="配置和使用"&gt;配置和使用&lt;/h2&gt;
&lt;p&gt;将如下配置添加到 &lt;code&gt;.vimrc&lt;/code&gt; 中：  &lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;" RSpec.vim mappings
map &amp;lt;Leader&amp;gt;t :call RunCurrentSpecFile()&amp;lt;CR&amp;gt;
map &amp;lt;Leader&amp;gt;s :call RunNearestSpec()&amp;lt;CR&amp;gt;
map &amp;lt;Leader&amp;gt;l :call RunLastSpec()&amp;lt;CR&amp;gt;
map &amp;lt;Leader&amp;gt;a :call RunAllSpecs()&amp;lt;CR&amp;gt;

" Link to current terminal app
let g:rspec_runner = "os_x_iterm"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在我的 &lt;code&gt;Vim&lt;/code&gt; 中，&lt;code&gt;&amp;lt;Leader&amp;gt;&lt;/code&gt; 键直接使用的 &lt;code&gt;/&lt;/code&gt;：  &lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let mapleader = "/"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上述配置等同：    &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/t&lt;/code&gt;，运行当前测试&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/s&lt;/code&gt;，运行当前的 &lt;code&gt;it&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/l&lt;/code&gt;，运行上一次测试&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/a&lt;/code&gt;，运行所有测试&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="写在最后"&gt;写在最后&lt;/h2&gt;
&lt;p&gt;Vim 是一个非常强大的工具，它可以让 Developer 双手保持的键盘上，熟练的使用 Vim 可以让我们保持专注，达到眼到手到的境界。对于如何学习 Vim，唯有大量的使用才能提升 Vim 的能力。经过 10000 小时的练习后，Vim 会是 Developer 手上的瑞士军刀。&lt;/p&gt;</description>
      <author>lvjian700</author>
      <pubDate>Tue, 19 Jan 2016 12:35:18 +0800</pubDate>
      <link>https://ruby-china.org/topics/28776</link>
      <guid>https://ruby-china.org/topics/28776</guid>
    </item>
    <item>
      <title>现在开始为 jQuery 编写插件</title>
      <description>&lt;p&gt;jQuery 已经是现在 Front-End 开发必备工具，本文将介绍如何为 jQuery 编写一个 &lt;code&gt;ColorButton&lt;/code&gt; plugin。 &lt;/p&gt;

&lt;p&gt;内容将涉及： &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;jQuery plugin 基本结构&lt;/li&gt;
&lt;li&gt;编写一个简单的 plugin&lt;/li&gt;
&lt;li&gt;引入封装，完成 &lt;code&gt;ColorButton&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;为 plugin 编写 Unit Test&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="编写 jQuery 基本结构"&gt;编写 jQuery 基本结构&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;创建 &lt;code&gt;plugin&lt;/code&gt; 代码&lt;/li&gt;
&lt;li&gt;为 &lt;code&gt;plugin&lt;/code&gt; 添加方法&lt;/li&gt;
&lt;li&gt;将 &lt;code&gt;plugin&lt;/code&gt; 注册到 jQuery&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;// 1. 创建 `plugin` 代码&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ColorButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$select&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 初始化&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$select&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="c1"&gt;// 默认参数&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;

    &lt;span class="c1"&gt;// binding 事件&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. 为 `plugin` 添加方法&lt;/span&gt;
  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ColorButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 这里定义方法&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// 3. 将 `plugin` 绑定到 jQuery&lt;/span&gt;
  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;colorbutton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&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="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;$select&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;$select&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;colorbutton&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;$select&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;colorbutton&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ColorButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$select&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="p"&gt;})(&lt;/span&gt;&lt;span class="nx"&gt;jQuery&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 使用&lt;/span&gt;
&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.color-button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;colorbutton&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;JSFildder 代码：&lt;a href="https://jsfiddle.net/lvjian700/kn6qvmcz/1/" rel="nofollow" target="_blank"&gt;https://jsfiddle.net/lvjian700/kn6qvmcz/1/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="编写一个简单的 plugin"&gt;编写一个简单的 plugin&lt;/h2&gt;
&lt;p&gt;这里按照上述模板代码做一个最小的插件。  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;将 &lt;code&gt;ColorButton&lt;/code&gt; 绑定到 button 上&lt;/li&gt;
&lt;li&gt;当点击 button 时，在控制台输出 &lt;code&gt;clicked&lt;/code&gt; &lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 1. 创建 plugin 代码&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ColorButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$select&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// 组建初始化代码&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$select&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="c1"&gt;// 默认参数&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;

  &lt;span class="c1"&gt;// binding 事件&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;clicked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;JSFildder 上的完整代码：&lt;a href="https://jsfiddle.net/lvjian700/kn6qvmcz/2/" rel="nofollow" target="_blank"&gt;https://jsfiddle.net/lvjian700/kn6qvmcz/2/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="引入封装，完成 ColorButton"&gt;引入封装，完成 ColorButton&lt;/h2&gt;
&lt;p&gt;上篇我们在 &lt;code&gt;plugin&lt;/code&gt; 初始化时进行了事件绑定。现在我们实现 &lt;code&gt;ColorButton&lt;/code&gt; 的功能。当用户点击 &lt;code&gt;ColorButton&lt;/code&gt; 时改变 button 的背景色。    &lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/813fd668beac1043656aaa96af9369af.gif" title="" alt="color-button"&gt;&lt;/p&gt;

&lt;p&gt;步骤：  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;接收参数，提供 colors 选项，并且为 colors 提供默认参数&lt;/li&gt;
&lt;li&gt;提供 &lt;code&gt;getColor&lt;/code&gt; 方法提供当前的背景色&lt;/li&gt;
&lt;li&gt;提供 &lt;code&gt;changeColor&lt;/code&gt; 方法改变颜色&lt;/li&gt;
&lt;li&gt;将 &lt;code&gt;changeColor&lt;/code&gt; 班定到 &lt;code&gt;click&lt;/code&gt; 事件&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ColorButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$select&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$select&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;colorIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// 1. 接收参数，提供 colors 选项，并且为 colors 提供默认参数&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#ff0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#f0f&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#0ff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;

  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;that&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// 4. 将 changeColor 班定到 click 事件&lt;/span&gt;
    &lt;span class="nx"&gt;that&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;changeColor&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ColorButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// 3. 提供 changeColor 方法改变颜色&lt;/span&gt;
  &lt;span class="na"&gt;changeColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;css&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getColor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="c1"&gt;// 2. 提供 getColor 方法提供当前的背景色&lt;/span&gt;
  &lt;span class="na"&gt;getColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;currentIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;colorIndex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;colorIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;colorIndex&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;currentIndex&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;JSFildder 上的完整代码：&lt;a href="https://jsfiddle.net/lvjian700/kn6qvmcz/3/" rel="nofollow" target="_blank"&gt;https://jsfiddle.net/lvjian700/kn6qvmcz/3/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="为 plugin 编写 Unit Test"&gt;为 plugin 编写 Unit Test&lt;/h2&gt;
&lt;p&gt;Web 开发中我们一般使用 &lt;code&gt;Cucumber&lt;/code&gt; 和 &lt;code&gt;Capybara&lt;/code&gt; 对 UI 进行测试。这个层级的测试属于 &lt;code&gt;UI Automattion Test&lt;/code&gt;，也可成为 &lt;code&gt;Acceptance Test&lt;/code&gt;。这类测试运行成本比较高。&lt;/p&gt;

&lt;p&gt;所以这里采用 Unit Test`方式对 plugin 进行测试。       &lt;/p&gt;

&lt;p&gt;工具：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://mochajs.org/" rel="nofollow" target="_blank" title=""&gt;mocha&lt;/a&gt;：提供类 RSpec 风格的 Test Case&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://chaijs.com/" rel="nofollow" target="_blank" title=""&gt;chai&lt;/a&gt;：提供 assertion 和 matcher&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/chaijs/chai-jquery" rel="nofollow" target="_blank" title=""&gt;chai-jquery&lt;/a&gt;：提供更好用的 assertion&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;更好的工具，但是本文没有使用：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://karma-runner.github.io/0.13/index.html" rel="nofollow" target="_blank" title=""&gt;karma&lt;/a&gt;：当然我们可以使用 karma 在命令行中运行测试，本文暂时没有使用&lt;/li&gt;
&lt;li&gt;
&lt;a href="sinonjs.org" title=""&gt;sinon&lt;/a&gt;：可以做 &lt;code&gt;sub&lt;/code&gt;、&lt;code&gt;mock&lt;/code&gt;、&lt;code&gt;spy&lt;/code&gt;, &lt;code&gt;mock network&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果你用的 Rails，可以使用 &lt;a href="https://github.com/modeset/teaspoon" rel="nofollow" target="_blank" title=""&gt;teaspoon&lt;/a&gt; 来运行测试。 &lt;/p&gt;

&lt;p&gt;编写测试的基本结构：  &lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ColorButton&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="nf"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// 加载 fixture&lt;/span&gt;
      &lt;span class="c1"&gt;// 初始化 plugin&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;afterEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// remove fixture&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;描述 Context&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// 准备 Context&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;assertion 描述&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// 我们期待 ...&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// 更多 Context ...&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;对 &lt;code&gt;ColorButton&lt;/code&gt; 的测试：  &lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ColorButton&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="nf"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;body&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;a id="color-button" href="#"&amp;gt;Button&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#color-button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;colorbutton&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#ff0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#f0f&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;afterEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#color-button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;when click button once&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#color-button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should change button color&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#color-button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;backgroundColor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgb(255, 255, 0)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// 更多 Context ...&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;    
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;完整的代码：&lt;a href="https://github.com/lvjian700/color-button" rel="nofollow" target="_blank"&gt;https://github.com/lvjian700/color-button&lt;/a&gt;，参考 readme 即可运行测试。 &lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/53ab0a32f0dbaaf172dbed32dc5a74b7.png" title="" alt="unit-test"&gt;&lt;/p&gt;
&lt;h2 id="写在最后"&gt;写在最后&lt;/h2&gt;
&lt;p&gt;如果你的项目只想用简单的 jQuery 并不想使用类似 &lt;code&gt;BackBone&lt;/code&gt;、&lt;code&gt;Angular.js&lt;/code&gt;、&lt;code&gt;React.js&lt;/code&gt; 的框架，使用 jQuery plugin 模块化 UI 组建也是一个不错的选择。&lt;/p&gt;</description>
      <author>lvjian700</author>
      <pubDate>Fri, 15 Jan 2016 09:19:01 +0800</pubDate>
      <link>https://ruby-china.org/topics/28736</link>
      <guid>https://ruby-china.org/topics/28736</guid>
    </item>
    <item>
      <title>理解 Ruby 中的 include 和 prepend</title>
      <description>&lt;p&gt;Ruby 中使用 mixin 优雅的解决了 multiple inheritance 问题。在 Java 世界中使用 interface 解决这个问题，在 Ruby 中使用 module，与 Java 中不同的是：&lt;code&gt;Ruby 中的 module 不但可以定义接口，而且还能提供实现&lt;/code&gt;。 &lt;/p&gt;

&lt;p&gt;在 Ruby 提供 include 和 prepend 两种方式使用 module，而对 include 或者 prepend module 这种方式，Ruby 称为 mixin。本文将介绍这两种不同的 mixin 方式。   &lt;/p&gt;
&lt;h2 id="先认识一下 include 和 prepend"&gt;先认识一下 include 和 prepend&lt;/h2&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;SuperSpeaker&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;speak&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'Super class speaking...'&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;Chinese&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;speak&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'在说中文...'&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Thai&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;speak&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'ฉันกำลังพูด...'&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Speaker&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;SuperSpeaker&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Chinese&lt;/span&gt;
  &lt;span class="n"&gt;prepend&lt;/span&gt; &lt;span class="no"&gt;Thai&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;speak&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'Subclass speaking...'&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="no"&gt;Speaker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ancestors&lt;/span&gt;
&lt;span class="n"&gt;speaker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Speaker&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;speaker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;speak&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="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;Thai&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Speaker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Chinese&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;SuperSpeaker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Kernel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;BasicObject&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;ฉันกำลังพูด&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="no"&gt;Speaker&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;speaking&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;在说中文&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="no"&gt;Super&lt;/span&gt; &lt;span class="n"&gt;speaker&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;speaking&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过输出我们可以看到：  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;include 会将 Chinese module 加入到 Speaker 的&lt;strong&gt;后面&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;prepend 会将 Thai module 加入到 Speaker 的&lt;strong&gt;前面&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="从 Ruby 的 Method lookup 机制理解 include 和 prepend"&gt;从 Ruby 的 Method lookup 机制理解 include 和 prepend&lt;/h2&gt;
&lt;p&gt;在理解 include 和 prepend 时，我们需要知道，当我在一个 instance 上调用方法时，都发生了什么？    &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ruby 中方法存在 Class 中，instance 只有变量没有方法。当在 instance 上调用方法时，会先从 instance 对应的 Class 中查找（先向右查找）&lt;/li&gt;
&lt;li&gt;当无法在当前 Class 找到改方法时，Ruby 会按照继承链向上查找方法（后向上查找）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这个机制在 Ruby 中称为 Method lookup，一句话总结 Method lookup 机制： &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;『先向右，后向上』&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;在上段代码中，我们输出了 Speaker 的继承链：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="no"&gt;Speaker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ancestors&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; [Thai, Speaker, Chinese, SuperSpeaker, Object, Kernel, BasicObject]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Method lookup 过程是这样的：  &lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/3658fed7bcf84b85ae069fc658f07fc3.png" title="" alt="method lookup"&gt;&lt;/p&gt;

&lt;p&gt;从 Method lookup 角度来理解 include 和 prepend：  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;include 将 Chinese module 加入到 Speaker 继承链的&lt;strong&gt;上方&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;prepend 将 Thai module 加入到 Speaker 继承链的&lt;strong&gt;下方&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这样也就解释了，为什么 &lt;code&gt;Speaker#speak&lt;/code&gt; 会先输出泰文：『ฉันกำลังพูด...』。&lt;/p&gt;
&lt;h2 id="使用 include 和 prepend 实现一个简单的 DSL"&gt;使用 include 和 prepend 实现一个简单的 DSL&lt;/h2&gt;
&lt;p&gt;在 Rails controller 中，before_action 非常好用，这里我们使用 include 和 prepend 实现一个简单的 before_action：    &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;BeforeAction&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;klass&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ClassMethods&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;module&lt;/span&gt; &lt;span class="nn"&gt;ClassMethods&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;before_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;action_module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="nb"&gt;send&lt;/span&gt; &lt;span class="ss"&gt;:define_method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:for&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="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
          &lt;span class="nb"&gt;send&lt;/span&gt; &lt;span class="n"&gt;method_name&lt;/span&gt;

          &lt;span class="k"&gt;super&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="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="n"&gt;prepend&lt;/span&gt; &lt;span class="n"&gt;action_module&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;Speaker&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;BeforeAction&lt;/span&gt;

  &lt;span class="n"&gt;before_action&lt;/span&gt; &lt;span class="ss"&gt;:chinese_self_intro&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;for: :speak&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;speak&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'I am speaking...'&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;chinese_self_intro&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'Hello, I come from china.'&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;Speaker&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;speak&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出一下 Speaker 的继承链，便于我们理解 before_action 的原理：    &lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;action_module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class_methods&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; [:speak]&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="no"&gt;Speaker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ancestors&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; [#&amp;lt;Module:0x007fe1ca254be0&amp;gt;, Speaker, BeforeAction, Object, Kernel, BasicObject]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/0489811912c7d809b87c8e18e1941b3c.png" title="" alt="before action"&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;使用 include 将 BeforeAction mixin 到 Speaker 继承链上面，此时在定义 Speaker 时便可使用 before_action&lt;/li&gt;
&lt;li&gt;在 before_action 中 prepend 将带有 speak 方法的 Anonymous module，将 Anonymous module 插入到继承链的底端。这样在调用 &lt;code&gt;Speaker#speak&lt;/code&gt; 时，先调用 Anonymous module 中的 speak&lt;/li&gt;
&lt;li&gt;在 Anonymous module 中的 speak 方法调用 for 参数指定的 chinese_self_intro&lt;/li&gt;
&lt;li&gt;在 Anonymous module 中的 speak 方法的最后，使用 &lt;code&gt;super(*arg, &amp;amp;block)&lt;/code&gt; 调用继承链上方的方法（即：&lt;code&gt;Speaker#speak&lt;/code&gt;）&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;Ruby 中的 include 和 prepend 为 Ruby 提供了 mixin 机制，这个机制不但解决 multiple inheritance 问题，而且为 Ruby 的 DSL 能力提供了强大的支持。&lt;/p&gt;

&lt;p&gt;那么什么时候用 mixin，什么时候用 inheritance？    &lt;/p&gt;

&lt;p&gt;从一个 Java 程序员角度来讲，当你想定义 interface 时便可使用 mixin。&lt;/p&gt;</description>
      <author>lvjian700</author>
      <pubDate>Tue, 12 Jan 2016 22:40:30 +0800</pubDate>
      <link>https://ruby-china.org/topics/28712</link>
      <guid>https://ruby-china.org/topics/28712</guid>
    </item>
    <item>
      <title>Gem 总是报 Insecure world writable dir /Users/lvjian/.rvm/gems in PATH, mode 040777 错误</title>
      <description>&lt;p&gt;使用 gem 总是会包这个警告：
/Users/lvjian/.rvm/rubies/ruby-2.0.0-p247/bin/gem:4: warning: Insecure world writable dir /Users/lvjian/.rvm/gems in PATH, mode 040777&lt;/p&gt;

&lt;p&gt;这个怎么解决？&lt;/p&gt;</description>
      <author>lvjian700</author>
      <pubDate>Mon, 26 Aug 2013 17:19:36 +0800</pubDate>
      <link>https://ruby-china.org/topics/13635</link>
      <guid>https://ruby-china.org/topics/13635</guid>
    </item>
  </channel>
</rss>
