<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>zgm (zhouguangming)</title>
    <link>https://ruby-china.org/zgm</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>[上海] Change 招收 Ruby 工程师以及 iOS/Android 工程师 (15K~25K)</title>
      <description>&lt;p&gt;&lt;strong&gt;&lt;em&gt;注：不是我自己的招聘 朋友不是技术 刚注册了没有发帖权限&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="关于 Change"&gt;关于 Change&lt;/h3&gt;
&lt;p&gt;牵记网络是致力于传播健身文化。旗下在开发中的 Change 移动端是一款欧美健身生活方式类短视频社区应用。跟随 Change, 发现更好的你。We commit to changing lives and becoming our best selves.&lt;/p&gt;

&lt;p&gt;公司官网： &lt;a href="http://change.so" rel="nofollow" target="_blank" title=""&gt;http://change.so&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="融资情况"&gt;融资情况&lt;/h3&gt;
&lt;p&gt;2017.1：天使轮 数百万&lt;/p&gt;
&lt;h3 id="福利待遇"&gt;福利待遇&lt;/h3&gt;
&lt;p&gt;股票期权，带薪年假，五险一金，年度奖金&lt;/p&gt;
&lt;h3 id="简历投递"&gt;简历投递&lt;/h3&gt;
&lt;p&gt;huang.huang@change.so&lt;/p&gt;
&lt;h3 id="工作地点"&gt;工作地点&lt;/h3&gt;
&lt;p&gt;上海黄浦外滩 SOHO&lt;/p&gt;
&lt;h3 id="Ruby 工程师"&gt;Ruby 工程师&lt;/h3&gt;
&lt;p&gt;职责：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;负责后端 API/网站的研发工作&lt;/li&gt;
&lt;li&gt;设计软件测试用例以提升软件质量&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;必需具备的技能：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;3 年以上 Ruby on rails/Padrino/Sinatra 开发经验&lt;/li&gt;
&lt;li&gt;对 Linux, Git 有基本的了解&lt;/li&gt;
&lt;li&gt;对 BDD, TDD 有基本的了解&lt;/li&gt;
&lt;li&gt;对 HTML, CSS, Javascript 有基本的了解&lt;/li&gt;
&lt;li&gt;善于学习，良好的沟通技巧&lt;/li&gt;
&lt;li&gt;独立工作能力&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;加分：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;有 React Native 经验 &lt;/li&gt;
&lt;li&gt;有 IOS/Android 开发经验&lt;/li&gt;
&lt;li&gt;Web API (Twitter, Facebok, Twilo, Flickr) 开发经验&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;薪资范围:15-25k&lt;/p&gt;
&lt;h3 id="Android 工程师"&gt;Android 工程师&lt;/h3&gt;
&lt;p&gt;职责：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;与后端工程师配合，完成公司 Android 平台产品的开发、维护&lt;/li&gt;
&lt;li&gt;参与产品整体设计&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;必需具备的技能：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;3-5 年 Android 平台开发经验&lt;/li&gt;
&lt;li&gt;精通 Android Studio/Eclipse 以及相关的开发测试环境&lt;/li&gt;
&lt;li&gt;熟悉以 TDD 方式进行项目开发&lt;/li&gt;
&lt;li&gt;善于学习、良好的沟通技巧&lt;/li&gt;
&lt;li&gt;独立工作的能力&lt;/li&gt;
&lt;li&gt;有应用商店上架应用&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;加分：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;有 Ruby/ROR 开发经验&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;薪资范围：15-25k&lt;/p&gt;
&lt;h3 id="iOS 工程师"&gt;iOS 工程师&lt;/h3&gt;
&lt;p&gt;职责：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;与后端工程师配合，完成公司 iOS 端产品的开发、维护&lt;/li&gt;
&lt;li&gt;参与产品整体设计&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;必需具备的技能：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;3 年以上 iOS 开发经验&lt;/li&gt;
&lt;li&gt;精通 ObjectiveC/Swift，XCode，iOS/OSX SDK 以及相关的开发测试环境&lt;/li&gt;
&lt;li&gt;熟悉相关的 web 开发技术&lt;/li&gt;
&lt;li&gt;善于学习、良好的沟通技巧&lt;/li&gt;
&lt;li&gt;独立工作的能力&lt;/li&gt;
&lt;li&gt;有应用商店上架应用&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;加分：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;懂设计，会切图&lt;/li&gt;
&lt;li&gt;有 Android 开发经验&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;薪资范围：15-25k&lt;/p&gt;</description>
      <author>zgm</author>
      <pubDate>Mon, 16 Jan 2017 12:33:29 +0800</pubDate>
      <link>https://ruby-china.org/topics/32141</link>
      <guid>https://ruby-china.org/topics/32141</guid>
    </item>
    <item>
      <title>[上海] [2015 年 11 月 24 日] Ruby 聚会召集</title>
      <description>&lt;p&gt;时间：2015 年 11 月 24 日 (周二) 19:00 - 21:00&lt;/p&gt;

&lt;p&gt;地点：COSTA coffee 国金中心店，浦东新区世纪大道 8 号上海国金中心 D 座 B1 楼 LG1-37 室&lt;/p&gt;

&lt;p&gt;百度地图：&lt;a href="http://j.map.baidu.com/6iIsp" rel="nofollow" target="_blank"&gt;http://j.map.baidu.com/6iIsp&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="主题:  他山之石可以攻玉"&gt;主题：他山之石可以攻玉&lt;/h2&gt;
&lt;p&gt;每个人抛出自己公司在开发过程中遇到的问题（架构、测试、性能等等），大家一起讨论真实案例，共同给出最佳的方案。&lt;/p&gt;</description>
      <author>zgm</author>
      <pubDate>Wed, 18 Nov 2015 23:16:48 +0800</pubDate>
      <link>https://ruby-china.org/topics/28107</link>
      <guid>https://ruby-china.org/topics/28107</guid>
    </item>
    <item>
      <title>Common Network Exception in Ruby (1)</title>
      <description>&lt;h3 id="Errno::EHOSTUNREACH"&gt;Errno::EHOSTUNREACH&lt;/h3&gt;
&lt;p&gt;当试图连接一个无法通过 ARP 协议获取 MAC 地址的 IP 地址时抛出该异常。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'socket'&lt;/span&gt;

&lt;span class="k"&gt;begin&lt;/span&gt;
  &lt;span class="c1"&gt;# 假设 192.169.1.100 这个 IP 与请求方在同一子网, 但是该子网内不存在该主机.&lt;/span&gt;
  &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;TCPSocket&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="s1"&gt;'192.168.1.100'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;

&lt;span class="no"&gt;Errno&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;EHOSTUNREACH&lt;/span&gt;
&lt;span class="no"&gt;No&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="s2"&gt;"192.168.1.100"&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="Errno::ETIMEDOUT"&gt;Errno::ETIMEDOUT&lt;/h3&gt;
&lt;p&gt;当建立连接时，请求方由于未收到接收方的 ACK 报文，经过 N 次超时重传之后抛出该异常。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'socket'&lt;/span&gt;

&lt;span class="k"&gt;begin&lt;/span&gt;
  &lt;span class="c1"&gt;# 已通过 iptables DROP 掉所有到达 3000 端口的 TCP 数据包.&lt;/span&gt;
  &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;TCPSocket&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="s1"&gt;'localhost'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;

&lt;span class="no"&gt;Errno&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ETIMEDOUT&lt;/span&gt;
&lt;span class="no"&gt;Connection&lt;/span&gt; &lt;span class="n"&gt;refused&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="s2"&gt;"localhost"&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="Errno::ECONNREFUSED"&gt;Errno::ECONNREFUSED&lt;/h3&gt;
&lt;p&gt;当建立连接时，接收方对应的端口未开启会传回一个 RST 报文抛出该异常。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'socket'&lt;/span&gt;

&lt;span class="k"&gt;begin&lt;/span&gt;
  &lt;span class="c1"&gt;# 接收方并未开启 3000 端口的服务.&lt;/span&gt;
  &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;TCPSocket&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="s1"&gt;'localhost'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;

&lt;span class="no"&gt;Errno&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ECONNREFUSED&lt;/span&gt;
&lt;span class="no"&gt;Connection&lt;/span&gt; &lt;span class="n"&gt;refused&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="s2"&gt;"localhost"&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="Errno::EPIPE"&gt;Errno::EPIPE&lt;/h3&gt;
&lt;p&gt;当接收方正常关闭连接并发送 FIN 报文后，如果发送方执行写操作，则接收方会返回一个 RST 报文，如果发送方继续执行写操作，则抛出该异常。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# server.rb&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'socket'&lt;/span&gt;

&lt;span class="k"&gt;begin&lt;/span&gt;
  &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;TCPServer&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="s1"&gt;'localhost'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accept&lt;/span&gt;
  &lt;span class="c1"&gt;# 让客户端先关闭, 并发送 FIN 报文&lt;/span&gt;
  &lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="c1"&gt;# 此时客户端发送 RST 报文&lt;/span&gt;
  &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt; &lt;span class="s1"&gt;'hello'&lt;/span&gt;
  &lt;span class="c1"&gt;# 抛出 Error::EPIPE 异常&lt;/span&gt;
  &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt; &lt;span class="s1"&gt;'world'&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;

&lt;span class="no"&gt;Errno&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;EPIPE&lt;/span&gt;
&lt;span class="no"&gt;Broken&lt;/span&gt; &lt;span class="n"&gt;pipe&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# client.rb&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'socket'&lt;/span&gt;

&lt;span class="k"&gt;begin&lt;/span&gt;
  &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;TCPSocket&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="s1"&gt;'localhost'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="Errno::ECONNRESET"&gt;Errno::ECONNRESET&lt;/h3&gt;
&lt;p&gt;当接收方异常关闭连接并发送 RST 报文后，如果发送方继续执行读或者写操作，则抛出该异常。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# server.rb&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'socket'&lt;/span&gt;

&lt;span class="k"&gt;begin&lt;/span&gt;
  &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;TCPServer&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="s1"&gt;'localhost'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accept&lt;/span&gt;
  &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt; &lt;span class="s1"&gt;'hello'&lt;/span&gt;
  &lt;span class="c1"&gt;# 等待客户端异常退出, 发送 RST 报文&lt;/span&gt;
  &lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="c1"&gt;# 抛出 Errno::ECONNRESET&lt;/span&gt;
  &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt; &lt;span class="s1"&gt;'world'&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;

&lt;span class="no"&gt;Errno&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ECONNRESET&lt;/span&gt;
&lt;span class="no"&gt;Connection&lt;/span&gt; &lt;span class="n"&gt;reset&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# client.rb&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'socket'&lt;/span&gt;

&lt;span class="k"&gt;begin&lt;/span&gt;
  &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;TCPSocket&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="s1"&gt;'localhost'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;# 等待服务端发送数据过来&lt;/span&gt;
  &lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="c1"&gt;# 由于缓冲区仍有数据, 此时关闭 socket 会导致发送一个 RST 报文到服务端&lt;/span&gt;
  &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;原文 &lt;a href="http://zhouguangming.me/2015/05/26/common-network-exception-in-ruby(1)" rel="nofollow" target="_blank" title=""&gt;http://zhouguangming.me/2015/05/26/common-network-exception-in-ruby(1)&lt;/a&gt;&lt;/p&gt;</description>
      <author>zgm</author>
      <pubDate>Tue, 04 Aug 2015 11:35:38 +0800</pubDate>
      <link>https://ruby-china.org/topics/26761</link>
      <guid>https://ruby-china.org/topics/26761</guid>
    </item>
    <item>
      <title>[上海][2015年7月14日] Ruby 聚会召集</title>
      <description>&lt;p&gt;时间：2015 年 7 月 14 日 (周二) 19:00 - 21:00&lt;/p&gt;

&lt;p&gt;地点：COSTA coffee 国金中心店，浦东新区世纪大道 8 号上海国金中心 D 座 B1 楼 LG1-37 室&lt;/p&gt;

&lt;p&gt;百度地图：&lt;a href="http://j.map.baidu.com/6iIsp" rel="nofollow" target="_blank"&gt;http://j.map.baidu.com/6iIsp&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;主题：&lt;/p&gt;

&lt;p&gt;1) &lt;a href="/ericguo" class="user-mention" title="@ericguo"&gt;&lt;i&gt;@&lt;/i&gt;ericguo&lt;/a&gt; 分享 Ember.JS 相关。&lt;/p&gt;</description>
      <author>zgm</author>
      <pubDate>Tue, 07 Jul 2015 10:02:19 +0800</pubDate>
      <link>https://ruby-china.org/topics/26355</link>
      <guid>https://ruby-china.org/topics/26355</guid>
    </item>
    <item>
      <title>[上海][2015年01月20日] 2015年 第一次月中聚会 技术聚会</title>
      <description>&lt;p&gt;时间：2015 年 01 月 20 日 (周二) 晚上 7 点半到 9 点
地点：浦东新区世纪大道 8 号上海国金中心 D 座 B1 楼 LG1-37 室 COSTA COFFEE(国金中心店)
           &lt;strong&gt;在浦东陆家嘴的 Apple Store 旁边&lt;/strong&gt;，提早到达的同学请负责占座&lt;/p&gt;

&lt;p&gt;暂无技术主题，如果谁有什么好玩的分享可在下面留言。&lt;/p&gt;

&lt;p&gt;&lt;a href="/gene_wu" class="user-mention" title="@gene_wu"&gt;&lt;i&gt;@&lt;/i&gt;gene_wu&lt;/a&gt;
&lt;a href="/iBachue" class="user-mention" title="@iBachue"&gt;&lt;i&gt;@&lt;/i&gt;iBachue&lt;/a&gt;
&lt;a href="/tzwm" class="user-mention" title="@tzwm"&gt;&lt;i&gt;@&lt;/i&gt;tzwm&lt;/a&gt;
&lt;a href="/howellh" class="user-mention" title="@howellh"&gt;&lt;i&gt;@&lt;/i&gt;howellh&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="/lgn21st" class="user-mention" title="@lgn21st"&gt;&lt;i&gt;@&lt;/i&gt;lgn21st&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;------------------------------------------------------------------------------------------------------&amp;gt;&lt;/p&gt;

&lt;p&gt;2015-01-20 更新&lt;/p&gt;

&lt;p&gt;&lt;a href="/mingyuan0715" class="user-mention" title="@mingyuan0715"&gt;&lt;i&gt;@&lt;/i&gt;mingyuan0715&lt;/a&gt; 分享 
&lt;a href="/robbin" class="user-mention" title="@robbin"&gt;&lt;i&gt;@&lt;/i&gt;robbin&lt;/a&gt; 分享 &lt;/p&gt;</description>
      <author>zgm</author>
      <pubDate>Mon, 12 Jan 2015 10:13:49 +0800</pubDate>
      <link>https://ruby-china.org/topics/23677</link>
      <guid>https://ruby-china.org/topics/23677</guid>
    </item>
    <item>
      <title>计时攻击原理以及 Rails 对应的防范</title>
      <description>&lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/Timing_attack" rel="nofollow" target="_blank" title=""&gt;计时攻击&lt;/a&gt; 属于旁路攻击的一种，所谓旁路攻击就是通过对系统的物理学分析和实现方式分析，而不是密码学分析或暴力破解，来尝试破解密码学系统的行为。密码学系统的电力消耗，电磁波泄露，时间差等信息都有可能提供对破解系统有帮助的信息。&lt;/p&gt;

&lt;p&gt;而计时攻击就是利用时间差来对计算机进行攻击，那么它的原理是什么？我们拿 Rails 中的一段&lt;a href="https://github.com/rails/rails/blob/master/activesupport/lib/active_support/message_verifier.rb#L57" rel="nofollow" target="_blank" title=""&gt;代码&lt;/a&gt;进行分析：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# constant-time comparison algorithm to prevent timing attacks&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;secure_compare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bytesize&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bytesize&lt;/span&gt;

  &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unpack&lt;/span&gt; &lt;span class="s2"&gt;"C&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bytesize&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

  &lt;span class="n"&gt;res&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;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each_byte&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;|=&lt;/span&gt; &lt;span class="n"&gt;byte&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shift&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这段代码的一个重要使用场景是验证签名 cookie 的有效性，即外部传入的摘要与内部计算的摘要是否相等，但是为什么不直接使用 &lt;code&gt;a == b&lt;/code&gt;, 而是一个字符一个字符的比较，并且看上去以一种十分低效的方式进行的？&lt;/p&gt;

&lt;p&gt;原因正是为了预防计时攻击。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;a == b&lt;/code&gt; 在 &lt;a href="https://github.com/ruby/ruby/blob/trunk/string.c#L2461" rel="nofollow" target="_blank" title=""&gt;Ruby&lt;/a&gt; 内部使用 &lt;a href="http://man7.org/linux/man-pages/man3/memcmp.3.html" rel="nofollow" target="_blank" title=""&gt;memcmp&lt;/a&gt; 函数进行比较，当首次发现有两个字符不一致时，便直接返回两个字符在 ASCII 码表中的差值。在这样的机制下，CPU 运行的时间与字符串匹配度是有正关系的，字符串匹配度越高，CPU 运行时间越长，因此便可以通过对比时间差的方式逐一猜测破解。&lt;/p&gt;

&lt;p&gt;预防计时攻击的方法通常也很简单，使用 constant-time 的方式。如上述比较摘要是否相等的示例代码，当发现两个字符不一致时，并没有立即返回，而是继续比较下去，因此使得计算时间不会变化。&lt;/p&gt;

&lt;p&gt;虽然这样的处理，时间复杂度提高了，在语言级别的效率也降低了，但在系统安全的关键部分，付出了一点点性能的代价还是值得的。&lt;/p&gt;

&lt;p&gt;也许你会觉得不解 (我也是), 每个请求在不同的情况下包括网络延迟，计算机运行状态都是不可能完全一致的，况且这么一点点的时间差在 Rails 处理整个复杂的请求过程中 (你应该知道，在 Rails 中一个请求可能生成数以万计的 Ruby 对象) 也显得非常的微不足道，应该是非常难以利用的，计时攻击难道仅仅存活在理论当中吗？这篇&lt;a href="http://www.cs.rice.edu/~dwallach/pub/crosby-timing2009.pdf" rel="nofollow" target="_blank" title=""&gt;论文&lt;/a&gt;也许可以给我们带来一些参考。&lt;/p&gt;

&lt;p&gt;&lt;a href="http://zhouguangming.me/2014/08/30/timing-attack/" rel="nofollow" target="_blank" title=""&gt;原文链接&lt;/a&gt;&lt;/p&gt;</description>
      <author>zgm</author>
      <pubDate>Thu, 04 Sep 2014 10:18:28 +0800</pubDate>
      <link>https://ruby-china.org/topics/21380</link>
      <guid>https://ruby-china.org/topics/21380</guid>
    </item>
    <item>
      <title>[上海][2014年08月12日] Ruby Tuesday 活动召集</title>
      <description>&lt;p&gt;时间：2014 年 08 月 12 日 (周二) 晚上 7 点到 9 点&lt;/p&gt;

&lt;p&gt;地点：浦东新区世纪大道 8 号上海国金中心 D 座 B1 楼 LG1-37 室 COSTA COFFEE(国金中心店)&lt;/p&gt;

&lt;p&gt;位置：在浦东陆家嘴的 Apple Store 旁边，提早到达的同学请负责占座&lt;/p&gt;

&lt;p&gt;网址：&lt;a href="http://www.dianping.com/shop/4600640" rel="nofollow" target="_blank" title=""&gt;点评网址&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;主题：暂无技术主题，&lt;a href="/gene_wu" class="user-mention" title="@gene_wu"&gt;&lt;i&gt;@&lt;/i&gt;gene_wu&lt;/a&gt; 同学和大家讨论一下 &lt;a href="https://ruby-china.org/topics/20701" title=""&gt;七周七语言之 Ruby&lt;/a&gt; 的事情，如果谁有什么好玩的分享也可在下面留言。&lt;/p&gt;</description>
      <author>zgm</author>
      <pubDate>Thu, 07 Aug 2014 10:24:12 +0800</pubDate>
      <link>https://ruby-china.org/topics/20886</link>
      <guid>https://ruby-china.org/topics/20886</guid>
    </item>
    <item>
      <title>Why Ruby blocks exist?</title>
      <description>&lt;p&gt;&lt;a href="http://programming.oreilly.com/2014/03/why-ruby-blocks-exist-part" rel="nofollow" target="_blank"&gt;http://programming.oreilly.com/2014/03/why-ruby-blocks-exist-part&lt;/a&gt;
&lt;a href="http://programming.oreilly.com/2014/03/why-ruby-blocks-exist-part-ii.html" rel="nofollow" target="_blank"&gt;http://programming.oreilly.com/2014/03/why-ruby-blocks-exist-part-ii.html&lt;/a&gt;
&lt;a href="http://programming.oreilly.com/2014/05/why-ruby-blocks-exist-part-iii.html" rel="nofollow" target="_blank"&gt;http://programming.oreilly.com/2014/05/why-ruby-blocks-exist-part-iii.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;适合新手阅读。&lt;/p&gt;</description>
      <author>zgm</author>
      <pubDate>Tue, 20 May 2014 17:04:11 +0800</pubDate>
      <link>https://ruby-china.org/topics/19416</link>
      <guid>https://ruby-china.org/topics/19416</guid>
    </item>
    <item>
      <title>DHH 今天发布 Active Job</title>
      <description>&lt;p&gt;DHH 今天发布了 &lt;a href="https://github.com/rails/activejob" rel="nofollow" target="_blank" title=""&gt;Active Job&lt;/a&gt;, 预计 4.2 release, Rails 集团又庞大了一部分，他真是在下一盘很大的棋啊。&lt;/p&gt;</description>
      <author>zgm</author>
      <pubDate>Mon, 19 May 2014 00:17:30 +0800</pubDate>
      <link>https://ruby-china.org/topics/19361</link>
      <guid>https://ruby-china.org/topics/19361</guid>
    </item>
    <item>
      <title>[上海] [2014年5月13日] 周中聚会</title>
      <description>&lt;p&gt;时间：2014 年 5 月 13 日 19:00~21:00&lt;/p&gt;

&lt;p&gt;地点：地点浦东新区世纪大道 8 号上海国金中心 D 座 B1 楼 LG1-37 室 COSTA COFFEE(国金中心店)&lt;/p&gt;

&lt;p&gt;交通：位置在浦东陆家嘴的 Apple Store 旁边，提早到达的同学请负责占座&lt;/p&gt;

&lt;p&gt;网址：&lt;a href="dianping.com/shops/4600640" title=""&gt;大众点评&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;主题：&lt;/p&gt;

&lt;p&gt;1: &lt;a href="/ShiningRay" class="user-mention" title="@ShiningRay"&gt;&lt;i&gt;@&lt;/i&gt;ShiningRay&lt;/a&gt;, 主题待定。&lt;/p&gt;

&lt;p&gt;2: 其他主题&lt;/p&gt;

&lt;p&gt;无需报名，欢迎大家前来围观。&lt;/p&gt;

&lt;p&gt;&lt;a href="/lgn21st" class="user-mention" title="@lgn21st"&gt;&lt;i&gt;@&lt;/i&gt;lgn21st&lt;/a&gt; 请求置顶。&lt;/p&gt;</description>
      <author>zgm</author>
      <pubDate>Wed, 07 May 2014 19:39:24 +0800</pubDate>
      <link>https://ruby-china.org/topics/19082</link>
      <guid>https://ruby-china.org/topics/19082</guid>
    </item>
    <item>
      <title>DDD 4 Rails Developers.Part 2 : Entities and Values</title>
      <description>&lt;p&gt;在我前面关于 DDD for Rails Developers 的文章中提到使用分层架构来解决域的复杂度。我展示了一些典型的不符合分层架构的例子并给出了一些改正的建议。&lt;/p&gt;
&lt;h3 id="DDD 的构建模块"&gt;DDD 的构建模块&lt;/h3&gt;
&lt;p&gt;这回我要开始聊一聊 DDD 的构建模块以及他们如何被用于建模当中。&lt;/p&gt;
&lt;h4 id="实体与值"&gt;实体与值&lt;/h4&gt;
&lt;p&gt;在 DDD 中，实体和值对象的区别很大。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;“一个实体是由一个持续性和一致性的线程而不是其属性定义的对象”. 例如银行住户是一个实体。系统可以共存多个银行账户。它们中的一部分可以被分配个同一个支行或者拥有同一个主人，只要他们有不同的身份系统就必须将他们看做不同的账户。在 Rails 应用中，实体的身份通常用一个自动生成的主键来表示。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;“值对象是一个描述了一些特性或者属性但是没有身份概念的对象”由于没有身份的概念，若两个值对象的所有属性相同，那它们就是相同的。货币就是一个值对象的例子。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="深入实体"&gt;深入实体&lt;/h4&gt;
&lt;p&gt;Rails 社区对于什么是实体有着很好的理解。几乎所以扩展了 ActiveRecord::Base 的对象，都是一个实体。&lt;/p&gt;
&lt;h5 id="实体有如下特性:"&gt;实体有如下特性：&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;身份对于实体来说很重要。它通常是用一个自动生成的主键来表示，可用于比较两个实体。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;它们是可变的并且通常只有主键是不能被改变的。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;它们很长寿，大多数实体会一直呆在数据库里。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;由于实体的多变并且长寿，它们有一个复杂的生命周期：&lt;/p&gt;

&lt;p&gt;1. 一个实体对象被创建。&lt;/p&gt;

&lt;p&gt;2. 它被保存到数据库中。&lt;/p&gt;

&lt;p&gt;3. 它被从数据库中读取出来。&lt;/p&gt;

&lt;p&gt;4. 它被更新。&lt;/p&gt;

&lt;p&gt;5. 它被删除 (或者被标记为删除).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;由于实体的多变和复杂的生命周期，它的处理也变得复杂起来。因此，你每次创建一个实体，思考一下你将会如何持久化它，哪些属性将会被频繁更新，会被哪个聚合包含？(更多关于聚合的精彩内容，见下篇文章).&lt;/p&gt;
&lt;h3 id="深入值对象"&gt;深入值对象&lt;/h3&gt;
&lt;p&gt;Rails 社区对之对象的理解并不充分，因此大多数 Rails 应用都会有“原始类型困扰”.&lt;/p&gt;

&lt;p&gt;如整形或字符串等原始类型的数据用于表示域中重要的概念。&lt;/p&gt;

&lt;p&gt;首先，处理一组属性的逻辑会被分散到很多类中。原始类型困扰是重复代码的源头。其二，使用原始数据代替特定于域的高度抽象的服务将会使代码变得不清晰。值对象为原始类型困扰提供了一个很好的补救。&lt;/p&gt;
&lt;h5 id="值对象有如下特性"&gt;值对象有如下特性&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;值对象没有身份。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;他们是不可变得。对于对象来说，3 增加到 5 不会改变他们的值而是被一个新的对象代替。理想状态下，使用值对象就如同使用原始类型一样。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;值对象没有复杂的生命周期。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="在 Rails 中创建值对象"&gt;在 Rails 中创建值对象&lt;/h4&gt;
&lt;p&gt;在 Rails 中有很多种方式可以创建和管理值对象，我将会列举三条。&lt;/p&gt;
&lt;h4 id="使用 composed_of"&gt;使用 composed_of&lt;/h4&gt;
&lt;p&gt;想象着，我们正在开发一个 blog 应用。我们决定使用如下模型：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;blog 有多个 posts&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;每个 post 有多个 comments&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;post 和 comment 都拥有 location 字段&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我们可以这样创建 posts 和 comments:&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;blog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Blog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;
&lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;blog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;make_post&lt;/span&gt; &lt;span class="ss"&gt;text: &lt;/span&gt;&lt;span class="s1"&gt;'great post'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;location_country: &lt;/span&gt;&lt;span class="s1"&gt;'Canada'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;location_city: &lt;/span&gt;&lt;span class="s1"&gt;'Toronto'&lt;/span&gt;
&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;make_comment&lt;/span&gt; &lt;span class="ss"&gt;text: &lt;/span&gt;&lt;span class="s1"&gt;'great comment'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;location_country: &lt;/span&gt;&lt;span class="s1"&gt;'Canada'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;location_city: &lt;/span&gt;&lt;span class="s1"&gt;'Toronto'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们还可以通过 location 来搜索他们：&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;Blog&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="o"&gt;...&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;all_posts_from&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt;
    &lt;span class="o"&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;all_comments_from&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt;
    &lt;span class="o"&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;除此之外，需要一个 presenter 用来显示 location 字段：&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;LocationPresenter&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;country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt;
    &lt;span class="o"&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;现在我们需要将 location_contry 和 location_city 包到 location 对象当中。&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;Post&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;composed_of&lt;/span&gt; &lt;span class="ss"&gt;:location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;mapping: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sx"&gt;%w(location_country country)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                   &lt;span class="sx"&gt;%w(location_city city)&lt;/span&gt;&lt;span class="p"&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;all_posts_from&lt;/span&gt; &lt;span class="n"&gt;location&lt;/span&gt;
    &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt; &lt;span class="ss"&gt;location: &lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们重构的结果如下：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;blog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;make_post&lt;/span&gt; &lt;span class="ss"&gt;text: &lt;/span&gt;&lt;span class="s1"&gt;'great post 2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;location: &lt;/span&gt;&lt;span class="no"&gt;Location&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="s1"&gt;'Canada'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Toronto'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;重构后获得的最大收获是对包含在原码中的域有了一个重要的概念，同时，提取出值对象帮助我们提高抽象水平。使代码更具可读性。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;Toronto&lt;/span&gt;
  &lt;span class="no"&gt;Location&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="s1"&gt;'Canada'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Toronto'&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="n"&gt;blog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;make_post&lt;/span&gt; &lt;span class="ss"&gt;text: &lt;/span&gt;&lt;span class="s1"&gt;'great post'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;location: &lt;/span&gt;&lt;span class="no"&gt;Toronto&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h5 id="ActiveRecord::Base 拓展的值对象"&gt;ActiveRecord::Base 拓展的值对象&lt;/h5&gt;
&lt;p&gt;有些人说从 ActiveRecord::Base 拓展的任何对象都是一个实体。我不同意这个观点。我认为，无论你如何实现你的值对象，只要他们没有状态和身份就好了。&lt;/p&gt;

&lt;p&gt;我们定义一个 location 类：&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;Location&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;validates&lt;/span&gt; &lt;span class="ss"&gt;:city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:uniqueness&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;:scope&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:country&lt;/span&gt;&lt;span class="p"&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;get&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt;
    &lt;span class="n"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by_country_and_city&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s2"&gt;"There is no '&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;' in '&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;location&lt;/span&gt;
    &lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readonly!&lt;/span&gt;
    &lt;span class="n"&gt;location&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;使用起来与上面的 location 差不多：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;toronto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Location&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="s1"&gt;'Canada'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Toronto'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;blog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;make_post&lt;/span&gt; &lt;span class="ss"&gt;text: &lt;/span&gt;&lt;span class="s1"&gt;'great post 2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;location: &lt;/span&gt;&lt;span class="n"&gt;toronto&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;像这样动态地管理一个任意位置列表，或者为每个对象增加一些额外信息 (例如，一个连接到 wikipedia 的文章) 会让你决定支持这种方式。&lt;/p&gt;
&lt;h5 id="原始的 Ruby 对象"&gt;原始的 Ruby 对象&lt;/h5&gt;
&lt;p&gt;那些通过 Rails 学习 Ruby 的开发人员更乐于使用 Rails 的组件来解决问题 —— 持久话个对象？只有 ActiveRecord. 需要一个值对象？使用 compose_of. 没了 Rails 就感觉浑身不自在，例如所有不是从 ActiveRecord::Base 扩展来的模型都会被他们塞到 lib 目录之下。即使是一个小应用，创建一个复杂模型也需要使用各种 Factories, Services, Value Objects, 等等。因此，不要惧怕离开 Rails 实现和使用值对象。&lt;/p&gt;
&lt;h5 id="总结"&gt;总结&lt;/h5&gt;
&lt;p&gt;总而言之，实体和值对象及其重要。他们是对象模型的核心要素。软件工程师们一定要深入理解他们之间的不同之处。&lt;/p&gt;</description>
      <author>zgm</author>
      <pubDate>Sun, 26 Jan 2014 18:18:49 +0800</pubDate>
      <link>https://ruby-china.org/topics/17009</link>
      <guid>https://ruby-china.org/topics/17009</guid>
    </item>
    <item>
      <title>[上海][2013年11月23日] Ruby Tuesday 活动召集 (周六)  (注意: 已更新活动时间)</title>
      <description>&lt;p&gt;&lt;del&gt;时间：2013 年 11 月 23 日 (周六) 下午 3 点至 6 点&lt;/del&gt;
时间：2013 年 11 月 23 日 (周六) 下午 2 点至 5 点&lt;/p&gt;

&lt;p&gt;地点：浦东新区张衡路 1000 弄润和国际总部 30 号楼&lt;/p&gt;

&lt;p&gt;交通：地铁 2 号线张江高科站 5 号口出来转张江南环线在张衡路哈雷路下车&lt;/p&gt;

&lt;p&gt;地图:
&lt;img src="//l.ruby-china.com/photo/2013/f86efb39d2e192522e667ce4512730bb.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Topic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://ruby-china.org/quakewang" title=""&gt;&lt;/a&gt;&lt;a href="/quakewang" class="user-mention" title="@quakewang"&gt;&lt;i&gt;@&lt;/i&gt;quakewang&lt;/a&gt; Prawn&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://ruby-china.org/vincent" title=""&gt;&lt;/a&gt;&lt;a href="/Vincent" class="user-mention" title="@Vincent"&gt;&lt;i&gt;@&lt;/i&gt;Vincent&lt;/a&gt; 如何搞垮一个 Ruby on Rails 系统&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://ruby-china.org/gene_wu" title=""&gt;&lt;/a&gt;&lt;a href="/gene_wu" class="user-mention" title="@gene_wu"&gt;&lt;i&gt;@&lt;/i&gt;gene_wu&lt;/a&gt; Make You Test Faster&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果谁还有主题要讲，可以在下面留言，我加到上面去。如果有需要，可以使用 PPT, 公司有投影仪。&lt;/p&gt;

&lt;p&gt;无需报名，没有限制，只要有兴趣，想来就来。&lt;/p&gt;

&lt;p&gt;欢迎大家前来捧场。&lt;/p&gt;

&lt;hr&gt;
&lt;h3 id="鉴于一些同学要赶着回家吃晚饭, 并且现在天黑的也比较早了, 所以我们把时间提前了一个小时, 请相互转告."&gt;鉴于一些同学要赶着回家吃晚饭，并且现在天黑的也比较早了，所以我们把时间提前了一个小时，请相互转告。&lt;/h3&gt;
&lt;hr&gt;
&lt;h3 id="本次聚会很荣幸收到 安全宝 赞助的一些红酒和抱枕, 由于数量有限, 届时将以抽奖的形式发给过来的同学."&gt;本次聚会很荣幸收到 &lt;a href="http://anquanbao.com/" rel="nofollow" target="_blank" title=""&gt;安全宝&lt;/a&gt; 赞助的一些红酒和抱枕，由于数量有限，届时将以抽奖的形式发给过来的同学。&lt;/h3&gt;</description>
      <author>zgm</author>
      <pubDate>Fri, 15 Nov 2013 17:55:12 +0800</pubDate>
      <link>https://ruby-china.org/topics/15573</link>
      <guid>https://ruby-china.org/topics/15573</guid>
    </item>
    <item>
      <title>看这个笑了一个晚上，哈哈哈哈哈～～</title>
      <description>&lt;p&gt;&lt;span class="embed-responsive embed-responsive-16by9"&gt;&lt;iframe class="embed-responsive-item" src="//www.youtube.com/embed/uyYGWo_XweM" allowfullscreen=""&gt;&lt;/iframe&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <author>zgm</author>
      <pubDate>Sun, 24 Mar 2013 20:52:49 +0800</pubDate>
      <link>https://ruby-china.org/topics/9719</link>
      <guid>https://ruby-china.org/topics/9719</guid>
    </item>
  </channel>
</rss>
