<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>jesktop (Ray)</title>
    <link>https://ruby-china.org/jesktop</link>
    <description>欲变世界，先变其身。</description>
    <language>en-us</language>
    <item>
      <title> [远程][兼职] 广州营地学园科技找兼职 Rails 全栈 和 小程序前端工程师 [已结束]</title>
      <description>&lt;p&gt;地点：不限&lt;/p&gt;

&lt;p&gt;项目简介：&lt;/p&gt;

&lt;p&gt;该项目 2022 年 6 月份开始开发，服务器端使用的是 Rails，还包括有小程序和 App 端，App 是使用 Flutter 开发的。目前 Rails 端的开发主要是补全后台功能，所以除了 Model 层已经建立好了，工作内容主要是 Controller、View 层和前端交互的事情。&lt;/p&gt;

&lt;p&gt;项目 7 月已经上线，并且在 7、8、9 月期间已经正常使用，业务逻辑也基本清晰。&lt;/p&gt;

&lt;p&gt;主要使用的时期是暑假和寒假期间，所以目前主要应对的是寒假的使用，完善后台功能，方便工作人员对所有时间的操作。&lt;/p&gt;

&lt;hr&gt;
&lt;h2 id="Rails 全栈工程师"&gt;Rails 全栈工程师&lt;/h2&gt;
&lt;p&gt;工作职责：
每周兼职工作 10+ 小时，最好可以明确每个月能够兼职的时间。&lt;/p&gt;

&lt;p&gt;营地学员后台系统全栈开发，使用的是 Rails 7.0.4 版本，前端使用的是 Rails 的前端方案 Turbo Streams，CSS 使用的是 Tailwindcss。&lt;/p&gt;

&lt;p&gt;兼职当前主要负责的功能是排班系统，例如，每个学校里的学员进行转校、拖拽排班等操作，方便工作人员对于每个学员进行调班操作。有此类开发经验的，优先选择。&lt;/p&gt;

&lt;hr&gt;
&lt;h2 id="前端工程师"&gt;前端工程师&lt;/h2&gt;
&lt;p&gt;工作职责：&lt;/p&gt;

&lt;p&gt;小程序的前端开发&lt;/p&gt;

&lt;p&gt;目前小程序已经在使用，但是由于需要 UI 全部重新替换。
新的 UI 会稍微复杂些。目前工作主要是替换 UI。&lt;/p&gt;

&lt;hr&gt;
&lt;h2 id="待遇："&gt;待遇：&lt;/h2&gt;
&lt;p&gt;时薪：大约 100~200 元/小时，具体可根据能力谈。&lt;/p&gt;

&lt;p&gt;工作优势：
工作时间灵活、远程。&lt;/p&gt;

&lt;p&gt;如果有兴趣，简历可以发至 frayay at gmail.com (24 小时内回复)&lt;/p&gt;</description>
      <author>jesktop</author>
      <pubDate>Fri, 28 Oct 2022 15:22:15 +0800</pubDate>
      <link>https://ruby-china.org/topics/42708</link>
      <guid>https://ruby-china.org/topics/42708</guid>
    </item>
    <item>
      <title>Rails 5.2 中的 Credentials 和 Active Storage</title>
      <description>&lt;p&gt;看到很多朋友们对 Rails 5.2 里的新东西，感觉云里雾里的，不太清楚它们究竟为我们提供了什么便利，所以我这里主要讲讲新版本中的 Credentials 和 Active Storage。&lt;/p&gt;
&lt;h2 id="为什么使用credentials？"&gt;为什么使用 credentials？&lt;/h2&gt;
&lt;p&gt;先回顾在 5.2 版本以前，我们为了处理很多私密的信息，需要在&lt;code&gt;.gitignore&lt;/code&gt;里加入多少的 yml 文件，有&lt;code&gt;secrets.yml&lt;/code&gt;、&lt;code&gt;database.yml&lt;/code&gt;、&lt;code&gt;cable.yml&lt;/code&gt;，甚至会加入一些自定义的配置文件，如：&lt;code&gt;application.yml&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;然后在部署配置中：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:linked_files&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:linked_files&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]).&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'config/database.yml'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'config/secrets.yml'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'config/cable.yml'&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而我们升级到 Rails 5.2 后，很可能，只需要在&lt;code&gt;.gitignore&lt;/code&gt;加入&lt;code&gt;master.key&lt;/code&gt;一个文件，就可以处理所有的私密信息了。究竟&lt;code&gt;credentials.yml.enc&lt;/code&gt;和&lt;code&gt;master.key&lt;/code&gt;是怎么办到的呢？&lt;/p&gt;
&lt;h2 id="把私密信息放入credentials中"&gt;把私密信息放入 credentials 中&lt;/h2&gt;
&lt;p&gt;因为我们在使用&lt;code&gt;credentials.yml.enc&lt;/code&gt;后，可以把所有私密的信息都放到里面，然后通过&lt;code&gt;master.key&lt;/code&gt;进行解密。所以当我们团队在一起完成一个项目时，需要分享&lt;code&gt;master.key&lt;/code&gt;文件，但是考虑到安全的问题，不要把&lt;code&gt;master.key&lt;/code&gt;上传到 git 的仓库中。&lt;/p&gt;

&lt;p&gt;当我们打开&lt;code&gt;credentials.yml.enc&lt;/code&gt;发现内容是一串随机串，如下：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;qEbH&lt;/span&gt;&lt;span class="o"&gt;/+&lt;/span&gt;&lt;span class="n"&gt;fadmlPVeMVgFvpwk4ADadW2LbMkzMKJKkHrZeFNooiKKJvOoe5YJlbab1wJLHL77nSohvEm6MYnl9krXLFnDG0iSWm&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;svtipruMCc1FVfhSpmXSvLNJI1RUk2VeZCFjYkT8&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="no"&gt;PG4N7oj1OLrSq4yeRsbKTrS&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="n"&gt;izcMm9ndJkcd4&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;wR7WAMReQSRGt5YGNZ4E3Jt9Wgg7ls2okZcwxEv&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="n"&gt;brdgIyHrmfyEWb50YSe5oDDyscfRNX70uwZieSVGn99fFcexYUL8F0dxSrVNaix&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="no"&gt;UAeApq6Ifhs0&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;p9eXk0349f8dEMFkp5A3I4j0ubgjZ&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ncdLTct37OxxhfucWukCtP6oSFvpC&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="n"&gt;ma2epjjTSJM25&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="no"&gt;Vv3GQy7xfSdwbsEq8jm3tqT&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;zGr2M9iRuEX&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="no"&gt;LJrxzhDHnHC0&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;jQ&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="no"&gt;G6M9HWGe7zlFb&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;ltdsqYuI&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="no"&gt;O8cqw&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bcJRJw&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;非常棒，&lt;code&gt;secret_key_base&lt;/code&gt;之类的信息都在里面安全的保护着，可是如果我要修改里面的内容，怎么办？我们不可能直接修改加密后的内容啊。&lt;/p&gt;

&lt;p&gt;不要着急，Rails 这里给我们提供了一个方便的方法：&lt;code&gt;EDITOR="mate --wait" bin/rails credentials:edit&lt;/code&gt;。当然，EDITOR 的信息需要根据我们使用的工具进行调整。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;EDITOR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;vim&lt;/span&gt; &lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;rails&lt;/span&gt; &lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="ss"&gt;:edit&lt;/span&gt;  &lt;span class="c1"&gt;# 使用VIM编辑&lt;/span&gt;
&lt;span class="no"&gt;EDITOR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"subl --wait"&lt;/span&gt; &lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;rails&lt;/span&gt; &lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="ss"&gt;:edit&lt;/span&gt;  &lt;span class="c1"&gt;# 使用Sublime编辑&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="c1"&gt;# aws:&lt;/span&gt;
&lt;span class="c1"&gt;#   access_key_id: 123&lt;/span&gt;
&lt;span class="c1"&gt;#   secret_access_key: 345&lt;/span&gt;

&lt;span class="c1"&gt;# Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies.&lt;/span&gt;
&lt;span class="ss"&gt;secret_key_base: &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;b4d8892f41d7b89127b3ad997ca9ca581fe7f84bf890c85095b635c651a9de00edd1032aeb377a5753f0236e526cb61d995d01fc3d52bb5644a33dbfbc69335&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以我们就可以在里面输入需要加密的信息，例如输入在生产环境里，数据库的密码：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="ss"&gt;production:
  database:
    database: &lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;
    &lt;span class="ss"&gt;password: &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="n"&gt;f797275f3f&lt;/span&gt;
&lt;span class="o"&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="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;credentials&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;env&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="ss"&gt;:database&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="ss"&gt;:database&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;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;credentials&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;env&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="ss"&gt;:database&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="ss"&gt;:password&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;剩下部署的时候，就记得把&lt;code&gt;master.key&lt;/code&gt;放到服务器里，不要弄丢了。&lt;/p&gt;
&lt;h2 id="如何使用Active Storage"&gt;如何使用 Active Storage&lt;/h2&gt;
&lt;p&gt;因为 Active Storage 和 Rails 结合紧密，所以使用起来很方便。在项目中运行&lt;code&gt;rails active_storage:install&lt;/code&gt;，就会发现生成了一个数据迁移的文件，里面会给我们加入两个表，分别是&lt;code&gt;active_storage_blobs（储存文件信息）&lt;/code&gt;和&lt;code&gt;active_storage_attachments（与业务表的多对多关系）&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;如果我们需要在&lt;code&gt;Project&lt;/code&gt;模型中，保存&lt;code&gt;image&lt;/code&gt;信息时，就不需要像使用&lt;code&gt;CarrierWave&lt;/code&gt;时，在&lt;code&gt;Project&lt;/code&gt;中另外添加字段，因为&lt;code&gt;Active Storage&lt;/code&gt;会把文件信息直接保存到&lt;code&gt;active_storage_blobs&lt;/code&gt;和&lt;code&gt;active_storage_attachments&lt;/code&gt;中，我们只需要在&lt;code&gt;Project&lt;/code&gt;的模型中加入：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;has_one_attached&lt;/span&gt; &lt;span class="ss"&gt;:image&lt;/span&gt;  &lt;span class="c1"&gt;## 一对一关系 或&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="c1"&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="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:project&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# 一对一关系 或&lt;/span&gt;
&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:project&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;images: &lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt;   &lt;span class="c1"&gt;# 一对多关系&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;##通过 Active Storage 上传的文件会保存在哪里？
通过&lt;code&gt;config/storage.yml&lt;/code&gt;，我们可以看到：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="ss"&gt;test:
  service: &lt;/span&gt;&lt;span class="no"&gt;Disk&lt;/span&gt;
  &lt;span class="ss"&gt;root: &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= Rails.root.join("tmp/storage") %&amp;gt;

local:
  service: Disk
  root: &amp;lt;%=&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;root&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="s2"&gt;"storage"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;默认的&lt;code&gt;test&lt;/code&gt;和&lt;code&gt;local&lt;/code&gt;是保存在本地的 Disk 中的，&lt;code&gt;local&lt;/code&gt;默认把文件保存在&lt;code&gt;storage&lt;/code&gt;文件夹中，所如果我们想要把文件保存在 public 内，可以修改成：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="ss"&gt;root: &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= Rails.root.join("public/storage") %&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当然无论我们保存在什么位置，记得把该目录加入到&lt;code&gt;.gitignore&lt;/code&gt;中。&lt;/p&gt;

&lt;p&gt;那么在开发环境中，它是如何知道使用&lt;code&gt;local&lt;/code&gt;配置的呢？在&lt;code&gt;config/environments/development.rb&lt;/code&gt;中，我们可以看到：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Store files locally.&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_storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:local&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以，如果我们生产环境也希望保存在&lt;code&gt;Disk&lt;/code&gt;的话，只需要修改&lt;code&gt;config/environments/production.rb&lt;/code&gt;即可，当然我们会建议使用云服务，因为结合&lt;code&gt;Active Storage&lt;/code&gt;可以让一切变得很简单，例如保存在&lt;code&gt;aws&lt;/code&gt;，只需要：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/environments/production.rb&lt;/span&gt;
&lt;span class="c1"&gt;# Store files on Amazon S3.&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_storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:amazon&lt;/span&gt;

&lt;span class="c1"&gt;# config/storage.yml&lt;/span&gt;
&lt;span class="ss"&gt;amazon:
  service: &lt;/span&gt;&lt;span class="no"&gt;S3&lt;/span&gt;
  &lt;span class="ss"&gt;access_key_id: &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
  &lt;span class="ss"&gt;secret_access_key: &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
  &lt;span class="ss"&gt;region: &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
  &lt;span class="ss"&gt;bucket: &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在前端，可以看到&lt;code&gt;application.js&lt;/code&gt;里加入了&lt;code&gt;//= require activestorage&lt;/code&gt;，通过&lt;code&gt;activestorage&lt;/code&gt;可以使用&lt;code&gt;&amp;lt;%= form.file_field :image, direct_upload: true %&amp;gt;&lt;/code&gt;，全交给客户端直接往云服务器上传文件，避免经过服务器。&lt;/p&gt;

&lt;p&gt;同时，如果需要调整图片尺寸大小时，我们加入&lt;code&gt;gem 'mini_magick'&lt;/code&gt;后，直接在页面就可以对图片尺寸进行处理：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= image_tag project.image.variant(resize: "100x100") %&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;国内云服务的&lt;code&gt;Active Storage&lt;/code&gt;相关 gem：&lt;br&gt;
&lt;a href="https://github.com/mycolorway/activestorage_qiniu" rel="nofollow" target="_blank"&gt;https://github.com/mycolorway/activestorage_qiniu&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/doabit/activestorage_upyun" rel="nofollow" target="_blank"&gt;https://github.com/doabit/activestorage_upyun&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/huacnlee/activestorage-aliyun" rel="nofollow" target="_blank"&gt;https://github.com/huacnlee/activestorage-aliyun&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;参考：&lt;br&gt;
&lt;a href="https://evilmartians.com/chronicles/rails-5-2-active-storage-and-beyond" rel="nofollow" target="_blank" title=""&gt;Rails 5.2: Active Storage and beyond&lt;/a&gt;&lt;br&gt;
&lt;a href="https://joey.io/active-storage-environment-specific-credentials/" rel="nofollow" target="_blank" title=""&gt;Active Storage Environment-Specific Credentials&lt;/a&gt;&lt;br&gt;
&lt;a href="https://blog.botreetechnologies.com/encrypted-credentials-a-new-way-to-use-secrets-in-rails-5-2-eca929629bb4" rel="nofollow" target="_blank" title=""&gt;Encrypted Credentials — A new way to use Secrets in Rails 5.2&lt;/a&gt;&lt;br&gt;
&lt;a href="https://medium.com/cedarcode/rails-5-2-credentials-9b3324851336" rel="nofollow" target="_blank" title=""&gt;Rails 5.2 credentials&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;原文：&lt;a href="https://www.jianshu.com/p/b0312d926899" rel="nofollow" target="_blank"&gt;https://www.jianshu.com/p/b0312d926899&lt;/a&gt;&lt;/p&gt;</description>
      <author>jesktop</author>
      <pubDate>Fri, 27 Apr 2018 15:55:28 +0800</pubDate>
      <link>https://ruby-china.org/topics/36081</link>
      <guid>https://ruby-china.org/topics/36081</guid>
    </item>
    <item>
      <title>在 Rails 中实现拖拽排序功能</title>
      <description>&lt;p&gt;回想到很久以前，JavaScript 为了实现前端拖拽排序的事情，都是一个不容易的事。自从有了 jQuery 和&lt;a href="https://jqueryui.com/" rel="nofollow" target="_blank" title=""&gt;jQuery UI&lt;/a&gt;后，很多常见的功能直接使用起来已经非常方便了，不需要自己进行二次开发。&lt;/p&gt;

&lt;p&gt;好，今天我们就使用&lt;a href="https://jqueryui.com/sortable/" rel="nofollow" target="_blank" title=""&gt;sortable&lt;/a&gt;，完成加上服务器端（Rails）的逻辑，完成一个完整的拖拽排序功能。&lt;/p&gt;
&lt;h2 id="实现的逻辑是什么？"&gt;实现的逻辑是什么？&lt;/h2&gt;
&lt;p&gt;在服务器端添加排序的字段（position），然后我们的 order 排序根据 position 进行排序。每当我们前端拖拽结束后，通过异步 Ajax 把排序的结果发送到服务器，服务器根据传送过来的内容，重新设置 position 信息。&lt;/p&gt;
&lt;h2 id="添加需要使用的Gem"&gt;添加需要使用的 Gem&lt;/h2&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'acts_as_list'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'jquery-ui-rails'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;jquery-ui&lt;/code&gt;中的 sortable 提供前端拖拽排序效果的功能，&lt;code&gt;acts_as_list&lt;/code&gt;则是一款服务器端排序功能的 gem，提供丰富的方法，当然如果仅仅是一个简单的排序功能，这里不使用&lt;code&gt;acts_as_list&lt;/code&gt;也是可行的。&lt;/p&gt;
&lt;h2 id="前端加入拖拽排序效果"&gt;前端加入拖拽排序效果&lt;/h2&gt;
&lt;p&gt;我们先在&lt;code&gt;application.js&lt;/code&gt;引入&lt;code&gt;jquery-ui&lt;/code&gt;：&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//= require jquery-ui/widgets/sortable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后我们加入调用&lt;code&gt;sortable&lt;/code&gt;的方法：&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;turbolinks:load&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[data-behavior='sortable']&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;sortable&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;这样一来，只要在需要排序的 dom 中，加入&lt;code&gt;data-behavior='sortable'&lt;/code&gt;就可以实现拖拽排序的效果了，参考：&lt;/p&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;table&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"table table-bordered table-hover"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;thead&amp;gt;&lt;/span&gt;
    ...
  &lt;span class="nt"&gt;&amp;lt;/thead&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;tbody&lt;/span&gt; &lt;span class="na"&gt;data-behavior=&lt;/span&gt;&lt;span class="s"&gt;'sortable'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    ...
  &lt;span class="nt"&gt;&amp;lt;/tbody&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="加入排序接口"&gt;加入排序接口&lt;/h2&gt;
&lt;p&gt;首先我们根据&lt;code&gt;acts_as_list&lt;/code&gt;的文档，加入一个排序需要用到的字段：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rails g migration AddPositionToTodoItem position:integer
rails db:migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后我们需要添加一个接口，用于处理排序信息：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;js&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;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; 
  &lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="ss"&gt;:todo_items&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;collection&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;patch&lt;/span&gt; &lt;span class="ss"&gt;:sort&lt;/span&gt; 
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt; 
  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TodoItemsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="vi"&gt;@todo_items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;TodoItems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:position&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sort&lt;/span&gt;

  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="发送异步请求"&gt;发送异步请求&lt;/h2&gt;
&lt;p&gt;现在服务器端的接口完成了，我们的目的就是当排序结束后，把数据打包发到这个接口中，为了方便操作，我把接口地址放在 Dom 里的&lt;code&gt;data-url&lt;/code&gt;属性中。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;tbody data-url="&amp;lt;%= sort_todo_items_path %&amp;gt;" data-behavior='sortable'&amp;gt;
...
&amp;lt;/tbody&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;并且每个拖拽的元素，我们把它的 ID 设置为&lt;code&gt;model的名称_model的id&lt;/code&gt;：&lt;/p&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;tbody&lt;/span&gt; &lt;span class="na"&gt;data-url=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;sort_todo_items_path&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="na"&gt;data-behavior=&lt;/span&gt;&lt;span class="s"&gt;'sortable'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="vi"&gt;@todo_items.each_with_index&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;todo_item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;tr&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;dom_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todo_item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      ...
    &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/tbody&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后我们在原来的&lt;code&gt;sortable&lt;/code&gt;中，添加 update 方法，处理异步事务：&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;[data-behavior='sortable']&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;sortable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;update&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ajax&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PATCH&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;sortable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;serialize&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过上面的 JavaScript 代码，就可以实现，每天拖拽更新后，就会发送数据到对应的接口上，那发送过去的数据会是怎么样的呢？&lt;/p&gt;
&lt;h2 id="处理数据"&gt;处理数据&lt;/h2&gt;
&lt;p&gt;当我们尝试操作一遍后，就会发现发送到服务器的数据格式如下：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"todo_item"&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;"2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"3"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个就是&lt;code&gt;todo_item&lt;/code&gt;数据里，每个 id 的排序情况。所以我们可以根据这个排序信息，重新设置&lt;code&gt;position&lt;/code&gt;就可以达到我们想要的排序结果。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sort&lt;/span&gt;
  &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:todo_item&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;each_with_index&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="no"&gt;TodoItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;position: &lt;/span&gt;&lt;span class="n"&gt;index&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="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;好了，目前我们前后端的功能都完成了，赶快试试效果吧。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2018/ffa03baa-563d-49c8-9035-0aa9f7e4c71b.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;原文链接：&lt;a href="https://www.jianshu.com/p/d9911c234107" rel="nofollow" target="_blank"&gt;https://www.jianshu.com/p/d9911c234107&lt;/a&gt;&lt;/p&gt;</description>
      <author>jesktop</author>
      <pubDate>Wed, 18 Apr 2018 17:26:48 +0800</pubDate>
      <link>https://ruby-china.org/topics/35481</link>
      <guid>https://ruby-china.org/topics/35481</guid>
    </item>
    <item>
      <title>使用 Redis 处理 Rails Model 缓存</title>
      <description>&lt;p&gt;原文：&lt;a href="http://www.sitepoint.com/rails-model-caching-redis/" rel="nofollow" target="_blank" title=""&gt;Rails Model Caching with Redis&lt;/a&gt;
第一次翻译文章，估计翻译问题多，欢迎拍砖。&lt;/p&gt;

&lt;p&gt;Model 层的缓存常常都会被忽略，甚至是经验丰富的码农。当你对视图层做缓存时，你不需要进行底层缓存，这是一个非常常见的误解。虽然在 Rails 里大部分的瓶颈在于视图层，但是总有个别情况不是这样的。&lt;/p&gt;

&lt;p&gt;底层缓存是非常灵活的，可以工作于任何一个应用程序。在本教程中，我将演示如何使用 Redis 来缓存你的 model 层。&lt;/p&gt;
&lt;h2 id="缓存是如何工作的？"&gt;缓存是如何工作的？&lt;/h2&gt;
&lt;p&gt;过去，访问磁盘的成本已经非常高了。而且从磁盘访问数据经常会对性能产生不利的影响。为了解决这个问题，我们可以在应用程序和数据库服务器之间加上缓存层。&lt;/p&gt;

&lt;p&gt;缓存层在初始化时是没有任何数据的。当它接收到数据请求时，它会调用数据库并将结果存储在内存中（缓存）。所有后续的请求将从缓存层直接读取数据，所以可以避免不重复往返的访问数据库服务器，从而提高性能。&lt;/p&gt;
&lt;h2 id="为什么使用Redis？"&gt;为什么使用 Redis？&lt;/h2&gt;
&lt;p&gt;Redis 是一个基于内存、Key-Value 存储系统。它的速度极快，几乎是瞬间完成数据检索。Redis 支持先进的数据结构，如链表，哈希表，集合，并能持续保存到磁盘。&lt;/p&gt;

&lt;p&gt;虽然大多数码农更喜欢使用 Memcache 和 Dalli 去处理他们的缓存化的需求，但我发现 Redis 非常容易的安装和方便管理。另外，如果你使用的是 resque 或 Sidekiq 管理你的队列，你很可能已经安装了 Redis 了。对于那些有兴趣了解何时使用 Redis 的朋友们，可以到这个 讨论 里了解更多相关信息。&lt;/p&gt;
&lt;h2 id="前提"&gt;前提&lt;/h2&gt;
&lt;p&gt;我假设你的项目正在使用 Rails，文章中的例子是使用 Rails 4.2.rc1，使用 haml 渲染视图和 MongoDB 作为数据库，但是本教程的片段应该适用于任何版本的 Rails。&lt;/p&gt;

&lt;p&gt;在开始之前，你需要安装和运行 Redis。进入你的应用程序目录，并执行以下命令：&lt;/p&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;$ wget http://download.redis.io/releases/redis-2.8.18.tar.gz
$ tar xzf redis-2.8.18.tar.gz
$ cd redis-2.8.18
$ make
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个命令将需要一段时间才能完成。一旦完成了，就可以开启 Redis 服务了：&lt;/p&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;$ cd redis-2.8.18/src
$ ./redis-server
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用 gem“rack-mini-profiler”可以测量性能提升，这个 gem 可以帮助我们正确的体现出性能的改善。&lt;/p&gt;
&lt;h2 id="开始"&gt;开始&lt;/h2&gt;
&lt;p&gt;例如，让我们构建一个虚拟的在线故事书阅读书店。这个书店有各种各样的书籍和语言。首先，让我们创建模型：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/category.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Category&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Mongoid&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Document&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Mongoid&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Timestamps&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Mongoid&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Paranoia&lt;/span&gt;

  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;CommonMeta&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# app/models/language.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Language&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Mongoid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="ss"&gt;:Document&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Mongoid&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Timestamps&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Mongoid&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Paranoia&lt;/span&gt;

  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;CommonMeta&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# app/models/concerns/common_meta.rb&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;CommonMeta&lt;/span&gt;
  &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Concern&lt;/span&gt;
  &lt;span class="n"&gt;included&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:desc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:page_title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我在&lt;a href="https://github.com/skmvasu/redis_cache_sitepoint/blob/master/db/seeds.rb" rel="nofollow" target="_blank" title=""&gt;这里&lt;/a&gt;包括了一个 seed 数据文件。只要复制粘贴到你的 seeds.rb 和运行 rake seed 任务，数据就会加载到我们的数据库中。&lt;/p&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;rake db:seed
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在，让我们创建一个简单的 Category 列表页面，该页面显示了所有 Categories 的描述和标记信息。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/controllers/category_controller.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CategoryController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;CategoryHelper&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="vi"&gt;@categories&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;all&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# app/helpers/category_helper.rb&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;CategoryHelper&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_categories&lt;/span&gt;
    &lt;span class="vi"&gt;@categories&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;all&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# app/views/category/index.html.haml&lt;/span&gt;

&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;h1&lt;/span&gt;
  &lt;span class="no"&gt;Category&lt;/span&gt; &lt;span class="no"&gt;Listing&lt;/span&gt;
&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;ul&lt;/span&gt;&lt;span class="c1"&gt;#categories&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="vi"&gt;@categories.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;cat&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;li&lt;/span&gt;
        &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;h3&lt;/span&gt;
          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;
        &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nb"&gt;p&lt;/span&gt;
          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;desc&lt;/span&gt;

&lt;span class="c1"&gt;# config.routes.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;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="ss"&gt;:languages&lt;/span&gt;
  &lt;span class="n"&gt;resources&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;p&gt;当你打开浏览器并将其地址指向/category 时，你会发现 mini-profiler benchmarking 显示在后端执行每一个动作的时间。这些正确的数据是告诉你，你的应用程序哪部分比较缓慢和应该如何优化它们。本页面执行了两条 SQL 语句并且使用了 5ms 的时间完成查询。&lt;/p&gt;

&lt;p&gt;虽然起初看起来好像 5ms 是无关紧要的，特别是在需要更多时间去渲染视图时，但在一个生产级别的应用程序中有多次数据库查询时，它们可以明显的降低软件的性能。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2015/853480fe43430b40618f7cc08e4987da.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;由于元数据模型是不太可能发生改变的，这样就可以避免不必要的数据库切换。这一点也是底层缓存的用武之地。&lt;/p&gt;
&lt;h2 id="安装Redis"&gt;安装 Redis&lt;/h2&gt;
&lt;p&gt;使用 Redis 基于 Ruby 的客户端来帮助我们非常方便的链接 Redis 实例：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'redis'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'redis-namespace'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'redis-rails'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'redis-rack-cache'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一但安装好这些 gem 后，就可以配置 Rails 使用 Redis 来作为缓存存储：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/application.rb&lt;/span&gt;

&lt;span class="c1"&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;cache_store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:redis_store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'redis://localhost:6379/0/cache'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;expires_in: &lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;minutes&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;#.........&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用 redis-namespace gem 可以让我们创建一个更好的 Redis 命名空间：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/initializers/redis.rb &lt;/span&gt;

&lt;span class="vg"&gt;$redis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Redis&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Namespace&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="s2"&gt;"site_point"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:redis&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在所有的 Redis 功能都可以通过&lt;code&gt;$redis&lt;/code&gt;进行全局使用了。以下的一个例子是体现如何访问在 redis 服务器上的值（运行于 Rails console）：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="vg"&gt;$redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"test_key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Hello World!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个命令创建了一个 key：“test_key”和 value：“Hello World”保存在 Redis 中。要取这个值，只做：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="vg"&gt;$redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"test_key"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在，我们有了基础知识，让我们开始重写我们的 helper 方法：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/helpers/category_helper.rb&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;CategoryHelper&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_categories&lt;/span&gt;
    &lt;span class="n"&gt;categories&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="vg"&gt;$redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"categories"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;categories&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nil?&lt;/span&gt;
      &lt;span class="n"&gt;categories&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;all&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;
      &lt;span class="vg"&gt;$redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"categories"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;categories&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="vi"&gt;@categories&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt; &lt;span class="n"&gt;categories&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;在第一次执行这部分代码时，内存/缓存中是没有任何东西的。所以我们请求 Rails 把数据从数据库推送到 Redis 中。注意到 &lt;code&gt;to_json&lt;/code&gt;的调用了吗？当要写对象进 Redis，我们多种方式。一种选择是遍历对象中的每个属性，然后将它们保存为一个哈希函数，但是这种方式较为缓慢。最简单的方法是将它们保存为一个 JSON 编码的字符串。解码，只需使用&lt;code&gt;JSON.load&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;然而，这有一个意想不到的副作用。当我们正在检索这个值时，一个简易的对象符号不工作。我们需要更新视图并使用哈希语法来显示该类型：&lt;/p&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;# app/views/category/index.html.haml

%h1
  Category Listing
%ul#categories
  - @categories.each do |cat|
    %li
      %h3
        = cat["name"]
      %p
        = cat["desc"]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;重新启动浏览器，并看看性能是否有所不同。首次访问，我们仍然访问数据库，但随后的重新加载将不在访问数据库了。以后所有的请求都将直接从缓存中读取。这个简单的变化非常有效。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2015/fa943998d309ec7184d200795c4d8425.png" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="管理缓存"&gt;管理缓存&lt;/h2&gt;
&lt;p&gt;我刚发现一个关于 categories 的错误。让我们先解决它：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;rails&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;

&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Category&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Famly and Frends"&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;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Family and Friends"&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;save&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;重新加载并查看该更新是否显示在视图中：&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2015/8a2c5ac24c03612698f75fcf390f766b.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;很遗憾，我们的视图上并没有体现出这个变化。因为我们并没有访问数据库，所有的数据都直接从缓存中读取。唉，现在的缓存已经过期，直到 Redis 重启前被更新都数据都无法使用。这个对于大多数应用程序来说真是一个破坏者啊。我们偶尔可以使用缓存到期来解决这个问题：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/helpers/category_helper.rb&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;CategoryHelper&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_categories&lt;/span&gt;
    &lt;span class="n"&gt;categories&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="vg"&gt;$redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"categories"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;categories&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nil?&lt;/span&gt;
      &lt;span class="n"&gt;categories&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;all&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;
      &lt;span class="vg"&gt;$redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"categories"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;categories&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="c1"&gt;# Expire the cache, every 3 hours&lt;/span&gt;
      &lt;span class="vg"&gt;$redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expire&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"categories"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hour&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="vi"&gt;@categories&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt; &lt;span class="n"&gt;categories&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;缓存将会在每 3 个小时就失效。虽然这对大多数情况下工作，缓存中的数据将滞后于现在数据库。这种工作方式很可能不抬适合你。如果你喜欢保持缓存的更新，我们可以使用&lt;code&gt;after_save&lt;/code&gt;这个回调：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/category.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Category&lt;/span&gt;
  &lt;span class="c1"&gt;#...........&lt;/span&gt;
  &lt;span class="n"&gt;after_save&lt;/span&gt; &lt;span class="ss"&gt;:clear_cache&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;clear_cache&lt;/span&gt;
    &lt;span class="vg"&gt;$redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;del&lt;/span&gt; &lt;span class="s2"&gt;"categories"&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;p&gt;每次模型的更新，我们都将通知 Rails 去清除缓存。这样可以确保缓存是最新的。Yay!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;你应该使用类似&lt;code&gt;cache_observers&lt;/code&gt;在生产环境中，为了保持简洁，我们在这里坚持使用&lt;code&gt;after_save&lt;/code&gt;。如果你不知道哪种方法最适合你，这里的&lt;a href="http://stackoverflow.com/questions/15165260/rails-observer-alternatives-for-4-0" rel="nofollow" target="_blank" title=""&gt;讨论&lt;/a&gt;可能会对你有所启发。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="结论"&gt;结论&lt;/h2&gt;
&lt;p&gt;底层缓存是非常简单的，如果使用得当，它是非常有价值的。它可以在你花费最小的努力下瞬间提高你的系统的性能。在这篇文章中所有的&lt;a href="https://github.com/skmvasu/redis_cache_sitepoint" rel="nofollow" target="_blank" title=""&gt;代码片断&lt;/a&gt;可以在 GitHub 上找到。 &lt;/p&gt;

&lt;p&gt;希望喜欢读篇文章。欢迎在评论中分享你的想法。&lt;/p&gt;

&lt;p&gt;译文地址：&lt;a href="http://jesktop.com/2015/07/17/rails-model-caching-with-redis/" rel="nofollow" target="_blank"&gt;http://jesktop.com/2015/07/17/rails-model-caching-with-redis/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;感谢 &lt;a href="/superbear" class="user-mention" title="@superbear"&gt;&lt;i&gt;@&lt;/i&gt;superbear&lt;/a&gt;和&lt;a href="/javie007" class="user-mention" title="@javie007"&gt;&lt;i&gt;@&lt;/i&gt;javie007&lt;/a&gt; 协助修订校对&lt;/p&gt;</description>
      <author>jesktop</author>
      <pubDate>Thu, 30 Jul 2015 23:35:57 +0800</pubDate>
      <link>https://ruby-china.org/topics/26711</link>
      <guid>https://ruby-china.org/topics/26711</guid>
    </item>
    <item>
      <title>当 CSS 遇上逗逼</title>
      <description>&lt;p&gt;CSS Puns: &lt;a href="http://saijogeorge.com/css-puns/" rel="nofollow" target="_blank" title=""&gt;CSS Puns &amp;amp; CSS Jokes ~ Curated by Saijo George&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;分享其中几个有趣的：
&lt;img src="https://l.ruby-china.com/photo/2015/776e6d758e1df331d8ea959ba78a8c8c.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2015/a1dfafe5c70ccbce4501f3a7af86ef8a.png" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>jesktop</author>
      <pubDate>Fri, 13 Feb 2015 13:04:34 +0800</pubDate>
      <link>https://ruby-china.org/topics/24248</link>
      <guid>https://ruby-china.org/topics/24248</guid>
    </item>
    <item>
      <title>使用 Form Objects 处理结构复杂的表单</title>
      <description>&lt;p&gt;参考：&lt;a href="http://railscasts.com/episodes/416-form-objects" rel="nofollow" target="_blank" title=""&gt;RailsCasts 416-form-objects&lt;/a&gt;&lt;br&gt;
自从使用 Form Objects 处理复杂的表单后，我就不想在考虑使用 Rails 带的那套方法了。&lt;br&gt;
原来的方法是，如果一个 Form 需要同时处理两个 Model 的数据时，就需要考虑使用&lt;code&gt;accepts_nested_attributes_for&lt;/code&gt;，来建立数据间的关系。但是如果同时需要处理三个或三个以上的 Model 数据时，就会变得很混乱，form 页面需要各种嵌套，然后 model 层需要小心翼翼的设置&lt;code&gt;accepts_nested_attributes_for&lt;/code&gt;。&lt;br&gt;
但是自从学会了 Form Objects 后，妈妈在也不担心我写复杂的表单了。  &lt;/p&gt;
&lt;h2 id="模拟场景"&gt;模拟场景&lt;/h2&gt;
&lt;p&gt;模拟一个现实中的场景，我们需要一个 Form 处理三个 model 的事情，这里先介绍即将登场的三位 Model：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Class&lt;/span&gt; &lt;span class="no"&gt;Product&lt;/span&gt;  &lt;span class="c1"&gt;# 产品&lt;/span&gt;
  &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:product_prices&lt;/span&gt;
  &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:product_color&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="no"&gt;Class&lt;/span&gt; &lt;span class="no"&gt;ProductPrice&lt;/span&gt;  &lt;span class="c1"&gt;# 产品价格（多种价格）&lt;/span&gt;
  &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:product&lt;/span&gt;
  &lt;span class="n"&gt;enum&lt;/span&gt; &lt;span class="ss"&gt;category: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;sell: &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;cost: &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;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Class&lt;/span&gt; &lt;span class="no"&gt;ProductColor&lt;/span&gt;  &lt;span class="c1"&gt;# 产品颜色&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;img src="https://l.ruby-china.com/photo/2015/bf917af736056bfd93c59e6904f941d5.png" title="" alt=""&gt;
Form 里的操作是，当我创建一个 Product 时，需要同时添加多个 Product Price（销售价和成本价），和关联一个 Product Color（如果颜色存在，则直接进行关联，不存在就重新创建）。&lt;br&gt;
所以我们需要设计四个 input：name, sell_price, cost_price, color；别看就这么简单的四个玩意，其实是同时涉及三张表的操作哦。&lt;/p&gt;
&lt;h2 id="Form的设计"&gt;Form 的设计&lt;/h2&gt;
&lt;p&gt;这里谈的设计不是指怎么美化这个表单，这里我就不谈如何美化表单的事情了，简单的使用 simple_form 来处理这个表单把，接招：  &lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simple_form_for&lt;/span&gt; &lt;span class="vi"&gt;@product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;url: &lt;/span&gt;&lt;span class="n"&gt;products_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;method: :post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;html: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;class: &lt;/span&gt;&lt;span class="s1"&gt;'form-horizontal'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; 
  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;
  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt; &lt;span class="ss"&gt;:sell_price&lt;/span&gt;
  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt; &lt;span class="ss"&gt;:cost_price&lt;/span&gt;
  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt; &lt;span class="ss"&gt;:color&lt;/span&gt;

  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;form&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;actions&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;button&lt;/span&gt; &lt;span class="ss"&gt;:submit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;class: &lt;/span&gt;&lt;span class="s1"&gt;'btn btn-primary'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;value: &lt;/span&gt;&lt;span class="s2"&gt;"提交"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;请先不要着急着去看这个 form 究竟长什么样了，如果你跑去看，估计就是满满的错误，因为&lt;a href="/product" class="user-mention" title="@product"&gt;&lt;i&gt;@&lt;/i&gt;product&lt;/a&gt;还没定义呢！&lt;br&gt;
当然有同鞋们会说，&lt;a href="/product" class="user-mention" title="@product"&gt;&lt;i&gt;@&lt;/i&gt;product&lt;/a&gt;不就是&lt;code&gt;@product = Product.new&lt;/code&gt;吗？当然不是！！因为 Product 没有 sell_price 等字段啊！&lt;/p&gt;
&lt;h2 id="建立Form Objects"&gt;建立 Form Objects&lt;/h2&gt;
&lt;p&gt;好了，主菜来了！首先我们在&lt;code&gt;app/forms/&lt;/code&gt;里建一个&lt;code&gt;products_create_form.rb&lt;/code&gt;文件，然后咱们来看看应该如何完善。&lt;/p&gt;
&lt;h5 id="使用ActiveModel::Model"&gt;使用 ActiveModel::Model&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Class&lt;/span&gt; &lt;span class="no"&gt;ProductsCreateForm&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;ActiveModel&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Model&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_name&lt;/span&gt;
    &lt;span class="no"&gt;ActiveModel&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Name&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="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Product"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;i18n_scope&lt;/span&gt;
      &lt;span class="ss"&gt;:activerecord&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;为什么在这里要&lt;code&gt;include ActiveModel::Model&lt;/code&gt;呢？因为这样可以使用大部分我们在 Model 中常用到的方法，这里我们主要要使用&lt;code&gt;validates&lt;/code&gt;去验证必填项。&lt;br&gt;
然后另外&lt;code&gt;self.model_name&lt;/code&gt;这里把 ActiveModel::Name 设置为'Product&lt;code&gt;了，那生成的form中input的name就是&lt;/code&gt;product[name]&lt;code&gt;。  
而&lt;/code&gt;i18n_scope`则是让你把对于 model 来说的 i18n 设置放回 activerecord 中，不然要设置 Form Objects 下的 i18n 就需要针对 ActiveModel 来写 i18n 信息，这个就主要看个人需求是怎么样，一般我都会加上。&lt;/p&gt;
&lt;h5 id="完善Form Objects"&gt;完善 Form Objects&lt;/h5&gt;
&lt;p&gt;然后我们需要补全剩余的信息了：  &lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Class&lt;/span&gt; &lt;span class="no"&gt;ProductsCreateForm&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;ActiveModel&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Model&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_name&lt;/span&gt;
    &lt;span class="no"&gt;ActiveModel&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Name&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="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Product"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;i18n_scope&lt;/span&gt;
      &lt;span class="ss"&gt;:activerecord&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;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:sell_price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:cost_price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:color&lt;/span&gt;

  &lt;span class="n"&gt;validates&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:sell_price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:cost_price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;presence: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sell_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:sell_price&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cost_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:cost_price&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:color&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;valid?&lt;/span&gt;
      &lt;span class="n"&gt;product_color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ProductColor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;color&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;first_or_create&lt;/span&gt;
      &lt;span class="n"&gt;product&lt;/span&gt; &lt;span class="o"&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;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name: &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;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;product_color: &lt;/span&gt;&lt;span class="n"&gt;product_color&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="no"&gt;ProductPrice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;category: &lt;/span&gt;&lt;span class="s1"&gt;'sell'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;value: &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;sell_price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;product: &lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="no"&gt;ProductPrice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;category: &lt;/span&gt;&lt;span class="s1"&gt;'cost'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;value: &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;cost_price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;product: &lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="kp"&gt;false&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 attr_accessor 中加入我们需要输入的参数，并且把必填项加入到 validates 中。在 submit 时，valid？方法就会生效了。这一个 create 的流程下来，还是非常简单的。&lt;/p&gt;
&lt;h5 id="调用 ProductsCreateForm"&gt;调用 ProductsCreateForm&lt;/h5&gt;
&lt;p&gt;所以在刚刚&lt;a href="/product" class="user-mention" title="@product"&gt;&lt;i&gt;@&lt;/i&gt;product&lt;/a&gt;中，调用 ProductsCreateForm 就可以了：  &lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="vi"&gt;@product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ProductsCreateForm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而在 create 方法时，也是非常的简单的：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="vi"&gt;@product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ProductsCreateForm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@product.submit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:product&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;整个流程下来，controller 的代码已经非常简单了，把需要用的逻辑单独放在 ProductsCreateForm 中，不需要放到 Model 中，避免 Model 的臃肿，而且非常直观。&lt;br&gt;
那么问题来了，如果我是需要 Update 这些信息，那么直接调用 ProductsCreateForm 可以吗？显然那是不行的，Update 的话会比较复杂些。&lt;/p&gt;
&lt;h2 id="使用Form Objects更新信息"&gt;使用 Form Objects 更新信息&lt;/h2&gt;
&lt;p&gt;我们重新建一个&lt;code&gt;ProductUpdateForm&lt;/code&gt;来专门处理 Update：  &lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Class&lt;/span&gt; &lt;span class="no"&gt;ProductsUpdateForm&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;ActiveModel&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Model&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_name&lt;/span&gt;
    &lt;span class="no"&gt;ActiveModel&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Name&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="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Product"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;i18n_scope&lt;/span&gt;
      &lt;span class="ss"&gt;:activerecord&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;validates&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:sell_price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:cost_price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;presence: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;
    &lt;span class="vi"&gt;@product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;
    &lt;span class="vi"&gt;@product_sell_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@product.product_price&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;category: &lt;/span&gt;&lt;span class="no"&gt;ProductPrice&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;categories&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:sell&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="vi"&gt;@product_cost_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@product.product_price&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;category: &lt;/span&gt;&lt;span class="no"&gt;ProductPrice&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;categories&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:cost&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt;
    &lt;span class="vi"&gt;@name&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="vi"&gt;@product.name&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sell_price&lt;/span&gt;
    &lt;span class="vi"&gt;@sell_price&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="vi"&gt;@product_sell_price.value&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;cost_price&lt;/span&gt;
    &lt;span class="vi"&gt;@cost_price&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="vi"&gt;@product_cost_price.value&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;color&lt;/span&gt;
    &lt;span class="vi"&gt;@color&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="vi"&gt;@product.product_color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="vi"&gt;@sell_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:sell_price&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="vi"&gt;@cost_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:cost_price&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="vi"&gt;@color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:color&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;valid?&lt;/span&gt;
      &lt;span class="vi"&gt;@product.update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name: &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;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="vi"&gt;@product_sell_price.update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;value: &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;sell_price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="vi"&gt;@product_cost_price.update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;value: &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;cost_price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="vi"&gt;@product.product_color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name: &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;color&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="kp"&gt;false&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;相比 Create 来说，主要不同的地方也就是每个参数的定义方式不在使用&lt;code&gt;attr_accessor&lt;/code&gt;，因为参数已经不会为空的，而需要从数据库中读取。然后别的相比之下差别也不大，我这里就不另外做解释了，如果还想了解多一些，可以看看 RailsCasts 的视频，非常不错的哦。&lt;/p&gt;

&lt;p&gt;原文：
&lt;a href="http://jesktop.com/2015/02/08/use-form-objects/" rel="nofollow" target="_blank" title=""&gt;使用 Form Objects 处理结构复杂的表单&lt;/a&gt;&lt;/p&gt;</description>
      <author>jesktop</author>
      <pubDate>Mon, 09 Feb 2015 20:49:17 +0800</pubDate>
      <link>https://ruby-china.org/topics/24185</link>
      <guid>https://ruby-china.org/topics/24185</guid>
    </item>
    <item>
      <title>微信开发简介</title>
      <description>&lt;h2 id="推荐使用GEM"&gt;推荐使用 GEM&lt;/h2&gt;
&lt;p&gt;在开始前，推荐 &lt;a href="/ruby_sky" class="user-mention" title="@ruby_sky"&gt;&lt;i&gt;@&lt;/i&gt;ruby_sky&lt;/a&gt; 同学无私贡献的两个 gem：（这里要特别感谢我们社区的 &lt;a href="/ruby_sky" class="user-mention" title="@ruby_sky"&gt;&lt;i&gt;@&lt;/i&gt;ruby_sky&lt;/a&gt; 同学） 
&lt;a href="https://github.com/lanrion/weixin_rails_middleware" rel="nofollow" target="_blank" title=""&gt;gem 'weixin_rails_middleware'&lt;/a&gt; 
&lt;a href="https://github.com/lanrion/weixin_authorize" rel="nofollow" target="_blank" title=""&gt;gem 'weixin_authorize'&lt;/a&gt; 
具体可以查看 gem 的文档，使用后可以很方便的处理一些问题，例如签名，加密和接口参数拼接等。  &lt;/p&gt;
&lt;h2 id="基础接口"&gt;基础接口&lt;/h2&gt;
&lt;p&gt;如果只使用基础接口，只需要使用&lt;code&gt;weixin_rails_middleware&lt;/code&gt;这个 gem 就可以了。基础接口指的是一般用户主动发出的请求，例如：发送文本或图片等消息到公众帐号时，公众帐号发出的回应消息。而相比于高级接口来说，基础接口要简单很多。&lt;/p&gt;
&lt;h3 id="验证微信请求"&gt;验证微信请求&lt;/h3&gt;
&lt;p&gt;在开始时，微信需要先验证你的接口，根据官方文档的验证方式大概是：&lt;br&gt;
在微信后台填入服务器地址（URL）、Token 和 EncodingAESKey，然后微信通过提供的参数和一个随机数（echostr）进行字典序排序后，进行 sha1 加密，然后发送至服务器端，服务器端只要对其验证并且返回随机数（echostr），即表示验证成功。（具体内容参考微信文档）&lt;br&gt;
而使用 gem 后，整个验证流程基本不需要自己操心，只需要添加正确的 secret_key 和 token，验证就通过了。&lt;/p&gt;
&lt;h3 id="业务逻辑实现"&gt;业务逻辑实现&lt;/h3&gt;
&lt;p&gt;使用&lt;code&gt;weixin_rails_middleware&lt;/code&gt;后，执行 &lt;code&gt;rails generate weixin_rails_middleware:install&lt;/code&gt;，就生成了整个业务逻辑的简单实现了，包括发送来的是图片，文本或者是关注之类的操作。只要是用户主动发起的操作和请求，基本都实现了简单的回复，所以如果不是特别复杂的操作，基础接口中业务逻辑的实现还是相当方便的。&lt;/p&gt;
&lt;h2 id="高级接口"&gt;高级接口&lt;/h2&gt;
&lt;p&gt;这里主要想说一下高级接口，因为在第一次开发微信公众号时，还是走了不少弯路。   &lt;/p&gt;
&lt;h3 id="简介"&gt;简介&lt;/h3&gt;
&lt;p&gt;在使用高级接口时，我们使用&lt;code&gt;gem 'weixin_authorize'&lt;/code&gt;。高级接口和基础接口有什么不一样呢？我觉得最大的区别在于高级接口基本上都是服务器端主动发起的请求，因为是服务器端主动发起的请求，所以每次需要带上&lt;code&gt;access_token&lt;/code&gt;来提供微信验证此信息是否是服务器端发送的。所以我们先来看看&lt;code&gt;access_token&lt;/code&gt;是如何获得的。&lt;/p&gt;
&lt;h3 id="获取access_token"&gt;获取 access_token&lt;/h3&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;access_token是公众号的全局唯一票据，公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时，需定时刷新，重复获取将导致上次获取的access_token失效。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因为&lt;code&gt;access_token&lt;/code&gt;是会失效的，所以&lt;code&gt;gem 'weixin_authorize'&lt;/code&gt;中使用 Redis 来存储&lt;code&gt;access_token&lt;/code&gt;是一个不错的方式，根据文档配置，然后通过：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="vg"&gt;$client&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="no"&gt;WeixinAuthorize&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"APPID"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"APPSECRET"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;即可获得对应公众号的&lt;code&gt;access_token&lt;/code&gt;（公众号可以使用 AppID 和 AppSecret 调用接口来获取 access_token）。&lt;br&gt;
现在咱们手持&lt;code&gt;access_token&lt;/code&gt;自然如有神助，可以大显身手了。&lt;/p&gt;
&lt;h3 id="自定义菜单"&gt;自定义菜单&lt;/h3&gt;
&lt;p&gt;因为使用了开发者模式，所以就算是希望完成自定义菜单这样的事情也变得困难起来。那我们应该如何自定义菜单呢？&lt;br&gt;
建议参考：&lt;a href="https://github.com/lanrion/weixin_rails_middleware/wiki/DIY-menu" rel="nofollow" target="_blank" title=""&gt;自定义菜单的实现&lt;/a&gt;&lt;br&gt;
文档中讲的很详细，表的结构可以参考文档。这里主要说一下，如何把定义好的菜单结构发送给微信，并且生成对应的菜单。&lt;br&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;generate_menu&lt;/span&gt;
  &lt;span class="n"&gt;weixin_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;WeixinAuthorize&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@current_public_account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;app_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@current_public_account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;app_secret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;menu&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@current_public_account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build_menu&lt;/span&gt;
  &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;weixin_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_menu&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;menu&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;set_error_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"errmsg"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"errcode"&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;redirect_to&lt;/span&gt; &lt;span class="n"&gt;public_account_diymenus_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@current_public_account&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其实就是带着&lt;code&gt;access_token&lt;/code&gt;，把微信需要的菜单 JSON 结构拼装好，发送到微信对应的接口，然后微信就会生成菜单了。因为&lt;code&gt;gem 'weixin_authorize'&lt;/code&gt;把事情都处理好了，所以自定义菜单对于我们来说，就变得非常的简单了。&lt;/p&gt;
&lt;h3 id="关联用户和微信帐号"&gt;关联用户和微信帐号&lt;/h3&gt;
&lt;p&gt;大家如果使用过银行的一些公众帐号，一定会用过这样的功能，就是把微信帐号和银行卡关联起来，然后银行卡发生交易时，微信都会收到相应的提示。那么它们是如何关联起来的呢？&lt;br&gt;
我这里介绍一个关联方式，就是用户通过公众帐号的菜单，点击“登录”，然后进行关联。那么公众帐号菜单的登录和直接用网页登录究竟有什么不一样呢？&lt;/p&gt;
&lt;h4 id="请求授权页面"&gt;请求授权页面&lt;/h4&gt;
&lt;p&gt;在微信菜单中的“登录”按钮跳到的页面也是一个网站的登录页面，但是他们不一样的地方是，这里需要微信做一个授权，也就是菜单中的“登录”按钮指向的链接是这样的： &lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&amp;amp;redirect_uri=REDIRECT_URI&amp;amp;response_type=code&amp;amp;scope=SCOPE&amp;amp;state=STATE#wechat_redirect
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;里面两个必须的参数是 APPID 和 REDIRECT_URI，其中 REDIRECT_URI 是需要跳转的链接地址。为什么需要这样的地址呢？&lt;br&gt;
因为随着微信的 REDIRECT_URI，去到登录页面时会带着一个 code 值，而我们需要用 code 来获取用户的 openid。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;code说明 ：
code作为换取access_token的票据，每次用户授权带上的code将不一样，code只能使用一次，5分钟未被使用自动过期。

openid说明：
普通用户的标识，对当前公众号唯一
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;有了 code 值，就可以立刻向微信获取用户的 openid 值了：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;sns_info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vg"&gt;$client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_oauth_access_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:code&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;sns_info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_ok?&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;openid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sns_info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:openid&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;只要这样，当用户通过该页面登录成功后，就可以把 openid 和 user 关联起来，如果往后有消息需要推送给用户的话就相当方便了，调用微信对应的接口加上 openid，就可以发送消息到对应的微信用户上了。 &lt;/p&gt;

&lt;p&gt;以上就是这些时间里对微信公众帐号开发的一些看法和认识，都是一些比较简单的介绍。下面在介绍大家开发时记得使用微信提供的沙箱进行调试：
&lt;a href="http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login" rel="nofollow" target="_blank" title=""&gt;http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;原文：
&lt;a href="http://jesktop.com/2015/01/18/weixin-development-profile/" rel="nofollow" target="_blank" title=""&gt;微信开发简介&lt;/a&gt;&lt;/p&gt;</description>
      <author>jesktop</author>
      <pubDate>Mon, 19 Jan 2015 21:22:39 +0800</pubDate>
      <link>https://ruby-china.org/topics/23822</link>
      <guid>https://ruby-china.org/topics/23822</guid>
    </item>
    <item>
      <title>[北京] Fiberead 诚聘 Rails 工程师</title>
      <description>&lt;h2 id="公司介绍"&gt;公司介绍&lt;/h2&gt;
&lt;p&gt;我们是专注数字出版的互联网平台，科技与文化齐飞；
我们是服务于广大作者和译者的自出版平台，使命感超强；
我们做国家扶持的朝阳行业，前（钱）途无限；
我们有无限零食和水果，回味无穷；
我们上班不打卡；匆忙拥挤 say 拜拜；&lt;/p&gt;

&lt;p&gt;我们就是 Fiberead，一个年轻的创业团队，拥有无限的潜力，欢迎能够吃苦耐劳，抗压能力超强的能人加入。&lt;/p&gt;

&lt;p&gt;我们的办公地点：北京市海淀区中关村鼎好大厦 A 座&lt;/p&gt;

&lt;p&gt;我们要招的岗位：&lt;/p&gt;
&lt;h2 id="Ruby on Rails 工程师"&gt;Ruby on Rails 工程师&lt;/h2&gt;&lt;h6 id="薪水待遇"&gt;薪水待遇&lt;/h6&gt;
&lt;p&gt;薪水 8K ~ 16K，会根据情况给到期权，详细面谈&lt;/p&gt;
&lt;h6 id="职位描述"&gt;职位描述&lt;/h6&gt;
&lt;p&gt;负责网站业务逻辑的实现；
负责网站管理后台的实现；
根据项目计划，按时提交合格的功能版本&lt;/p&gt;
&lt;h6 id="岗位要求"&gt;岗位要求&lt;/h6&gt;
&lt;p&gt;至少 1 年或以上 Rails 开发经验；
良好编程习惯，追求优雅的代码；
自我驱动能力强，快速的学习能力；
热爱技术开发，有极客精神，参与过开源项目或常写技术博客加分哦！
在开源社区活跃并有积极贡献者优先。&lt;/p&gt;
&lt;h2 id="产品经理"&gt;产品经理&lt;/h2&gt;&lt;h6 id="薪水待遇"&gt;薪水待遇&lt;/h6&gt;
&lt;p&gt;薪水 10K ~ 20K，会根据情况给到期权，详细面谈&lt;/p&gt;
&lt;h6 id="职位描述"&gt;职位描述&lt;/h6&gt;
&lt;p&gt;负责 &lt;a href="http://fiberead.com/" rel="nofollow" target="_blank" title=""&gt;fiberead.com&lt;/a&gt; 产品需求分析和设计；
负责产品的用户体验优化；
负责协同产品团队，确保产品实施的整体进度。&lt;/p&gt;
&lt;h6 id="岗位要求"&gt;岗位要求&lt;/h6&gt;
&lt;p&gt;一年以上产品相关经验；
自我驱动能力强，快速的学习能力；
熟悉流程图，产品原型工具；
出色的语言、沟通、组织和协调能力；
有缜密的逻辑思维和优秀的分析能力。&lt;/p&gt;

&lt;p&gt;简历请投：contact@fiberead.com&lt;/p&gt;</description>
      <author>jesktop</author>
      <pubDate>Fri, 14 Nov 2014 18:56:10 +0800</pubDate>
      <link>https://ruby-china.org/topics/22693</link>
      <guid>https://ruby-china.org/topics/22693</guid>
    </item>
    <item>
      <title>如何理解 form_for 和 form_tag </title>
      <description>&lt;h2 id="Form_for是什么"&gt;Form_for 是什么&lt;/h2&gt;
&lt;p&gt;在我们使用 Rails 的过程中，必然离不开&lt;code&gt;Form_for&lt;/code&gt;，甚至在刚入门没有多长的时间里就会接触到&lt;code&gt;Form_for&lt;/code&gt;。本来&lt;code&gt;Form_for&lt;/code&gt;并不是一个如何特别神奇的东西，无非就是一个 Helper，Helper 的作用是什么？对于 Helper 而言，无论它的逻辑多么的复杂，其实本质的作用就是生成 HTML 代码。所以我们应该以 HTML 的角度，去理解&lt;code&gt;Form_for&lt;/code&gt;.&lt;br&gt;
为什么特别写一篇文章来讲&lt;code&gt;Form_for&lt;/code&gt;呢？主要的原因是之前有个人问了我一个这样的问题：&lt;br&gt;
&lt;code&gt;&amp;lt;input type="reset"/&amp;gt;&lt;/code&gt;是用于重置表单的，可是我并不知道应该怎么放入&lt;code&gt;Form_for&lt;/code&gt;当中！&lt;br&gt;
关于这类型的问题，我已经遇到很多次了，例如说 Boostrap 中关于一些 form 的样式代码，不知道如何应用到&lt;code&gt;Form_for&lt;/code&gt;中之类。那究竟关于&lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt;里的一些事情，如何应用到&lt;code&gt;Form_for&lt;/code&gt;中呢？ 
先看看官方的文档怎么介绍&lt;code&gt;Form_for&lt;/code&gt;的：  &lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= form_for @article, url: {action: "create"}, html: {class: "nifty_form"} do |f| %&amp;gt;
  &amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text_field&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= f.text_area :body, size: "60x12" %&amp;gt;
  &amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;submit&lt;/span&gt; &lt;span class="s2"&gt;"Create"&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;% end &lt;/span&gt;&lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;生成如下的 HTML：  &lt;/p&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;accept-charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/articles/create"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"post"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"nifty_form"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"article_title"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"article[title]"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;textarea&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"article_body"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"article[body]"&lt;/span&gt; &lt;span class="na"&gt;cols=&lt;/span&gt;&lt;span class="s"&gt;"60"&lt;/span&gt; &lt;span class="na"&gt;rows=&lt;/span&gt;&lt;span class="s"&gt;"12"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/textarea&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"commit"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"Create"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其实如果你愿意的话，上面的&lt;code&gt;Form_for&lt;/code&gt;是可以混着 HTML 写的，当然我觉得这不会是一个好的案例，如：  &lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= form_for @article, url: {action: "create"}, html: {class: "nifty_form"} do |f| %&amp;gt;
  &amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text_field&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;textarea&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"article_body"&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"article[body]"&lt;/span&gt; &lt;span class="n"&gt;cols&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"60"&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"12"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/textarea&amp;gt;
  &amp;lt;%= f.submit "Create" %&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以对于之前如何添加&lt;code&gt;&amp;lt;input type="reset"/&amp;gt;&lt;/code&gt;的问题来说，答案已经很清晰了。当然如何解决这个问题并不是主要想讨论的事情，关键的一点是非常多的初学者对&lt;code&gt;Form_for&lt;/code&gt;的理解误区，他们普遍觉得在&lt;code&gt;Form_for&lt;/code&gt;里写 input 是应该像这样的：&lt;code&gt;&amp;lt;%= f.text_field :title %&amp;gt;&lt;/code&gt;，而不是&lt;code&gt;&amp;lt;input id="article_title" name="article[title]" type="text"/&amp;gt;&lt;/code&gt;，关键是忽略了&lt;code&gt;Form_for&lt;/code&gt;主要责任是生成 HTML 而已。所以特别需要记住的一点是，就算是在 Rails 里，HTTP 的表单请求依然是依赖 HTML 的&lt;code&gt;&amp;lt;form&amp;gt;&amp;lt;/form&amp;gt;&lt;/code&gt;标签，而不是 Rails 的&lt;code&gt;Form_for&lt;/code&gt;Helper，而&lt;code&gt;Form_for&lt;/code&gt;是用于生成&lt;code&gt;&amp;lt;form&amp;gt;&amp;lt;/form&amp;gt;&lt;/code&gt;。所以这个问题的产生其实是源自于对 HTML 本身的不够熟悉。&lt;/p&gt;
&lt;h2 id="Form_for与Form_tag"&gt;Form_for 与 Form_tag&lt;/h2&gt;
&lt;p&gt;好了，那说完以上的问题后，在说说那&lt;code&gt;Form_for&lt;/code&gt;和&lt;code&gt;Form_tag&lt;/code&gt;的区别是什么？其实它们的区别就是生成的 HTML 不一样，而有非常一大部分的初学者会认为他们的区别是一个用于创建和更新用，对于搜索就使用&lt;code&gt;Form_tag&lt;/code&gt;。初学者的观点也不能说不对，但是如果要了解为什么需要在不同的情况使用不同的 Helper 还是需要了解一下它们的区别。那它们生成的 HTML 究竟有什么不一样呢？&lt;br&gt;
关于&lt;code&gt;Form_for&lt;/code&gt;的例子，可以看上面提到的那个例子，而现在看看官方文档中对&lt;code&gt;Form_tag&lt;/code&gt;的描述是怎么样的：  &lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= form_tag("/search", method: "get") do %&amp;gt;
  &amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;label_tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:q&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Search for:"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= text_field_tag(:q) %&amp;gt;
  &amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;submit_tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Search"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;% end &lt;/span&gt;&lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;生成如下的 HTML：  &lt;/p&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;accept-charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/search"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"get"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"margin:0;padding:0;display:inline"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"utf8"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"&amp;amp;#x2713;"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"q"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Search for:&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"q"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"q"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"commit"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"Search"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;它们最大的区别和最关键的区别就是 input 中的 name 值，分别是：&lt;code&gt;Form_for&lt;/code&gt;是&lt;code&gt;name="article[body]"&lt;/code&gt;，而&lt;code&gt;Form_tag&lt;/code&gt;是&lt;code&gt;name="q"&lt;/code&gt;，而这个 name 值决定了数据发往服务器时的格式是怎么的：&lt;code&gt;Form_for&lt;/code&gt;传出去的格式是&lt;code&gt;{article: {body: value}}&lt;/code&gt;，而&lt;code&gt;Form_tag&lt;/code&gt;则是&lt;code&gt;{q: value}&lt;/code&gt;。关于&lt;code&gt;article&lt;/code&gt;这个名称的定义则来源于&lt;code&gt;form_for @article&lt;/code&gt;中的&lt;code&gt;@article&lt;/code&gt;。&lt;br&gt;
然后清楚了它们的两个的区别后，又会衍生另一个问题，就是，那我用&lt;code&gt;Form_for&lt;/code&gt;做搜索不就也可以了？答案是：当然可以。但这里会有一个问题，如果你直接这样使用&lt;code&gt;form_for @search&lt;/code&gt;的话，整个页面就会出错了。原因在于 Rails 并不知道&lt;code&gt;@search&lt;/code&gt;是不是一个 hash 类型，而且并没有定义里面的参数值，所以你这样定义就可以顺利通过了：&lt;code&gt;@search = {q: ""}&lt;/code&gt;。同理可得，其实&lt;code&gt;@article = Article.new&lt;/code&gt;就相当是一个给予&lt;code&gt;article&lt;/code&gt;默认 key 值和默认 value 值的过程。所以其实无论是&lt;code&gt;Form_for&lt;/code&gt;或&lt;code&gt;Form_tag&lt;/code&gt;都好，Rails 无形中都帮我们把整个概念整理的更简洁了。&lt;br&gt;
所以在创建和更新方面常见的操作为&lt;code&gt;@article = Article.new(params[:article])&lt;/code&gt;，而在 search 中则多为&lt;code&gt;@article = Article.search(name: params[:name])&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;原文：
&lt;a href="http://jesktop.com/2014/09/14/form-for-and-form-tag/" rel="nofollow" target="_blank" title=""&gt;如何理解 FORM_FOR 和 FORM_TAG&lt;/a&gt;&lt;/p&gt;</description>
      <author>jesktop</author>
      <pubDate>Sun, 14 Sep 2014 12:20:59 +0800</pubDate>
      <link>https://ruby-china.org/topics/21523</link>
      <guid>https://ruby-china.org/topics/21523</guid>
    </item>
    <item>
      <title>Rails 中如何自定义 Error Pages</title>
      <description>&lt;h2 id="修改配置"&gt;修改配置&lt;/h2&gt;
&lt;p&gt;首先需要修改项目的配置，让项目接收到错误提示时，从 route 中寻找，而不直接读 public 的文件：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/application.rb   &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;exceptions_app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;routes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;加入配置之后，避免在读取到&lt;code&gt;/404&lt;/code&gt;, &lt;code&gt;/422&lt;/code&gt;, &lt;code&gt;/500&lt;/code&gt;时，仍然从 public 中寻找，建议删除 public 下的这三个 html 文件。&lt;/p&gt;
&lt;h2 id="加入controler，view和route"&gt;加入 controler，view 和 route&lt;/h2&gt;
&lt;p&gt;因为 public 的对应错误信息页面删除了，所以需要重新定义错误信息的 Route：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/routes.rb&lt;/span&gt;
&lt;span class="sx"&gt;%w(404 422 500)&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;code&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="s2"&gt;"errors#show"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;code: &lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在加好 Errors 后，自然需要对应的 Controller 文件：&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;ErrorsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

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

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;status_code&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:code&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;500&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;这样就会根据错误的提示，去 render 对应的模板，如出现 404 错误，则去寻找&lt;code&gt;errors/404.html.erb&lt;/code&gt;，所以加入相应的&lt;code&gt;404.html.erb&lt;/code&gt;, &lt;code&gt;422.html.erb&lt;/code&gt;, &lt;code&gt;500.html.erb&lt;/code&gt;。参考：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/views/errors/404.html.haml&lt;/span&gt;
&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;h1&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="no"&gt;Not&lt;/span&gt; &lt;span class="no"&gt;Found&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="开发环境下，测试错误信息页面"&gt;开发环境下，测试错误信息页面&lt;/h2&gt;
&lt;p&gt;因为开发环境下出现错误，都会呈现对应的错误信息，包括代码位置等。而不会弹出 404 或 500 页面，其实要在开发环境下，检测这些页面也是很方便的：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/environments/development.rb&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;consider_all_requests_local&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;只需要把上面的设置从 true 改为 false 就可以了。&lt;/p&gt;
&lt;h2 id="Rails内自带的500错误提示"&gt;Rails 内自带的 500 错误提示&lt;/h2&gt;
&lt;p&gt;当把错误自定义以 route 的方式展现后，如果本来的错误信息页面，例如 404 页面出错了，就会出现这样的错误：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="s2"&gt;"500 Internal Server Error&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;"If you are the administrator of this website, then please read this web "&lt;/span&gt; &lt;span class="p"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;"application's log file and/or the web server's log file to find out what "&lt;/span&gt; &lt;span class="p"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;"went wrong."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个问题就像刚刚上面说的，是因为你的错误提示信息页面出错了，无法展现 404 页面了，所以就调用了 Rails 下的一个 500 错误提示信息，源码位置在：&lt;a href="https://github.com/rails/rails/blob/4-0-stable/actionpack/lib/action_dispatch/middleware/show_exceptions.rb#L18-L22" rel="nofollow" target="_blank" title=""&gt;show_exceptions.rb#L18-L22&lt;/a&gt;。
所以，如果出现了这样的错误，需要仔细看看自己的错误信息页面是否在哪里出了问题。&lt;/p&gt;

&lt;p&gt;*我在一个开源项目里，也加入了此功能，可以浏览对应的 commit，位置在：&lt;a href="https://github.com/minnowlab/giggle/commit/62c7d6e15f9c14d9d111a6a25ceb841b33a7db24" rel="nofollow" target="_blank" title=""&gt;Merge branch 'dynamic_error_pages'&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;参考：
&lt;a href="http://wearestac.com/blog/dynamic-error-pages-in-rails" rel="nofollow" target="_blank" title=""&gt;DYNAMIC ERROR PAGES IN RAILS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;原文：
&lt;a href="http://jesktop.com/2014/09/05/dynamic-error-pages-in-rails/" rel="nofollow" target="_blank" title=""&gt;RAILS 中如何自定义 ERROR PAGES&lt;/a&gt;&lt;/p&gt;</description>
      <author>jesktop</author>
      <pubDate>Sun, 07 Sep 2014 01:59:32 +0800</pubDate>
      <link>https://ruby-china.org/topics/21413</link>
      <guid>https://ruby-china.org/topics/21413</guid>
    </item>
    <item>
      <title>「开源了一个产品」淘宝商家展示产品的社区 Giggle</title>
      <description>&lt;h2 id="Giggle"&gt;&lt;a href="https://github.com/minnowlab/giggle" rel="nofollow" target="_blank" title=""&gt;Giggle&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;分享一个开源的淘宝商家展示产品的社区（Giggle）&lt;/p&gt;

&lt;p&gt;简介：
对于淘宝商家来说一直存在一个让大家困扰的问题，就是维护客户关系一直不是一件容易的事情。
在微博和各种社交平台出现之前，普遍会采用论坛形式或手机短信来与客户进行互动、交流或宣传，最常用的如：淘宝江湖，群发手机短信。
往后出现了，微博、美丽说和蘑菇街后，各种新的营销概念如雨后春笋般萌发出来，令人目不暇接。最火爆的莫过于，微博营销。后来移动互联网的爆发，微信成为每个人手机上必备的软件，商家们又大量涌入微信公众帐号，微信营销大战开响。
其实上面说的那么多，就是淘宝商家一直以来都希望与客户建立非常接近的关系，而淘宝一直以来社区方面的产品都做的不温不火，所以蘑菇街和美丽说才会有机会火爆起来。然后 Giggle 就是希望提供一个给淘宝商家建立社区，方便与客户间建立联系的开源产品。
当然，上面说的都不是我们凭空想象出来的概念和需求。我们都运营过淘宝店铺，并且取得了“皇冠”信誉的成绩。然后在前段时间，我们看到了 KnewOne：&lt;a href="http://knewone.com/" rel="nofollow" target="_blank"&gt;http://knewone.com/&lt;/a&gt;  这个产品后，发觉 KnewOne 的概念和定位都非常不错。看着从一个引流进淘宝购买的平台发展到独立的平台，而且速度非常惊人。所以我们决定开发一款类似 KnewOne 的产品，并且开源，也就是你们现在看到的 Giggle。&lt;/p&gt;

&lt;p&gt;源码地址：&lt;a href="https://github.com/minnowlab/giggle" rel="nofollow" target="_blank"&gt;https://github.com/minnowlab/giggle&lt;/a&gt;
项目展示地址（Demo）：&lt;a href="http://gmi.minnowlab.com/" rel="nofollow" target="_blank"&gt;http://gmi.minnowlab.com/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="系统需求"&gt;系统需求&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Ruby 2.0.0 +&lt;/li&gt;
&lt;li&gt;PostgreSQL 9.3.0 +&lt;/li&gt;
&lt;li&gt;ImageMagick 6.5+&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="待完成"&gt;待完成&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;完整的 Rspec 测试覆盖&lt;/li&gt;
&lt;li&gt;对 IE 浏览器的兼容&lt;/li&gt;
&lt;li&gt;首页的产品搜索&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="License"&gt;License&lt;/h2&gt;
&lt;p&gt;Giggle is free software available under the &lt;a href="http://en.wikipedia.org/wiki/MIT_License" rel="nofollow" target="_blank" title=""&gt;MIT license&lt;/a&gt;.&lt;/p&gt;</description>
      <author>jesktop</author>
      <pubDate>Fri, 05 Sep 2014 22:12:24 +0800</pubDate>
      <link>https://ruby-china.org/topics/21405</link>
      <guid>https://ruby-china.org/topics/21405</guid>
    </item>
    <item>
      <title>使用 CAPISTRANO3 和 PUMA 进行部署</title>
      <description>&lt;h2 id="添加需要使用到的gem"&gt;添加需要使用到的 gem&lt;/h2&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="ss"&gt;:development&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'capistrano-rails'&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'capistrano3-puma'&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'capistrano-rvm'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'puma'&lt;/span&gt;    &lt;span class="c1"&gt;#使用puma做server&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="编辑配置文件"&gt;编辑配置文件&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;初始化 cap:&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;cap &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;初始化后，会生成多个我们需要用的文件，代码可以直接使用&lt;code&gt;Capfile&lt;/code&gt;：&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'capistrano/setup'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'capistrano/deploy'&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'capistrano/rvm'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'capistrano/bundler'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'capistrano/rails/assets'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'capistrano/rails/migrations'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'capistrano/puma'&lt;/span&gt;      &lt;span class="c1"&gt;#因为使用puma做Server，所以要加上这一条&lt;/span&gt;

&lt;span class="no"&gt;Dir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'lib/capistrano/tasks/*.cap'&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;r&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;import&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;  然后关键的一些 task 都是写在&lt;code&gt;config/deploy.rb&lt;/code&gt;里，假设我们的项目名字叫 example：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;lock&lt;/span&gt; &lt;span class="s1"&gt;'3.1.0'&lt;/span&gt;

&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:application&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'example'&lt;/span&gt;      &lt;span class="c1"&gt;#项目名称&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:repo_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'git@example.com:example.git'&lt;/span&gt;    &lt;span class="c1"&gt;#git仓库的存放地址&lt;/span&gt;

&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:linked_files&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sx"&gt;%w{config/database.yml}&lt;/span&gt;       &lt;span class="c1"&gt;#需要做链接的文件，一般database.yml和部分配置文件&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:linked_dirs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sx"&gt;%w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system}&lt;/span&gt;

&lt;span class="n"&gt;namespace&lt;/span&gt; &lt;span class="ss"&gt;:deploy&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s1"&gt;'Restart application'&lt;/span&gt;
  &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;:restart&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="ss"&gt;in: :sequence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;wait: &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="k"&gt;do&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;after&lt;/span&gt; &lt;span class="ss"&gt;:restart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:'puma:restart'&lt;/span&gt;    &lt;span class="c1"&gt;#添加此项重启puma&lt;/span&gt;
  &lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="ss"&gt;:publishing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:restart&lt;/span&gt;

  &lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="ss"&gt;:restart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:clear_cache&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:web&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="ss"&gt;in: :groups&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;limit: &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;wait: &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;  这里我们只使用 production 环境，所以只对&lt;code&gt;config/deploy/production.rb&lt;/code&gt;介绍，这是 cap3 的一个特别的地方，它把不同环境的部署方案分开放在 deploy 文件内，并且部署命令改为&lt;code&gt;cap 环境名称 deploy&lt;/code&gt;，这样分开来后，整个部署配置文件结构变得非常清晰：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="ss"&gt;:app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sx"&gt;%w{deploy@example.com}&lt;/span&gt;     &lt;span class="c1"&gt;#服务器地址&lt;/span&gt;
&lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="ss"&gt;:web&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sx"&gt;%w{deploy@example.com}&lt;/span&gt;
&lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="ss"&gt;:db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="sx"&gt;%w{deploy@example.com}&lt;/span&gt;
&lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="s1"&gt;'example.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="s1"&gt;'deploy'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;roles: &lt;/span&gt;&lt;span class="sx"&gt;%w{web app}&lt;/span&gt;

&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:deploy_to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'/home/deploy/example'&lt;/span&gt;     &lt;span class="c1"&gt;#部署的位置&lt;/span&gt;

&lt;span class="c1"&gt;# PUMA&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:puma_state&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;shared_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/tmp/pids/puma.state"&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:puma_pid&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;shared_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/tmp/pids/puma.pid"&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:puma_bind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"unix:///tmp/example.sock"&lt;/span&gt;      &lt;span class="c1"&gt;#根据nginx配置链接的sock进行设置，需要唯一&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:puma_conf&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;shared_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/puma.rb"&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:puma_access_log&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;shared_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/log/puma_error.log"&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:puma_error_log&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;shared_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/log/puma_access.log"&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:puma_role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:app&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:puma_env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:rack_env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:rails_env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'production'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:puma_threads&lt;/span&gt;&lt;span class="p"&gt;,&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;16&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:puma_workers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:puma_init_active_record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:puma_preload_app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="开始部署"&gt;开始部署&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;执行&lt;code&gt;cap production deploy:check&lt;/code&gt;检查涉及需要用上的部署文件是否齐全，运行后会检测出不存在&lt;code&gt;database.yml&lt;/code&gt;，需要在&lt;code&gt;/home/deploy/example/shared/config/&lt;/code&gt;中创建&lt;code&gt;database.yml&lt;/code&gt;，可以写一个 task 把文件上传上去，也可以直接创建。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;完成后，就可以执行&lt;code&gt;cap production deploy&lt;/code&gt;进行部署了。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;使用&lt;code&gt;capistrano3&lt;/code&gt;后，发现比 2 方便了很多，而且整个结构非常清晰，配合 puma 使用非常方便。上面介绍的是非常简单的，因为还没有涉及自己写 task，所以以后应该会写一个专门介绍如何写部署 task 的文章。&lt;/p&gt;

&lt;p&gt;如果希望把自己的项目从 capistrano2 升级到 3 的话，可以参考 &lt;a href="https://semaphoreapp.com/blog/2013/11/26/capistrano-3-upgrade-guide.html" rel="nofollow" target="_blank" title=""&gt;Capistrano 3 Upgrade Guide&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;原文：&lt;a href="http://jesktop.com/2014/02/23/capistrano3-with-puma/" rel="nofollow" target="_blank" title=""&gt;使用 CAPISTRANO3 和 PUMA 进行部署&lt;/a&gt;&lt;/p&gt;</description>
      <author>jesktop</author>
      <pubDate>Sun, 23 Feb 2014 00:58:47 +0800</pubDate>
      <link>https://ruby-china.org/topics/17425</link>
      <guid>https://ruby-china.org/topics/17425</guid>
    </item>
    <item>
      <title>根据 ruby china 配置 faye，但是提示错误，是不是漏了什么？</title>
      <description>&lt;p&gt;1.&lt;a href="https://github.com/ruby-china/ruby-china/blob/master/Gemfile#L66" rel="nofollow" target="_blank" title=""&gt;首先添加 gem&lt;/a&gt;&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gem 'faye-rails','1.0.0'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2.&lt;a href="https://github.com/ruby-china/ruby-china/blob/master/faye_server/config.ru" rel="nofollow" target="_blank" title=""&gt;后建 server&lt;/a&gt;
3.&lt;a href="https://github.com/ruby-china/ruby-china/blob/master/faye_server/thin.yml" rel="nofollow" target="_blank" title=""&gt;server 的配置文件&lt;/a&gt;
4.&lt;a href="https://github.com/ruby-china/ruby-china/blob/master/start_faye_server" rel="nofollow" target="_blank" title=""&gt;如何启动 server&lt;/a&gt;
5.&lt;a href="https://github.com/ruby-china/ruby-china/blob/master/app/assets/javascripts/app.coffee#L20" rel="nofollow" target="_blank" title=""&gt;加入 faye js 文件&lt;/a&gt;
6.&lt;a href="https://github.com/ruby-china/ruby-china/blob/master/Gemfile#L59" rel="nofollow" target="_blank" title=""&gt;使用 settingslogic 导入配置信息&lt;/a&gt;
7.&lt;a href="https://github.com/ruby-china/ruby-china/blob/master/config/config.yml.default" rel="nofollow" target="_blank" title=""&gt;配置信息样例&lt;/a&gt;
8.&lt;a href="https://github.com/ruby-china/ruby-china/blob/master/app/models/setting.rb" rel="nofollow" target="_blank" title=""&gt;Setting model 用来引入设置信息到 model 层&lt;/a&gt;
9.&lt;a href="https://github.com/ruby-china/ruby-china/blob/c1bebe0bd0cd6b727a5ca1634b8ff624abdb8bf7/app/views/layouts/application.html.erb#L95" rel="nofollow" target="_blank" title=""&gt;faye 的全局设置&lt;/a&gt;
10.&lt;a href="https://github.com/ruby-china/ruby-china/blob/master/app/assets/javascripts/app.coffee#L113" rel="nofollow" target="_blank" title=""&gt;Faye 的前端监听设置&lt;/a&gt;
11.&lt;a href="https://github.com/ruby-china/ruby-china/blob/master/app/assets/javascripts/app.coffee#L137" rel="nofollow" target="_blank" title=""&gt;Faye 开启 url 监听&lt;/a&gt;
12.&lt;a href="https://github.com/ruby-china/ruby-china/blob/master/app/assets/javascripts/notifier.coffee" rel="nofollow" target="_blank" title=""&gt;webkitNotifications 的设置&lt;/a&gt;
13.&lt;a href="https://github.com/ruby-china/ruby-china/blob/master/app/models/faye_client.rb" rel="nofollow" target="_blank" title=""&gt;Faye 服务器端的发送配置&lt;/a&gt;
14.&lt;a href="https://github.com/ruby-china/ruby-china/blob/master/app/models/notification/base.rb#L22" rel="nofollow" target="_blank" title=""&gt;Notification 创建后发送信息到客户端&lt;/a&gt;
15.&lt;a href="https://github.com/ruby-china/ruby-china/blob/master/app/models/user.rb#L99" rel="nofollow" target="_blank" title=""&gt;通过 temp_access_token 来判断接收者&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;根据上面的步骤，自己添加了一个 faye 服务，可是提示错误：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Uncaught TypeError: Cannot read property 'JSON' of undefined
Uncaught Error: Could not find a usable connection type for http://127.0.0.1:9292/faye 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;貌似是没有发现 faye 的服务。我看了官方文档，&lt;a href="http://faye.jcoglan.com/browser.html" rel="nofollow" target="_blank" title=""&gt;Browser client&lt;/a&gt;有个这样的操作：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script type="text/javascript" src="http://localhost:8000/faye/client.js"&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是在 ruby-china 上没有看到有，我在重翻了一遍 ruby-china 的源码，但是发现不了我这个问题是从哪里出来的，请问是不是因为遗漏了哪些地方，所有才会出现这个错误？&lt;/p&gt;</description>
      <author>jesktop</author>
      <pubDate>Sun, 28 Jul 2013 11:20:47 +0800</pubDate>
      <link>https://ruby-china.org/topics/12844</link>
      <guid>https://ruby-china.org/topics/12844</guid>
    </item>
    <item>
      <title>JavaScript MVC 框架 PK：Angular、Backbone、CanJS 与 Ember</title>
      <description>&lt;p&gt;本人 ember.js 脑残粉。&lt;img title=":smile:" alt="😄" src="https://twemoji.ruby-china.com/2/svg/1f604.svg" class="twemoji"&gt;
个人看过不少李松峰翻译的 js 书籍，觉得他翻译的还是挺不错的。&lt;/p&gt;

&lt;p&gt;&lt;img src="//l.ruby-china.com/photo/e8f23cc48f17115b16f650a0925eff78.jpg" title="" alt=""&gt;
原文：&lt;a href="http://www.ituring.com.cn/article/38394" rel="nofollow" target="_blank"&gt;http://www.ituring.com.cn/article/38394&lt;/a&gt;&lt;/p&gt;</description>
      <author>jesktop</author>
      <pubDate>Tue, 23 Apr 2013 12:28:18 +0800</pubDate>
      <link>https://ruby-china.org/topics/10430</link>
      <guid>https://ruby-china.org/topics/10430</guid>
    </item>
    <item>
      <title>谁才是 “邪恶轴心”？</title>
      <description>&lt;p&gt;最近在美国最重大的事件无疑是“波士顿恐怖袭击”事件，现在网上分成两派人：1. 炸的好，该死的美帝；2. 对死伤人表示痛心。这样的情况让我想起美国 911 事件，貌似是 2001 年的事情，我那时候还读初中吧，当时候我听到身边蛮多的人说这恐怖事件干的好，当时候我对此没有太大的感觉，因为常在我们教科书上的“美帝恐怖组织”被人整了一把，好像还是蛮爽的事情，但是又好像惨无人道了一点。总之就是，没感觉。&lt;/p&gt;

&lt;p&gt;现在看着“波士顿恐怖袭击”真的看着觉得很心痛，看着那些人抱着自己的家人在痛哭，觉得这类型的恐怖事件针对平民，绝对是一件反人类的事情。更可怕的是看到网上评论说“炸的好”的人，更让人痛心，甚至是回想起 911 事件时候我的想法也是觉得非常的可怕。无论是任何人，死伤都将对身边的人会带来沉痛的打击，或者换个角度，如果发生在我们身边，那是多可怕的事情。就针对这样的事情，我们有什么理由可以那么高兴？&lt;/p&gt;

&lt;p&gt;然后有人会说，美帝发动战争，攻击各种国家，那边的平民不是更惨吗？有人反驳，你们不知道那边的人民被那些独裁统治者压迫的多么可怜！针对这种问题，我想应该客观的去看待问题，我不了解那些国家的情况是不是真的那么惨，也不知道美帝发动战争的目的是否是为了钱财，为了石油。我希望在有生之年可以寻找到这些答案。这样我才能知道，谁才是“邪恶轴心”！&lt;/p&gt;

&lt;p&gt;原文：&lt;a href="http://jesktop.writings.io/articles/700a3cba" rel="nofollow" target="_blank"&gt;http://jesktop.writings.io/articles/700a3cba&lt;/a&gt;&lt;/p&gt;</description>
      <author>jesktop</author>
      <pubDate>Wed, 17 Apr 2013 12:08:49 +0800</pubDate>
      <link>https://ruby-china.org/topics/10271</link>
      <guid>https://ruby-china.org/topics/10271</guid>
    </item>
    <item>
      <title>购买 Mac 正版软件促销包 2013 春季版 ，谁下手了？</title>
      <description>&lt;p&gt;来自：&lt;a href="http://www.iplaysoft.com/mac-bundle-spring-2013.html#665594-tsina-1-71472-557ef1fe535cf3a0e8489d390d5bf581" rel="nofollow" target="_blank" title=""&gt;异次元软件世界&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;这个 Mac 软件促销包中全部均为正版授权，其中包括 Parallels Desktop 8 虚拟机、DEVONthink Pro 2、Prizmo、MotionComposer、MacUpdate Desktop、iStat Menus、DiskAid、PhotoStyler、Internet Security 等软件以及 1000 款正版英文设计字体！其中，仅是人尽熟知的 Parallels 的原售价都要 79 刀了，而这一整个包的总价格才 49 刀，如果你是打算入正，那么千万别浪费这个绝佳机会了……&lt;/p&gt;

&lt;p&gt;原文：&lt;a href="https://deals.macupdate.com/bundle/affil/14052" rel="nofollow" target="_blank"&gt;https://deals.macupdate.com/bundle/affil/14052&lt;/a&gt;&lt;/p&gt;</description>
      <author>jesktop</author>
      <pubDate>Sat, 30 Mar 2013 17:40:05 +0800</pubDate>
      <link>https://ruby-china.org/topics/9859</link>
      <guid>https://ruby-china.org/topics/9859</guid>
    </item>
    <item>
      <title>人生旅程 (梦想就是一坨屎)</title>
      <description>&lt;p&gt;每个人对人生都有很多很多的追求，如买豪宅，买靓车等等。似乎人的欲望就像个沼泽一样，看上去很浅，跳下去却深不见底。简单的说，你想要的永远无法满足于你。等等，我突然想到了一部电影的一句台词：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;广告诱惑我们买车子，衣服，于是拼命工作买不需要的东西，我们是被历史遗忘的一代，没有目的，没有地位，没有世界大战，没有经济大恐慌，我们的大战只是心灵之战，我们的恐慌只是我们的生活。我们从小看电视，相信有一天会成为富翁，明星或摇滚巨星，但是，我们不会。那是我们逐渐面对着的现实，所以我们非常愤怒。----《搏击俱乐部》
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;有时候和别人谈理想，觉得很幼稚，还以为你在读书阿！生活还是要继续下去的，生活就是结婚生子，赚钱养家，买房买车。梦想能送你兰博基尼吗？这是中国，你的这里没有“美国梦”，醒醒吧！&lt;/p&gt;

&lt;p&gt;争论是没有意义的，我觉得一个人已经决定很你争辩，那他不会有像你妥协的理由。也许你说，只要你有足够的证明，他自然会相信你那套理论。算了吧，简直是自讨苦吃。其实生活有套准则，就是“胜者为王，败者为寇”，你一天没有成功，所有东西给人看起来，就是屎一样。毕竟没有人会愿意承认自己的那套是错的，毕竟人生最痛就是发现自己一直坚持的竟然通通都是错的，那很尴尬阿！&lt;/p&gt;

&lt;p&gt;对比好多人，我对生活真的没有什么追求。我见过一些人疯狂追求奢侈品，疯狂追求高端电子产品等等。我也很希望那么败家一把，但是绝对当自己得到后一定会发现这并不是自己想要的，就像自己买了一套很潮的衣服，结果穿不上几次就挂在家里了的感觉那样。那我有什么追求？想一想，我一直有一个愿望，我想从事互联网行业，我想有一个像 37signals 那样的公司，小而巧，不追超大规模，超大投资。有个好团队，人员能尽量的少而精，有很好的产品和工作环境。Oh，那真是太棒了。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;其实，人生有梦想，真的挺好。。。。&lt;/strong&gt;&lt;/p&gt;</description>
      <author>jesktop</author>
      <pubDate>Wed, 20 Feb 2013 01:31:31 +0800</pubDate>
      <link>https://ruby-china.org/topics/8783</link>
      <guid>https://ruby-china.org/topics/8783</guid>
    </item>
    <item>
      <title>capistrano 简易速成部署方案 (含 deploy.rb 模板)</title>
      <description>&lt;p&gt;先在这里放上来自《Agile Web Development with Rails 4th for Rails3.2》的一个 deploy.rb 模板：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# be sure to change these&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'rubys'&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="ss"&gt;:domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'depot.pragprog.com'&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="ss"&gt;:application&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'depot'&lt;/span&gt;

&lt;span class="c1"&gt;# adjust if youareusing RVM, remove if youarenot&lt;/span&gt;
&lt;span class="vg"&gt;$:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unshift&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;expand_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'./lib'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'rvm_path'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"rvm/capistrano"&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="ss"&gt;:rvm_ruby_string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'1.9.2'&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="ss"&gt;:rvm_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:user&lt;/span&gt;

&lt;span class="c1"&gt;# file paths&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="ss"&gt;:repository&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;user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:git/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.git"&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="ss"&gt;:deploy_to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"/home/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="c1"&gt;# distribute your applications across servers (the instructions below putthem&lt;/span&gt;
&lt;span class="c1"&gt;# allon thesame server, defined above as 'domain', adjust as necessary)&lt;/span&gt;

&lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="ss"&gt;:app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt;
&lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="ss"&gt;:web&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt;
&lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="ss"&gt;:db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:primary&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="c1"&gt;# youmight need to setthis if youaren't seeing password prompts&lt;/span&gt;
&lt;span class="c1"&gt;# default_run_options[:pty] = true&lt;/span&gt;
&lt;span class="c1"&gt;# As Capistrano executes in a non-interactive mode andtherefore doesn't cause&lt;/span&gt;
&lt;span class="c1"&gt;# anyof your shell profile scripts to be run, thefollowing might be needed&lt;/span&gt;
&lt;span class="c1"&gt;# if (for example) youhave locally installed gems or applications. Note:&lt;/span&gt;
&lt;span class="c1"&gt;# this needs to contain thefull values forthevariables set, notsimply&lt;/span&gt;
&lt;span class="c1"&gt;# thedeltas.&lt;/span&gt;
&lt;span class="c1"&gt;# default_environment['PATH']='&amp;lt;your paths&amp;gt;:/usr/local/bin:/usr/bin:/bin'&lt;/span&gt;
&lt;span class="c1"&gt;# default_environment['GEM_PATH']='&amp;lt;your paths&amp;gt;:/usr/lib/ruby/gems/1.8'&lt;/span&gt;

&lt;span class="c1"&gt;# miscellaneous options&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="ss"&gt;:deploy_via&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:remote_cache&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="ss"&gt;:scm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'git'&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="ss"&gt;:branch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'master'&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="ss"&gt;:scm_verbose&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="ss"&gt;:use_sudo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="ss"&gt;:rails_env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:production&lt;/span&gt;

&lt;span class="n"&gt;namespace&lt;/span&gt; &lt;span class="ss"&gt;:deploy&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"cause Passenger to initiate a restart"&lt;/span&gt;

  &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;:restart&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="s2"&gt;"touch &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;current_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/tmp/restart.txt"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"reload thedatabase with seed data"&lt;/span&gt;
  &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;:seed&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="s2"&gt;"cd&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;current_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;; rake db:seed RAILS_ENV=&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;rails_env&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="s2"&gt;"deploy:update_code"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:bundle_install&lt;/span&gt;
&lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"install thenecessary prerequisites"&lt;/span&gt;

&lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;:bundle_install&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:roles&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:app&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="s2"&gt;"cd&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;release_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &amp;amp;&amp;amp; bundle install"&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;p&gt;首先在加入一个 gem，&lt;code&gt;gem 'capistrano'&lt;/code&gt;。然后创建相关文件，只需要执行一个命令就可以：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ capify .
[add] writing './Capfile'
[add] writing './config/deploy.rb'
[done] capified!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这时候生成两个文件。如果在用使用 3.1 以上版本的 Rails，在 Capfile 加上一句：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;load&lt;/span&gt; &lt;span class="s1"&gt;'deploy/assets'&lt;/span&gt;    &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;/assets操作
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后剩下的就是 deploy.rb 上的了，我发一下我的配置，然后备注一下，到时候大家按照我那个配置设置一下就可以了，如下：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"bundler/capistrano"&lt;/span&gt;   &lt;span class="c1"&gt;#部署时运行bundle&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'deploy'&lt;/span&gt;            &lt;span class="c1"&gt;#存放web的用户名&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'106.XX.XX.XX'&lt;/span&gt;        &lt;span class="c1"&gt;#服务器IP&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:application&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'ruby-china.org'&lt;/span&gt;    &lt;span class="c1"&gt;#项目文件名，既将会在user里面创建这个名字的文件夹&lt;/span&gt;
&lt;span class="c1"&gt;# adjust if you are using RVM, remove if you are not&lt;/span&gt;
&lt;span class="vg"&gt;$:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unshift&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;expand_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'./lib'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'rvm_path'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"rvm/capistrano"&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:rvm_ruby_string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'1.9.3'&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:rvm_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:user&lt;/span&gt;
&lt;span class="c1"&gt;# file paths&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:repository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"git@&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:ruby-china.git"&lt;/span&gt;  &lt;span class="c1"&gt;#git所存在的地址，我存放在自己的服务器中&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:deploy_to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"/home/deploy/ruby-china.org"&lt;/span&gt;  &lt;span class="c1"&gt;#部署的地址&lt;/span&gt;
&lt;span class="c1"&gt;# distribute your applications across servers (the instructions below put them&lt;/span&gt;
&lt;span class="c1"&gt;# all on the same server, defined above as 'domain', adjust as necessary)&lt;/span&gt;
&lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="ss"&gt;:app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt;
&lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="ss"&gt;:web&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt;
&lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="ss"&gt;:db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:primary&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;ssh_options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:forward_agent&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;  &lt;span class="c1"&gt;#forward_agent方式&lt;/span&gt;

&lt;span class="c1"&gt;# miscellaneous options&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:deploy_via&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:remote_cache&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:scm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'git'&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:branch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'master'&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:scm_verbose&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:use_sudo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:rails_env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"production"&lt;/span&gt;

&lt;span class="n"&gt;namespace&lt;/span&gt; &lt;span class="ss"&gt;:deploy&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"cause Passenger to initiate a restart"&lt;/span&gt;
  &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;:restart&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="s2"&gt;"touch &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;current_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/tmp/restart.txt"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"reload the database with seed data"&lt;/span&gt;
  &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;:seed&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="s2"&gt;"cd &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;current_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;; rake db:seed RAILS_ENV=&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;rails_env&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;注意：&lt;/strong&gt;这里涉及一个叫“forward_agent 方式”，简单的来说就是服务器在 clone git 的时候是不成功的，因为他的 ssh 公钥并不在 git 的账户中，而我们电脑的公钥已经存放在 git 服务器里了，所以可以直接 push 或者 pull 操作 git，所以这类需要使用 ssh agent forward，也就是在部署时，VPS 使用你的 SSH 密钥。参考：
&lt;a href="https://help.github.com/articles/using-ssh-agent-forwarding" title=""&gt;Using ssh agent forwarding&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;git 的地址当然也可以在 github 上，但是我觉得竟然有 VPS 了，放在自己的 VPS 上更加方便。我以前有个教人在自己 VPS 上部署 GIT 的方法：&lt;a href="http://ruby-china.org/topics/5040" title=""&gt;Linode VPS 小组级 Git 服务器搭建&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;如果你使用的个别东西不一样就需要参考 capistrano 的 wiki 进行修改了，例如你用的不是 git，服务器不是 Passenger 等等。&lt;/p&gt;

&lt;p&gt;写好 deploy.rb 后的事情就不多了，首先把修改过的内容 push 到 git 上面。然后开始部署了，第一次部署需要运行：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cap deploy:setup
$ cap deploy:check
$ cap deploy:migrations
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上条命令下来，如果没有问题就已经成功的设置成功了，然后进行发布：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cap deploy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;非常方便的就把网站部署到 VPS 上了，如果以后有修改了东西也非常容易发布：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git add .
$ git commit -m "add capfiles"
$ git push
$ cap deploy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;非常的简单方便，只需要几条命令，服务器上的信息就更新了。这个文章介绍的比较简单，没有去讲一些任务的写法，就简单的项目来说精简够用了。需要更深入的了解可以通过以上方式：
&lt;a href="http://ruby-china.org/wiki/capistrano-2_x-getting-started" title=""&gt;Capistrano 2.x Getting Started&lt;/a&gt;
&lt;a href="https://github.com/capistrano/capistrano/wiki" title=""&gt;capistrano wiki&lt;/a&gt;&lt;/p&gt;</description>
      <author>jesktop</author>
      <pubDate>Wed, 31 Oct 2012 11:43:22 +0800</pubDate>
      <link>https://ruby-china.org/topics/6424</link>
      <guid>https://ruby-china.org/topics/6424</guid>
    </item>
    <item>
      <title>嵌套属性 (nested attribute) 与 Simple_form</title>
      <description>&lt;p&gt;最近一个项目有这样的一个功能，简单的来说就是零件与库存的联系。
零件表保存的是零件的相关信息，库存表保存的就是零件的数量，入库人等等。&lt;/p&gt;

&lt;p&gt;其实是个很简单的地方，就是当我们需要入库的时候，也就是新建一个零件（fitting），但是我们需要在填写零件信息的时候，同时填写库存（stock）的相关信息，库存中保存（fitting_id），两者关系为一对一。在这里我使用了&lt;code&gt;accepts_nested_attributes_for&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;参考：&lt;a href="http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html" rel="nofollow" target="_blank" title="Active Record Nested Attributes"&gt;Active Record Nested Attributes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;new.slim:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;h1 New Fitting
= simple_form_for @fitting do |f|
  = f.input :name
  = f.simple_fields_for :stock_attributes do |d|
    = d.input :amount
  = f.button :submit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里出现了一个&lt;code&gt;:stock_attributes&lt;/code&gt;值，是因为在 model 中使用了&lt;code&gt;accepts_nested_attributes_for&lt;/code&gt;。
fitting.rb&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;Fitting&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_one&lt;/span&gt; &lt;span class="ss"&gt;:stock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:dependent&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:destroy&lt;/span&gt;
  &lt;span class="n"&gt;accepts_nested_attributes_for&lt;/span&gt; &lt;span class="ss"&gt;:stock&lt;/span&gt;
  &lt;span class="n"&gt;attr_accessible&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:stock_attributes&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;accepts_nested_attributes_for 这个类方法，就在该 model 上生成一个属性 writer。
属性 writer 是以该关联命名。例如，为你的 model 增加两个新方法：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;author_attributes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;和&lt;/span&gt; &lt;span class="n"&gt;pages_attributes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;FittingsController 中的 new 与 create 则不需要做任何的特殊变化：&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;new&lt;/span&gt;
  &lt;span class="vi"&gt;@fitting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Fitting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
  &lt;span class="vi"&gt;@fitting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Fitting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:fitting&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="vi"&gt;@fitting.save&lt;/span&gt;
  &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="n"&gt;fittings_path&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在点击 submit 时候，传输的数据如下：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "fitting"=&amp;gt;
  {
    "name"=&amp;gt;"XXX",
    "stock_attributes"=&amp;gt;
    {
      "amount"=&amp;gt;"XXX"
    },
    "commit"=&amp;gt;"XX"
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也就是为什么我们需要使用&lt;code&gt;:stock_attributes&lt;/code&gt;值的原因了。&lt;/p&gt;

&lt;p&gt;通过这样的方式，就顺利的在创建 fitting 的同时也创建并且关联了 stock。&lt;/p&gt;</description>
      <author>jesktop</author>
      <pubDate>Thu, 11 Oct 2012 11:16:26 +0800</pubDate>
      <link>https://ruby-china.org/topics/5992</link>
      <guid>https://ruby-china.org/topics/5992</guid>
    </item>
    <item>
      <title>gem 'carrierwave'简易实用介绍</title>
      <description>&lt;h2 id="gem 'carrierwave'"&gt;gem 'carrierwave'&lt;/h2&gt;
&lt;p&gt;Carrierwave 的主要功能是用于上传文件或者图片的 Gem，功能非常强大。我在这里做一个非常简单的使用步骤，并不对 Carrierwave 做详细的介绍。&lt;/p&gt;

&lt;hr&gt;
&lt;h2 id="创建uploader"&gt;创建 uploader&lt;/h2&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;rails&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="n"&gt;uploader&lt;/span&gt; &lt;span class="no"&gt;Photo&lt;/span&gt;
   &lt;span class="n"&gt;create&lt;/span&gt;  &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;uploaders&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;photo_uploader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rb&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;uploader 这个文件非常重要，carrierwave 许多功能都将在 photo_uploader.rb 里实现。&lt;/p&gt;
&lt;h2 id="创建字段存储文件信息"&gt;创建字段存储文件信息&lt;/h2&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;rails&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="n"&gt;migration&lt;/span&gt; &lt;span class="no"&gt;AddPhotoToUsers&lt;/span&gt; &lt;span class="n"&gt;photo&lt;/span&gt;&lt;span class="ss"&gt;:string&lt;/span&gt; 
      &lt;span class="n"&gt;invoke&lt;/span&gt;  &lt;span class="n"&gt;active_record&lt;/span&gt;
      &lt;span class="n"&gt;create&lt;/span&gt;    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;migrate&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;20120816163431&lt;/span&gt;&lt;span class="n"&gt;_add_photo_to_users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rb&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;photo 字段使用与存储上传文件的地址字段。
然后在 User 的 model 中加入调用 carrierwave 的信息：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="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;mount_uploader&lt;/span&gt; &lt;span class="ss"&gt;:photo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;PhotoUploader&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样 Model 和数据库就可以处理好上传上来的文件了。
接下来需要处理一下 View，在正常的 new 下添加上传的按钮。&lt;/p&gt;
&lt;h2 id="Views中添加相关设置"&gt;Views 中添加相关设置&lt;/h2&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= form_for @user, :html =&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:multipart&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="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="sx"&gt;%&amp;gt;
  &amp;lt;p&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="no"&gt;My&lt;/span&gt; &lt;span class="no"&gt;Photo&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/label&amp;gt;
    &amp;lt;%= f.file_field :photo %&amp;gt;
    &amp;lt;%= f.hidden_field :photo_cache %&amp;gt;
  &amp;lt;/&lt;/span&gt;&lt;span class="nb"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;% end &lt;/span&gt;&lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;关键在 form_for 中添加&lt;code&gt;:html =&amp;gt; {:multipart =&amp;gt; true}&lt;/code&gt;，就可以使用上传功能了。&lt;/p&gt;

&lt;p&gt;使用只需要在需要的地方添加&lt;code&gt;&amp;lt;%= image_tag(@user.photo_url) %&amp;gt;&lt;/code&gt;就可以了。
这样，就完成了上传功能了。下面对上传图片的一些特别事项，做一些简单的描述。&lt;/p&gt;

&lt;hr&gt;
&lt;h2 id="使用RMagick生成多个图片版本"&gt;使用 RMagick 生成多个图片版本&lt;/h2&gt;
&lt;p&gt;这里需要使用到另外一个 Gem，名字为 RMagick。
如果在安装 gem 'rmagick'的过程中出现“Can't find Magick-config”的错误，以下链接就有处理这个问题的方法。
&lt;a href="http://stackoverflow.com/questions/5201689/rmagick-gem-install-cant-find-magick-config" rel="nofollow" target="_blank"&gt;http://stackoverflow.com/questions/5201689/rmagick-gem-install-cant-find-magick-config&lt;/a&gt;
安装完 RMagick 后，就可以设置版本信息。
打开 class PhotoUploader，然后添加 RMagick 支持：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;CarrierWave&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RMagick&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="注意：由于RMagick会导致内存泄露，建议使用Mini_Magick，gem "&gt;注意：由于 RMagick 会导致内存泄露，建议使用 Mini_Magick，gem "mini_magick"，使用方法基本和 RMagick 差不多&lt;/h3&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;CarrierWave&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;MiniMagick&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;process&lt;/span&gt; &lt;span class="ss"&gt;:resize_to_fit&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;       &lt;span class="c1"&gt;#图片大小不超过800*800&lt;/span&gt;

&lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="ss"&gt;:thumb&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;                                 &lt;span class="c1"&gt;#版本名为thumb&lt;/span&gt;
  &lt;span class="n"&gt;process&lt;/span&gt; &lt;span class="ss"&gt;:resize_to_fill&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;      &lt;span class="c1"&gt;#想图片处理成200*200大小&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;  不设置 vesrion 则为默认版本，既使用方法为&lt;code&gt;&amp;lt;%= image_tag(@user.photo_url) %&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  而 thumb 版本的使用方法也很简单，&lt;code&gt;&amp;lt;%= image_tag(@user.photo_url(:thumb)) %&amp;gt;&lt;/code&gt;也就是添加 thumb 参数就可以了，版本号为什么就添加什么。非常方便。
# 限制文件上传格式 #
添加如下代码就可以让用户只上传图片文件了。
&lt;code&gt;ruby
class PhotoUploader &amp;lt; CarrierWave::Uploader::Base
def extension_white_list
%w(jpg jpeg gif png)
end
end
&lt;/code&gt;
* * *
这是个非常简单的教程，也谈不上是教程，也就是说明书而已，因为在 github 上，我说写的上面的写的非常清楚。我就是抽出一部分我比较常用的内容写了下来，这样就可以直接按照上面的流程下来，完成需要的部分功能。
如需要了解更加详细的内容，可以登陆：&lt;a href="https://github.com/jnicklas/carrierwave" rel="nofollow" target="_blank"&gt;https://github.com/jnicklas/carrierwave&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>jesktop</author>
      <pubDate>Fri, 17 Aug 2012 01:19:05 +0800</pubDate>
      <link>https://ruby-china.org/topics/4992</link>
      <guid>https://ruby-china.org/topics/4992</guid>
    </item>
  </channel>
</rss>
