<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>grantbb (奔跑的大兵)</title>
    <link>https://ruby-china.org/grantbb</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>Rails5.2 用 Wepacker 安装和使用 Materialize CSS</title>
      <description>&lt;p&gt;&lt;code&gt;Materialize CSS&lt;/code&gt;是一个比较新的基于&lt;code&gt;Google Material Design&lt;/code&gt;的前端框架，界面非常简洁，而且很容易使用。&lt;/p&gt;

&lt;p&gt;其中，在最新发布的&lt;code&gt;1.0&lt;/code&gt;版本中，去掉了对于&lt;code&gt;JQuery&lt;/code&gt;的依赖，这是一个很大进步。因为在&lt;code&gt;Rails5&lt;/code&gt;以后也是移除了&lt;code&gt;JQuery&lt;/code&gt;，而且接下来在我的项目中，我会开始使用&lt;code&gt;React.js&lt;/code&gt;作为前端的开发框架。&lt;/p&gt;

&lt;p&gt;下面就来介绍一下通过&lt;code&gt;Webpacker&lt;/code&gt;安装和使用&lt;code&gt;Materialize&lt;/code&gt;的步骤：&lt;/p&gt;

&lt;p&gt;准备工作：
在&lt;code&gt;app/javascript/&lt;/code&gt;下面创建&lt;code&gt;stylesheets&lt;/code&gt;目录以及&lt;code&gt;application.scss&lt;/code&gt;文件：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mkdir app/javascript/stylesheets
$ touch app/javascript/stylesheets/application.scss
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后我们需要&lt;code&gt;import&lt;/code&gt;刚才创建的&lt;code&gt;scss&lt;/code&gt;文件，让&lt;code&gt;Webpacker&lt;/code&gt;帮我们编译&lt;code&gt;scss&lt;/code&gt;文件&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/javascript/packs/application.js&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../stylesheets/application&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在页面中引入&lt;code&gt;Webpacker&lt;/code&gt;帮我们编译的&lt;code&gt;application.js&lt;/code&gt;和&lt;code&gt;application.css&lt;/code&gt;文件&lt;/p&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;# app/views/layouts/application.html.erb

&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;stylesheet_pack_tag&lt;/span&gt; &lt;span class="s1"&gt;'application'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;media: &lt;/span&gt;&lt;span class="s1"&gt;'all'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'data-turbolinks-track'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'reload'&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;javascript_pack_tag&lt;/span&gt; &lt;span class="s1"&gt;'application'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'data-turbolinks-track'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'reload'&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用&lt;code&gt;yarn&lt;/code&gt;安装&lt;code&gt;materialize-css&lt;/code&gt;，&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add materialize-css
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们可以在&lt;code&gt;node_modules/materialize-css&lt;/code&gt;下面找到安装好的文件
下面我们要把&lt;code&gt;materialize.js&lt;/code&gt;加入到&lt;code&gt;application.js&lt;/code&gt;中&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/javascript/packs/application.js&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;materialize-css/dist/js/materialize&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后到&lt;code&gt;application.scss&lt;/code&gt;中引入&lt;code&gt;materialize.css&lt;/code&gt;&lt;/p&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/javascript/stylesheets/application.scss&lt;/span&gt;

&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="nt"&gt;materialize-css&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;dist&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;css&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;materialize&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最后我们还需要在&lt;code&gt;application.html.erb&lt;/code&gt;中加入&lt;code&gt;Materialize&lt;/code&gt;依赖的字体和图标&lt;/p&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;// app/views/layouts/application.html.erb

&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://fonts.googleapis.com/icon?family=Material+Icons"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;安装配置到这里就完成了，接下来就是找一些 Materialize 的实例，应用到网站上吧。
更多的细节可以参考我完成的 commit： &lt;a href="https://github.com/bingxie/investment-radar/commit/b58632eb40ec2fe9e42976f5cb48f8a8e0f5987f" rel="nofollow" target="_blank" title=""&gt;Github commit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;u&gt;快速应用 Starter Template 的效果&lt;/u&gt;
&lt;img src="https://l.ruby-china.com/photo/2019/7976d927-c1c0-46b7-b34e-664a96e1f451.png!large" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>grantbb</author>
      <pubDate>Thu, 18 Apr 2019 12:33:55 +0800</pubDate>
      <link>https://ruby-china.org/topics/38419</link>
      <guid>https://ruby-china.org/topics/38419</guid>
    </item>
    <item>
      <title>2019 RubyConf AU 精彩内容回顾</title>
      <description>&lt;h2 id="大会总结"&gt;大会总结&lt;/h2&gt;
&lt;p&gt;两天的 2019 年澳洲的 Ruby 技术大会顺利结束了，这次大会组织的还是很不错的，现场的秩序和投影和音效都不错，不足的地方就是由于场地选在比较老的 Forum Melbourne，里面的座椅不是特别舒服。&lt;/p&gt;

&lt;p&gt;本次大会的内容还是很丰富的，很多演讲者都是来自英国，美国。除了 Ruby 相关的技术主题，还有不少人文关怀的内容，比如 ADD（注意障碍），情绪管理和全球变暖的话题。&lt;/p&gt;

&lt;p&gt;下面我来推荐几个我觉得非常不错的分享：&lt;/p&gt;
&lt;h3 id="The Case Of The Missing Method - A Ruby Mystery Story"&gt;The Case Of The Missing Method - A Ruby Mystery Story&lt;/h3&gt;
&lt;p&gt;Nadia Odunayo 通过一个非常好的故事解释了 Ruby 中的 singleton class 的秘密，以及讲解了 singleton class 在 DSL 的实际应用，Nadia 的演讲节奏控制的非常好，声音非常清楚。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/aRkHqYDi_wQ" rel="nofollow" target="_blank" title=""&gt;点击观看&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="How to hijack, proxy and smuggle sockets with Rack/Ruby"&gt;How to hijack, proxy and smuggle sockets with Rack/Ruby&lt;/h3&gt;
&lt;p&gt;Dávid Halász 通过一个非常具体和实际的例子，演示了 Ruby 中的 Socket 编程相关的技术。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/dU7192V1mLI" rel="nofollow" target="_blank" title=""&gt;点击观看&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="Taming Monoliths Without Microservices"&gt;Taming Monoliths Without Microservices&lt;/h3&gt;
&lt;p&gt;Kelly Sutton 在演讲中再次强调了如果要对 Monolithic Rails 应用进行拆分的话，重要的还是要把 Domain 划分清楚，以及如何恰当得处理 Domain 的界限和依赖，着重强调了不要有相互的依赖，依赖最好就是单向的。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/-ovkkvvTiRM" rel="nofollow" target="_blank" title=""&gt;点击观看&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="Representations Count"&gt;Representations Count&lt;/h3&gt;
&lt;p&gt;Tom Stuart, 来自英国，Understanding Computation 书的作者。他有着一口典型的英式口音。一听到这样的口音就让我想到了 IT Crowd. Tom 演示了通过不同的抽象表达，会对写代码解决问题产生巨大的影响。他用直观的图形解释了如何优雅的实现一个带负号的四则运算的实现。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/ej-W956YQN0" rel="nofollow" target="_blank" title=""&gt;点击观看&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="A Branch in Time (a story about revision histories)"&gt;A Branch in Time (a story about revision histories)&lt;/h3&gt;
&lt;p&gt;Tekin Süleyman 也是通过一个生动的故事讲解了写好 git commit 信息的重要性，视频中还会学到几个新的 git 命令可以帮助你快速查找历史，以及展示了一些写好 git commit 的最佳实践。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/1NoNTqank_U" rel="nofollow" target="_blank" title=""&gt;点击观看&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="Views, from the top"&gt;Views, from the top&lt;/h3&gt;
&lt;p&gt;Tim Riley 系统的介绍了 dry-rb view 的主要功能和使用方法。演示了如何通过 dry-rb 来写一个更加 OO 的 view 层的代码。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/VGWt1OLFzdU" rel="nofollow" target="_blank" title=""&gt;点击观看&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="Learn to make the point: data visualisation strategy"&gt;Learn to make the point: data visualisation strategy&lt;/h3&gt;
&lt;p&gt;Mila Dymnikova 解释了数据可视化的重要作用，即使对于开发人员，在进行工作总结和汇报的时候也是非常重要的。她给出很具体的实现方法和一些好用的工具和服务。她的 PPT 做的非常生动。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/4OkoJxR3jdM" rel="nofollow" target="_blank" title=""&gt;点击观看&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="Building APIs you want to hug with GraphQL"&gt;Building APIs you want to hug with GraphQL&lt;/h3&gt;
&lt;p&gt;Tom Ridge 介绍了如何设计和实现一个好的 GraphQL APIs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/lyJebJuG_sk" rel="nofollow" target="_blank" title=""&gt;点击观看&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="What the hell is a JRuby?"&gt;What the hell is a JRuby?&lt;/h3&gt;
&lt;p&gt;Tom Gamon 非常简洁清楚得总结了几种不同的 Ruby 的实现，重点介绍了 JRuby 的实现以及在什么情况下我们可能会考虑在产品环境使用 JRuby。视频里面提到的两本书《Working with ruby threads》《Ruby Under a Microscope》确实都是非常不错的。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/qJqC6xm-2PI" rel="nofollow" target="_blank" title=""&gt;点击观看&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="It's Down! Simulating Incidents in Production"&gt;It's Down! Simulating Incidents in Production&lt;/h3&gt;
&lt;p&gt;Kelsey Pedersen 通过一个具体的实例，讲解了如何通过 Feature Flag 模拟产品环境的故障，然后如何分析故障以及完善相关的工具和流程。非常具有实战意义。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/NYZdambrG_I" rel="nofollow" target="_blank" title=""&gt;点击观看&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="Mechanically Confident"&gt;Mechanically Confident&lt;/h3&gt;
&lt;p&gt;Adam Cuppy 演员出身的他有丰富的舞台经验，声音非常洪亮，非常能够调动现场的气氛，他总结了如何通过一些实践来建立好的 routine，从而不断取得进步然后建立自信。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/Btsx9YzhJVM" rel="nofollow" target="_blank" title=""&gt;点击观看&lt;/a&gt;&lt;/p&gt;
&lt;h4 id="What were they thinking?"&gt;What were they thinking?"&lt;/h4&gt;
&lt;p&gt;Keith Pitty 凭借非常多年的经验，总结了软件开发方面的一些思考。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/KiGbAbjYAjI" rel="nofollow" target="_blank" title=""&gt;点击观看&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;更多视频请到 Ruby Australia 的 Youtube 官网查看！&lt;/p&gt;</description>
      <author>grantbb</author>
      <pubDate>Sat, 09 Feb 2019 08:06:29 +0800</pubDate>
      <link>https://ruby-china.org/topics/38092</link>
      <guid>https://ruby-china.org/topics/38092</guid>
    </item>
    <item>
      <title>使用 Rails Migration 转换 MySQL 数据库和表的字符集总结</title>
      <description>&lt;h2 id="MySQL Character Set基础知识"&gt;MySQL Character Set 基础知识&lt;/h2&gt;
&lt;p&gt;对于 MySQL 数据库你可以在不同的 Level 设置 Character Set 和 Collation，包括：Server Level，Database Level，Table Level，Column Level 还有 Application Level.&lt;/p&gt;
&lt;h3 id="Server Level:"&gt;Server Level:&lt;/h3&gt;
&lt;p&gt;可以通过命令行设置，也可以通过配置文件设置&lt;/p&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="err"&gt;默认：&lt;/span&gt; &lt;span class="c1"&gt;--character-set-server=latin1   &lt;/span&gt;

&lt;span class="n"&gt;latin1_swedish_ci&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;collation&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;latin1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;还可以通过重新编译时指定参数实现：&lt;code&gt;use the DEFAULT_CHARSET and DEFAULT_COLLATION&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;作用范围：如果创建数据库时不指定，那么就使用 Server Level 的设置&lt;/p&gt;

&lt;p&gt;查看当前的设定，可以查看系统变量：&lt;code&gt;character_set_server and collation_server&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="Database Level:"&gt;Database Level:&lt;/h3&gt;
&lt;p&gt;可以在创建数据库时设置：&lt;/p&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;db_name&lt;/span&gt; &lt;span class="nb"&gt;CHARACTER&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;latin1&lt;/span&gt; &lt;span class="k"&gt;COLLATE&lt;/span&gt; &lt;span class="n"&gt;latin1_swedish_ci&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;默认值：可以由 &lt;code&gt;character_set_database&lt;/code&gt; and &lt;code&gt;collation_database&lt;/code&gt; 系统变量决定。&lt;/p&gt;

&lt;p&gt;可以通过下面的命令查看当前的设置：&lt;/p&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;USE&lt;/span&gt; &lt;span class="n"&gt;db_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;@@&lt;/span&gt;&lt;span class="n"&gt;character_set_database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@@&lt;/span&gt;&lt;span class="n"&gt;collation_database&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 sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;DEFAULT_CHARACTER_SET_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DEFAULT_COLLATION_NAME&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;INFORMATION_SCHEMA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SCHEMATA&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;SCHEMA_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'db_name'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;作用：如果建表时没有指定，那么会作为表的默认值，同时也是作为 LOAD DATA 的默认值&lt;/p&gt;
&lt;h4 id="修改数据库level 的 character set 和 collation"&gt;修改数据库 level 的 character set 和 collation&lt;/h4&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;db_name&lt;/span&gt; &lt;span class="nb"&gt;CHARACTER&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;utf8&lt;/span&gt; &lt;span class="k"&gt;COLLATE&lt;/span&gt; &lt;span class="n"&gt;utf8_unicode_ci&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="err"&gt;或者单独修改&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;my_database&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;COLLATE&lt;/span&gt; &lt;span class="n"&gt;utf8_unicode_ci&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;my_database&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="nb"&gt;CHARACTER&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;utf8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="Table Level:"&gt;Table Level:&lt;/h3&gt;
&lt;p&gt;可以在建表的语句中进行设置&lt;/p&gt;

&lt;p&gt;作用：如果字段没有具体制定，那么会作为字段的默认值
note：该功能是 mysql 的一个扩展，不是标准的 SQL&lt;/p&gt;

&lt;p&gt;使用下面语句可以同时修改 table 和 table 中字段的设置&lt;/p&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;tbl_name&lt;/span&gt; &lt;span class="k"&gt;CONVERT&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="nb"&gt;CHARACTER&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;charset_name&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;COLLATE&lt;/span&gt; &lt;span class="k"&gt;collation_name&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 sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;STATUS&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;db_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="Column Level:"&gt;Column Level:&lt;/h3&gt;
&lt;p&gt;N/A&lt;/p&gt;
&lt;h3 id="Application Connection Level:"&gt;Application Connection Level:&lt;/h3&gt;
&lt;p&gt;对于 Rails 应用，在 database.yml 的数据库连接设置中加上 &lt;code&gt;?reconnect=true&amp;amp;encoding=utf8&amp;amp;collation=utf8_unicode_ci&lt;/code&gt;&lt;/p&gt;
&lt;h4 id="查看数据库不同级别的元数据的设置的语句，比如：character set"&gt;查看数据库不同级别的元数据的设置的语句，比如：character set&lt;/h4&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;DEFAULT_COLLATION_NAME&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;information_schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SCHEMATA&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;schema_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'db_name'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;DEFAULT_COLLATION_NAME&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;'utf8_unicode_ci'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;TABLE_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TABLE_COLLATION&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;information_schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TABLES&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;table_schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'db_name'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;table_collation&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;'utf8_unicode_ci'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;information_schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;COLUMNS&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;table_schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'db_name'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="k"&gt;collation_name&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;'utf8_unicode_ci'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="ConvertDatabaseCharacterSetAndCollationToUtf8 Migration"&gt;ConvertDatabaseCharacterSetAndCollationToUtf8 Migration&lt;/h3&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ConvertDatabaseCharacterSetAndCollationToUtf8&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Migration&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;up&lt;/span&gt;
    &lt;span class="n"&gt;execute&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;~&lt;/span&gt;&lt;span class="no"&gt;SQL&lt;/span&gt;&lt;span class="sh"&gt;
      ALTER DATABASE &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current_database&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt; CHARACTER SET utf8 COLLATE utf8_unicode_ci;
&lt;/span&gt;&lt;span class="no"&gt;    SQL&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;down&lt;/span&gt;
    &lt;span class="n"&gt;execute&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;~&lt;/span&gt;&lt;span class="no"&gt;SQL&lt;/span&gt;&lt;span class="sh"&gt;
      ALTER DATABASE &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current_database&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt; CHARACTER SET latin1 COLLATE latin1_swedish_ci;
&lt;/span&gt;&lt;span class="no"&gt;    SQL&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="ConvertTablesCharacterSetAndCollationToUtf8 Migration"&gt;ConvertTablesCharacterSetAndCollationToUtf8 Migration&lt;/h3&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ConvertTablesCharacterSetAndCollationToUtf8&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Migration&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;up&lt;/span&gt;
    &lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"SET foreign_key_checks = 0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;latin_tables_sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;~&lt;/span&gt;&lt;span class="no"&gt;SQL&lt;/span&gt;&lt;span class="sh"&gt;
      SELECT TABLE_NAME, TABLE_COLLATION
      FROM information_schema.TABLES
      WHERE table_schema = '&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current_database&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'
      AND table_collation != 'utf8_unicode_ci';
&lt;/span&gt;&lt;span class="no"&gt;    SQL&lt;/span&gt;

    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;latin_tables_sql&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;say&lt;/span&gt; &lt;span class="s2"&gt;"Total: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="n"&gt;results&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;result&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;alter_table_sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ALTER TABLE &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;"&lt;/span&gt;
      &lt;span class="n"&gt;execute&lt;/span&gt; &lt;span class="n"&gt;alter_table_sql&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"SET foreign_key_checks = 1"&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;h3 id="Rails Migration tips"&gt;Rails Migration tips&lt;/h3&gt;
&lt;p&gt;在调试该功能的时候，学到的一些 tips：&lt;/p&gt;

&lt;p&gt;&lt;code&gt;rake db:migrate:status&lt;/code&gt;  查看当前 migration 的状态，包括版本信息等&lt;/p&gt;

&lt;p&gt;&lt;code&gt;rake db:migrate VERSION=33333333&lt;/code&gt; migrate 指定 version&lt;/p&gt;

&lt;p&gt;&lt;code&gt;rake db:rollback STEP=n&lt;/code&gt; 通过 STEP 参数指定回滚的范围&lt;/p&gt;

&lt;p&gt;&lt;code&gt;User.connection&lt;/code&gt; 可以用来检查当前数据库设置和连接的信息&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ActiveRecord::Base.connection.current_database&lt;/code&gt; 获取当前连接的数据库&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ActiveRecord::Migrator.current_version&lt;/code&gt;  查看当前的版本&lt;/p&gt;

&lt;p&gt;在 Rails Console 或者 Runner 中执行 SQL 语句，可以使用 &lt;code&gt;ActiveRecord::Migration.execute("SQL")&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;参考文章： 
    &lt;a href="https://confluence.atlassian.com/jirakb/how-to-change-all-columns-and-tables-collation-to-utf8_bin-in-mysql-601456761.html" rel="nofollow" target="_blank" title=""&gt;How to change all columns' and tables' collation to 'utf8_bin' in MySQL&lt;/a&gt;&lt;/p&gt;</description>
      <author>grantbb</author>
      <pubDate>Wed, 20 Sep 2017 13:02:46 +0800</pubDate>
      <link>https://ruby-china.org/topics/34197</link>
      <guid>https://ruby-china.org/topics/34197</guid>
    </item>
    <item>
      <title>ActiveRecord Enum 实战总结</title>
      <description>&lt;h2 id="基本使用方法"&gt;基本使用方法&lt;/h2&gt;
&lt;p&gt;案例说明：给已经存在的 Company 增加一个 size 属性，属性包括 large, medium, small 三个选项&lt;/p&gt;

&lt;p&gt;从 Rails4.1 开始，可以通过 &lt;code&gt;ActiveRecord::Enum&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;Company&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="n"&gt;enum&lt;/span&gt; &lt;span class="ss"&gt;size: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:large&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:medium&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:small&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;enum&lt;/code&gt; 后，Rails 会自动产生下面这些方法&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sizes&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; {"large"=&amp;gt;0, "medium"=&amp;gt;1, "small"=&amp;gt;2}&lt;/span&gt;

&lt;span class="c1"&gt;# Scope methods&lt;/span&gt;
&lt;span class="no"&gt;Company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;large&lt;/span&gt;
&lt;span class="no"&gt;Company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;medium&lt;/span&gt;
&lt;span class="no"&gt;Company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;small&lt;/span&gt;

&lt;span class="c1"&gt;# Query methods&lt;/span&gt;
&lt;span class="n"&gt;company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;large?&lt;/span&gt;
&lt;span class="n"&gt;company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;medium?&lt;/span&gt;

&lt;span class="c1"&gt;# Action methods&lt;/span&gt;
&lt;span class="n"&gt;company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;large!&lt;/span&gt;
&lt;span class="n"&gt;company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;small!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="Migration"&gt;Migration&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;enum&lt;/code&gt; 的实现是基于该字段是一个 integer 类型的。所以添加这个新的字段我们需要下面的 migration&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;AddSizeToCompanies&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Migration&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;change&lt;/span&gt;
    &lt;span class="n"&gt;add_column&lt;/span&gt; &lt;span class="ss"&gt;:companies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;null: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;default: &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;add_index&lt;/span&gt;  &lt;span class="ss"&gt;:companies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:size&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="赋值操作"&gt;赋值操作&lt;/h2&gt;
&lt;p&gt;通过上面 Company.sizes 方法，我们看到 Rails 默认是从 0 开始对应的。所以上面的 migration 中默认值是 0。当新建一个 Company 时，默认 company 的 size 是 large&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;company&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Company&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;company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; "large"&lt;/span&gt;

&lt;span class="c1"&gt;# 多种赋值操作&lt;/span&gt;
&lt;span class="n"&gt;company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:medium&lt;/span&gt;
&lt;span class="n"&gt;company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;medium?&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; true&lt;/span&gt;

&lt;span class="n"&gt;company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="n"&gt;company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;small?&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; true&lt;/span&gt;

&lt;span class="n"&gt;company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;large&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'large'&lt;/span&gt;
&lt;span class="n"&gt;company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;large?&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;enum&lt;/code&gt; 会自动添加一些验证，如果给 size 属性赋错误的值，Rails 会抛出异常&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; ArgumentError: 5 is not a valid billing_category&lt;/span&gt;
&lt;span class="n"&gt;company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:bala&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; ArgumentError: 'bala' is not a valid billing_category&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="Form下拉菜单的填充"&gt;Form 下拉菜单的填充&lt;/h2&gt;
&lt;p&gt;当在前端的 Form 中填充到下拉列表中，可以这样做&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt; &lt;span class="ss"&gt;:size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;collection: &lt;/span&gt;&lt;span class="no"&gt;Company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sizes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="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="nf"&gt;titleize&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="ss"&gt;prompt: &lt;/span&gt;&lt;span class="s2"&gt;"Select a size"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="需要注意的地方:"&gt;需要注意的地方：&lt;/h2&gt;
&lt;p&gt;由于存到数据库的仍然是 number，所以如果有别的应用也使用同样的数据库，那么该应用需要知道对应关系。&lt;/p&gt;

&lt;p&gt;Rails4.1 及之后的版本中，由于 &lt;code&gt;enum&lt;/code&gt; 会自动产生一些方法，所以要特别注意选项的命名问题，尽量用明确的命名。另外如果你还需要不同的属性，拥有相同的选项，那么你可以考虑这个 gem &lt;a href="https://rubygems.org/gems/activerecord-enum-without-methods/versions/1.0.0" rel="nofollow" target="_blank" title=""&gt;activerecord-enum-without-methods&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;同时在 Rails5 中，就可以&lt;a href="https://github.com/rails/rails/pull/19813" rel="nofollow" target="_blank" title=""&gt;使用&lt;code&gt;_prefix&lt;/code&gt;和&lt;code&gt;_postfix&lt;/code&gt;选项&lt;/a&gt;来避免相应的问题。·&lt;/p&gt;
&lt;h2 id="更好的Migration方法"&gt;更好的 Migration 方法&lt;/h2&gt;
&lt;p&gt;上面的 migration 方法对于产品环境已经有数据的情况下，可能会产生问题。&lt;/p&gt;

&lt;p&gt;对于 Mysql 数据库，新加字段的时候对于已有的记录，mysql 会自动设置 &lt;code&gt;NOT NULL&lt;/code&gt; 的已有记录为 default
但是对于 postgreSQL 就会出现下面的错误：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;PG::NotNullViolation: ERROR: column "size" contains null values&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这时可以先&lt;code&gt;add_column&lt;/code&gt;添加字段，不要加 NOT NULL 的限制，然后更新已有数据，然后再通过&lt;code&gt;change_column_null&lt;/code&gt;来添加 &lt;code&gt;NOT NULL&lt;/code&gt; 限制。&lt;/p&gt;</description>
      <author>grantbb</author>
      <pubDate>Tue, 07 Mar 2017 13:58:13 +0800</pubDate>
      <link>https://ruby-china.org/topics/32470</link>
      <guid>https://ruby-china.org/topics/32470</guid>
    </item>
    <item>
      <title>找工作分享： 顺利通过 Code Test 的 Checklist</title>
      <description>&lt;p&gt;最近在找新的工作，澳洲这里几乎所有招 Ruby 工程师的公司都会要求你先做一个 Code Test。就是给你一个需求，用代码实现。
Code Test 通过后，才会进入面对面的面试，这其中还会有&lt;code&gt;Pair Programming&lt;/code&gt;，所以一定要自己好好实践。下面是我总结的一些经验：&lt;/p&gt;
&lt;h3 id="需求"&gt;需求&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;要很好的理解需求，不要忽略一些细节&lt;/li&gt;
&lt;li&gt;不要忘记一些非功能性的需求（比如要提供 github 地址，或者发送 zip 包）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="面向对象设计"&gt;面向对象设计&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;要符合一些基本的 OOD 的原则，比如&lt;code&gt;Single Responsibility Principle&lt;/code&gt;, &lt;code&gt;Open Close Principle&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;充分利用语言的特性，设计好类，接口以及抽象和继承的层次&lt;/li&gt;
&lt;li&gt;使用常用的设计模式：&lt;code&gt;Factory Pattern&lt;/code&gt;，&lt;code&gt;Template Pattern&lt;/code&gt;，&lt;code&gt;Observer Pattern&lt;/code&gt;，&lt;code&gt;Command Pattern&lt;/code&gt;，&lt;code&gt;Null Ojbect&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;设想一两个需求变更的场景，验证自己的代码能够很容易的扩展&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="系统和架构的设计"&gt;系统和架构的设计&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;尽量采用 MVC 分层架构模式，有&lt;code&gt;Data Model&lt;/code&gt;，&lt;code&gt;Business Model&lt;/code&gt;，&lt;code&gt;Controller&lt;/code&gt;， &lt;code&gt;Display/Runner Class&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="异常的处理"&gt;异常的处理&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;自定义异常，然后做好异常的处理&lt;/li&gt;
&lt;li&gt;提供友好的错误的提示&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="Test，Test, Test"&gt;Test, Test, Test&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;尽可能采用 TDD，有清楚的 commits，体现代码的更新过程&lt;/li&gt;
&lt;li&gt;每个测试尽量保证一个 assertion&lt;/li&gt;
&lt;li&gt;保证足够高的测试覆盖率，100% 是可以有的，可以使用&lt;code&gt;simplecov&lt;/code&gt;来帮助&lt;/li&gt;
&lt;li&gt;要有集成测试，有正常的流程测试和非正常的流程测试&lt;/li&gt;
&lt;li&gt;一定要测试好各种 edge cases&lt;/li&gt;
&lt;li&gt;提供易用的&lt;code&gt;rake&lt;/code&gt; tasks，方便运行相关的测试，比如单元测试，集成测试等&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="良好的接口设计"&gt;良好的接口设计&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;非常容易安装和执行，减少需要的依赖（比如尽量少的 gems，Ruby 版本）&lt;/li&gt;
&lt;li&gt;有很好的命令行接口和帮助说明&lt;/li&gt;
&lt;li&gt;要有一份&lt;code&gt;README&lt;/code&gt;，说明如何安装和使用&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="注意代码质量和规范："&gt;注意代码质量和规范：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;按照产品级别的代码要求自己&lt;/li&gt;
&lt;li&gt;采用&lt;code&gt;Rubocop&lt;/code&gt;进行质量检查&lt;/li&gt;
&lt;li&gt;不要遗留任何的&lt;code&gt;TODO&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;方法尽可能短小，不要超过 5 行&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;最后祝所有找工作的朋友好运！&lt;/p&gt;</description>
      <author>grantbb</author>
      <pubDate>Mon, 17 Oct 2016 15:36:12 +0800</pubDate>
      <link>https://ruby-china.org/topics/31353</link>
      <guid>https://ruby-china.org/topics/31353</guid>
    </item>
    <item>
      <title>干货分享：通过 Spree 开源代码学 Ruby 和 Rails</title>
      <description>&lt;p&gt;最近在做&lt;code&gt;Spree&lt;/code&gt;的定制开发，翻看其中的代码，发现其中有下面这么一段代码信息量是非常的大。所以就想通过这一小段代码来好好学习一下相关的 Ruby 知识。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# spree/api/lib/spree/api/engine.rb&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;activate&lt;/span&gt;
  &lt;span class="no"&gt;Dir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;glob&lt;/span&gt;&lt;span class="p"&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;join&lt;/span&gt;&lt;span class="p"&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;dirname&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="s2"&gt;"../../../app/**/*_decorator*.rb"&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;c&lt;/span&gt;&lt;span class="o"&gt;|&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;configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cache_classes&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&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;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_prepare&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:activate&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_proc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;就是这样的一段代码却包含了大量的 Ruby 和 Rails 的知识。其中最后一行&lt;code&gt;config.to_prepare &amp;amp;method(:activate).to_proc &lt;/code&gt;是本文重点解释的对象，接下来我们就一个一个来学习。&lt;/p&gt;
&lt;h4 id="self.active"&gt;self.active&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;self.activate&lt;/code&gt;方法其实不难理解，这是一个类方法，会在引入 Spree 的应用中查找&lt;code&gt;app&lt;/code&gt;目录下面文件名中带有&lt;code&gt;_decorator&lt;/code&gt;的文件，这些文件都是用来定制扩展 Spree 功能的文件。在这里，找到后会判断是否设置了&lt;code&gt;Rails.configuration.cache_classes&lt;/code&gt;这个选项，一般在开发环境中，该选项是 false，这样当你修改代码后 Rails 会自动 reload 相关的文件。而在产品环境中，该选项为 true，因为我们不会在产品环境修改代码让 Rails 自动 reload。&lt;/p&gt;

&lt;p&gt;后面&lt;code&gt;require&lt;/code&gt;和&lt;code&gt;load&lt;/code&gt;的最重要的区别就是，&lt;code&gt;require&lt;/code&gt;如果发现该文件已经加载过后就不会重新载入，而&lt;code&gt;load&lt;/code&gt;不管之前是否已经载入都会加载该文件。&lt;/p&gt;

&lt;p&gt;重点来了，让我们看看下面这行信息量巨大的代码吧。一眼就看明白的同学，请自行绕过！&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_prepare&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:activate&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_proc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="method(:activate)"&gt;method(:activate)&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;method&lt;/code&gt;方法来自于&lt;code&gt;Object&lt;/code&gt;，会在当前的实例 (上面的例子就是 self) 上查找通过参数指定的方法。找到后返回一个&lt;code&gt;Method&lt;/code&gt;的实例，这个 Method 的对象就是一个封装了方法所属的对象以及该对象的实例变量的闭包。&lt;/p&gt;
&lt;h4 id="method(:activate).to_proc"&gt;method(:activate).to_proc&lt;/h4&gt;
&lt;p&gt;接下来就是调用&lt;code&gt;Method&lt;/code&gt;对象的&lt;code&gt;to_proc&lt;/code&gt;方法，产生一个对应的&lt;code&gt;Proc&lt;/code&gt;对象。&lt;/p&gt;
&lt;h4 id="&amp;amp;method(:activate).to_proc"&gt;&amp;amp;method(:activate).to_proc&lt;/h4&gt;
&lt;p&gt;在&lt;code&gt;Proc&lt;/code&gt;对象前面加上&lt;code&gt;&amp;amp;&lt;/code&gt;符号，作为参数传递，相当于给方法传递了一个&lt;code&gt;block&lt;/code&gt;，然后方法内部通过&lt;code&gt;yield&lt;/code&gt;调用&lt;code&gt;block&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;to_prepare&lt;/span&gt;
  &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;block_given?&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'Done prepare!'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;to_prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;相反，如果在接收的&lt;code&gt;block&lt;/code&gt;参数前面加上&lt;code&gt;&amp;amp;&lt;/code&gt;符号，那就相当于给方法传递了一个&lt;code&gt;Proc&lt;/code&gt;的对象，然后方法内部通过&lt;code&gt;proc.call&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;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'done config!'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'I am block!'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="config.to_prepare"&gt;config.to_prepare&lt;/h4&gt;
&lt;p&gt;这是来自 Rails 配置里面功能，该配置是全局性的，会在所有的 initializers 运行之后运行。很重要的一点是，该配置中的代码，在 production 和 test 环境中默认只会执行一次，而在开发环境 dev 中，会在每次修改文件，重新发出请求时，Rails 完成 reload，在实际进行请求处理之前执行这段代码。（好绕的流程）&lt;/p&gt;

&lt;p&gt;所以，你会看到上面的代码就是来重新加载一些定制 Spree 功能的代码。&lt;/p&gt;

&lt;p&gt;在 Rails 中你还可以手动调用&lt;code&gt;ActionDispatch::Reloader.to_prepare&lt;/code&gt;来实现同样地功能。&lt;/p&gt;

&lt;p&gt;另外还有一个与之对应的&lt;code&gt;ActionDispatch::Reloader.to_cleanup&lt;/code&gt;,区别是该&lt;code&gt;callback&lt;/code&gt;会在请求处理完成后执行。&lt;/p&gt;

&lt;p&gt;上面提到开发环境的不同，主要是受到&lt;code&gt;config.cache_classes&lt;/code&gt;配置的控制。开发环境 dev 下，该属性为&lt;code&gt;false&lt;/code&gt;，所以 Rails 会发现有文件修改后，自动 realod。&lt;/p&gt;
&lt;h4 id="实例"&gt;实例&lt;/h4&gt;
&lt;p&gt;介绍完这些基本的知识点后，你可能会觉得还是有点糊涂，为什么 Spree 中要这么做呢？&lt;/p&gt;

&lt;p&gt;这种做法其实在 Ruby 的世界是一种常见的用法，主要就是利用闭包的特点，把相关的逻辑更好的组织和封装。关于这一点上面 Spree 的例子不是很完美。&lt;/p&gt;

&lt;p&gt;下面有给出一段利用闭包中封装的实例变量的例子。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MethodTest&lt;/span&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="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:root_path&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;activate&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="vi"&gt;@root_path&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;helper&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;helper&lt;/span&gt;
      &lt;span class="s2"&gt;"I am helper method. &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@root_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;to_prepare&lt;/span&gt;
  &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;block_given?&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'Done prepare!'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;MethodTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;root_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'/root'&lt;/span&gt;
&lt;span class="n"&gt;to_prepare&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;MethodTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:activate&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_proc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;看完后，有学到新东西吗？Yes！那就点个💖呗。谢谢&lt;/p&gt;</description>
      <author>grantbb</author>
      <pubDate>Thu, 29 Sep 2016 08:38:39 +0800</pubDate>
      <link>https://ruby-china.org/topics/31191</link>
      <guid>https://ruby-china.org/topics/31191</guid>
    </item>
    <item>
      <title>理解 Rails 5 中 Controller 和 Integration 测试</title>
      <description>&lt;p&gt;这篇文章首先介绍 Rails5 中 controller 测试的变化，然后通过类图来分析 Rails5 中的&lt;code&gt;IntegrationTest&lt;/code&gt;相关的类组织结构。通过理解相关的类和模块的关系来帮助我们写出更好的测试。&lt;/p&gt;

&lt;p&gt;在之前的文章&lt;a href="http://www.bigbing.net/2015/11/23/rails4-minitest-capybara-devise/" rel="nofollow" target="_blank" title=""&gt;minitest + capybara 测试基于 devise 的用户注册&lt;/a&gt;中，也是通过创建继承自&lt;code&gt;ActionDispatch::IntegrationTest&lt;/code&gt;的&lt;code&gt;FeatureTest&lt;/code&gt;类，然后引入 Capybara 的 DSL 模块，来方便我们创建其他 Feature Test 或者叫 User Acceptance Test。&lt;/p&gt;
&lt;h2 id="第一部分：Rails5中controller测试的变化"&gt;第一部分：Rails5 中 controller 测试的变化&lt;/h2&gt;&lt;h3 id="1. ActionController::TestCase废弃掉了"&gt;1. ActionController::TestCase 废弃掉了&lt;/h3&gt;
&lt;p&gt;在 Rails5 中 Controller 测试都是继承自&lt;code&gt;ActionDispatch::IntegrationTest&lt;/code&gt;类，而不是之前的&lt;code&gt;ActionController::TestCase&lt;/code&gt;。如果还想继续使用，那么可以使用这个 gem: &lt;a href="https://github.com/rails/rails-controller-testing" rel="nofollow" target="_blank" title=""&gt;rails-controller-testing&lt;/a&gt;。&lt;/p&gt;
&lt;h3 id="2. assigns和assert_template也废弃掉了"&gt;2. assigns 和 assert_template 也废弃掉了&lt;/h3&gt;
&lt;p&gt;在 Rails4 的 controller 测试中&lt;code&gt;assigns&lt;/code&gt;方法用于获得 action 中的实例变量然后进行验证，&lt;code&gt;assert_template&lt;/code&gt;用于验证 action 最后渲染了指定的 template。&lt;/p&gt;

&lt;p&gt;在 Rails5 中，controller 测试强调的更多的是 action 的处理结果，比如响应的状态和响应的结果。&lt;/p&gt;

&lt;p&gt;如果你还是想使用上面这两个方法，还是可以在&lt;a href="https://github.com/rails/rails-controller-testing" rel="nofollow" target="_blank" title=""&gt;rails-controller-testing&lt;/a&gt;这个 gem 中找到他们。&lt;/p&gt;
&lt;h3 id="3. 移走assert_select等方法"&gt;3. 移走 assert_select 等方法&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;assert_select&lt;/code&gt;等验证响应的 HTML 内容的方法，已经移到单独的&lt;a href="https://github.com/rails/rails-dom-testing" rel="nofollow" target="_blank" title=""&gt;rails-dom-testing&lt;/a&gt;这个 gem 中。&lt;/p&gt;

&lt;p&gt;所以，如果是我要验证页面的内容和样式等，还是通过 capybara 来进行精确的操作和验证。&lt;/p&gt;
&lt;h3 id="4. 使用URL而不是Action来发送请求"&gt;4. 使用 URL 而不是 Action 来发送请求&lt;/h3&gt;
&lt;p&gt;在 Rails4 中，通过 action 的名字来发送请求（说实话我一直很不习惯）&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;PicturesControllerTest&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TestCase&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_index_response&lt;/span&gt;
    &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="ss"&gt;:index&lt;/span&gt;
    &lt;span class="n"&gt;assert_response&lt;/span&gt; &lt;span class="ss"&gt;:success&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而在 Rails5 中，要换成 URL（多直观），否则就会抛出异常：&lt;code&gt;URI::InvalidURIError: bad URI&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;PicturesControllerTest&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionDispatch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;IntegrationTest&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_index&lt;/span&gt;
    &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="n"&gt;pictures_url&lt;/span&gt;
    &lt;span class="n"&gt;assert_response&lt;/span&gt; &lt;span class="ss"&gt;:success&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="5. HTTP的请求方法中必须使用关键字参数"&gt;5. HTTP 的请求方法中必须使用关键字参数&lt;/h3&gt;
&lt;p&gt;在 Rails5 中，HTTP 请求的方法参数必须明确指定关键字，比如 params，flash 等。这样会让代码更加清楚。请看例子：&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;PicturesControllerTest&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionDispatch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;IntegrationTest&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_create&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="n"&gt;picture_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;params: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;picture: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s2"&gt;"sea"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;assert_response&lt;/span&gt; &lt;span class="ss"&gt;:success&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="第二部分：理解ActionDispatch::IntegrationTest类和相关module"&gt;第二部分：理解 ActionDispatch::IntegrationTest 类和相关 module&lt;/h2&gt;
&lt;p&gt;在继承了&lt;code&gt;ActionDispatch::IntegrationTest&lt;/code&gt;类的 Controller 测试中，我们可以使用很多方便的 helper 方法和大量用于结果验证的 assertions 方法。
比如跟响应相关的：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parsed_body&lt;/span&gt; &lt;span class="c1"&gt;# 解析json格式的响应结果&lt;/span&gt;
&lt;span class="n"&gt;assert_response&lt;/span&gt; &lt;span class="ss"&gt;:success&lt;/span&gt; &lt;span class="c1"&gt;# 验证成功的请求&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;还有跟路由 routing 相关的&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;assert_routing&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="ss"&gt;method: &lt;/span&gt;&lt;span class="s1"&gt;'post'&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;'/pictures'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="ss"&gt;controller: &lt;/span&gt;&lt;span class="s1"&gt;'pictures'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;action: &lt;/span&gt;&lt;span class="s1"&gt;'create'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;assert_recognizes&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="ss"&gt;controller: &lt;/span&gt;&lt;span class="s1"&gt;'pictures'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;action: &lt;/span&gt;&lt;span class="s1"&gt;'index'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你要测试文件上传功能，Rails 提供了非常方便的方法&lt;code&gt;fixture_file_upload&lt;/code&gt;。但是你会发现你无法在 controller 中直接使用，你需要引入&lt;code&gt;ActionDispatch::TestProcess&lt;/code&gt;模块。有点奇怪？&lt;/p&gt;

&lt;p&gt;所以，为了搞清楚这些 helper 方法和 assertions 的来源，也方便我们日后查询相关的文档，我会通过下面的类图来理解 IntegrationTest 这个类。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/c4dd78aeaf77f044e9e7437737f3ad2a.jpg!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h3 id="1. ActiveSupport::TestCase"&gt;1. ActiveSupport::TestCase&lt;/h3&gt;
&lt;p&gt;首先在 Rails 中我们有&lt;code&gt;ActiveSupport::TestCase&lt;/code&gt;类，它继承自&lt;code&gt;Minitest::Test&lt;/code&gt;类，然后像&lt;code&gt;ActionDispatch::IntegrationTest&lt;/code&gt;, &lt;code&gt;ActionView::TestCase&lt;/code&gt;, &lt;code&gt;ActiveJob::TestCase&lt;/code&gt;等我们自己的测试需要继承的测试基类，都继承自&lt;code&gt;ActiveSupport::TestCase&lt;/code&gt;类。同时你会发现，我们自己的 model 的测试都是直接继承自&lt;code&gt;ActiveSupport::TestCase&lt;/code&gt;类。&lt;/p&gt;

&lt;p&gt;所以在我们的测试中，可以直接使用 minitest 提供的一些 assertions，比如常见的：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;assert_equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;actual&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;assert_includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;assert_instance_of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;由于&lt;code&gt;ActiveSupport::TestCase&lt;/code&gt;引入了&lt;code&gt;ActiveSupport::Testing::Assertions&lt;/code&gt;模块，所以我们可以使用非常方便的方法&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;assert_difference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;difference&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="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="c1"&gt;# 例如&lt;/span&gt;
&lt;span class="n"&gt;assert_difference&lt;/span&gt; &lt;span class="s1"&gt;'Article.count'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="ss"&gt;:create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;params: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;article: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;assert_no_difference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="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;/code&gt;&lt;/pre&gt;
&lt;p&gt;另外还有两个比较有用的被引入的模块是&lt;code&gt;ActiveSupport::Testing::FileFixtures&lt;/code&gt;和&lt;code&gt;ActiveSupport::Testing::TimeHelpers&lt;/code&gt;。他们分别提供了访问 fixtures 下面的文件和修改测试时间的方法&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;file_fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fixture_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;travel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;travel_back&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;travel_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date_or_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="2. ActionDispatch::IntegrationTest"&gt;2. ActionDispatch::IntegrationTest&lt;/h3&gt;
&lt;p&gt;接下来我们再来看看&lt;code&gt;IntegrationTest&lt;/code&gt;这个类，它首先通过引入&lt;code&gt;Integration::Runner&lt;/code&gt;模块，从而一起引入了&lt;code&gt;ActionDispatch::Assertions&lt;/code&gt;模块，然后 Runner 中运行测试的时候，会创建&lt;code&gt;Integration::Session&lt;/code&gt;类的实例，&lt;code&gt;Integration::Session&lt;/code&gt;引入了&lt;code&gt;Integration::RequestHelpers&lt;/code&gt;模块，所以我们就可以使用像 get, post, put 等 HTTP 请求相关的方法。
具体的这些请求相关的方法，参考文档： &lt;a href="http://api.rubyonrails.org/classes/ActionDispatch/Integration/RequestHelpers.html" rel="nofollow" target="_blank" title=""&gt;ActionDispatch::Integration::RequestHelpers&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;了解了发送请求的方法后，我们再来看看&lt;code&gt;ActionDispatch::Assertions&lt;/code&gt;模块，它只是引入了另外两个重要的 module：&lt;code&gt;ActionDispatch::Assertions::ResponseAssertions&lt;/code&gt;和 &lt;code&gt;ActionDispatch::Assertions::RoutingAssertions&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ResponseAssertions&lt;/code&gt;中提供了常用的 a&lt;code&gt;ssert_redirected_to&lt;/code&gt;和&lt;code&gt;assert_response&lt;/code&gt;方法&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;assert_redirected_to&lt;/span&gt; &lt;span class="n"&gt;login_url&lt;/span&gt;
&lt;span class="n"&gt;assert_response&lt;/span&gt; &lt;span class="ss"&gt;:redirect&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;RoutingAssertions&lt;/code&gt;中提供了上面展示过的：&lt;code&gt;assert_generates&lt;/code&gt;， &lt;code&gt;assert_recognizes&lt;/code&gt;， &lt;code&gt;assert_routing&lt;/code&gt;方法，用于进行路由 Routing 相关的测试。&lt;/p&gt;

&lt;p&gt;所以，理解了&lt;code&gt;IntegrationTest&lt;/code&gt;的结构后，我们就知道为什么我们还需要引入&lt;code&gt;ActionDispatch::TestProcess&lt;/code&gt;来测试文件上传，详细的如何测试 Carrierwave 的文件上传功能我会在下一篇文章中介绍。&lt;/p&gt;</description>
      <author>grantbb</author>
      <pubDate>Sun, 07 Aug 2016 12:44:30 +0800</pubDate>
      <link>https://ruby-china.org/topics/30758</link>
      <guid>https://ruby-china.org/topics/30758</guid>
    </item>
    <item>
      <title>Rails4 新项目的测试：minitest + capybara 测试基于 devise 的用户注册</title>
      <description>&lt;blockquote&gt;
&lt;p&gt;没有测试就会让写程序的开发者没有自信&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这次的新项目没有想当然的选择之前一直在用 Rspec 测试框架，主要是我感觉 Rspec 里面有太多的 magic，在 helper 文件中很多 tricks, 很快相关的配置文件就非常大。这篇 Blog 说的跟我的体会很像 &lt;a href="http://brandonhilkert.com/blog/7-reasons-why-im-sticking-with-minitest-and-fixtures-in-rails/" rel="nofollow" target="_blank" title=""&gt;7 REASONS I'M STICKING WITH MINITEST AND FIXTURES IN RAILS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;minitest 是 Rails4 后默认支持的测试框架，另外 rails 也提供了很多方便和强大的 assert 方法。可以参考&lt;a href="http://guides.rubyonrails.org/testing.html" rel="nofollow" target="_blank" title=""&gt;官方文档&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;下面我就总结和分享一下，Rails4 新项目中 minitest 和 capybara 的集成和配置：&lt;/p&gt;
&lt;h2 id="Gemfile"&gt;Gemfile&lt;/h2&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="ss"&gt;:test&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"minitest-reporters"&lt;/span&gt;

  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'capybara'&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"connection_pool"&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"launchy"&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"selenium-webdriver"&lt;/span&gt;

  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"mocha"&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"poltergeist"&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'phantomjs'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:require&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'phantomjs/poltergeist'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;minitest-reporters&lt;/code&gt; 是为了美化 minitest 的测试结果输出地，有颜色，有百分比。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mocha&lt;/code&gt; 是一个方便进行 mock 和 stub 的工具&lt;/li&gt;
&lt;li&gt;其他几个都是跟 capybara 相关的工具，具体会在配置中看到。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="test_helper中添加的配置内容"&gt;test_helper 中添加的配置内容&lt;/h2&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"mocha/mini_test"&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"capybara/rails"&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"capybara/poltergeist"&lt;/span&gt;

&lt;span class="c1"&gt;# 注释掉默认的driver，打开后方便切换driver测试，也可以在代码中指定current_driver&lt;/span&gt;
&lt;span class="c1"&gt;# Capybara.default_driver = :selenium&lt;/span&gt;
&lt;span class="c1"&gt;# Capybara.default_driver = :poltergeist&lt;/span&gt;

&lt;span class="no"&gt;Capybara&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;javascript_driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:poltergeist&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ActionDispatch::IntegrationTest&lt;/span&gt;
  &lt;span class="c1"&gt;# 可以在功能测试中使用capybara的DSL&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Capybara&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DSL&lt;/span&gt;

  &lt;span class="c1"&gt;# 默认的失败测试提示不是很有用，扩展后方便调试&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;assert_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has_content?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sx"&gt;%Q{Expected to found "&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sx"&gt;" in: "&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&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;def&lt;/span&gt; &lt;span class="nf"&gt;refute_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;refute&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has_content?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sx"&gt;%Q{Expected not to found "&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sx"&gt;" in: "&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&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="c1"&gt;# See: https://gist.github.com/mperham/3049152&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ActiveRecord::Base&lt;/span&gt;
  &lt;span class="n"&gt;mattr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:shared_connection&lt;/span&gt;
  &lt;span class="vc"&gt;@@shared_connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;

  &lt;span class="k"&gt;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;connection&lt;/span&gt;
    &lt;span class="vc"&gt;@@shared_connection&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="no"&gt;ConnectionPool&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Wrapper&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;:size&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;retrieve_connection&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="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shared_connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="测试确认邮件"&gt;测试确认邮件&lt;/h2&gt;
&lt;p&gt;如果要测试邮件的内容，内容中一般都会有链接，所以需要在 config/environments/test.rb 中加上：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;action_mailer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;default_url_options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;host: &lt;/span&gt;&lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;port: &lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果使用了 delayed_job 可以在其配置中加上：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Delayed&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delay_jobs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&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;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;表明测试环境下，邮件任务都是直接发送出去的&lt;/p&gt;
&lt;h2 id="用户注册和确认邮件测试的样例代码"&gt;用户注册和确认邮件测试的样例代码&lt;/h2&gt;
&lt;p&gt;在 test 目录下面，新建 features 目录，专门存放网站功能测试代码
&lt;code&gt;test/features/user_authentication_test.rb&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="s1"&gt;'test_helper'&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserAuthenticationTest&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionDispatch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;IntegrationTest&lt;/span&gt;
  &lt;span class="n"&gt;setup&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;ActionMailer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deliveries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clear&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="s1"&gt;'user can sign up with email and password'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;visit&lt;/span&gt; &lt;span class="n"&gt;new_user_registration_path&lt;/span&gt;

    &lt;span class="n"&gt;assert_content&lt;/span&gt; &lt;span class="s2"&gt;"注册帐号"&lt;/span&gt;

    &lt;span class="n"&gt;within&lt;/span&gt; &lt;span class="s1"&gt;'#new_user'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;fill_in&lt;/span&gt; &lt;span class="s1"&gt;'user[email]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;with: &lt;/span&gt;&lt;span class="s1"&gt;'test_user@gmail.com'&lt;/span&gt;
      &lt;span class="n"&gt;fill_in&lt;/span&gt; &lt;span class="s1"&gt;'user[password]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;with: &lt;/span&gt;&lt;span class="s1"&gt;'abcd1234'&lt;/span&gt;

      &lt;span class="n"&gt;click_button&lt;/span&gt; &lt;span class="s1"&gt;'创建新帐号'&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;assert_equal&lt;/span&gt; &lt;span class="n"&gt;sign_up_success_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current_path&lt;/span&gt;

    &lt;span class="n"&gt;assert_content&lt;/span&gt; &lt;span class="s2"&gt;"test_user@gmail.com"&lt;/span&gt;

    &lt;span class="n"&gt;test_confirm_email&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;test_confirm_email&lt;/span&gt;
    &lt;span class="n"&gt;assert_not&lt;/span&gt; &lt;span class="no"&gt;ActionMailer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deliveries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt;

    &lt;span class="n"&gt;cf_email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ActionMailer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deliveries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last&lt;/span&gt;

    &lt;span class="n"&gt;assert_equal&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"support@guangchuan.com"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;cf_email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;
    &lt;span class="n"&gt;assert_equal&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'test_user@gmail.com'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;cf_email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;
    &lt;span class="n"&gt;assert_equal&lt;/span&gt; &lt;span class="s1"&gt;'确认信息'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cf_email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subject&lt;/span&gt;
    &lt;span class="n"&gt;assert_match&lt;/span&gt; &lt;span class="sr"&gt;/Confirm my account/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cf_email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&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;所以可以看到用 minitest 写的代码都非常直接，都是纯 ruby 代码。等代码增多后，可以按照 ruby 的方式来组织，重用和重构。&lt;/p&gt;

&lt;p&gt;原文链接 &lt;a href="http://www.bigbing.net/2015/11/23/rails4-minitest-capybara-devise/" rel="nofollow" target="_blank" title=""&gt;Rails4 新项目的测试：minitest + capybara 测试基于 devise 的用户注册&lt;/a&gt;&lt;/p&gt;</description>
      <author>grantbb</author>
      <pubDate>Mon, 23 Nov 2015 19:45:11 +0800</pubDate>
      <link>https://ruby-china.org/topics/28166</link>
      <guid>https://ruby-china.org/topics/28166</guid>
    </item>
    <item>
      <title>Rails 4 新项目的选择 2：前端框架 Simple_form 后台任务 搜索和用户认证</title>
      <description>&lt;p&gt;Rails 的世界各种开源的优秀解决方案非常多，项目开始的时候比较痛苦地就是要根据自己的原则和项目的特点来做出正确的选择。&lt;/p&gt;
&lt;h2 id="前端开发框架的选择"&gt;前端开发框架的选择&lt;/h2&gt;
&lt;p&gt;原本打算在新的项目中开始使用 ES6，然后前端的开发框架选择容易上手的，轻量级的&lt;a href="http://vuejs.org" rel="nofollow" target="_blank" title=""&gt;vue.js&lt;/a&gt;。由于 Vue.js 只支持 IE9 以及以上的浏览器，考虑到新项目是面向国内用户的产品，用户中还是有很大一部分的 IE8，以及以下的浏览器用户。所以，最后还是决定使用 Jquery+&lt;a href="http://knockoutjs.com" rel="nofollow" target="_blank" title=""&gt;knockout.js&lt;/a&gt;这样的组合。&lt;/p&gt;

&lt;p&gt;通过在论坛里面帖子&lt;a href="https://ruby-china.org/topics/27898" title=""&gt;前端框架选择的一个现实的问题&lt;/a&gt;评论了解到。jquery 也有支持双向绑定的 lib： &lt;a href="http://jquerymy.com" rel="nofollow" target="_blank" title=""&gt;jquerymy.js&lt;/a&gt;
还有就是在这种情况下，如果要做 SPA 的话，可以使用&lt;a href="http://backbonejs.org" rel="nofollow" target="_blank" title=""&gt;backbone.js&lt;/a&gt;，兼容性也是很好的。&lt;/p&gt;
&lt;h2 id="放弃Simple-form"&gt;放弃 Simple-form&lt;/h2&gt;
&lt;p&gt;不再使用 simple-form，主要是感觉它提供的一些功能对现在的项目帮助不大，还有就是 simple form 的 wrapper 定制比较麻烦，也会把页面代码弄得很臃肿。在这种前提下还是使用 rails 自带的 form_for 就可以了。原则就是如果要使用的 gem 帮助不是很大，还是最优先用 Ruby 和 Rails 自带的一些方案。&lt;/p&gt;
&lt;h2 id="表单验证"&gt;表单验证&lt;/h2&gt;
&lt;p&gt;尝试使用&lt;a href="https://github.com/DavyJonesLocker/client_side_validations" rel="nofollow" target="_blank" title=""&gt;client side validations&lt;/a&gt;这个 gem，这样前后端尽可能共享验证的规则，减少自己写相关 js 代码。少些代码，少制造 bug。&lt;/p&gt;
&lt;h2 id="Rails后台任务框架的选择："&gt;Rails 后台任务框架的选择：&lt;/h2&gt;
&lt;p&gt;网上已经有很多资料比较 Sidekiq，Resque 和 DelayedJob。我自己也都有简单用过。
所以决定在项目开始时（流量不大）使用 DelayedJob，简单易用，不要单独安装和配置 Redis，同时使用 ActiveJob 提供的 API，这样以后迁移到 Sidekiq 或者 Resque 也就不会很麻烦。&lt;/p&gt;
&lt;h2 id="搜索：Elasticsearch"&gt;搜索：Elasticsearch&lt;/h2&gt;
&lt;p&gt;本周也开始看一下搜索功能的实现，以前我做过几年的基于 Lucene 的搜索开发，搜索服务就准备用 elasticsearch，发现相关的 Rails 的整合方案也很多。有官方的 elasticsearch-rails，还有一个比较流行的是&lt;a href="https://github.com/ankane/searchkick" rel="nofollow" target="_blank" title=""&gt;searchkick&lt;/a&gt;,看了一下感觉功能很强大，所以下周准备拿 searchkick 试试。&lt;/p&gt;
&lt;h2 id="用户认证：Devise"&gt;用户认证：Devise&lt;/h2&gt;
&lt;p&gt;这个选择主要是因为 Devise 功能强大，相关文档也很多，之前在项目中也用过。&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.bigbing.net/2015/11/08/rails4-js-form-backjob-search-ua/" rel="nofollow" target="_blank" title=""&gt;带有美图的原文链接&lt;/a&gt;&lt;/p&gt;</description>
      <author>grantbb</author>
      <pubDate>Sun, 08 Nov 2015 19:54:16 +0800</pubDate>
      <link>https://ruby-china.org/topics/27993</link>
      <guid>https://ruby-china.org/topics/27993</guid>
    </item>
    <item>
      <title>Rails 4 新项目之 CSS 框架的选择：Bourbon</title>
      <description>&lt;p&gt;我在之前的项目中使用过&lt;a href="http://getbootstrap.com/" rel="nofollow" target="_blank" title=""&gt;Bootstrap&lt;/a&gt;和&lt;a href="http://foundation.zurb.com/" rel="nofollow" target="_blank" title=""&gt;Foundation&lt;/a&gt;，确实非常方便使用，把相关的 gem 放到项目中之后，然后再到网站上找到一些 HTML 和 CSS 的例子，基本能够满足需求，但是要说到对这两个框架有多了解，还真是谈不上，这两个都是大而全的东西，在 Rails 中基本上都是一股脑全部引入，然后就只管用，确实省心。&lt;/p&gt;

&lt;p&gt;但是时间长了就发现，用这两个框架写出来的页面代码还是非常啰嗦，易读性和可维护性随着项目的变大越来越差。&lt;/p&gt;

&lt;p&gt;之前还有一个项目用到了&lt;a href="http://compass-style.org/" rel="nofollow" target="_blank" title=""&gt;Compass&lt;/a&gt;，&lt;code&gt;Compass&lt;/code&gt;中也是包含了很多内容，我个人一直比较喜欢简单，轻量的东西，所以看到这种庞大的库就有点害怕。&lt;/p&gt;

&lt;p&gt;然后我就想有没有其他选择呢？后来，在一个开源项目&lt;a href="https://github.com/DefactoSoftware/Hours" rel="nofollow" target="_blank" title=""&gt;Hours&lt;/a&gt;中就发现了&lt;a href="http://bourbon.io/" rel="nofollow" target="_blank" title=""&gt;Bourbon&lt;/a&gt;，然后又是&lt;a href="http://neat.bourbon.io/" rel="nofollow" target="_blank" title=""&gt;Neat&lt;/a&gt;，还有&lt;a href="http://bitters.bourbon.io/" rel="nofollow" target="_blank" title=""&gt;Bitters&lt;/a&gt;和&lt;a href="http://refills.bourbon.io/" rel="nofollow" target="_blank" title=""&gt;Refills&lt;/a&gt;，一开始一下看到四个东西，感觉摸不清思路，都不知道是干什么的。后来，读了几篇文章后，就清楚了&lt;code&gt;Bourbon&lt;/code&gt;整个技术栈的理念。就是把像&lt;code&gt;Bootstrap&lt;/code&gt;这种大而全的框架，清楚地拆分成四个小的工具库，然后可以组合在一起使用，也可以根据需要来选择使用。&lt;/p&gt;

&lt;p&gt;下面简单介绍一下四个工具：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://bourbon.io/" rel="nofollow" target="_blank" title=""&gt;Bourbon&lt;/a&gt;: 简单的轻量级的&lt;code&gt;Sass Maxin&lt;/code&gt;库&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://neat.bourbon.io/" rel="nofollow" target="_blank" title=""&gt;Neat&lt;/a&gt;: 一个轻量级的语义化的页面网格框架&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://bitters.bourbon.io/" rel="nofollow" target="_blank" title=""&gt;Bitters&lt;/a&gt;: 脚手架 (scaffold) 样式，变量的定义，方便快速开始 Bourbon 的项目&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://refills.bourbon.io/" rel="nofollow" target="_blank" title=""&gt;Refills&lt;/a&gt;: 基于以上三个的一些实用的组件库&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;另外看到&lt;code&gt;Bourbon&lt;/code&gt;是&lt;a href="https://thoughtbot.com/" rel="nofollow" target="_blank" title=""&gt;thoughtbot&lt;/a&gt;出品的，我相信这些工具一定在实际项目中得到了验证。我一直以来都很喜欢&lt;code&gt;thoughtbot&lt;/code&gt;开发一些 gems，非常实用，而且轻量，比如&lt;code&gt;clearance&lt;/code&gt;等。&lt;/p&gt;

&lt;p&gt;其中下面这两篇文章说服了我：&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.amplificommerce.com/5-reasons-we-chose-bourbonneat-over-foundation-or-bootstrap/" rel="nofollow" target="_blank" title=""&gt;5 Reasons We Chose Bourbon/Neat Over Foundation or Bootstrap&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://federicoramirez.name/why-i-prefer-bourbon-over-bootstrap/" rel="nofollow" target="_blank" title=""&gt;Why I prefer Bourbon over Bootstrap&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;最吸引我得就是使用&lt;code&gt;Bourbon&lt;/code&gt;和&lt;code&gt;Neat&lt;/code&gt;，可以写出非常易读，易懂的&lt;code&gt;语义化的HTML&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;特别是当我们使用&lt;code&gt;Haml&lt;/code&gt;或者&lt;code&gt;Slim&lt;/code&gt;这样的模板语言后，在模板中如果都是一些&lt;code&gt;语义化的CSS class&lt;/code&gt;，代码看起来就非常直观。&lt;/p&gt;

&lt;p&gt;比如：
用 Bootstrap 会这样写 (如果再加上一些响应式的样式就更加冗长)：&lt;/p&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.container&lt;/span&gt;
  &lt;span class="nc"&gt;.row&lt;/span&gt;
    &lt;span class="nc"&gt;.col-md-6&lt;/span&gt; &lt;span class="c"&gt;/* some content */&lt;/span&gt;
    &lt;span class="nc"&gt;.col-md-6&lt;/span&gt; &lt;span class="c"&gt;/* some content */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用 Borbon 可能会是这样，非常清晰，直观：&lt;/p&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.news&lt;/span&gt;
  &lt;span class="nc"&gt;.hot-news&lt;/span&gt;
  &lt;span class="nc"&gt;.latest-news&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接下来简单说一下我是如何在新的 Rails 项目中使用的。&lt;/p&gt;
&lt;h4 id="1. 基本安装:"&gt;1. 基本安装：&lt;/h4&gt;
&lt;p&gt;这里没有什么特别需要说明的，大家只要按照官方 github 上的文档说明，很容易搞定。&lt;/p&gt;
&lt;h4 id="2. 使用normalize.css"&gt;2. 使用 normalize.css&lt;/h4&gt;
&lt;p&gt;在 Bootstrap 中默认是集成了&lt;code&gt;normalize.css&lt;/code&gt;，所以我们不用单独引入，在 Bourbon 栈中默认是没有的，需要我们单独引用。这里使用 normalize-rails 这个 gem 就可以了。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;gem "normalize-rails"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="3. 使用autoprefixer"&gt;3. 使用&lt;code&gt;autoprefixer&lt;/code&gt;
&lt;/h4&gt;
&lt;p&gt;Bourbon 中有一大块功能是关于&lt;code&gt;vendor prefixes&lt;/code&gt;的。需要开发者自己主动地去使用封装好的一些 Sass 的 mixin，这个对于我一个后端出身的号称全栈的开发者要求有些高了。
同时 autoprefixer 的处理方式就是非常的傻瓜式，开发者自己不要管 vendor prefixes，尽管写标准的 CSS，剩下的 autoprefixer 自动搞定。
Bourbon 的开发者也同意这一点，所以在&lt;code&gt;Bourbon5.0&lt;/code&gt;中，关于 vendor prefixes 的部分也将会删除。
所以，我们只需要使用 gem "autoprefixer-rails"就可以了。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;gem "autoprefixer-rails"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;如果想了解和深入学习 Bourbon 相关的技术，可以到官方网站上查看详细的使用文档。&lt;/p&gt;

&lt;p&gt;这里还有一个 youtube 视频系列，可以帮大家系统学习：
&lt;a href="https://www.youtube.com/playlist?list=PLfdtiltiRHWErI0VSxDCbeDyEJm_kVt3p" rel="nofollow" target="_blank" title=""&gt;Awesome CSS with Bourbon, Neat, Bitters &amp;amp; Refills!&lt;/a&gt; 国内的朋友记得翻墙哦。&lt;/p&gt;

&lt;p&gt;BTW：随着项目的进行，我会逐步写出对于一些架构和工具选择总结的文章。&lt;/p&gt;

&lt;p&gt;原文链接：&lt;a href="http://www.bigbing.net/2015/10/31/new-app-css-bourbon-neat-bitters-refills/" rel="nofollow" target="_blank" title=""&gt;BigBing 技术博客 - Rails4 新项目之 CSS 框架的选择：Bourbon&lt;/a&gt;&lt;/p&gt;</description>
      <author>grantbb</author>
      <pubDate>Sun, 01 Nov 2015 07:10:52 +0800</pubDate>
      <link>https://ruby-china.org/topics/27911</link>
      <guid>https://ruby-china.org/topics/27911</guid>
    </item>
    <item>
      <title>分享：产品环境 Nginx + Unicorn + Eye 的 Rails 应用配置</title>
      <description>&lt;p&gt;最近整理了一下个人项目产品环境的配置，加了一些注释，分享出来希望对大家有用。
欢迎大家提改进建议。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nginx 的配置&lt;/strong&gt;
&lt;a href="https://gist.github.com/bingxie/a0039781a52ad908b0a2" rel="nofollow" target="_blank"&gt;https://gist.github.com/bingxie/a0039781a52ad908b0a2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unicorn 的配置&lt;/strong&gt;
&lt;a href="https://gist.github.com/bingxie/0da00d3a22905cab30b7" rel="nofollow" target="_blank"&gt;https://gist.github.com/bingxie/0da00d3a22905cab30b7&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Eye 配置&lt;/strong&gt;
&lt;a href="https://gist.github.com/bingxie/4684a7455edebbaa5a34" rel="nofollow" target="_blank"&gt;https://gist.github.com/bingxie/4684a7455edebbaa5a34&lt;/a&gt;&lt;/p&gt;</description>
      <author>grantbb</author>
      <pubDate>Mon, 22 Dec 2014 14:37:42 +0800</pubDate>
      <link>https://ruby-china.org/topics/23335</link>
      <guid>https://ruby-china.org/topics/23335</guid>
    </item>
    <item>
      <title>分享 - 免费的 Rails 安全相关的 screencasts</title>
      <description>&lt;p&gt;分享给喜欢研究和学习安全相关的技术的同学&lt;/p&gt;

&lt;p&gt;&lt;a href="https://seccasts.com/mror" rel="nofollow" target="_blank"&gt;https://seccasts.com/mror&lt;/a&gt;&lt;/p&gt;</description>
      <author>grantbb</author>
      <pubDate>Wed, 10 Dec 2014 07:30:15 +0800</pubDate>
      <link>https://ruby-china.org/topics/23111</link>
      <guid>https://ruby-china.org/topics/23111</guid>
    </item>
  </channel>
</rss>
