<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>dddd1919 (龙虎人丹)</title>
    <link>https://ruby-china.org/dddd1919</link>
    <description>康忙北鼻</description>
    <language>en-us</language>
    <item>
      <title>ElasticSearch 使用总结</title>
      <description>&lt;p&gt;Elasticsearch 通常作为业务搜索的重要组件，或者用于 ELK 做日志分析使用，支持从最简单的全文搜索，模糊/精确搜索，到复杂的组合搜索，还有一些特殊场景下的搜索如地理坐标搜索，甚至可以自己定制搜索脚本来完成自定义的搜索，更重要的是搜索效率非常高。但对于初入者来说，理解 es 的常规使用还是有一定门槛。虽然自己也使用了很久 es，但对使用 es 仅限边边角角的改动，正好前段时间整体重构了一次搜索，顺便把 es 从 2.3 升级到了 5.5，做些总结，应该对新手或是进阶多少有些帮助吧。&lt;/p&gt;
&lt;h2 id="前言"&gt;前言&lt;/h2&gt;
&lt;p&gt;本文侧重总结对 es 本身的使用，平时在开发时还是会借助工具来调试功能，比如 sense，有本地版和 chrome 的插件，在调试错误时能更清晰的定位到问题。还有 es 的 web 管理后台 &lt;a href="https://github.com/mobz/elasticsearch-head" rel="nofollow" target="_blank" title=""&gt;elasticsearch-head&lt;/a&gt;，主要用来看 es 数据和调试查询，有了这俩工具，可以像调试数据库一样来调试搜索。&lt;/p&gt;
&lt;h2 id="查询表达式"&gt;查询表达式&lt;/h2&gt;
&lt;p&gt;自我感觉对 elasticsearch 使用理解的关键还是在对表达式的理解上。大部分开发者对 sql 的搜索都比较熟悉，但当看到 es 这种缩进式的搜索语句还是一头雾水（至少我入门时是这样的）。es 的搜索采用了 json 结构的查询表达式，所有的查询表达式包裹在最外层的&lt;code&gt;query&lt;/code&gt;关键字下，像这样&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /_search
{
    query: YOUR_QUERY_HASH_HERE
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;比起 sql 固定的表达式结构，看起来 json 结构的查询语实在太灵活了，但明确一点：这是个 json 结构的 DSL。每一层的 key 都是有使用规则，错误的查询关键字会导致 ES 在解析查询语句时抛出异常，所以在使用前要多看看官方文档，每种关键字下面可用 options 都有明确的说明，而且在查询文档时一定要确定好自己使用的 es 版本。基于自己常用的一些查询语句画了个图，帮助理解一下 es 搜索语句的结构&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2017/4661d09e-0a4c-4a9d-bffb-c581d31d9cbd.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;理解 es 的搜索结构，基本可以丝滑的用起来了&lt;/p&gt;
&lt;h2 id="SQL like的搜索方式"&gt;SQL like 的搜索方式&lt;/h2&gt;
&lt;p&gt;习惯了 sql 搜索语句，初见 es 的时候我也是一脸无辜，看起不知道从哪下手，但是类比 sql 搜索之后，发现 es 搜索的结构还是很简单的。&lt;/p&gt;

&lt;p&gt;通常使用的查询结构是这样的：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /houses/_search

{
  query: {
    bool: {
      must: {
        country_id: 123
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样的一个语句就类似 sql 中的 &lt;code&gt;House.where(country_id: 123)&lt;/code&gt;，组合搜索的话，可以参考 &lt;a href="https://www.elastic.co/guide/cn/elasticsearch/guide/current/combining-filters.html" rel="nofollow" target="_blank" title=""&gt;一个 2.x 版本的关于组合搜索的中文文档&lt;/a&gt; 类似这样常规的搜索就可以完成很多条件的组合搜索，像 sql 一样完成结构化搜索。&lt;/p&gt;
&lt;h2 id="查询嵌套"&gt;查询嵌套&lt;/h2&gt;
&lt;p&gt;es 的搜索语句很多是可以嵌套的，例如可以在 bool 中嵌套 bool 等等，这样更复杂的嵌套组合语句可以完成更为复杂的搜索。
上面链接里的示例是基于 2.x 的，升级到 5.x 后&lt;code&gt;query&lt;/code&gt;不再支持&lt;code&gt;filterd&lt;/code&gt;，所以改写成 5.x 的结构就会是这样：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /my_store/products/_search
{
   "query" : {
      "bool" : {
        "filter":{ # 同2.x的filterd
          "bool": { # 这里嵌套一层bool
            "should" : [
              { "term" : {"productID" : "KDKE-B-9947-#kL5"}}, 
              { "bool" : { # 这里又嵌套了一层bool
                "must" : [
                  { "term" : {"productID" : "JODL-X-1937-#pV7"}}, 
                  { "term" : {"price" : 30}} 
              ]
            }}
          ]
         }
       }  
     }
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="关于搜索和过滤"&gt;关于搜索和过滤&lt;/h2&gt;
&lt;p&gt;es 默认搜索会给每个结果文档算出一个排序分数，如果只是简单的过滤结果而不需要排序，或是某些过滤条件和结果排序无关，可以把过滤条件嵌套到 filter 中，这样搜索的性能会有所提升（因为不需要算分）。如果只是单纯过滤的话，结果还可以被缓存，增加下一次相同搜索的速度&lt;/p&gt;
&lt;h2 id="关于脚本"&gt;关于脚本&lt;/h2&gt;
&lt;p&gt;作为搜索引擎，支持脚本语句给搜索带来了更多可能，完成一些结构化搜索语句难以完成的功能。es 支持的脚本语言有很多种，python, javasript, 还有内建的 groovy，5.x 的 painless 等。5.x 下推荐使用内建的 painless，性能很强大，另外是运行在沙盒环境下，安全性要比其他的要高，语法类似 java 上手还比较容易。&lt;/p&gt;

&lt;p&gt;举个例子，找出当前商品折扣价高于 500 的商品，要搜索出这些商品，用一个简单的脚本搜索即可完成：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /my_store/products/_search
{
  "query": {
    "bool": {
      "must": {
        "script": {
          "script": {
            "lang": "painless",
            "inline": "doc['price'] * doc['discount'] &amp;gt;= 500"
          }
        }
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果不固定是 500，可能是其他值的话，还可以为脚本传参数：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /my_store/products/_search
{
  "query": {
    "bool": {
      "must": {
        "script": {
          "script": {
            "lang": "painless",
            "params": {
              "lowest_price": 500,
            },
            "inline": "doc['price'] * doc['discount'] &amp;gt;= lowest_price"
          }
        }
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因为脚本语句在执行时也需要编译，所以也会消耗一定的执行时间，第二种传参的写法好有个好处，语句中不存在变量时查询语句会被缓存，这样下次执行同样的查询时可以省去编译的时间。&lt;/p&gt;

&lt;p&gt;如果使用脚本搜索的话还有几个小建议：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;尽量选择脚本原生提供的语法支持，某些扩展的语法可能会带来性能问题。比如在 painless 中没有支持 java string 的 split 方法，但可以修改 es 配置开启正则的 split 语法，但在实际使用中发现性能损耗很大，一条执行一百毫秒左右的搜索语句，加上 split 后搜索时间升到七百毫秒左右，所以官方在未支持 split 方法时也指出本身 string 的 split 性能也不是很好，尽量避免使用&lt;/li&gt;
&lt;li&gt;数组字段的值和 object 类型的字段，在索引中都会打乱顺序，按照 es 的索引顺序去进行索引，可能会导致搜索的结果未按照预期的顺序给出。因为 es 是以 key/value 方式存储索引信息，所以复杂格式类型的字段都会被拍平后按顺序索引起来，比如数字数组 [3,2,1] 在索引中会被索引为 [1,2,3]，[{name: ‘a’, age: 12}, {name: ‘b’, age:14}] 这样的信息也会被拍平后索引为 name: [‘a’, ‘b’], age: [12, 14], 从而失去原有数据的顺序或对应关系，在搜索中如果使用类似‘doc.field[1] == 3’或‘doc.field[’name’] == ‘a’ &amp;amp;&amp;amp; doc.field[‘age’] == 14’时，搜索结果很有可能是错误的。这类字段只能用来做非顺序或对应关系的搜索&lt;/li&gt;
&lt;li&gt;在脚本中避免使用&lt;code&gt;source&lt;/code&gt;去搜索原文档，上面提到的问题，其实用&lt;code&gt;souce['name'] == 'a'&lt;/code&gt; 是可以解决的，但读取原文档的性能也是非常低的，所以不建议使用 &lt;code&gt;source&lt;/code&gt; 去做原文档的查询&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="最后，一些琐碎的建议："&gt;最后，一些琐碎的建议：&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt; 深度使用 es 一定要理解 es 的索引方式，避免误解使用而产生奇怪的问题。es 的字段支持很多基础类型，比如 int/string/boolean 等，如果想要将字段定义为数组类型，只要定义该字段数组中元素的类型即可，因为在存储时字段存储一个和多个值对于 es 来说都是一样的处理方式。某字段如果未指定格式并且存储多个值，es 会以第一个值的格式作为该字段所有存储数据的格式，所以 es 的数组字段不支持存储不同类型的值，如果存储了不同类型的值，es 会尝试去转换值存储，一旦转换失败则会抛出异常&lt;/li&gt;
&lt;li&gt; Rails 集成 es 也有很多好用的 gem，比如 &lt;a href="https://github.com/ankane/searchkick" rel="nofollow" target="_blank" title=""&gt;Searchkick&lt;/a&gt;可以像写 sql 一样做 es 搜索，或者干脆使用 jbuilder 等工具手撕搜索 json，不管哪种工具，了解 es 才是重中之重&lt;/li&gt;
&lt;li&gt;es 更新换代很快，选好版本，看好文档别出错。前一年才升级到的 2.3，转眼间已经到了 5.5。使用 5.5 开发完成后，发现 6.0 也出来了，在查文档时，发现 5.6 文档中 script 的 inline 关键字变成了 source，调试时没注意到小版本改动踩了个小坑，不禁感慨世界变化太快。。。看文档一定得注意完整版本号&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;到此！&lt;/p&gt;</description>
      <author>dddd1919</author>
      <pubDate>Mon, 13 Nov 2017 00:40:56 +0800</pubDate>
      <link>https://ruby-china.org/topics/34563</link>
      <guid>https://ruby-china.org/topics/34563</guid>
    </item>
    <item>
      <title>记一次由 ActiveRecord 到接口数据的替换历程</title>
      <description>&lt;p&gt;Rails 的项目的发展历程一般是先把各种功能都怼到一个项目代码下，随着业务逐渐增长，项目会变得越来越庞大，融合了各种功能于一身，这时如果项目的某一个小功能出问题，很有可能影响整个业务，于是开始逐渐拆分解耦系统。&lt;/p&gt;

&lt;p&gt;我们的业务过去也进行了各种拆分的尝试，开始是把非常独立的功能模块直接拆分出一套独立的代码项目，但主项目里仍然功能交错。于是乎，又开始考虑把主项目某些很通用且独立的数据拆分成微服务，其他项目使用 RPC 或 HTTP API 来读写数据，这样可以逐渐的去中心化，各个项目之间减少依赖。&lt;/p&gt;

&lt;p&gt;起初简单的考虑使用一套服务独立存储这些数据，并提供 Restful 的 HTTP API 或 RPC 协议的接口，其他项目都通过该接口完成对数据的存取&lt;/p&gt;
&lt;h2 id="轮子调研"&gt;轮子调研&lt;/h2&gt;
&lt;p&gt;因为起初没有确定是使用 HTTP API 还是 RPC 来进行数据交互，所以找了一些类似 AR 的中间层，发现还是有些质量不错的&lt;/p&gt;

&lt;p&gt;比如 &lt;a href="https://github.com/liveh2o/active_remote" rel="nofollow" target="_blank" title=""&gt;这个基于 RPC 的类 AR 数据层&lt;/a&gt;，使用类似 Mongoid 的字段声明方式，约定好对应的 service 来实现具体的 CURD 操作，比较简单。&lt;/p&gt;

&lt;p&gt;另外还发现一个&lt;a href="https://github.com/remiprev/her" rel="nofollow" target="_blank" title=""&gt;人气很高的基于 Restful API 的 ORM&lt;/a&gt;，基本实现了 AR 提供的大部分 model 方法，包括数据缓存，脏数据检查，关联关系，回调，数据校验，调用服务的接口认证等等，可以比较小的代价把 model 层从本地数据库替换成接口调用，只需要对应的接口服务提供一套 Restful 的数据接口即可。&lt;/p&gt;

&lt;p&gt;两者相对 AR 的功能也都够用，但是还是和 AR 有着同样的问题，抽象成和连接层耦合的太紧密，如果想把 API 调用改为 RPC，或是混合使用，替换起来还是要大费周折。期望是能够把抽象成和连接层分隔开，对接方式如果更新只需要更改连接层即可，另外因为业务需要，也没法完全替换到，只是在项目中小范围尝试，还是期望以较小的改动完成一次替换。所以借鉴这些比较不错的代码，开始自己写轮子（虽然最后因为种种没有上线，过程还是非常有意思）&lt;/p&gt;
&lt;h2 id="替换model"&gt;替换 model&lt;/h2&gt;
&lt;p&gt;替换 model 首先预期对接的是一套 Restful API 服务，提供的数据和从数据库读到的结构一致，这样以来，只需要实现一个数据的抽象层，然后对应一个连接层用来对服务数据做读取。&lt;/p&gt;

&lt;p&gt;首先实现一个类似 &lt;code&gt;Activerecord::Base&lt;/code&gt; 的基类，定义好抽象层的功能以及和连接层的对接"，思路也是借鉴了 &lt;a href="https://github.com/liveh2o/active_remote" rel="nofollow" target="_blank" title=""&gt;active_remote&lt;/a&gt; 的实现方式&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ActiveRemoteRecord&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Virtus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model&lt;/span&gt;  &lt;span class="c1"&gt;# 模型字段的getter/setter&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;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;# 查找数据&lt;/span&gt;
    &lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="n"&gt;proxy_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&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="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;created&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="c1"&gt;# 创建数据&lt;/span&gt;
    &lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="n"&gt;proxy_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&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;update_attributes&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="c1"&gt;# 更新数据&lt;/span&gt;
    &lt;span class="n"&gt;proxy_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update_attributes&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;id&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="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;destroy&lt;/span&gt;
  &lt;span class="c1"&gt;# 删除数据&lt;/span&gt;
    &lt;span class="n"&gt;proxy_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&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="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&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="c1"&gt;# 按条件过滤数据&lt;/span&gt;
    &lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="n"&gt;proxy_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&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="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;proxy_class&lt;/span&gt;
  &lt;span class="c1"&gt;# 这里直接约定一个连接层service的类，model的CRUD全部调用约定类来和数据服务做交互&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;ProxyService"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;constantize&lt;/span&gt;
  &lt;span class="k"&gt;end&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;search&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="c1"&gt;# 因为model层的where方法在下面还有他用，这里声明一个search 方法和where区别一下&lt;/span&gt;
    &lt;span class="n"&gt;proxy_service&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="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

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

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;proxy_service&lt;/span&gt;
    &lt;span class="n"&gt;proxy_class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance&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;proxy_service&lt;/span&gt;
    &lt;span class="vi"&gt;@proxy_service&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;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;proxy_service&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;定义好这样一个 model 的话，如果对接一个 user 数据，只需要声明类来继承基类即可&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;ActiveRemoteRecord&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;然后在 services 中实现对应的 proxy 类&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/services/user_proxy_service.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserProxyService&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Singleton&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;create&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="c1"&gt;# request_create_user_api&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_attributes&lt;/span&gt;&lt;span class="p"&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;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# request_find_user_api&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样就基本完成了一个 model 到 API model 的替换，如果想要替换 API 改用 RPC 或其他形式的调用协议，修改 proxy 并保持返回值一致即可。&lt;/p&gt;
&lt;h2 id="Assocation"&gt;Assocation&lt;/h2&gt;
&lt;p&gt;完成 model 的替换后，在业务逻辑中还掺杂了很多关系方法调用，也为了保持上层逻辑的兼容，遂开始研究实现关系声明&lt;/p&gt;

&lt;p&gt;简单来讲像 has_many/belongs_to 等声明关系的方法，在 AR 里主要就是生成特定的查询语句去获取到对应的数据，换成 API 来讲也就是使用 /users/:id/orders 这样的方法获取到 users 的所有 orders，所以思路也比较简单，就来实现一个简单的 has_many：&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;ActiveRemoteRecord&lt;/span&gt;

  &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;has_many&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;has_many_klass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
      &lt;span class="c1"&gt;# 动态定义关系方法&lt;/span&gt;
      &lt;span class="n"&gt;define_method&lt;/span&gt; &lt;span class="n"&gt;has_many_klass&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="c1"&gt;# 获取实例缓存的关系变量&lt;/span&gt;
        &lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;instance_variable_get&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;has_many_klass&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blank?&lt;/span&gt;
          &lt;span class="c1"&gt;# 获取关系对象类&lt;/span&gt;
          &lt;span class="n"&gt;class_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:class_name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;has_many_klass&lt;/span&gt;
          &lt;span class="n"&gt;klass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;class_name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;classify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;constantize&lt;/span&gt;
          &lt;span class="n"&gt;search_params&lt;/span&gt; &lt;span class="o"&gt;=&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;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:as&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="c1"&gt;# 实现 polymorphic&lt;/span&gt;
            &lt;span class="n"&gt;search_params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:as&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="ss"&gt;_id"&lt;/span&gt;&lt;span class="p"&gt;]&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;id&lt;/span&gt;
            &lt;span class="n"&gt;search_params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:as&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="ss"&gt;_type"&lt;/span&gt;&lt;span class="p"&gt;]&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;class&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;else&lt;/span&gt;
            &lt;span class="n"&gt;foreign_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:"&lt;/span&gt;&lt;span class="si"&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;class&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="nf"&gt;demodulize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;underscore&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="ss"&gt;_id"&lt;/span&gt;
            &lt;span class="n"&gt;search_params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;foreign_key&lt;/span&gt;&lt;span class="p"&gt;]&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;id&lt;/span&gt;
          &lt;span class="k"&gt;end&lt;/span&gt;

           &lt;span class="c1"&gt;# 调用关系类的where方法&lt;/span&gt;
          &lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;search_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="nb"&gt;instance_variable_set&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;has_many_klass&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;values&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;values&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;这样当 User 关联多个 Order 时，可以直接在 User 表中声明 &lt;code&gt;has_many :orders&lt;/code&gt;，在 Order 声明多态的关联关系时只需加上 &lt;code&gt;as&lt;/code&gt; 参数即可。
同理，实现 &lt;code&gt;has_one&lt;/code&gt;, &lt;code&gt;belongs_to&lt;/code&gt; 也都是类似的思路&lt;/p&gt;
&lt;h2 id="链式调用"&gt;链式调用&lt;/h2&gt;
&lt;p&gt;在 AR 中可以这样无数次的调用查询方法 &lt;code&gt;User.where(condition_1).where(condition_2).where(...)&lt;/code&gt;，直到最后要使用 &lt;code&gt;.first&lt;/code&gt; &lt;code&gt;.all&lt;/code&gt; 这样的方法时才会真正的去执行 SQL 获取数据，这也是 AR 中懒加载的套路，在一次调用后会返回一个 &lt;code&gt;ActiveRecord::Relation&lt;/code&gt; 的实例，保存着查询的条件，再次调用实际上是再次调用这个实例的方法而不是 model 类的 where 方法，直到最后需要真正读取数据时去执行搜索。按照这个思路也来实现一版简单的链式调用：&lt;/p&gt;

&lt;p&gt;首先来实现一个类似 &lt;code&gt;ActiveRecord::Relation&lt;/code&gt; 的类&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RemoteRecordCondition&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@klass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;klass&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;criteria&lt;/span&gt;
    &lt;span class="c1"&gt;# 初始化的搜索条件&lt;/span&gt;
    &lt;span class="vi"&gt;@criteria&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:conditions&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{}}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# 再次调用时合并搜索条件并返回当前对象&lt;/span&gt;
    &lt;span class="c1"&gt;# 这里简单实现把多条筛选语句直接合并&lt;/span&gt;
    &lt;span class="n"&gt;criteria&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:conditions&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;merge!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;self&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;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# 调用该方法时才真正的搜索数据&lt;/span&gt;
    &lt;span class="vi"&gt;@klass.search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;criteria&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:conditions&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;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;first&lt;/span&gt;
    &lt;span class="c1"&gt;# 同上&lt;/span&gt;
    &lt;span class="vi"&gt;@klass.search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;criteria&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:conditions&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;method_missing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# 如果调用了数据类的类方法，中断当前的查询并直接返回类方法的结果&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@klass.respond_to&lt;/span&gt;&lt;span class="p"&gt;?(&lt;/span&gt;&lt;span class="n"&gt;method_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@klass.send&lt;/span&gt; &lt;span class="n"&gt;method_name&lt;/span&gt;
      &lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ret&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="n"&gt;criteria&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;conditions&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;ret&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance_of?&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;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ret&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="k"&gt;super&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后把数据类的 where 方法从直接调用查询改为返回 RemoteRecordCondition 的实例：&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;ActiveRemoteRecord&lt;/span&gt;
&lt;span class="o"&gt;...&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;where&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="no"&gt;RemoteRecordCondition&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="nf"&gt;where&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="k"&gt;end&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;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;从头到尾复刻了 AR 的一些基础功能，如 &lt;code&gt;attributes getter/setter&lt;/code&gt;, &lt;code&gt;assocation&lt;/code&gt;, 并且可以和 AR 的 model 混用，定义数据间的简单关联关系。另外把连接层剥离开，方便替换。不过改动也比较底层最终还是没用到线上，不过过程比较有意思，也发现了很多好用的轮子&lt;/p&gt;</description>
      <author>dddd1919</author>
      <pubDate>Tue, 03 Oct 2017 23:52:38 +0800</pubDate>
      <link>https://ruby-china.org/topics/34319</link>
      <guid>https://ruby-china.org/topics/34319</guid>
    </item>
    <item>
      <title>LeetCode 可以上 Ruby 了哦，筒子们还不上</title>
      <description>&lt;p&gt;前几个月有&lt;a href="https://ruby-china.org/topics/23351" title=""&gt;童鞋提到 leetcode&lt;/a&gt;正准备支持 ruby，早晨来了无意间上去一瞧，小惊喜呢。&lt;/p&gt;

&lt;p&gt;先来个图：&lt;/p&gt;

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

&lt;p&gt;不知道是不太擅长算法，还是这运行效率不够杠杠的（应该是前者 &lt;img title=":smile:" alt="😄" src="https://twemoji.ruby-china.com/2/svg/1f604.svg" class="twemoji"&gt; ），结果是这样：&lt;/p&gt;

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

&lt;p&gt;。。。。。。。。&lt;/p&gt;

&lt;p&gt;还在犹豫什么？坐下来撸！&lt;/p&gt;</description>
      <author>dddd1919</author>
      <pubDate>Thu, 26 Mar 2015 09:43:09 +0800</pubDate>
      <link>https://ruby-china.org/topics/24842</link>
      <guid>https://ruby-china.org/topics/24842</guid>
    </item>
    <item>
      <title>minimagick 使用 strip 后图片变大</title>
      <description>&lt;p&gt;用 minimagick 来压图，主要靠&lt;code&gt;strip&lt;/code&gt;和&lt;code&gt;quality&lt;/code&gt;来减小图片占用空间，不过最近发现个奇怪的问题：
在 centOS 机器上装了 imagemagick 后，压缩完的图有时会比原图大，于是用问题图片，经过各种注释代码，发现是经过&lt;code&gt;strip&lt;/code&gt;后图片会变的略大一点，然后使用系统命令直接操作图片
&lt;code&gt;convert -strip image.jpg new_image.jpg&lt;/code&gt;
发现 new_image.jpg 比 image.jpg 要大一点。(奇怪，在 mac 上同样的文件同样操作却都是正常的)&lt;/p&gt;

&lt;p&gt;查了很多 imagemagick 相关的资料，只说明 &lt;code&gt;strip&lt;/code&gt; 是用来去除图片 EXIF 信息，这么说的话去除之后怎么着也不应该变大了啊，在&lt;a href="http://stackoverflow.com/questions/26030260/minimagicks-strip-function-makes-picture-filesize-bigger" rel="nofollow" target="_blank" title=""&gt;SO&lt;/a&gt; 上提问了暂时还没人回，也没搜索到类似的问题，不知道有人知道这到底是怎么回事么？如果能解决的话顺便一块都答了把 &lt;img title=":cry:" alt="😢" src="https://twemoji.ruby-china.com/2/svg/1f622.svg" class="twemoji"&gt; 实在不行只能把 strip 操作去掉了。&lt;/p&gt;</description>
      <author>dddd1919</author>
      <pubDate>Thu, 25 Sep 2014 15:38:50 +0800</pubDate>
      <link>https://ruby-china.org/topics/21724</link>
      <guid>https://ruby-china.org/topics/21724</guid>
    </item>
    <item>
      <title>有没有处理即时并发任务的 gem 或方法推荐？</title>
      <description>&lt;p&gt;写个 task 把一堆文件处理并且信息保存到 mongo 数据库，本地文件量有近千万，任务是顺序执行，目前的速度瓶颈主要在文件处理上，每秒不到 2 个的处理速度，算下来执行完任务。。。。。。。哭了（其实任务操作很简单，就是文件量太大了），所以想找个&lt;code&gt;多线程/多进程/并发任务&lt;/code&gt;的工具来批量执行文件处理任务，具体需求：&lt;/p&gt;

&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt; 暂时只考虑 ruby (非 rails，觉得 ruby 目前还成不了 task 的瓶颈)&lt;/li&gt;
&lt;li&gt; 能并发的执行任务 (可以对每个文件拆分出数据和操作独立的的子任务)&lt;/li&gt;
&lt;li&gt; 可以控制并发数量 (控制任务的资源占用量)&lt;/li&gt;
&lt;li&gt; 尽量简单&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;由于对大量数据处理没什么经验（这里仅仅是数据多），大部分工作还是在逻辑处理上，特求经验分享！&lt;/p&gt;

&lt;p&gt;另外贴上自己的尝试：&lt;/p&gt;

&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Thread: 不好拿捏，曾经用 thread 做过并发的消息处理，某个 thread 自身出现问题处理起来不方便，另外没法对 thread 并发做限制（可能个人经验不足把，thread 跑起来和 nodejs 一样像个疯子）&lt;/li&gt;
&lt;li&gt;目前正在尝试 resque 和 sidekiq（刚上手），把文件处理放到队列中，减少顺序任务的执行时间，不过不太了解队列任务是否并发执行，如果是而且可以设置并发量那太好了，基本解决现有问题&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;</description>
      <author>dddd1919</author>
      <pubDate>Mon, 18 Aug 2014 14:34:09 +0800</pubDate>
      <link>https://ruby-china.org/topics/21085</link>
      <guid>https://ruby-china.org/topics/21085</guid>
    </item>
    <item>
      <title>使用 mina deploy 更新 crontab 遇到的一个问题</title>
      <description>&lt;p&gt;用&lt;code&gt;whenever&lt;/code&gt;写定时任务，每次部署完后会执行 &lt;code&gt;whenever --update-crontab ....&lt;/code&gt;
但是由于 mina 部署的目录结构，&lt;code&gt;current&lt;/code&gt; 是直接软链接到 &lt;code&gt;releases/xx/&lt;/code&gt; 目录下的，&lt;code&gt;whenever&lt;/code&gt;的任务更新又是按照工程的绝对路径来识别的，结果实际更新的是&lt;code&gt;releases/xx/&lt;/code&gt;下的任务，而不是&lt;code&gt;current&lt;/code&gt;下的，&lt;code&gt;crontab&lt;/code&gt;并没更新老任务，而是新建了一套任务，老的任务还在。
想问下使用&lt;code&gt;mina&lt;/code&gt;部署的同学们有没有什么解决方法？我想过的方法就是能不能把&lt;code&gt;releases/xx&lt;/code&gt;硬拷贝到&lt;code&gt;current&lt;/code&gt;目录下？&lt;/p&gt;</description>
      <author>dddd1919</author>
      <pubDate>Thu, 03 Jul 2014 12:04:55 +0800</pubDate>
      <link>https://ruby-china.org/topics/20292</link>
      <guid>https://ruby-china.org/topics/20292</guid>
    </item>
    <item>
      <title> 使用 mina 部署,输入 ssh 密码时非常难操作</title>
      <description>&lt;p&gt;在用 mina 部署的时候，遇到一个很奇怪的问题，各种搜索无解，有没有用过的人遇到过这个问题？&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; ~/workspace/todo-rails4-angularjs$mina setup --trace
** Invoke setup (first_time)
** Invoke environment (first_time)
** Execute environment
** Execute setup
test@192.168.1.106's password: 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;到这一步后，输入密码敲回车没有反应，再敲一下回车又提示密码错误，而且在输入密码时，光标闪动也会卡，有时候到这一步先敲一下回车再输入密码再敲回车就能过去，不知道我这个是怎么回事？有人遇到过同样的问题么？
我在两台机器上试过了，同样的问题，两台机器都是&lt;code&gt;ubuntu 12.04&lt;/code&gt; ,&lt;code&gt;mina (0.3.0)&lt;/code&gt;,mina 部署起来确实方便很多，唯独这个输入密码很难捱，臣妾实在做不到.......&lt;/p&gt;

&lt;p&gt;Ps: mina 的配置文件是直接用&lt;code&gt;mina init&lt;/code&gt;生成的，改了 domain/deploy_to 等四五个参数，domain 的地址就是部署机器的 ip(直接往自己的机器上部署测试)&lt;/p&gt;</description>
      <author>dddd1919</author>
      <pubDate>Mon, 16 Jun 2014 20:54:10 +0800</pubDate>
      <link>https://ruby-china.org/topics/19976</link>
      <guid>https://ruby-china.org/topics/19976</guid>
    </item>
    <item>
      <title>thread 超时如何强制退出？</title>
      <description>&lt;p&gt;使用 ruby 多线程处理并行任务时，某些线程因为任务执行出现异常（我想可能是挂起了），使得进程最后成了垃圾进程。查 ruby 手册发现&lt;code&gt;Thread#join&lt;/code&gt;方法还有个参数，如果加个参数会设置为线程对象的超时时间，过时后自动退出。
写了两个测试：
（1）&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;test_thread&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Thread&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;sleep&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Thread completed"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;test_thread&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="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt;  &lt;span class="s2"&gt;"------------END----------------"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 thread 的 join 加上参数后三秒线程退出了&lt;/p&gt;

&lt;p&gt;（2）根据我的任务场景写了个测试&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;threads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&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;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;threads&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"This is the &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; thread!"&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"All threads create completed!"&lt;/span&gt;
&lt;span class="n"&gt;threads&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;t&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&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="p"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"------------END----------------"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;结果很奇怪：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;All threads create completed!
This is the 0 thread!
This is the 1 thread!
This is the 2 thread!
This is the 3 thread!
This is the 4 thread!
This is the 5 thread!
This is the 6 thread!
This is the 7 thread!
This is the 8 thread!
This is the 9 thread!
------------END----------------
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;按照文档应该输出到 &lt;code&gt;This is the 2 thread!&lt;/code&gt; 后其他进程就该退出了，但是十个都执行完了，求解！
ps：用多线程执行的单个任务一般不会超过 10s，但是有可能出现无响应的状态，本来想让线程执行超时设置到 30s，这样能保证主进程完毕后退出，但有可能是线程执行异常的问题，过很长时间后发现任务机器上残留几个 ruby 进程，每次都要手动清理。可能是多线程理解不到位，求大神帮忙啊！&lt;/p&gt;</description>
      <author>dddd1919</author>
      <pubDate>Sat, 15 Mar 2014 15:28:20 +0800</pubDate>
      <link>https://ruby-china.org/topics/17904</link>
      <guid>https://ruby-china.org/topics/17904</guid>
    </item>
    <item>
      <title>desc 排序错误问题</title>
      <description>&lt;p&gt;在 mongoid 里用 &lt;code&gt;Data.desc(:created_at)&lt;/code&gt; 可以正确降序排序，对&lt;code&gt;Integer&lt;/code&gt;类型的 field 使用 &lt;code&gt;desc&lt;/code&gt; 也有效，但是对&lt;code&gt;BigDecimal&lt;/code&gt; 的 field，排序效果就如同 &lt;code&gt;["1111", "2", "33333333", "4"].sort&lt;/code&gt; 的结果。有没有人知道怎么处理？&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PS:&lt;/strong&gt; 在 stackoverflow 上也找到了 &lt;a href="http://stackoverflow.com/questions/4703789/how-to-make-mongoid-order-properly" rel="nofollow" target="_blank" title=""&gt;同样的问题&lt;/a&gt;，没找到直接的解决方法，算不算是 mongoid 的一个 bug？&lt;/p&gt;</description>
      <author>dddd1919</author>
      <pubDate>Thu, 07 Nov 2013 12:09:25 +0800</pubDate>
      <link>https://ruby-china.org/topics/15368</link>
      <guid>https://ruby-china.org/topics/15368</guid>
    </item>
    <item>
      <title>capybara 操作带有 optgroup 的 select</title>
      <description>&lt;p&gt;一个下拉表单可以直接用 capybara 提供的 &lt;code&gt;select label_text, :from =&amp;gt; select_id&lt;/code&gt;
要操作的表单现在多了一级 optgroup，结构类似&lt;/p&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;select&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"category"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"obj_category"&lt;/span&gt; &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;optgroup&lt;/span&gt; &lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s"&gt;"music"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"rock"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Rock&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
              &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"popular"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Popular&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/optgroup&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;optgroup&lt;/span&gt; &lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s"&gt;"video"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"short"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Short&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
              &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"long"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Long&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/optgroup&amp;gt;&lt;/span&gt;
          ....
&lt;span class="nt"&gt;&amp;lt;/select&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我要选择下拉节点下的选项，用 &lt;code&gt;select "Rock", :from =&amp;gt; "obj_category"&lt;/code&gt;，结果返回了 &lt;code&gt;Unable to find option "Rock"&lt;/code&gt;，如果把 optgroup 去掉了就没问题了，股沟了一下有用 selenium 解决的，但是只用 capybara，各位高手怎么破？&lt;/p&gt;</description>
      <author>dddd1919</author>
      <pubDate>Fri, 01 Nov 2013 18:08:49 +0800</pubDate>
      <link>https://ruby-china.org/topics/15228</link>
      <guid>https://ruby-china.org/topics/15228</guid>
    </item>
    <item>
      <title>求助一个优雅的翻译</title>
      <description>&lt;p&gt;&lt;code&gt;Please review the problems below&lt;/code&gt;
直译：&lt;code&gt;请看如下错误&lt;/code&gt;
我觉得用户看了提示会高概率弃用，自己的水平到这了。特求有爱翻译一枚！&lt;/p&gt;</description>
      <author>dddd1919</author>
      <pubDate>Tue, 10 Sep 2013 18:28:52 +0800</pubDate>
      <link>https://ruby-china.org/topics/14011</link>
      <guid>https://ruby-china.org/topics/14011</guid>
    </item>
    <item>
      <title>菜鸟问题求助，这个 curl 命令啥意思？</title>
      <description>&lt;p&gt;&lt;code&gt;curl http://localhost:3000/faye -d 'message={"channel":"/messages/new", "data":"hello"}'&lt;/code&gt; ，在用 faye，看到这么个命令，本想用 &lt;code&gt;net/http&lt;/code&gt; 写，百思不得其解这 curl 啥意思啊 &lt;img title=":astonished:" alt="😲" src="https://twemoji.ruby-china.com/2/svg/1f632.svg" class="twemoji"&gt; 
主要是中间那个 &lt;code&gt;-d&lt;/code&gt; ，查了好久没查到什么用法&lt;/p&gt;</description>
      <author>dddd1919</author>
      <pubDate>Mon, 02 Sep 2013 17:57:39 +0800</pubDate>
      <link>https://ruby-china.org/topics/13830</link>
      <guid>https://ruby-china.org/topics/13830</guid>
    </item>
    <item>
      <title>对于前端监测后端事件，有什么好的设计方法？轮询，or</title>
      <description>&lt;p&gt;最近在搞树莓派的 gpio 控制，想做一个实时监控针脚状态的 web 控制程序。现在要做的就是一个前台的显示界面，当针脚变为输入状态后在前端实时显示针脚的电压状态。
目前想到的是做个 ajax 轮询，但是有个问题就是 gpio 有 26 个针脚，只有当针脚为输入状态时才需要监控。&lt;/p&gt;

&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;如果每个针脚做一个轮询，26 个是不是太多？&lt;/li&gt;
&lt;li&gt;如果用一个轮询来监测所有针脚的状态，当在监测的过程中有一个针脚切换为输入，ajax 要重新发送一个轮询请求，但后台的轮询请求正在等待针脚变化，该如何处理？可以直接用请求中断请求？或者。。。。。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;刚把输出控制的搞定了，发现如果把输入输出混合控制做一块还真是有点乱啊，求高手指点一二！！！！！3ks（纯码字无代码）&lt;/p&gt;</description>
      <author>dddd1919</author>
      <pubDate>Mon, 26 Aug 2013 18:31:54 +0800</pubDate>
      <link>https://ruby-china.org/topics/13638</link>
      <guid>https://ruby-china.org/topics/13638</guid>
    </item>
    <item>
      <title>jquery-ajax 里的 $(this) 怎么了？</title>
      <description>&lt;p&gt;用 jquery 写了一个控件的 ajax 请求，发现 post 方法里的$(this) 出问题了，原文&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;.btn-onoff&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
        &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test&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="na"&gt;onoff&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;html&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;id&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;parent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&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="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;status&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;html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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;结果没返回任何内容，用 alert 看了下结果是 undefined，在 stackoverflow 上查了一下，然后改用建议的方式：&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;.btn-onoff&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;$this&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;//TODO why this&lt;/span&gt;
        &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test&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="na"&gt;onoff&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;html&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&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="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
          &lt;span class="nx"&gt;$this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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;结果正确了，想问下为什么 jq 里的 post 中直接写$(this) 无效呢？&lt;/p&gt;</description>
      <author>dddd1919</author>
      <pubDate>Mon, 19 Aug 2013 11:47:22 +0800</pubDate>
      <link>https://ruby-china.org/topics/13427</link>
      <guid>https://ruby-china.org/topics/13427</guid>
    </item>
    <item>
      <title>guard 启动很慢，为啥呢？</title>
      <description>&lt;p&gt;最近不知为何，启动 guard 后加载完 spork，总是停在这里：
Guard is now watching at /................/ 
大概要一两分钟才能出来 guard 的命令行，之前到这里秒入啊。
环境是 ubuntu12.04，在这之前貌似只是改过 guard 的配置：把 spork 和 rspec 的监视配置调了下位置，为了环境有变化先重启 soprk 后跑测试，另外在 spork 里加上了对 app 目录的监视，不知道是这些修改引起的？求高人指点啊。。。。。。。。。。。&lt;/p&gt;</description>
      <author>dddd1919</author>
      <pubDate>Wed, 26 Jun 2013 18:16:29 +0800</pubDate>
      <link>https://ruby-china.org/topics/12010</link>
      <guid>https://ruby-china.org/topics/12010</guid>
    </item>
  </channel>
</rss>
