<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>hegwin (Hegwin)</title>
    <link>https://ruby-china.org/hegwin</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>Ruby 3.0 升到 3.2 时的一些小问题整理</title>
      <description>&lt;p&gt;眼看着 Ruby 3.3 的 preview 2 版本都已经发出来了，于是我就着手把一个 3.0 的项目升级到 3.2 了。可能是我这样直接从 3.0 到 3.2 升级跨度点大，还是遇到了一些不大不小的问题，我在这里做一个整理，希望可以帮助到大家。&lt;/p&gt;
&lt;h2 id="Bundled gems"&gt;Bundled gems&lt;/h2&gt;
&lt;p&gt;在 Ruby 3.1 中，有些 lib 从标准库变成了 bundled gems，比如我项目中用到的 net-ftp 还有 matrix 这样的库。具体的列表可以在 &lt;a href="https://www.ruby-lang.org/en/news/2021/12/25/ruby-3-1-0-released/" rel="nofollow" target="_blank" title=""&gt;Ruby 3.1.0 Released&lt;/a&gt; 看到，滚动到“Standard libraries updates”这一部分就可以看到。&lt;/p&gt;

&lt;p&gt;这意味我们如果使用了 bundler，那就需要在 Gemfile 中把 matrix 等 gem 加进去，否则在运行的过程中会遇到这样的问题：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rails `require': cannot load such file -- matrix
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;顺便再说一些 bundled gems 和 default libraries 的区别。&lt;/p&gt;

&lt;p&gt;以 matrix 为例，在 Ruby 3.1 中，matrix 已从 Ruby 标准库的一部分转变为 bundled gem，这一变化并不意味着它从 Ruby 的默认库中移除，而是意味着 matrix 现在被视为 gem。&lt;/p&gt;

&lt;p&gt;bundled gems：这些 lib 现在被视为 gem，当使用 bundler 创建新的 Ruby 项目时，我们需要在 Gemfile 中包含这些 gem。但是作为 "bundled" gem，我们不需要单独安转它们，他们&lt;/p&gt;

&lt;p&gt;default libraries：在安装 Ruby 时，标准库中仍提供这些库，即在使用纯 Ruby 时，我们依然可以访问这些库。&lt;/p&gt;
&lt;h2 id="Psych （YAML）的安全性更新"&gt;Psych（YAML）的安全性更新&lt;/h2&gt;
&lt;p&gt;YAML 的 &lt;code&gt;load&lt;/code&gt; 和 &lt;code&gt;load_file&lt;/code&gt; 方法的行为有一些 breaking changes，主要也是底层的 Psych 库的改变。&lt;/p&gt;

&lt;p&gt;过去能正常运行的 &lt;code&gt;YAML.load&lt;/code&gt; 可能现在回出现这样的错误：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;YAML.load "--- !ruby/object:Matrix\nrows:\n- - 25\n  - 93\n- - -1\n  - 66\ncolumn_count: 2\n"


 Psych::DisallowedClass:
   Tried to load unspecified class: Matrix
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这是因为 &lt;code&gt;YAML.load&lt;/code&gt; 现在会调用 &lt;code&gt;Psych.safe_load&lt;/code&gt;，在解析 yaml 的时候，会限制 Ruby 对象的种类，以保证其安全性。&lt;/p&gt;

&lt;p&gt;解决办法如下：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;如果你信任你的数据，那么可以用 &lt;code&gt;YAML.unsafe_load&lt;/code&gt; 去替换 &lt;code&gt;YAML.load&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;如果数据的内容不可信，那么需要加上 permited_classes 参数，如下：&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;YAML.load "--- !ruby/object:Matrix\nrows:\n- - 25\n  - 93\n- - -1\n  - 66\ncolumn_count: 2\n", permitted_classes: [Matrix]

=&amp;gt; Matrix[[25, 93], [-1, 66]]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 Rails 项目中，也可以进行在 &lt;code&gt;config/application.rb&lt;/code&gt; 进行如下配置：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;config.after_initialize do
  ActiveRecord.yaml_column_permitted_classes += [Date, Time, ActiveSupport::HashWithIndifferentAccess]
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个改动的具体背景可以看这里：&lt;a href="https://discuss.rubyonrails.org/t/cve-2022-32224-possible-rce-escalation-bug-with-serialized-columns-in-active-record/81017" rel="nofollow" target="_blank" title=""&gt;[CVE-2022-32224] Possible RCE escalation bug with Serialized Columns in Active Record&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="Deprecating很久终于移除的方法"&gt;Deprecating 很久终于移除的方法&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;File.exists?&lt;/code&gt; 这个方法在 Ruby 2.1 中就被标记为 Deprecating，但是也没说具体何时移除。这次在升级到 Ruby 3.2 之后，我遇到了这个错误 &lt;code&gt;undefined method 'exists?' for File:Class&lt;/code&gt; ，才反应过来这个方法终于被移除了。一同移除的还有 &lt;code&gt;Dir.exists?&lt;/code&gt;这个方法。具体可以参见 release notes 中“Removed methods”这一小节。&lt;/p&gt;

&lt;p&gt;但是，在我全局搜索项目，并把 &lt;code&gt;File.exists?&lt;/code&gt; 都替换成 &lt;code&gt;File.exist?&lt;/code&gt; 后，我还是遇到了类似的错误。因为在我复制粘贴来的 chef 脚本里面，还有 &lt;code&gt;FileTest.exists?&lt;/code&gt; 这种用法。&lt;/p&gt;

&lt;p&gt;我随后查了下，&lt;code&gt;File.exist?&lt;/code&gt; 或者  &lt;code&gt;File.file?&lt;/code&gt; 这样的测试方法，都是定义在 &lt;code&gt;FileTest&lt;/code&gt; 这个 module 里的，而 File 这个类 include 了 FileTest module，而 Ruby 也允许我们直接用 FileTest 去调用这些方法。所以，对于  &lt;code&gt;FileTest.exists?&lt;/code&gt; 这样的用法，也需要改成 &lt;code&gt;FileTest.exist?&lt;/code&gt;。&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;不晓得是不是单次的错觉，似乎我在没进行任何配置的情况下，单单从 CI 跑 rspec 的时间来看，3.2 真的快很多呢：&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/hegwin/ff7bc768-4760-44b0-9250-ae0d154d9c5b.png!large" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>hegwin</author>
      <pubDate>Sun, 05 Nov 2023 20:42:43 +0800</pubDate>
      <link>https://ruby-china.org/topics/43456</link>
      <guid>https://ruby-china.org/topics/43456</guid>
    </item>
    <item>
      <title>Ruby on Rails was the most in-demand skill in 2022</title>
      <description>&lt;p&gt;看到各种裁员的消息还是让人伤心，但是 RoR 程序员需求最多，这点还是有些振奋精神的&lt;/p&gt;

&lt;p&gt;&lt;a href="https://hired.com/blog/highlights/hired-releases-2023-state-of-software-engineers-report/" rel="nofollow" target="_blank"&gt;https://hired.com/blog/highlights/hired-releases-2023-state-of-software-engineers-report/&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;2022 layoffs impacted demand and salaries for junior and nontraditional engineering candidates;
Ruby on Rails is the most in-demand skill;
natural language processing engineers earned highest average salaries&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <author>hegwin</author>
      <pubDate>Sun, 05 Mar 2023 23:54:40 +0800</pubDate>
      <link>https://ruby-china.org/topics/42922</link>
      <guid>https://ruby-china.org/topics/42922</guid>
    </item>
    <item>
      <title>Rails 6.1 升级到 7 的一个迷之错误，居然是缓存导致的 :) （以及另外三个问题）</title>
      <description>&lt;p&gt;从 Rails 6.1 升到 7 的时候，有仔细读过 ugprade guideline，但还是在部署之后遇到了一个很奇妙的错误，霸占了 Sentry…&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ActionView::Template::Error
The :mode option must be one of [:all, :n_plus_one_only].
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;strict_loading&lt;/code&gt;是 6.1 加的一个功能，用来检测 N+1 query 的，但是当时直接忽略了它，默认全局关闭，因为有用别的 gem 做了这件事。。&lt;/p&gt;

&lt;p&gt;等到了 7 遇到这个错误，尝试 debug 还翻到了这个错误的源码所在地，总之非常不理解：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# activerecord/lib/active_record/core.rb&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;strict_loading!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;mode: :all&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:n_plus_one_only&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ArgumentError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"The :mode option must be one of [:all, :n_plus_one_only] but &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; was provided."&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="vi"&gt;@strict_loading_mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;
  &lt;span class="vi"&gt;@strict_loading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最终没有在 stack overflow 找到原因，而是在 rails 的 PR 里看到了这条 &lt;a href="https://github.com/rails/rails/pull/44255#issuecomment-1239962562" rel="nofollow" target="_blank" title=""&gt;comment&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;解决方法是：清理掉 rails 的缓存就可以了。&lt;/p&gt;

&lt;p&gt;讲道理，修改代码后，缓存的模型和新代码不兼容，这本来是一件挺正常的事情，但是这个错误提示实在是让人费解。&lt;/p&gt;

&lt;p&gt;猜测原因可能是在 view 里缓存了一部分 active records，而这些缓存是在升 7 之前缓存的…当从缓存里拿出来这些 ActiveRecord[] 的时候，它们没有 mode 这个属性，于是就报错了。&lt;/p&gt;
&lt;h3 id="其他小问题"&gt;其他小问题&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Rails 6.x 及之前的 model_instance.errors.messages（Hash）没有 frozen 是可以自由修改的，而 Rails7 之后的 errors.messages 是一个 frozen hash。可能影响到的场景是，注册用户时，email 已经被注册了，我们这时候不是提示 email 的 error，而是引导用户去登录…但如果有其他 validation 失败，还是展示给用户看（emm 这逻辑有点奇妙是吗）。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Rails 6.1&lt;/span&gt;

&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;valid?&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; false&lt;/span&gt;
&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;messages&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; {:email=&amp;gt;["can't be blank"]}&lt;/span&gt;
&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;frozen?&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;

&lt;span class="c1"&gt;# Rails 7.0.4&lt;/span&gt;
&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;valid?&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; false&lt;/span&gt;
&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;frozen?&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;PG 的 created_at 类型，Rails 7 里变成了 TimeWithZone&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;详见这个 Issue &lt;a href="https://github.com/rails/rails/issues/45283" rel="nofollow" target="_blank" title=""&gt;Regression: Filtering a timestamp with timezone by date range&lt;/a&gt; 和这个 question&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;终于向 PG 学习，MySQL 在转化带变量的 LIKE 语句有了一致的（令人不爽的）行为&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"brand"&lt;/span&gt;

&lt;span class="c1"&gt;# 各种Rails版本下的的PG&lt;/span&gt;
&lt;span class="no"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"friendly_id like '%?-%'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_sql&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;  &lt;span class="s2"&gt;"WHERE (friendly_id like '%'brand'-%')"&lt;/span&gt;

&lt;span class="c1"&gt;# Rails 6 下的MySQL&lt;/span&gt;
&lt;span class="no"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"friendly_id like '%?-%'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_sql&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"WHERE (friendly_id like '%brand-%')"&lt;/span&gt;

&lt;span class="c1"&gt;# Rails 7下的MySQL&lt;/span&gt;
&lt;span class="no"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"friendly_id like '%?-%'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_sql&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;  &lt;span class="s2"&gt;"WHERE (friendly_id like '%'brand'-%')"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;于是就不得不这么写&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"friendly_id like ?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"%&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-%"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;</description>
      <author>hegwin</author>
      <pubDate>Thu, 24 Nov 2022 18:09:05 +0800</pubDate>
      <link>https://ruby-china.org/topics/42762</link>
      <guid>https://ruby-china.org/topics/42762</guid>
    </item>
    <item>
      <title>Rails 6.1 ActiveStorage 支持多个存储服务了</title>
      <description>&lt;p&gt;Rails &amp;gt;= 6.1 开始，&lt;code&gt;ActiveStorage&lt;/code&gt; 开始支持多个 storage service，可能不算新闻（这里有篇 &lt;a href="https://blog.saeloun.com/2020/02/03/rails-allows-configure-service-for-attachments-to-activestorage.html" rel="nofollow" target="_blank" title=""&gt;blog&lt;/a&gt; 介绍），但是最近升上 6.1 来才用到它，分享一下。&lt;/p&gt;
&lt;h4 id="背景"&gt;背景&lt;/h4&gt;
&lt;p&gt;在存储的时候，可能会有不同的存储服务，比如我们的要把图片存在 Cloudinary，其他文件存在 Amazon S3。在 6.1 之前，我们是打了一个 monkey patch；在 6.1 之后，就可以直接配置啦。&lt;/p&gt;
&lt;h4 id="6.1之前的做法"&gt;6.1 之前的做法&lt;/h4&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/initializers/activestorage.rb&lt;/span&gt;
&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;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="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;ActiveStorage&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Blob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class_eval&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="kp"&gt;alias_method&lt;/span&gt; &lt;span class="ss"&gt;:original_service&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:service&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;service&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;original_service&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;image?&lt;/span&gt;
      &lt;span class="vi"&gt;@service&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="n"&gt;load_active_storage_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:amazon&lt;/span&gt;&lt;span class="p"&gt;)&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;load_active_storage_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="no"&gt;ActiveStorage&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&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;active_storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;service_configurations&lt;/span&gt;
    &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
      &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Cannot load service &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backtrace&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;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="现在的做法"&gt;现在的做法&lt;/h4&gt;
&lt;p&gt;Monkey patch 就可以完全去掉了，因为 service 变得可以配置了；config/storage.yml 可以稍微修改下：&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;amazon&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;S3&lt;/span&gt;
  &lt;span class="na"&gt;access_key_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV['s3_access_key_id'] %&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;secret_access_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV['s3_secret_access_key'] %&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;bucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV['s3_bucket'] %&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV['s3_region'] %&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;cloudinary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cloudinary&lt;/span&gt;
  &lt;span class="na"&gt;fetch_format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;auto&lt;/span&gt;
  &lt;span class="na"&gt;quality&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;auto&lt;/span&gt;
  &lt;span class="na"&gt;secure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;folder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= 'dev' if Rails.env.development? %&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后在模型里，就可以这样：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Posting&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;has_many_attached&lt;/span&gt; &lt;span class="ss"&gt;:images&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;service: :cloudinary&lt;/span&gt;
  &lt;span class="n"&gt;has_one_attached&lt;/span&gt; &lt;span class="ss"&gt;:thumbnail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;service: :cloudinary&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;Project&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;has_one_attached&lt;/span&gt; &lt;span class="ss"&gt;:doc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;service: :amazon&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;</description>
      <author>hegwin</author>
      <pubDate>Tue, 29 Jun 2021 15:17:34 +0800</pubDate>
      <link>https://ruby-china.org/topics/41428</link>
      <guid>https://ruby-china.org/topics/41428</guid>
    </item>
    <item>
      <title>[上海] WeWork China 招聘 后端工程师 Ruby/Java 16K - 30K</title>
      <description>&lt;h2 id="WeWork China"&gt;WeWork China&lt;/h2&gt;&lt;h2 id="我们是谁："&gt;我们是谁：&lt;/h2&gt;
&lt;p&gt;我们的故事始于我们的空间，自由、梦想与激情相互碰撞，一起践行——“Do What You Love！”&lt;/p&gt;

&lt;p&gt;在 WeWork 的每一天都充满奇遇和冒险，我们共同奋斗，用热情点燃创造力与潜能，迎接各种各样的挑战。&lt;/p&gt;

&lt;p&gt;让每一个“我”在创造者社区中缔造有意义的事业和人生，把每一天的工作变成美好的日常，成就更好的“我们”。加入我们，成为改变世界的一份子！&lt;/p&gt;
&lt;h2 id="待遇，福利，以及工作环境:"&gt;待遇，福利，以及工作环境：&lt;/h2&gt;
&lt;p&gt;有竞争力的待遇和福利。Ruby，Java 两个技术栈，还有机会上手 Node。&lt;/p&gt;

&lt;p&gt;WeWork 超一流的办公环境，免费咖啡，书籍，游戏机。&lt;/p&gt;
&lt;h2 id="后端开发工程师"&gt;后端开发工程师&lt;/h2&gt;&lt;h3 id="岗位职责"&gt;岗位职责&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;开发基于微服务架构的后端业务系统，为前端用户体验提供坚实的后端保障。&lt;/li&gt;
&lt;li&gt;根据业务需求，研究评估功能级别的技术方案并且进行具体的技术实现。&lt;/li&gt;
&lt;li&gt;遵从软件开发中的最佳实践比如说 DRY 原则，持续集成，代码审核等等。&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;计算机或相关专业大学本科以上学历，或具有同等水平和经验&lt;/li&gt;
&lt;li&gt;1-3 年后端开发经验，能够独立完成功能级别的需求分析，代码开发和测试。&lt;/li&gt;
&lt;li&gt;熟悉 Ruby 语言，Rails 框架。&lt;/li&gt;
&lt;li&gt;熟悉 Java 程序开发和 Spring Cloud 等框架，熟悉 MySQL 和 PostgreSQL&lt;/li&gt;
&lt;li&gt;善于理解业务需求并能将其转换为技术需求，拥有出色的研究和解决问题的能&lt;/li&gt;
&lt;li&gt;有基于 Docker 和 K8S 微服务体系架构开发的经验更佳&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;有意请 email 简历到 &lt;code&gt;jack.wu8@wework.com&lt;/code&gt; ，邮件中请注明是来自 RubyChina。&lt;/p&gt;</description>
      <author>hegwin</author>
      <pubDate>Wed, 16 Sep 2020 17:40:14 +0800</pubDate>
      <link>https://ruby-china.org/topics/40412</link>
      <guid>https://ruby-china.org/topics/40412</guid>
    </item>
    <item>
      <title>我自己的一个帖子不知为何打不开了</title>
      <description>&lt;p&gt;&lt;a href="https://ruby-china.org/topics/31094" rel="nofollow" target="_blank"&gt;https://ruby-china.org/topics/31094&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;是我写了什么敏感词吗…&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2019/a241a7ff-20e5-49e2-8210-a9685b52ddcb.png!large" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>hegwin</author>
      <pubDate>Thu, 31 Jan 2019 16:22:34 +0800</pubDate>
      <link>https://ruby-china.org/topics/38072</link>
      <guid>https://ruby-china.org/topics/38072</guid>
    </item>
    <item>
      <title>Ruby 里为什么要有 unless？</title>
      <description>&lt;p&gt;本文属于“深夜睡不着，那就开个脑洞吧”系列。&lt;/p&gt;
&lt;h2 id="背景"&gt;背景&lt;/h2&gt;
&lt;p&gt;从我开始接触编程开始，编程语言的控制结构&lt;code&gt;if...else...&lt;/code&gt;便是所有语言的标配。从我最开始学的 VB，到后来的 C，Java，Lua，JavaScript 皆是如此。而当我开始学 Ruby 时，我发现 Ruby 除了&lt;code&gt;if&lt;/code&gt;，竟然还有一个在当时的我看来是代替&lt;code&gt;if not&lt;/code&gt;的&lt;code&gt;unless&lt;/code&gt;，这真是非常神奇了。再到后来接触到 Python 和 Perl，Perl 是第二个我所知的有&lt;code&gt;unless&lt;/code&gt;结构的语言。&lt;/p&gt;

&lt;p&gt;虽然我一开始也有点惊讶&lt;code&gt;unless&lt;/code&gt;的存在，但是写习惯了之后，觉得这货在很多时候的表现也挺好的，在某些时候能让代码的可读性更强，更像自然语言；另外，大家在也总结出了一些 unless 的风格指南：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;在单行语句的时候喜爱使用 if/unless 修饰符。另一个好的选择就是使 and/or 来做流程控制。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# bad&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;some_condition&lt;/span&gt;
  &lt;span class="n"&gt;do_something&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# good&lt;/span&gt;
&lt;span class="n"&gt;do_something&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;some_condition&lt;/span&gt;

&lt;span class="c1"&gt;# another good option&lt;/span&gt;
&lt;span class="n"&gt;some_condition&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;do_something&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以及：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;永远不要使用 unless 和 else 组合。将它们改写成肯定条件。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# bad&lt;/span&gt;
&lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;success?&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'failure'&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'success'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# good&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;success?&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'success'&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'failure'&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;每天都能在 Ruby 代码看见&lt;code&gt;unless&lt;/code&gt;，本来应该对他习以为常，但我的脑洞就一直很大啊，有时候就会问，为啥要有&lt;code&gt;unless&lt;/code&gt;呢？&lt;/p&gt;
&lt;h2 id="猜测一： 受其他语言启发"&gt;猜测一：受其他语言启发&lt;/h2&gt;
&lt;p&gt;根据 wiki 介绍，Ruby 的设计受到了 Perl, Smalltalk, Eiffel, Ada, and Lisp 的影响：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;According to the creator, Ruby was influenced by Perl, Smalltalk, Eiffel, Ada, and Lisp.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;在这几门语言中，Perl 上面已经提到了，是有&lt;code&gt;unless&lt;/code&gt;控制结构的。此外，我最近搜索了一下，发现 Common Lisp 竟然也是有&lt;code&gt;unless&lt;/code&gt;表达式的。以下 Common List 代码是从百度贴吧复制过来的，写的一个神经网络，这是摘抄的一部分（贴吧真是代有人才出）：&lt;/p&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;weights-change-network-module&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;unless&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;network-weight-change-rate-fn&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="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Batch-adding-neurons&lt;/span&gt; &lt;span class="vg"&gt;*new-neurons-number*&lt;/span&gt; &lt;span class="vg"&gt;*new-nodex-number*&lt;/span&gt; &lt;span class="vg"&gt;*new-nodey-number*&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;repeat-matching-nodes&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="nb"&gt;dolist&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;obj&lt;/span&gt; &lt;span class="vg"&gt;*neurons-list*&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;unless&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;weights-comprehensive-regulation&lt;/span&gt; &lt;span class="nv"&gt;obj&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="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;modify-weights-module&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;neurons-name&lt;/span&gt; &lt;span class="nv"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nv"&gt;weights-comprehensive-regulation&lt;/span&gt; &lt;span class="nv"&gt;obj&lt;/span&gt; &lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;把这段代码翻译成 Ruby 风格，大概就是这样：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;weights_change_network_module&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;network_weight_change_rate_fn&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="no"&gt;Batch_adding_neurons&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;new_neurons_number&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;new_nodex_number&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;new_nodey_number&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;
    &lt;span class="n"&gt;repeat_matching_nodes&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="c1"&gt;#节点重新分配&lt;/span&gt;
  &lt;span class="n"&gt;dolist&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;neurons_list&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;weights_comprehensive_regulation&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="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;modify_weights_module&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;neurons_name&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;weights_comprehensive_regulation&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;del&gt;可把自己牛逼坏了，居然看得懂 Lisp，我得叉会腰&lt;/del&gt; 好吧，其实并没有，我完全不懂那些 &lt;code&gt;*new_neurons_number*&lt;/code&gt;是什么东西&lt;/p&gt;

&lt;p&gt;所以，我第一个感觉应该是 Matz 应该是受到 Lisp 或者 Perl 的启发，在设计 Ruby 时，加入了&lt;code&gt;unless&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id="猜测二：if 比较反人类"&gt;猜测二：if 比较反人类&lt;/h2&gt;
&lt;p&gt;我一直觉得 &lt;code&gt;if condition; do something; else; do something else; end&lt;/code&gt;是一个非常符合人类思维的表达，直到 17 年 8 月，一个同学给我安利了一个游戏叫 HRM，界面如下：&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2018/f970db4b-e6cb-4707-9943-a191011787cd.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;这其实是一个编程小游戏：右边是程序代码，在这一关能使用的命令非常少，就只有“输入”，“输出”，“加法”， “减法”和三个 jump，左边的方块 7, 2, -3, -8...是代表给程序输入，中间地上的格子，0,1,2 是三个存储器，相当于变量。这道题的目标是每次取两个输入值，然后输出二者中比较大的那个，以此循环。&lt;/p&gt;

&lt;p&gt;这时候，我才忽然意识到，我们所写的：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
  &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在电脑看来，应该是这副模样：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00 if a -b &amp;gt; 0
01 goto 04
02 print b
03 goto 05
04 print a
05 end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;去掉 goto 语句，就会有一种这样的感觉，和我们直接感觉有些颠倒：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if a - b &amp;gt; 0
print b
print a
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那么，Ruby 真的时这个游戏里的这样去实现的吗？我也不知道，所以测试了下：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env ruby&lt;/span&gt;
&lt;span class="n"&gt;code&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;CODE&lt;/span&gt;&lt;span class="sh"&gt;
if a - b &amp;gt; 0
  p a
else
  p b
end
&lt;/span&gt;&lt;span class="no"&gt;CODE&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="no"&gt;RubyVM&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;InstructionSequence&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;disasm&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出的 YARV 指令如下：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;== disasm: #&amp;lt;ISeq:&amp;lt;compiled&amp;gt;@&amp;lt;compiled&amp;gt;&amp;gt;================================
0000 trace            1                                               (   1)
0002 putself          
0003 opt_send_without_block &amp;lt;callinfo!mid:a, argc:0, FCALL|VCALL|ARGS_SIMPLE&amp;gt;, &amp;lt;callcache&amp;gt;
0006 putself          
0007 opt_send_without_block &amp;lt;callinfo!mid:b, argc:0, FCALL|VCALL|ARGS_SIMPLE&amp;gt;, &amp;lt;callcache&amp;gt;
0010 opt_minus        &amp;lt;callinfo!mid:-, argc:1, ARGS_SIMPLE&amp;gt;, &amp;lt;callcache&amp;gt;
0013 putobject_OP_INT2FIX_O_0_C_ 
0014 opt_gt           &amp;lt;callinfo!mid:&amp;gt;, argc:1, ARGS_SIMPLE&amp;gt;, &amp;lt;callcache&amp;gt;
0017 branchunless     30
0019 trace            1                                               (   2)
0021 putself          
0022 putself          
0023 opt_send_without_block &amp;lt;callinfo!mid:a, argc:0, FCALL|VCALL|ARGS_SIMPLE&amp;gt;, &amp;lt;callcache&amp;gt;
0026 opt_send_without_block &amp;lt;callinfo!mid:p, argc:1, FCALL|ARGS_SIMPLE&amp;gt;, &amp;lt;callcache&amp;gt;
0029 leave                                                            (   1)
0030 trace            1                                               (   4)
0032 putself          
0033 putself          
0034 opt_send_without_block &amp;lt;callinfo!mid:b, argc:0, FCALL|VCALL|ARGS_SIMPLE&amp;gt;, &amp;lt;callcache&amp;gt;
0037 opt_send_without_block &amp;lt;callinfo!mid:p, argc:1, FCALL|ARGS_SIMPLE&amp;gt;, &amp;lt;callcache&amp;gt;
0040 leave           
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hmmm，虽然有点乱，但是连蒙带猜应该能明白是怎么回事。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;0002 putself&lt;/code&gt; - 把当前的 self 作为方法的接受者；&lt;/p&gt;

&lt;p&gt;&lt;code&gt;0003 opt_send_without_block &amp;lt;callinfo!mid:a, argc:0, FCALL|VCALL|ARGS_SIMPLE&amp;gt;, &amp;lt;callcache&amp;gt;&lt;/code&gt; ，发出名为&lt;code&gt;a&lt;/code&gt;的消息，0 个参数，不带块；&lt;/p&gt;

&lt;p&gt;&lt;code&gt;0010 opt_minus        &amp;lt;callinfo!mid:-, argc:1, ARGS_SIMPLE&amp;gt;, &amp;lt;callcache&amp;gt;&lt;/code&gt; 发送名为 &lt;code&gt;-&lt;/code&gt; 的消息，1 个参数，即做减法，opt_plus 或者 opt_minus 是优化后的专有 YARV 指令；&lt;/p&gt;

&lt;p&gt;&lt;code&gt;0014 opt_gt           &amp;lt;callinfo!mid:&amp;gt;, argc:1, ARGS_SIMPLE&amp;gt;, &amp;lt;callcache&amp;gt;&lt;/code&gt; 比较大小；&lt;/p&gt;

&lt;p&gt;然后重点来了！&lt;/p&gt;

&lt;p&gt;&lt;code&gt;0017 branchunless     30&lt;/code&gt;，否则跳到第 30 行，这里有个 unless！&lt;/p&gt;

&lt;p&gt;紧跟在这下面的指令是打印 a 的值：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0023 opt_send_without_block &amp;lt;callinfo!mid:a, argc:0, FCALL|VCALL|ARGS_SIMPLE&amp;gt;, &amp;lt;callcache&amp;gt;
0026 opt_send_without_block &amp;lt;callinfo!mid:p, argc:1, FCALL|ARGS_SIMPLE&amp;gt;, &amp;lt;callcache&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;虽然有个&lt;code&gt;unless&lt;/code&gt;在这里横着，可是“条件”和“结果”的顺序居然因为负负得正，又和我们人类写代码的顺序一样了，真是不可思议；不过 Ruby 是在 1.9 版本才有了 YARV...&lt;/p&gt;

&lt;p&gt;嘛，没有结论，只是我该关上脑洞去睡觉了。&lt;/p&gt;</description>
      <author>hegwin</author>
      <pubDate>Tue, 27 Mar 2018 01:40:57 +0800</pubDate>
      <link>https://ruby-china.org/topics/35330</link>
      <guid>https://ruby-china.org/topics/35330</guid>
    </item>
    <item>
      <title>求助：关于如何让用户自定义 CSS</title>
      <description>&lt;p&gt;场景是这样的，这个 app 近似一个电商网站（给甲方做的，很多地方我们修改不了），也可以给他人开店用，但是代码并没给这些人修改，用户可以通过 UI 对网站的一部分样式进行自定义。&lt;/p&gt;

&lt;p&gt;举个例子就是，原文我们的 base 的 CSS 文件里有定义一些颜色的变量，这些变量会在具体的 CSS 里被引用：&lt;/p&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;color-primary&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;#ed1c24&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;color-secondary&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#0092&lt;/span&gt;&lt;span class="nt"&gt;ff&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;color-action&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;#f2442e&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;color-success&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#28&lt;/span&gt;&lt;span class="nt"&gt;BD00&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;color-dark&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#2&lt;/span&gt;&lt;span class="nt"&gt;d3237&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;//...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后有这么个 UI，用户可以定义字体、颜色 bulabula 各种：
&lt;img src="https://l.ruby-china.com/photo/2017/f643022cd7b1b21435a7e62f6330ed11.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;我有一点思路，但是也有问题把我卡住了：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;一开始想过直接生成文字 CSS，然后直接作内部样式表嵌入 HTML 里面去，但是因为是 SASS 的变量，而原本的 CSS 结构我们又不能修改，毕竟那些变量是多处用到的，所以这个方法走不通&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;第二个想法就是在用户保存完之后，生成一个 SCSS 文件，覆盖掉默认的 CSS 变量，然后加入编译，那这里也有遇到问题，一个是不确定在服务器上我们的 app 有没有权限去写这个文件，当然这个还是好解决的；另一个问题就是，这个文件写出来是 SCSS，需要编译才能使用，那每次用户修改完，都需要编译一次，这个可以放到后台队列里去做，但是这意味着，这个过程中我要调用 rake 命令去编译 CSS？那按理说一般 production 在 &lt;code&gt;rake compile&lt;/code&gt; 之后项目不都是要重启么，还是说有不重启的办法，或者说就不需要重启？……这个 app 的 production 在 AWS 上，staging 在 heroku 上（甲方选的环境），heroku 上我还真不确定能不能这么玩耍。&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;P.S. 每次发完帖子，都莫名有种想要删帖的冲动 &lt;img title=":joy:" alt="😂" src="https://twemoji.ruby-china.com/2/svg/1f602.svg" class="twemoji"&gt; &lt;/p&gt;</description>
      <author>hegwin</author>
      <pubDate>Fri, 06 Jan 2017 15:31:08 +0800</pubDate>
      <link>https://ruby-china.org/topics/32069</link>
      <guid>https://ruby-china.org/topics/32069</guid>
    </item>
    <item>
      <title>吐槽向: 竟然有 eslint 这种东西，缩进不对报错，花括号有空格报错，少写分号也报错</title>
      <description>&lt;p&gt;各种不能有空格：&lt;/p&gt;

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

&lt;p&gt;以及 string 必须单引号，属性必须单引号，缩进必须是 2 格……我觉得四格党和两格党会打起来：&lt;/p&gt;

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

&lt;p&gt;嗯，一定要写三等号，一定不要漏分号：&lt;/p&gt;

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

&lt;p&gt;自从有了这么个神奇玩意儿，CI 就完全没消停过……不是应该&lt;em&gt;“温柔地”&lt;/em&gt;给出警告么，结果直接 error 就 fail 了。作为强迫症患者，我觉得自己已经算是病入膏肓了，没想到写这个 rule（尽管可以配置）的人更加无情。&lt;/p&gt;

&lt;p&gt;refs: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/eslint/eslint" rel="nofollow" target="_blank" title=""&gt;eslint&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="http://eslint.org/docs/rules/" rel="nofollow" target="_blank" title=""&gt;rule&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;以及，还有 ruby 版本的 &lt;a href="https://github.com/bbatsov/rubocop" rel="nofollow" target="_blank" title=""&gt;rubocop&lt;/a&gt;，处女座们请接好。&lt;/p&gt;

&lt;p&gt;P.S 项目的 rule 是这样的，还真不是我们能决定的，是客户那边写的，有点苛刻，不过习惯也还好：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "parser": "babel-eslint",
  "ecmaFeatures": {
    "modules": true,
    "jsx": true
  },
  "env": {
    "browser": true,
    "jquery": true,
    "es6": true,
    "mocha": true
  },
  "rules": {
    "eqeqeq": 2,
    "no-else-return": 2,
    "camelcase": 0,
    "comma-dangle": 2,
    "no-multi-spaces": 2,
    "no-unused-vars": 2,
    "dot-notation": 2,
    "no-shadow": 2,
    "strict": 0,
    "indent": [2, 2],
    "semi": [2, "always"],
    "quotes": [2, "single", "avoid-escape"],
    "no-dupe-keys": 2,
    "no-underscore-dangle": 0,
    "no-undef": 2,
    "eol-last": 2,
    "react/jsx-boolean-value": [2, "always"],
    "react/jsx-curly-spacing": [2, "never"],
    "react/jsx-no-duplicate-props": [2, { "ignoreCase": true }],
    "react/jsx-no-undef": 2,
    "react/jsx-quotes": [2, "single", "avoid-escape"],
    "react/jsx-uses-react": 2,
    "react/jsx-uses-vars": 2,
    "react/no-danger": 2,
    "react/no-did-mount-set-state": 2,
    "react/no-did-update-set-state": 2,
    "react/no-multi-comp": 2,
    "react/no-unknown-property": 2,
    "react/prop-types": [2, { ignore: [
        "dispatch",
        "children",
        "className"
    ]}],
    "react/react-in-jsx-scope": 2,
    "react/self-closing-comp": 2,
    "react/wrap-multilines": 2
  },
  "plugins": [
    "react"
  ],
  "globals": {
    "global": false,
    "require": false,
    "gon": false,
    "module": false,
    "__dirname": false,
    "__filename": false,
    "analytics": false,
    "process": false,
    "LE": false,
    "goog_report_conversion": false
  }
}


&lt;/code&gt;&lt;/pre&gt;</description>
      <author>hegwin</author>
      <pubDate>Tue, 11 Oct 2016 19:30:47 +0800</pubDate>
      <link>https://ruby-china.org/topics/31288</link>
      <guid>https://ruby-china.org/topics/31288</guid>
    </item>
    <item>
      <title>CoffeeScript or ES2015?</title>
      <description>&lt;p&gt;之所以会有“CoffeeScript or ES2015?”这个问题，一是项目里 (目前) 无法共存，二是很好奇 coffee 以后会怎么发展，还会被 rails 默认引入么。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Coffee 没法和 ES2015 新语法兼容，也就不能写在同一个文件里&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;如果分开不同文件写，dev 模式倒是没问题，等到 production 编译时傻眼了…没法通过编译，于是不得不把 es2015 语法的 js 改回旧版本的语法。&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;目前觉得 ES2015 写起来还算蛮爽的，直观的感觉就是可以少写好多东西，比如 &lt;code&gt;function&lt;/code&gt; 这个字啊，还有对象的 shorthand 写法; 另外就是 &lt;code&gt;this&lt;/code&gt;，我觉得它到底指向哪里在 es2015 里清晰多了。&lt;/p&gt;</description>
      <author>hegwin</author>
      <pubDate>Tue, 04 Oct 2016 21:41:41 +0800</pubDate>
      <link>https://ruby-china.org/topics/31238</link>
      <guid>https://ruby-china.org/topics/31238</guid>
    </item>
    <item>
      <title>Some tips for writing tests</title>
      <description>&lt;p&gt;I met something tricky or interesting in writing test scripts and I want to share them.&lt;/p&gt;

&lt;p&gt;There are no Chinese input methods in this PC.  &lt;del&gt;And I'm too lazy to install one&lt;/del&gt;  Let me write in English.&lt;/p&gt;

&lt;p&gt;Hope they are helpful.&lt;/p&gt;
&lt;h2 id="Contents"&gt;Contents&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#Test%20with%20Floats" title=""&gt;Test with Floats&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#Expect%20to%20Change" title=""&gt;Expect to Change&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#Freeze%20Time" title=""&gt;Freeze Time&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#Test%20after_commit" title=""&gt;Test after_commit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#Test%20ActiveJob" title=""&gt;Test ActiveJob&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#Others" title=""&gt;Others&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="Test with Floats"&gt;Test with Floats&lt;/h2&gt;
&lt;p&gt;Operations on float numbers sometimes return things unexpectedly. For example:&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"sums two float numbers properly"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mf"&gt;0.1&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;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.8&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;Looks OK? But actually this is what you'll get:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;expected: 0.8
got: 0.7999999999999999

(compared using ==)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The reason is that Ruby follows IEEE, which may cause some imprecisions in float values. And I guess it varies with different systems or even Ruby versions. It's a very common issue within many programming languages.&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Javascript&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="mf"&gt;0.1&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// =&amp;gt; 0.7999999999999999&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Python 2.7.12
&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.7999999999999999&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But not all the languages.&lt;/p&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Lua 5.2.4&lt;/span&gt;
&lt;span class="nb"&gt;print&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="mi"&gt;1&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="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And fortunately, SQL works correctly.&lt;/p&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- SQL in (PostgreSQL) 9.5.4&lt;/span&gt;
&lt;span class="k"&gt;select&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="mi"&gt;1&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="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="k"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="k"&gt;sum&lt;/span&gt; 
&lt;span class="c1"&gt;-----&lt;/span&gt;
 &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The solution is simple, just use the &lt;code&gt;be_within&lt;/code&gt; matcher.&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"sums two float numbers properly"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mf"&gt;0.1&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;be_within&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.8&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;h2 id="Expect to Change"&gt;Expect to Change&lt;/h2&gt;
&lt;p&gt;This is my favorite syntax in RSpec. It shortens our codes when we need to assert value changes.&lt;/p&gt;

&lt;p&gt;For example in a controller spec:&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'schedules a new attack task'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;{&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="n"&gt;attack_params&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;change&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;AttackTask&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I pick a piece of codes from my game. This is not a very good example, because the conception of ControllerTest in Rails 5 has been changed.&lt;/p&gt;

&lt;p&gt;Actually, you can use a single &lt;code&gt;from&lt;/code&gt;, &lt;code&gt;to&lt;/code&gt; or &lt;code&gt;by&lt;/code&gt;, or a combination of them if it is meaningful, or even none of them. I really love this.&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increase&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;change&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;radix&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;change&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;radix&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;from&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="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;someone&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="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;change&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;someone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'dead'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reverse&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;change&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And lastly, a tricky example with what I noticed in the first section:&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mf"&gt;1.05&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;change&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;a_value_within&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.05&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;h2 id="Freeze Time"&gt;Freeze Time&lt;/h2&gt;
&lt;p&gt;In some situations, you have to work with time or durations. And it is complicated for us to test them as time will never stop.&lt;/p&gt;

&lt;p&gt;You don't have magic in the real world to freeze time, but in the programming world, yes, you do, with the power of &lt;a href="https://github.com/travisjeffery/timecop" rel="nofollow" target="_blank" title=""&gt;timecop&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A very simple example:&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'should assign ended_at'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;stubbed_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2016&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;Timecop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stubbed_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;finish!&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;change&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ended_at&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stubbed_time&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;h2 id="Test after_commit"&gt;Test after_commit&lt;/h2&gt;
&lt;p&gt;Ask yourself whether you really have to use &lt;code&gt;after_commit&lt;/code&gt;, then whether you need to test it.&lt;/p&gt;

&lt;p&gt;If both of the answers are 'yes', then you start to write you specs and you may face this issue that &lt;code&gt;after_commit&lt;/code&gt; callbacks are &lt;strong&gt;NOT&lt;/strong&gt; getting called in &lt;strong&gt;transactional&lt;/strong&gt; tests (at least in Rails &amp;lt; 5).&lt;/p&gt;

&lt;p&gt;What you need is to try this gem &lt;a href="https://github.com/grosser/test_after_commit" rel="nofollow" target="_blank" title=""&gt;test_after_commit&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Well, the good news is that it will be fixed in Rails 5 &lt;a href="https://github.com/rails/rails/pull/18458" rel="nofollow" target="_blank" title=""&gt;https://github.com/rails/rails/pull/18458&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="Test ActiveJob"&gt;Test ActiveJob&lt;/h2&gt;
&lt;p&gt;ActiveJob has become a component of Rails since 4.2. And you really don't need to test ActiveJob functionality. But I just want to cover everything as if I am a Vigro programmer. I can give an simple example of ActiveJob specs.&lt;/p&gt;

&lt;p&gt;One of my jobs:&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;NoticeMailForNewUserJob&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveJob&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;queue_as&lt;/span&gt; &lt;span class="ss"&gt;:default&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;AdminMailer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;notify_new_player_registration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;deliver_now&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;And configs:&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;MyProject&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Application&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Application&lt;/span&gt;

    &lt;span class="c1"&gt;#....something&lt;/span&gt;

    &lt;span class="c1"&gt;# ActiveJob&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;active_job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queue_adapter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:sidekiq&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;active_job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queue_name_prefix&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="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here are the specs:&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;'rails_helper'&lt;/span&gt;

&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;NoticeMailForNewUserJob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :job&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;ActiveJob&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TestHelper&lt;/span&gt;

  &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;create&lt;/span&gt; &lt;span class="ss"&gt;:user&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:job&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;described_class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform_later&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;clear_enqueued_jobs&lt;/span&gt;
    &lt;span class="n"&gt;clear_performed_jobs&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'queues the job'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;job&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;change&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;ActiveJob&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;queue_adapter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enqueued_jobs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'is in default queue'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;described_class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queue_name&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;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'test_default'&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;it&lt;/span&gt; &lt;span class="s1"&gt;'performs'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;AdminMailer&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;receive_message_chain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:notify_new_player_registration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:deliver_now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;perform_enqueued_jobs&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="Others"&gt;Others&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Don't use &lt;code&gt;respond_to&lt;/code&gt; to test methods generated by &lt;code&gt;#method_missing&lt;/code&gt;. It will not work.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run &lt;code&gt;$ rake stats&lt;/code&gt;, you can see the &lt;code&gt;Code to Test Ratio&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;</description>
      <author>hegwin</author>
      <pubDate>Mon, 19 Sep 2016 16:53:21 +0800</pubDate>
      <link>https://ruby-china.org/topics/31094</link>
      <guid>https://ruby-china.org/topics/31094</guid>
    </item>
    <item>
      <title>今天在 GitHub 上遇到一个机器人给我发了 Pull Request</title>
      <description>&lt;p&gt;不知道算不算火星了，第一次遇到这个事觉得挺好玩的。&lt;/p&gt;

&lt;p&gt;我在项目里用了一个 CDN 上的 js，但是最近这个 CDN 改了域名，从 npmcdn.com 改成了 unpkg.com。今天收邮件发现这个项目有个 PR，是一个 ID 叫 npmcdn-to-unpkg-bot 的账号发起的。PR 的内容只是帮我把 js 文件的地址修改成新的了。感觉好人性化。嘛…不过他是逐个逐个 repo 去扫描的么。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/441c422d571add3218b460cb675e4d89.jpg!large" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>hegwin</author>
      <pubDate>Mon, 05 Sep 2016 18:01:07 +0800</pubDate>
      <link>https://ruby-china.org/topics/30990</link>
      <guid>https://ruby-china.org/topics/30990</guid>
    </item>
    <item>
      <title>求助：几乎没有访问量，CPU 占用接近 0，但是内存被占满是为什么？</title>
      <description>&lt;p&gt;看下 2 月 7-9 日的数据，真的觉得匪夷所思啊，我是遇到内存泄露了吗？这种情况要怎么排查才好？&lt;/p&gt;

&lt;p&gt;详细说来是这样的，的确服务器上运行了 9 个实例可能比较多，可是我们还有比这个项目要大且复杂的多项目也是跑的 9 个实例，只占到 2~3G 的内存。&lt;/p&gt;

&lt;p&gt;我排查了下面这些原因：
没有文件加载读写，除了默认的日志
session 里没有存太多东西，只有用户登陆的 session
尝试去看了下 ObjectSpace 的东西，但是我不太明白怎么去分析这个玩意：&lt;/p&gt;

&lt;p&gt;开发环境下项目刚启动时，count 了下部分对象是这样的：
Symbol =&amp;gt; 28823
String =&amp;gt; 139372
Array =&amp;gt; 35909
Hash =&amp;gt; 4689
内存（4G）占用 4.2%&lt;/p&gt;

&lt;p&gt;跑着跑着会慢慢增加：
"=============================="
Symbol =&amp;gt; 30069
String =&amp;gt; 481743
Array =&amp;gt; 64058
Hash =&amp;gt; 12637&lt;/p&gt;

&lt;p&gt;6.4%&lt;/p&gt;

&lt;p&gt;"=============================="
Symbol =&amp;gt; 30142
String =&amp;gt; 528109
Array =&amp;gt; 55563
Hash =&amp;gt; 15242&lt;/p&gt;

&lt;p&gt;7.2%&lt;/p&gt;

&lt;p&gt;"=============================="
Symbol =&amp;gt; 30726
String =&amp;gt; 550410
Array =&amp;gt; 45791
Hash =&amp;gt; 16344&lt;/p&gt;

&lt;p&gt;7.6%&lt;/p&gt;

&lt;p&gt;"=============================="
Symbol =&amp;gt; 30726
String =&amp;gt; 921422
Array =&amp;gt; 79129
Hash =&amp;gt; 25882&lt;/p&gt;

&lt;p&gt;9.2%&lt;/p&gt;

&lt;p&gt;"=============================="
Symbol =&amp;gt; 30749
String =&amp;gt; 545869
Array =&amp;gt; 63066
Hash =&amp;gt; 16949&lt;/p&gt;

&lt;p&gt;9.4%&lt;/p&gt;

&lt;p&gt;"==============================".
Symbol =&amp;gt; 30787
String =&amp;gt; 671939
Array =&amp;gt; 64253
Hash =&amp;gt; 21327&lt;/p&gt;

&lt;p&gt;9.6%&lt;/p&gt;

&lt;p&gt;另外最开始提到的 NewRelic 的监视的图
&lt;img src="https://l.ruby-china.com/photo/2015/365ebade2f2aa6c7dee53020fe673b10.png" title="" alt=""&gt;
&lt;img src="https://l.ruby-china.com/photo/2015/ce996a3b3d2ccfe8eaf07625f2574f6a.png" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>hegwin</author>
      <pubDate>Fri, 13 Feb 2015 14:38:50 +0800</pubDate>
      <link>https://ruby-china.org/topics/24250</link>
      <guid>https://ruby-china.org/topics/24250</guid>
    </item>
    <item>
      <title>[已结束] 苏州金鸡湖半程马拉松，有一起来玩的小伙伴么</title>
      <description>&lt;p&gt;如题，有木有一起的小伙伴？&lt;/p&gt;

&lt;p&gt;本人第一次跑半程，去年 10 月开始准备的……对，当时也是为了 RubyConfChina 的 VIP 门票的那个活动，不过没中奖，但是后来一直坚持着跑步了。今天跑了 16.5km，用了 1 小时 25 分，结束之后感觉还算轻松，由此观之，如果不出意外，跑到终点应该没问题。 ~~ 我要求真低 ~~ &lt;/p&gt;

&lt;p&gt;以下是吐槽：
第一次报名啊，不知道这马拉松 &lt;strong&gt;报名还得秒杀&lt;/strong&gt; 啊！！！！&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;苏州银行 2014 第五届苏州环金鸡湖国际半程马拉松赛，由于人数已达到规定的上限，现全面截止报名。 
小伙伴们不用着急，组委会尚有部分预留名额，3 月 8 日 -3 月 17 日您可以通过苏州银行手机客户端进行报名缴费，名额有限，可千万不要错过哦！ &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;然后我就去办了银行卡……没错！！我为了跑个步办了张卡！！我一定会在跑完之后注销的！！
&lt;img src="//l.ruby-china.com/photo/2014/31317f35928da09b49e43f41a7b9f50b.jpg" title="" alt=""&gt;
回到家，开始搜索 XX 银行的手机客户端，咦？？？！！啊咧咧咧咧！！！为什么没得手机 APP 下载？！！！问客服，她说，我们手机客户端还没开放！！！！
好吧，我等到 3 月 8 号！！！那天还是周六，真真是等到那天他们的手机 APP 才发布，我了个大去。
&lt;img src="//l.ruby-china.com/photo/2014/7d4c206e53eeea0639868c77df73199b.jpg" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;P.S. 删除线失灵了发帖不愉悦！&lt;/p&gt;</description>
      <author>hegwin</author>
      <pubDate>Sat, 22 Mar 2014 23:52:49 +0800</pubDate>
      <link>https://ruby-china.org/topics/18101</link>
      <guid>https://ruby-china.org/topics/18101</guid>
    </item>
    <item>
      <title>2 个 Ruby 题目，有效率要求</title>
      <description>&lt;p&gt;话说我之前有在 oDesk 注册过，最近收到他们一个邮件，被邀请参加一个 test 什么的，觉得好玩就去了，结果没搞定，大家觉得愉♂悦的话可以帮忙瞧瞧。&lt;/p&gt;

&lt;p&gt;如果之前有人发过，就让他沉了吧……&lt;/p&gt;

&lt;p&gt;2 道题目，60 分钟&lt;/p&gt;
&lt;h2 id="Challenge 1: Sequence"&gt;Challenge 1: Sequence&lt;/h2&gt;
&lt;p&gt;Given an array of integer numbers&lt;/p&gt;
&lt;h2 id="Your task is to"&gt;Your task is to&lt;/h2&gt;
&lt;p&gt;write a function that prints to the standard output (stdout) the largest sum that can be obtained using any sequence of consecutive elements from the array&lt;/p&gt;
&lt;h2 id="Note that your function will receive the following arguments:"&gt;Note that your function will receive the following arguments:&lt;/h2&gt;
&lt;p&gt;numbers
        which is the array of integers described above&lt;/p&gt;
&lt;h2 id="Data constraints"&gt;Data constraints&lt;/h2&gt;
&lt;p&gt;the length of the array will not exceed 200,000
    each element of the array will be in the [-10000, 10000]&lt;/p&gt;
&lt;h2 id="Efficiency constraints"&gt;Efficiency constraints&lt;/h2&gt;
&lt;p&gt;your function is expected to print the requested result and &lt;strong&gt;return in less than 2 seconds&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="Example"&gt;Example&lt;/h2&gt;
&lt;p&gt;Input    numbers: 2, -3, 2, 2, -1, 2, -2, 1&lt;/p&gt;

&lt;p&gt;Output:       5&lt;/p&gt;

&lt;p&gt;Explanation:&lt;/p&gt;

&lt;p&gt;The sequence: 2, 2, -1, 2 has sum 5 which is the largest possible.&lt;/p&gt;
&lt;h2 id="Challenge 2: Store"&gt;Challenge 2: Store&lt;/h2&gt;
&lt;p&gt;All products from an ecommerce site are stored as a list of strings representing their names.&lt;/p&gt;

&lt;p&gt;Whenever a user starts typing the name of a product the website immediately suggests the full product name that matches the query string.&lt;/p&gt;

&lt;p&gt;If a query string is prefix for a product name then they match.&lt;/p&gt;

&lt;p&gt;Given the list of products and a list of query strings&lt;/p&gt;
&lt;h2 id="Your task is to"&gt;Your task is to&lt;/h2&gt;
&lt;p&gt;* write a function that prints to the standard output (stdout) for each query the product name that matches the query
    * if there are multiple products matching the query please select the one that is the smallest lexicographically
    * all string matches must be case insensitive
    * if no match is found for a given query please print -1&lt;/p&gt;
&lt;h2 id="Note that your function will receive the following arguments:"&gt;Note that your function will receive the following arguments:&lt;/h2&gt;
&lt;p&gt;products
        which is a an array of strings representing the names of the products
    queries
        which is an array of strings representing the queries described above&lt;/p&gt;
&lt;h2 id="Data constraints"&gt;Data constraints&lt;/h2&gt;
&lt;p&gt;the length of the arrays above will not exceed 100,000 entries
    each product name or query string will not exceed 30 characters&lt;/p&gt;
&lt;h2 id="Efficiency constraints"&gt;Efficiency constraints&lt;/h2&gt;
&lt;p&gt;your function is expected to print the result &lt;strong&gt;in less than 2 seconds&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="Example"&gt;Example&lt;/h2&gt;
&lt;p&gt;Input   &lt;/p&gt;

&lt;p&gt;products: ["boat", "bike", "phone"]
queries: ["b", "bk", "PHo", "bO"]&lt;/p&gt;

&lt;p&gt;Output&lt;/p&gt;

&lt;p&gt;bike
-1
phone
boat&lt;/p&gt;

&lt;p&gt;然后是我的答案，可能都比较暴力的缘故，所以效率上比较差
另：不要吐嘈为何是 4 个空格，那个 test 的编辑器 tab 就是 4 个，复制下来懒得改了 :(
第一题（我没怎么研究过算法……感觉这个应该可以从算法上改进的）&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;max_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;max_sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zero?&lt;/span&gt;
        &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;    

        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;len&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="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;start&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;len&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="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;last&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
                &lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
                &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="n"&gt;max_sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;max_sum&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;sum&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;max_sum&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;6 个 test 过了 2 个，剩下 4 个超时……
Test 0: OK!
Test 1: OK!
Test 2: Error: your code didn't finish in less than 2 seconds.
Test 3: Error: your code didn't finish in less than 2 seconds.
Test 4: Error: your code didn't finish in less than 2 seconds.
Test 5: Error: your code didn't finish in less than 2 seconds.&lt;/p&gt;

&lt;p&gt;第二题&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;search_products&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;queries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Write your code here&lt;/span&gt;
    &lt;span class="c1"&gt;# To print results to the standard output you can use puts&lt;/span&gt;
    &lt;span class="c1"&gt;# Example puts "Hello world!"&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;
    &lt;span class="n"&gt;queries&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;query&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="n"&gt;found&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
        &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;downcase&lt;/span&gt;
        &lt;span class="n"&gt;products&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;product&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;downcase&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;
                &lt;span class="n"&gt;found&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
                &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;
                &lt;span class="k"&gt;break&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;results&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;found&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;6 个 test 过了 3 个，剩下 3 个超时……如图：
&lt;img src="//l.ruby-china.com/photo/2013/d0e0825a6a6d3678088b28931424fde7.jpg" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>hegwin</author>
      <pubDate>Mon, 23 Dec 2013 14:18:10 +0800</pubDate>
      <link>https://ruby-china.org/topics/16375</link>
      <guid>https://ruby-china.org/topics/16375</guid>
    </item>
    <item>
      <title>你们遇到过 validates_associated 会给出重复的 errors 信息的吗？</title>
      <description>&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;Company&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;AR&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:brands&lt;/span&gt;
  &lt;span class="n"&gt;validates_associated&lt;/span&gt; &lt;span class="ss"&gt;:brands&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;Brand&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;AR&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:company&lt;/span&gt;
  &lt;span class="n"&gt;validates_presence_of&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后前台更新 Company 的时候，如果添加一个 brand 但是不填名字，会给出两条出错的信息。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Brands is invalid&lt;/li&gt;
&lt;li&gt;Brands is invalid&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;BTW，如果添加两个 brand 且不填名字。会给出 3 条……&lt;/p&gt;

&lt;p&gt;我进 rails console 之后去试验，也会有同样的问题&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;c&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;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;valid?&lt;/span&gt;
  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;brands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;#&amp;lt;Brand id: nil, custom: nil, name: nil, company_id: 1, created_at: nil, updated_at: nil&amp;gt;&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;valid?&lt;/span&gt;
  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;full_message&lt;/span&gt;
  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Brands is invalid"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Brands is invalid"&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="nf"&gt;brands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;full_message&lt;/span&gt;
  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Name required"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;</description>
      <author>hegwin</author>
      <pubDate>Wed, 31 Jul 2013 14:20:14 +0800</pubDate>
      <link>https://ruby-china.org/topics/12940</link>
      <guid>https://ruby-china.org/topics/12940</guid>
    </item>
    <item>
      <title>JS 加载出的元素，Capybara 抛错 Capybara::ElementNotFound</title>
      <description>&lt;p&gt;背景：rspec+capybara 给一个 sinatra 的项目做测试&lt;/p&gt;

&lt;p&gt;页面上有一段 div 元素（里面有个 input），原先是隐藏的。&lt;/p&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"control-group"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"execute-time"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"display: none"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"control-label"&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"ExecuteTime"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;ExecuteTime&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"controls"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"input-small timepicker"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"schedule[ExecuteTime][]"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btn btn-mini add-time"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Add More&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在一个选择框选择某个选项后，上面那个 div 才会显示出来。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&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="s2"&gt;div#frequence select&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;change&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;//大致就是当select的val() == 'daily'&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;div#execute-time&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;slideDown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;500&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;自己手动在浏览器点击是好的，但是下面这个测试代码运行的时候就会出错&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require_relative&lt;/span&gt; &lt;span class="s1"&gt;'../spec_helper'&lt;/span&gt;

&lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s1"&gt;'Schedules'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

  &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="k"&gt;do&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;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;basic_authorize&lt;/span&gt; &lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'password'&lt;/span&gt;
    &lt;span class="n"&gt;visit&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;it&lt;/span&gt; &lt;span class="s2"&gt;"should create new schedule"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;click_link&lt;/span&gt; &lt;span class="s1"&gt;'New Schedule'&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;should&lt;/span&gt; &lt;span class="n"&gt;have_selector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"h1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:text&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"New Schedule"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;within&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:css&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'div.form'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="c1"&gt;# 填其他的值之类的&lt;/span&gt;
      &lt;span class="nb"&gt;select&lt;/span&gt; &lt;span class="s1"&gt;'daily'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:from&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'schedule[Frequence]'&lt;/span&gt;
      &lt;span class="n"&gt;fill_in&lt;/span&gt; &lt;span class="s1"&gt;'schedule[ExcuteTime][]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:with&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"08:00"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;click_button&lt;/span&gt; &lt;span class="s1"&gt;'Create'&lt;/span&gt;
    &lt;span class="c1"&gt;# .....&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;出错的信息是这样的&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Capybara&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ElementNotFound&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="n"&gt;cannot&lt;/span&gt; &lt;span class="n"&gt;fill&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="n"&gt;area&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="s1"&gt;'schedule[ExcuteTime][]'&lt;/span&gt; &lt;span class="n"&gt;found&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我尝试去 &lt;code&gt;p page.html&lt;/code&gt;，结果发现那个 div 的 style 依然是&lt;code&gt;display: none&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;嗯，这边是 rspec_helper.rb 的代码。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"RACK_ENV"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"test"&lt;/span&gt;

&lt;span class="nb"&gt;require_relative&lt;/span&gt; &lt;span class="s2"&gt;"../config/boot.rb"&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'rspec'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'capybara'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'capybara/dsl'&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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Sinatra&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Application&lt;/span&gt;

&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;before&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;FileUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rm&lt;/span&gt; &lt;span class="no"&gt;LOCK_FILE&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt; &lt;span class="no"&gt;LOCK_FILE&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;另外，我尝试去给 decribe 加了个 &lt;code&gt;:js =&amp;gt; true&lt;/code&gt;，然后 在 spec_helper.rb 里写上&lt;code&gt;require 'capybara/rspec'&lt;/code&gt;，结果是会报另个错误：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;NoMethodError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="n"&gt;undefined&lt;/span&gt; &lt;span class="nb"&gt;method&lt;/span&gt; &lt;span class="sb"&gt;`authorize' for #&amp;lt;Selenium::WebDriver::Driver:0x1c270508 browser=:firefox&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我把&lt;code&gt;authorize&lt;/code&gt;换了好几个写法，比如&lt;code&gt;basic_auth&lt;/code&gt;或者&lt;code&gt;basic_authorize&lt;/code&gt;等等，都会报这个错&lt;/p&gt;</description>
      <author>hegwin</author>
      <pubDate>Wed, 30 Jan 2013 02:28:25 +0800</pubDate>
      <link>https://ruby-china.org/topics/8473</link>
      <guid>https://ruby-china.org/topics/8473</guid>
    </item>
    <item>
      <title>Sinatra 项目里的文件如何拆分？</title>
      <description>&lt;p&gt;呃，叙事之前介绍背景总归是好的。那么，事情的经过是这样的：我们要做个小的项目，只是读写文件，不用连接数据库，也没什么复杂的逻辑。鉴于需求和实现都挺简单，于是我们就选择了 sinatra 这个框架。&lt;/p&gt;

&lt;p&gt;那开发之初，在项目的根目录下，于是就有了一个 application.rb 的文件，一个 views 目录和 public 目录，当然还有万众期待（雾）的 README。&lt;/p&gt;

&lt;p&gt;S01&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Root Directory
  |--public/
  |  |--CSS and JS here
  |--views/
  |  |--HTML here
  |--aplication.rb
  |--README.md
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;后来随着开发的进行，我们需要一些额外的 gem，于是就建了 Gemfile 这个文件，用于控制 gem 和其版本。&lt;/p&gt;

&lt;p&gt;S02&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Root Directory
  |--public/
  |  |--CSS and JS here
  |--views/
  |  |--HTML here
  |--aplication.rb
  |--Gemfile
  |--Gemfile.lock
  |--README.md
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;前面说过我们要读文件嘛，然后有的文件解析比较复杂点，放在 application.rb 会没法看出重点，又没有现成的 gem 用，于是自己简单写了个，放在 lib 目录下面。&lt;/p&gt;

&lt;p&gt;S03&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Root Directory
  |--lib/
  |  |--Some .rb files here
  |--public/
  |  |--CSS and JS here
  |--views/
  |  |--HTML here
  |--aplication.rb
  |--Gemfile
  |--Gemfile.lock
  |--README.md
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;后来项目需要进行一些数据的初始化，还需要一两个用于登陆的角色，鉴于之前都没用数据库，这里也只是把这些信息写在 yml 文件里，放在 config 这个目录下。&lt;/p&gt;

&lt;p&gt;S04&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Root Directory
  |--config/
  |  |--config.yml
  |  |--user.yml
  |--lib/
  |  |--Some .rb files here
  |--public/
  |  |--CSS and JS here
  |--views/
  |  |--HTML here
  |--aplication.rb
  |--Gemfile
  |--Gemfile.lock
  |--README.md
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;好，最后要部署了，加了 config.ru，然后还有些必要的文件，比如 tmp 和 log 这俩目录，那最终的结构就是这样的。&lt;/p&gt;

&lt;p&gt;S05&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Root Directory
  |--config/
  |  |--config.yml
  |  |--user.yml
  |--lib/
  |  |--Some .rb files here
  |--log/
  |--public/
  |  |--CSS and JS here
  |--tmp/
  |--views/
  |  |--HTML here
  |--aplication.rb
  |--config.ru
  |--Gemfile
  |--Gemfile.lock
  |--README.md
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;目前已经基本就是这样，那么下一步的打算是拆分 application.rb，原因是这个文件日渐臃肿并且 configure，helper 和那些 &lt;code&gt;get '/xxx' do; miao!; end&lt;/code&gt;混在一个文件里，文件行数很多不太爽。于是我就好奇这个应该要怎么拆才好。
我希望像 rails 那样有个 app 目录，把 application.rb 拆出来的文件丢进去，但是仍然犹疑不决。
helper 单独拿出来，这个没啥争议。
configure 也可以拿出来，但是一般这个文件似乎是放在根目录下吧。
然后那堆&lt;code&gt;get '/xxx' do; yeah!; end&lt;/code&gt;，这种到底算是 routes 呢还是 controller 呢……十分纠结，我还写了一些 before filter，如果那些算作是 routes 的话，before filter 和 routes 放在一起似乎不太合适。
在 application.rb 里还有一部分是作为 http 基本验证的部分，类似&lt;code&gt;use Rack::Auth::Basic do |username, password|&lt;/code&gt;，似乎是独立的逻辑，不知往何处放。
我去 github（默哀）上参考了些 sinatra 的项目，似乎也没什么定式……&lt;/p&gt;

&lt;p&gt;事情和我自己的想法就是这样，其实就现在的情况而言，我不去拆分 application.rb 也行，但是现在由于之前的 routes 写的不好，最终也免不了要改一番。如果是出于“这个文件不拆就看不下去”的理由，各位能否给点意见呢。&lt;/p&gt;</description>
      <author>hegwin</author>
      <pubDate>Tue, 22 Jan 2013 17:11:25 +0800</pubDate>
      <link>https://ruby-china.org/topics/8267</link>
      <guid>https://ruby-china.org/topics/8267</guid>
    </item>
    <item>
      <title>请教下 Sinatra 的项目 (或者说 Rack based 项目) 如何部署</title>
      <description>&lt;p&gt;关于 rails 项目的部署，网上的 step by step 的攻略很多。而我最近用 sinatra 写了个 app，一直不知道怎么去部署他，每次服务器重启之后都需要登陆上去，手动去启动。&lt;/p&gt;

&lt;p&gt;涉及到 rack-based 这方面我可能了解不多，只是了解到需要在项目跟目录下有一个 config.ru，我就抄了一个过来。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&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="s1"&gt;'application'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="no"&gt;Sinatra&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Application&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在在项目根目录下，运行&lt;code&gt;thin start&lt;/code&gt;，项目也的确可以跑起来。可是然后应该怎么做呢？&lt;/p&gt;

&lt;p&gt;===汗死了，和 rails 一样部署就行了，请无视我===&lt;/p&gt;</description>
      <author>hegwin</author>
      <pubDate>Tue, 08 Jan 2013 10:51:47 +0800</pubDate>
      <link>https://ruby-china.org/topics/7864</link>
      <guid>https://ruby-china.org/topics/7864</guid>
    </item>
    <item>
      <title>表间关联时候 attr_accessible 该怎么写</title>
      <description>&lt;p&gt;最近用 rails3.2.3 遇到的问题，在这个论坛里看到好几个这样的帖子，但是也没看到解决的方法&lt;/p&gt;

&lt;p&gt;用 generate 自动生成的 model 里 attr_accessible 后面都是那个 model 在数据库对应的表的字段，这个倒是好理解
比如说 product 里有 title 和 price&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt; &lt;span class="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;attr_accessible&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:price&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那如果是表间关联的时候（belongs_to）,这个 attr_accessible 应该怎么写？
假设产品 (products) 和类别 (categories) 这样&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt; &lt;span class="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;attr_accessible&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:price&lt;/span&gt;

  &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:category&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Category&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:products&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;假设有如下代码&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;category&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Category&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="no"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;title: &lt;/span&gt;&lt;span class="s2"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;price: &lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;categoy: &lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;直接这么写的话会报错：
&lt;code&gt;ActiveModel::MassAssignmentSecurity::Error: Can't mass-assign protected attributes: category&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;改环境里那个配置总觉得不太好。。。  &lt;/p&gt;</description>
      <author>hegwin</author>
      <pubDate>Thu, 14 Jun 2012 14:45:35 +0800</pubDate>
      <link>https://ruby-china.org/topics/3806</link>
      <guid>https://ruby-china.org/topics/3806</guid>
    </item>
  </channel>
</rss>
