<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Liang</title>
    <link rel="self" type="application/atom+xml" href="https://liangliu.me/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://liangliu.me"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2022-10-22T00:00:00+00:00</updated>
    <id>https://liangliu.me/atom.xml</id>
    <entry xml:lang="en">
        <title>沟通 - 回答问题的原则</title>
        <published>2022-10-22T00:00:00+00:00</published>
        <updated>2022-10-22T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://liangliu.me/principles-of-answering-questions/"/>
        <id>https://liangliu.me/principles-of-answering-questions/</id>
        
        <content type="html" xml:base="https://liangliu.me/principles-of-answering-questions/">&lt;p&gt;最近在听王建硕的一系列关于web3的&lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;playlist?list=PLHdtP1fOoM5phfxUCuwdrMlia6fhQwjmy&quot;&gt;视频&lt;&#x2F;a&gt;以及播客，在他与Sarah做的&lt;a href=&quot;https:&#x2F;&#x2F;www.xiaoyuzhoufm.com&#x2F;episode&#x2F;6308b3ec4ee49e5ecd8b8f8e&quot;&gt;第二次播客&lt;&#x2F;a&gt;的最后，有一个很有意思的讨论，也引起了我的一些共鸣和想法，值得记录一下。&lt;&#x2F;p&gt;
&lt;p&gt;在&lt;a href=&quot;https:&#x2F;&#x2F;www.xiaoyuzhoufm.com&#x2F;episode&#x2F;62d93b1cfa15142e17251e05&quot;&gt;第一集博客&lt;&#x2F;a&gt;播出之后有一位听众评论说为什么王建硕在回答Sarah的问题时候总是要先质疑问题本身，每个问题都觉得没意义，不准确，是习惯性的否定别人吗？&lt;&#x2F;p&gt;
&lt;p&gt;王建硕在这里谈了自己在回答别人问题时候的一些原则&lt;&#x2F;p&gt;
&lt;h4 id=&quot;1-zai-hui-da-wei-shen-me-wen-ti-shi-hou-xian-que-ding-wen-ti-shi-bu-shi-zhen-de&quot;&gt;1. 在回答为什么问题时候先确定问题是不是真的&lt;&#x2F;h4&gt;
&lt;p&gt;也就是说先搞清楚这个问题里涵盖的事实是不是真的，或者这个问题到底是不是一个问题。其实很多问题一开始就错了，如果你思考一下的话，也就根本没有必要去回答，会为你节省很多时间。&lt;&#x2F;p&gt;
&lt;p&gt;再发散一下呢，这一点也适用于&lt;a href=&quot;https:&#x2F;&#x2F;xyproblem.info&#x2F;&quot;&gt;XY问题&lt;&#x2F;a&gt;，在程序员的日常工作中，经常有同事会在群里问Y问题应该怎么解决呢？在你帮他回答Y问题的时候，可以先想一下这个原则，确定Y问题是不是那个真正困扰他的问题。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;2-dui-yu-wen-ti-li-de-gai-nian-jin-xing-cheng-qing-bao-zheng-da-jia-li-jie-de-shi-tong-yi-ge-dong-xi&quot;&gt;2. 对于问题里的概念进行澄清，保证大家理解的是同一个东西。&lt;&#x2F;h4&gt;
&lt;p&gt;在讨论问题时候如果涉及到专业领域的概念，尤其是讨论一些新兴技术的时候，其实很多人对同一个概念的理解都是不一样的，如果能在开始讨论之前就有意识的对这些概念进行澄清，保证你和听众的理解一致，那么相信听众接下来对你的回答也会理解的很到位，从而不会出现一些尴尬的场景，比如你讲了好几分钟后，发现听众有点懵，他可能在嘀咕怎么你说的跟他想问的不一样呢。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;3-hui-da-zhe-ge-wen-ti-dao-di-you-mei-you-yi-yi&quot;&gt;3. 回答这个问题到底有没有意义。&lt;&#x2F;h4&gt;
&lt;p&gt;考虑以下场景：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;有些问题它可能真的是一个问题，但是它重要吗？&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;一些无聊八卦类的问题，不回答也罢&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;回答它对于提问者和回答者有意义吗？&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;有些问题可能没有答案，或者答案其实根本无所谓&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;对于回答者来说：我是不是在一个适合评论和回答的位置或者时间？&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;可能基于当时的情况我们无法100%的了解问题发生的背景，过程以及影响，所以不适合作出回答和评论，需要时间来说明&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;还可以考虑：如果这个问题没有意义，那么这个问题可以稍作转化，让它成为一个有意义的问题再回答吗？&lt;&#x2F;p&gt;
&lt;p&gt;只有遵循这三个原则，你才是100%的投入在跟对方的交流中，而且你是真正做到高效沟通的。&lt;&#x2F;p&gt;
&lt;p&gt;联想到自己的日常工作，也可以应用这三条原则，让日常的技术讨论更加高效，同时我认为对于解决问题也更容易找到正确的方向，不会走弯路。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>数据库背后的数据结构</title>
        <published>2022-02-17T00:00:00+00:00</published>
        <updated>2022-02-17T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://liangliu.me/database-structures/"/>
        <id>https://liangliu.me/database-structures/</id>
        
        <content type="html" xml:base="https://liangliu.me/database-structures/">&lt;h3 id=&quot;lai-she-ji-yi-ge-zui-jian-dan-de-shu-ju-ku&quot;&gt;来设计一个最简单的数据库&lt;&#x2F;h3&gt;
&lt;p&gt;可以通过两个bash函数来实现一个简单的KV存储&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;#! &#x2F;bin&#x2F;bash
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;db_set&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;echo &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;,$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;&amp;gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt; database
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;db_get&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;grep &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;^$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;,&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt; database &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;sed -e &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;s&#x2F;^$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;s1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;,&#x2F;&#x2F;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;tail -n 1
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这里我们用两个函数，一个写入，一个读取，底层存储是一个叫&lt;code&gt;database&lt;&#x2F;code&gt;的文件来做的，每行保存一个KV对，由逗号分开，每次调用写入都会往文件后面append进去一个新的记录，所以如果你对同一个key做过多次更新，那么旧的记录不会删除，每次都会生成新的记录。读取时候会去查到包含key的那些记录，然后返回最后一条记录，也是最新的记录。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;$ db_set a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;a&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;$ db_set b &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;b&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;$ db_get a
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;a
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;$ db_set a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;A&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;$ db_get a
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;A
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;$ cat database
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;a,a
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;b,b
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;a,A
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在简单数据情况下这个函数的表现还不错，但是在数据库记录很大的时候就表现的很糟糕了。&lt;code&gt;db_get&lt;&#x2F;code&gt; 需要扫描整个数据库问题找到查找的那个key，时间复杂度是&lt;code&gt;O(n)&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;想要高效的从数据库查找特定的key，我们需要一个索引来帮我们快速查询数据。其背后的基本思想就是存储一些元数据，来帮助我们快速的找到查找的数据，如果我们想要通过不同的方式查找相同的数据，那么则需要在数据的不同字段上建立不同的索引。&lt;&#x2F;p&gt;
&lt;p&gt;索引的建立并不会影响存储数据的内容，它只会影响查找的性能。维护额外的索引也会导致额外的开销，例如写入的性能也会受到索引影响，因为每一个写操作都要去更新对应的索引。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;shi-yong-hashbiao-lai-jian-li-suo-yin&quot;&gt;使用Hash表来建立索引&lt;&#x2F;h3&gt;
&lt;p&gt;利用hash表来做一个KV存储是最常见的做法，我们这里稍微有一些不同，重新设计一个简单的数据库&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;它的存储还是按照之前append的方式增量式写入到一个文件中，存储在磁盘上。&lt;&#x2F;li&gt;
&lt;li&gt;key和value之间用逗号分割。&lt;&#x2F;li&gt;
&lt;li&gt;同时我们要维护一个hash表，在这个hash表里，它的key是这个数据的key，value是这个数据记录在存储文件中的字节偏移量。&lt;&#x2F;li&gt;
&lt;li&gt;每次append写入新的记录的时候也要去更新这个hash表&lt;&#x2F;li&gt;
&lt;li&gt;查找时候，先在hash表里找到这个key的偏移量，然后读入存储文件，通过偏移量快速找到对应数据的开始位置。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;liangliu.me&#x2F;database-structures&#x2F;ddia_0301.png&quot; alt=&quot;hash table index&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这个其实就是&lt;code&gt;Riak&lt;&#x2F;code&gt;的存储引擎&lt;code&gt;Bitcask&lt;&#x2F;code&gt;的基本实现原理&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Bitcask提供了高性能的读写操作&lt;&#x2F;li&gt;
&lt;li&gt;数据从硬盘加载只需要一次寻址，效率很高&lt;&#x2F;li&gt;
&lt;li&gt;适用于key经常更新的情况，比如记录一个URL被访问的次数，很多的写操作都是在更新同一个key&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;这里有个问题：数据都存储在一个文件里，而且都是增量式，那么如何避免磁盘空间被占满呢？
有个很好的解决办法就是把数据分成segment来存储&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;数据根据指定的大小，切分成一个一个的segment&lt;&#x2F;li&gt;
&lt;li&gt;每个segment存储在一个文件里，每个segment也有自己都应的哈希表&lt;&#x2F;li&gt;
&lt;li&gt;当这个文件达到指定的大小之后，则开始写下一个文件。&lt;&#x2F;li&gt;
&lt;li&gt;然后对之前的这些segment文件进行压缩，压缩的过程就是把重复的key的记录删掉，只保留最新的值&lt;&#x2F;li&gt;
&lt;li&gt;还可以把压缩后的多个文件再合并到一起&lt;&#x2F;li&gt;
&lt;li&gt;压缩和合并的操作可以在后台一个独立线程中执行，不影响数据库对外的服务&lt;&#x2F;li&gt;
&lt;li&gt;完成之后再对新的segment在内存中生成hash表&lt;&#x2F;li&gt;
&lt;li&gt;读请求这个时候可以切换到新的segment文件上，原始的那些文件可以删除。&lt;&#x2F;li&gt;
&lt;li&gt;查询时候先查最新的hash表，如果没有，则查找次新的，以此类推。&lt;&#x2F;li&gt;
&lt;li&gt;压缩和合并的操作会让segment的数量相对非常少，因此这个查找过程也不会查太多的hash表。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;liangliu.me&#x2F;database-structures&#x2F;ddia_0302.png&quot; alt=&quot;压缩一个数据segment，只保留最新的值&quot; &#x2F;&gt;
压缩一个数据segment，只保留最新的&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;liangliu.me&#x2F;database-structures&#x2F;ddia_0303.png&quot; alt=&quot;压缩和合并多个segments&quot; &#x2F;&gt;
压缩和合并多个segment&lt;&#x2F;p&gt;
&lt;p&gt;实际中还有很多细节来保证这个设计的正常工作&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;文件格式 - CSV并不是记录数据的最合适格式，计算出String的字节长度后存储在二进制文件中会更快也更简单。&lt;&#x2F;li&gt;
&lt;li&gt;删除记录 - 如果要删除某个记录，需要写入一个特殊的删除记录，当log文件合并在一起的时候，这个记录会告诉合并的代码来删掉这个key的任何记录&lt;&#x2F;li&gt;
&lt;li&gt;数据恢复 - 如果数据库重启了，内存中的哈希表记录就全部都丢了。理论上，可以通过从头到尾读取每个segment的记录来重建哈希表。但是这个可能会花费很长时间。Bitcask是通过从硬盘加载哈希表的快照来恢复的，这样时间更短。&lt;&#x2F;li&gt;
&lt;li&gt;不完整数据 - 如果DB崩溃导致一些数据没有写完全，出现部分数据，可以通过checksums来找到这些不完整的数据，然后忽略掉。&lt;&#x2F;li&gt;
&lt;li&gt;并发控制 - 只保持一个写线程，数据segment只能append写，而且是不可变的。这样可以支持多线程读取。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;同时&lt;code&gt;Append-only&lt;&#x2F;code&gt; 被证明是更好的设计， 而不是去修改已有的记录&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;增量式append写入和segment合并的过程都是顺序的写操作，对于磁盘写入来说比随机写入更快，因为不需要频繁的寻址，尤其是在传统的机械式磁盘中。SSD也更喜欢这种顺序写入&lt;&#x2F;li&gt;
&lt;li&gt;在append only和数据不变的情况下，并发和崩溃恢复会更简单，不需要考虑当修改一个记录到一半的时候crash掉的情况&lt;&#x2F;li&gt;
&lt;li&gt;合并segments可以避免产生更多的数据碎片&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;但是hash表作为索引实现也有一些限制:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;hash表都是加载到内存里的，对于非常大的数据，就会占用很多内存。也可以把索引保存在磁盘上，但是性能会大打折扣，因为需要很多随机读取I&#x2F;O, 同时hash冲突也需要复杂的逻辑来处理。&lt;&#x2F;li&gt;
&lt;li&gt;区间查询的性能不够好，例如想要查询所有在&lt;code&gt;kitty00000&lt;&#x2F;code&gt;到&lt;code&gt;kitty99999&lt;&#x2F;code&gt;的数据，需要在所有的hash表里查找每一个key&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;sstable-he-lsm-tree&quot;&gt;SSTable 和 LSM-Tree&lt;&#x2F;h3&gt;
&lt;p&gt;在之前的segment里面保存的是一些按照写入顺序排序的KV键值对，对于同一个key来说，后面写入的值最终会替代前面的值，除此之外，不同key之间的顺序其实并不重要。这样的话可以对我们的segment做一些小小的修改：我们要求segment里面的KV对要按照key排序。我们把这个叫做&lt;code&gt;Sorted String Table&lt;&#x2F;code&gt;，或者&lt;code&gt;SSTable&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;SSTable对比使用hash表的log segment来说有很多优势：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;合并segments非常简单和高效，同时从多个segment文件中读取记录，对比每个文件读入的当前key，根据排序规则找到排序在前的那个key，拷贝到新的segment中，不断重复。这样新生成的那个segment文件也是按照key来排序的。如果一个key在多个文件中都有，那么我们只取最新的segment里面的那个，把老的都丢弃。
&lt;img src=&quot;https:&#x2F;&#x2F;liangliu.me&#x2F;database-structures&#x2F;ddia_0304.png&quot; alt=&quot;merging SSTables&quot; &#x2F;&gt;&lt;&#x2F;li&gt;
&lt;li&gt;对于查找来说，我们的hash表不需要包含每一个key了，只需要包含少量的key可以，比如每5kb的数据只保存其中一个key和它在segment中的偏移量。在查找时候，根据查找的key和排序顺序找到在hash表里离它最近的前面key和后面那个key，然后去扫描这两个偏移量的区间就可以了。
&lt;img src=&quot;https:&#x2F;&#x2F;liangliu.me&#x2F;database-structures&#x2F;ddia_0305.png&quot; alt=&quot;SSTable and in-memory index&quot; &#x2F;&gt;&lt;&#x2F;li&gt;
&lt;li&gt;由于查找操作需要去扫描一个区间的数据，我们可以把这个区间的数据放到一起作为一个块压缩，然后写到磁盘上，这样每个hash表中的偏移量就是某个压缩块的起始点，这样可以节约磁盘空间，减少磁盘IO。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;可是数据库接受的数据写入请求顺序都是随机的，那么如何在一开始就能够有按照key来排序的segment呢？ 其实在内存中维护一个有序的数据结构是比较简单的，有很多树结构都可以使用，比如红黑树，AVL树，我们可以以任意顺序写入数据，读取时候可以读到排序后的数据。&lt;&#x2F;p&gt;
&lt;p&gt;这样我们的数据库设计看起来是这样：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;当写入数据时候，数据写入到在内存中维护的一个平衡树结构，这样的数据结构可以叫做&lt;code&gt;memtable&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;当&lt;code&gt;memtable&lt;&#x2F;code&gt;中的数据超过一定的阈值之后，比如5M，把它以SSTable的格式写入到磁盘上，这个很容易实现，因为我们可以按照一定顺序从树中读出数据。这样写入磁盘的segment是最新的，在这个过程中，数据库的写入请求会继续写到一个新的&lt;code&gt;memtable&lt;&#x2F;code&gt;中。&lt;&#x2F;li&gt;
&lt;li&gt;对于读请求，首先尝试去&lt;code&gt;memtable&lt;&#x2F;code&gt;中查找，如果没有，再去磁盘上最新的segment中查找，以此类推。&lt;&#x2F;li&gt;
&lt;li&gt;在后台定时的执行合并和压缩segment文件，以节省磁盘空间。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;em&gt;这里有个问题：所以每个segment都对应一个hash table，对吗？&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;不过这里我们还是能看到一个问题，最新的数据其实都是在&lt;code&gt;memtable&lt;&#x2F;code&gt;里保存着，如果数据库挂了，那么最新写入的数据还没来得及写到磁盘中，就会丢失了。为了避免这个问题，我们可以在磁盘上额外保存一个log文件，每一个写操作到&lt;code&gt;memtable&lt;&#x2F;code&gt;都会立即在这个log里记录，在这个log文件里是不需要排序的，只是用来恢复数据用的。当&lt;code&gt;memtable&lt;&#x2F;code&gt;中的数据写入到磁盘后，对应的log文件也就可以删掉了。&lt;&#x2F;p&gt;
&lt;p&gt;这样的数据库设计其实跟&lt;code&gt;LevelDB&lt;&#x2F;code&gt;和&lt;code&gt;RocksDB&lt;&#x2F;code&gt;的实现原理是一样的，这些KV存储库被设计为嵌入到其他应用程序里工作。LevelDB可以用在Riak里作为存储引擎替换Bitcask，类似的存储引擎同样也被用在Cassandra和HBase里，他们都是受Google的Bigtable的论文启发而设计实现的，在Google的论文中首次提出了&lt;code&gt;SSTable&lt;&#x2F;code&gt;和&lt;code&gt;memtable&lt;&#x2F;code&gt;的概念。&lt;&#x2F;p&gt;
&lt;p&gt;这种索引结构是Patrick O&#x27;Neil首次以&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Log-structured_merge-tree&quot;&gt;&lt;code&gt;Log-Structured Merge-Tree&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;的名字提出的，基于这种压缩合并有序文件的存储引擎经常被称作LSM存储引擎。&lt;&#x2F;p&gt;
&lt;p&gt;这里还是有很多细节需要考虑才能让这个设计应用到实际中，例如当要查的key在数据库中不存在的时候会非常慢，因为我们要检查memtable, 然后所有的segments，一一从磁盘中加载然后查找最后都查完发现这个key不存在。为了优化这个场景，数据库通常使用一个叫&lt;a href=&quot;https:&#x2F;&#x2F;dl.acm.org&#x2F;doi&#x2F;10.1145&#x2F;362686.362692&quot;&gt;&lt;code&gt;Bloom Filters&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;的东西来帮助我们检查一个key是否在数据库中。在实际应用中也会有不同的策略来决定key的排序方式和压缩合并的时间，例如常用的选项有&lt;code&gt;size-tiered&lt;&#x2F;code&gt;和&lt;code&gt;leveled&lt;&#x2F;code&gt;，在&lt;code&gt;size-tired&lt;&#x2F;code&gt;下，新的和小的SSTTable会被合并到老的和大的SSTable中去。在Leveled压缩算法下，key的区间范围会被分成更小的SSTable，同时旧的数据会被转移到其他level，这样会让压缩是增量式的，减少磁盘使用。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;b-treeping-heng-shu&quot;&gt;B-Tree平衡树&lt;&#x2F;h3&gt;
&lt;p&gt;LSM索引确实有它的优点，但是实际上应用最广泛的索引数据结构是&lt;code&gt;B-Tree&lt;&#x2F;code&gt;，几乎所有的关系型数据库和很多非关系型数据库都用这个实现。跟SSTable类似，B-tree也按照key的排序保存数据，这样可以提供高效的查找和区间查询，但是B-tree使用了完全不同的设计思想。&lt;&#x2F;p&gt;
&lt;p&gt;B-tree把数据拆分成固定大小的块或者分页，一般是4KB大小，一次只读写一个块或者分页，每个分页都有自己的地址，其他的分页都可以通过这个地址来引用并找到它，跟指针类似。这样用这些地址引用就可以构建一个包含多个分页的树。
&lt;img src=&quot;https:&#x2F;&#x2F;liangliu.me&#x2F;database-structures&#x2F;ddia_0306.png&quot; alt=&quot;B-tree index&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;在B-tree里查找一个key&lt;&#x2F;p&gt;
&lt;p&gt;在B-tree里有一个根节点，所有的查询都从这个节点开始，它里面包含了一些keys和child pages的引用，每个child page里包含一系列连续的key，多引用之间的key代表了某一段范围的key的开始和结束，通过那个引用可以找到这个范围内的任意一个key。&lt;&#x2F;p&gt;
&lt;p&gt;当要查询一个key的时候，从根节点开始，找到这个key所属的范围，然后通过这个范围内的引用找到下一个子分页，以此类推，最终找到下面包含这个key的分页中，可以读到它的值。&lt;&#x2F;p&gt;
&lt;p&gt;在一个分页里指向child page的引用的个数叫做分支因子，在上面那个图中，分支因子是6。分支因子取决于用来存储page引用的空间的大小和key起始范围的边界，一般都是几百。&lt;&#x2F;p&gt;
&lt;p&gt;写操作：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;更新某个key的时候，要先查找到这个key的分页，修改这个key的值，然后再写回磁盘，其他分页中包含这个修改过的分页的引用依然是有效的。&lt;&#x2F;li&gt;
&lt;li&gt;如果要增加一个新的key，首先要找到这个key最终属于的分页，然后把它加到这个分页里。&lt;&#x2F;li&gt;
&lt;li&gt;如果这个分页里的空间不够，不能存储新加入的key，那么这个分页会被拆分成2个分页，之前的父分页中会增加新的引用指向新生成的2个分页。
&lt;img src=&quot;https:&#x2F;&#x2F;liangliu.me&#x2F;database-structures&#x2F;ddia_0307.png&quot; alt=&quot;adding new key&quot; &#x2F;&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;这个算法保证树一直是平衡的&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;一个有n个key的B-tree的深度是&lt;code&gt;O(logn)&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;对于大部分的数据库来说一个深度为3或4的B-tree基本上足够了，查询不用去找很多层才能找到分页&lt;&#x2F;li&gt;
&lt;li&gt;例如一个4层的树，每个page如果是4KB的话，分支因子为500的时候可以存储多达250TB的数据&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;由于B-tree在写操作时候需要先找到那个分页，更新后再写回去，对于某些写操作，例如上面的例子，当分页上空间不够的时候，需要拆分成两个，这里涉及到多个分页的多个写操作，当数据库crash的时候这种写操作就很危险，容易丢数据或者产生脏数据。为了解决这样的问题，常用的方式就是使用WAL(write-ahead log)或者叫做redo log。这个log是一个&lt;code&gt;append-only&lt;&#x2F;code&gt;的文件，所有的B-tree的写操作都要先写入这个文件，然后再去更新分页文件。如果数据库崩溃了，当重启之后，这个log会用来恢复数据到之前的状态。&lt;&#x2F;p&gt;
&lt;p&gt;另外在考虑并发控制的时候，经常用 &lt;em&gt;latches&lt;&#x2F;em&gt; (一个轻量级的锁)来保证数据更新中的race condition。&lt;&#x2F;p&gt;
&lt;p&gt;B树的一些优化的设计：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;使用写时复制方案，例如LMDB，而不是覆盖页面并维护WAL来支持崩溃恢复。修改的页面被写入到不同位置，并且在树中创建了父页面的版本，指向新的位置，这样对于并发控制也很有用。&lt;&#x2F;li&gt;
&lt;li&gt;通过不存储整个键，而是缩短其大小，来节省也节省页面空间。特别是树内部的页面上，键只要提供足够的信息来充当键范围的边界。这样页面中包含更多键意味着更高的分支因子，允许更少的层级。&lt;&#x2F;li&gt;
&lt;li&gt;分页在硬盘上理论上来说可以存在任何位置，要想将分页在硬盘上连续的保存来获取高效的读取很困难的。相反LSM tree再写入时候可以很容易的维护序列的写入。&lt;&#x2F;li&gt;
&lt;li&gt;额外的指针被加入到树中。例如，每个分页可以有指向它的兄弟分页的指针来快速的扫描兄弟分页的键，而不用跳回父节点再找到兄弟节点。&lt;&#x2F;li&gt;
&lt;li&gt;有一些变种比如fractal tree借用了log structured思想来减少磁盘寻址。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;b-tree-he-lsm-treede-bi-jiao&quot;&gt;B-tree 和 LSM-tree的比较&lt;&#x2F;h3&gt;
&lt;p&gt;一般来说LSM tree写操作更快，而B-tree读更快。&lt;&#x2F;p&gt;
&lt;p&gt;LSM tree的优点：
B tree在写操作的时候要写两次，一次写WAL，一次写分页文件（也有可能或许更多，例如碰见需要分页的情况），另外，即使在一个分页上只有一点数据修改，B-tree也要写整个分页。
LSM tree也要写多次，由于不断的数据压缩和合并，这样的写一次导致接下来要再进行多次的写的过程叫做写放大。&lt;&#x2F;p&gt;
&lt;p&gt;参考文献：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;shop.oreilly.com&#x2F;product&#x2F;0636920032175.do&quot;&gt;«Designing Data-Intensive Applications»&lt;&#x2F;a&gt; 作者: Martin Kleppmann&lt;&#x2F;li&gt;
&lt;li&gt;文中的图也是从这本书中截取的&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Double-checked locking问题</title>
        <published>2021-12-08T00:00:00+00:00</published>
        <updated>2021-12-08T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://liangliu.me/double-checked-locking/"/>
        <id>https://liangliu.me/double-checked-locking/</id>
        
        <content type="html" xml:base="https://liangliu.me/double-checked-locking/">&lt;p&gt;今天在处理 Fortify 扫描出来的 report 的时候，有个 issue 觉得挺有必要去研究一下的，写个文章记录一下。
issue 里面报告了一个&lt;code&gt;Double-checked locking&lt;&#x2F;code&gt;的问题，对应的代码如下&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;java&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-java &quot;&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Class A &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;private final &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Map&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;B&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; configMap;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;A &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.configMap &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;new &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;HashMap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;B &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;getB(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt; name) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;!&lt;&#x2F;span&gt;&lt;span&gt;configMap.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;contains(name)&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;synchronized &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;!&lt;&#x2F;span&gt;&lt;span&gt;configMap.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;contains(name)&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;                    configMap.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;put(name, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;new &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;B&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;())&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;                }
&lt;&#x2F;span&gt;&lt;span&gt;            }
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;return&lt;&#x2F;span&gt;&lt;span&gt; configMap.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;get(name)&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;为了保证在多线程环境下相同的 name，不生成多个 B 的实例，这里使用了 Java 程序员经常会使用的双重检测同步代码块来保证对相同的 name，对应的 B 实例只会初始化一次，并且不用去锁住整个方法。&lt;&#x2F;p&gt;
&lt;p&gt;但是为什么这里 Fortify 会报这个错误呢？&lt;&#x2F;p&gt;
&lt;p&gt;去查了一下 Fortify 官方对这个的说明，简单说就是这是一个错误的用法，并不会达到想要的效果，参考引用的 David Bacon 等人的 &lt;a href=&quot;http:&#x2F;&#x2F;www.cs.umd.edu&#x2F;~pugh&#x2F;java&#x2F;memoryModel&#x2F;DoubleCheckedLocking.html&quot;&gt;文章&lt;&#x2F;a&gt; 我们来看看&lt;&#x2F;p&gt;
&lt;p&gt;这样的代码在编译器有优化或者多处理器共享内存的情况下，并不能达到期望的结果&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;java&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-java &quot;&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F; Broken multithreaded version
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F; &amp;quot;Double-Checked Locking&amp;quot; idiom
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Foo &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;private &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Helper &lt;&#x2F;span&gt;&lt;span&gt;helper &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;null&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Helper getHelper&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(helper &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;null&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;synchronized&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(helper &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;null&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;                helper &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;new &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Helper&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;            }
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;return&lt;&#x2F;span&gt;&lt;span&gt; helper;
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F; other functions and members...
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;第一个原因就是&lt;code&gt;new Helper()&lt;&#x2F;code&gt;这个操作和把这个对象赋值给&lt;code&gt;helper&lt;&#x2F;code&gt;变量这个操作并不会按照顺序执行，很有可能&lt;code&gt;new Helper()&lt;&#x2F;code&gt;会在将&lt;code&gt;helper&lt;&#x2F;code&gt;这个变量指向分配的内存之后，比如线程 A 调用&lt;code&gt;getHelper()&lt;&#x2F;code&gt;方法之后，由于指令重排，导致&lt;code&gt;helper&lt;&#x2F;code&gt;变量已经指向了一块分配出来的内存，这个时候它并不是&lt;code&gt;null&lt;&#x2F;code&gt;，但是&lt;code&gt;new Helper()&lt;&#x2F;code&gt;这个还没有执行，这个时候如果线程 B 也执行了&lt;code&gt;getHelper()&lt;&#x2F;code&gt;方法，会导致 B 拿到一个没完全初始化的&lt;code&gt;helper&lt;&#x2F;code&gt;，可能是个默认值，也有可能指向一个错误的内存地址，导致安全问题或者程序 crash。&lt;&#x2F;p&gt;
&lt;p&gt;如果编译器没有进行指令重排，在多核 CPU 平台上 CPU 或者内存系统也可能进行指令重排，也会导致这个问题。&lt;&#x2F;p&gt;
&lt;p&gt;文中给出了一个例子&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;to the following (note that the Symantec JIT using a handle-based object allocation system&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;0206106A   mov         eax,0F97E78h
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;0206106F   call        01F6B210                  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;allocate space for
&lt;&#x2F;span&gt;&lt;span&gt;                                                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;Singleton, return result in eax
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;02061074   mov         dword ptr &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;ebp&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;,eax       &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;EBP is &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;singletons[i].reference
&lt;&#x2F;span&gt;&lt;span&gt;                                                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;store the unconstructed object here.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;02061077   mov         ecx,dword ptr &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;eax&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;]       &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;dereference the handle to
&lt;&#x2F;span&gt;&lt;span&gt;                                                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;get the raw pointer
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;02061079   mov         dword ptr &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;ecx&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;,100h      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;Next 4 lines are
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;0206107F   mov         dword ptr &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;ecx+4&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;,200h    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;Singleton&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;s inlined constructor
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;    02061086   mov         dword ptr [ecx+8],400h
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;    0206108D   mov         dword ptr [ecx+0Ch],0F84030h
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;可以看出会先分配内存，然后赋值给变量，这个时候变量指向的就是一个未初始化的对象，然后才会执行构造函数初始化这个对象。&lt;&#x2F;p&gt;
&lt;p&gt;在 Java5 之后，可以使用&lt;code&gt;volatile&lt;&#x2F;code&gt;原语，声明&lt;code&gt;helper&lt;&#x2F;code&gt;这个 field 为&lt;code&gt;volatile&lt;&#x2F;code&gt;，这样对于这个 field 的写操作与它之前的任何读写操作不会进行指令重排。对于读操作也不会与之后的任何读写操作指令重排。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;java&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-java &quot;&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Foo &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;private volatile &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Helper &lt;&#x2F;span&gt;&lt;span&gt;helper;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Helper getHelper&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(helper &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;null&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;synchronized&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(helper &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;null&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;            helper &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;new &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Helper&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;return&lt;&#x2F;span&gt;&lt;span&gt; helper;
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F; other functions and members...
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;再回到我们的代码，我们这里是一个 map，在构造函数里已经初始化了，在加锁的这个 block 里只是生成一个 B 的对象，然后 put 进这个 map 里，这里不会有像&lt;code&gt;helper&lt;&#x2F;code&gt;那样未初始化的问题。另外就是在 map 上加了 volatile，并不会解决类似这样的问题，因为 volatile 是作用给了 map 本身，map 本身在这里的引用并没有变化，我们只是在更新它内部的状态。&lt;&#x2F;p&gt;
&lt;p&gt;参考&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;http:&#x2F;&#x2F;www.cs.umd.edu&#x2F;~pugh&#x2F;java&#x2F;memoryModel&#x2F;DoubleCheckedLocking.html&lt;&#x2F;li&gt;
&lt;li&gt;http:&#x2F;&#x2F;gee.cs.oswego.edu&#x2F;dl&#x2F;cpj&#x2F;jmm.html&lt;&#x2F;li&gt;
&lt;li&gt;http:&#x2F;&#x2F;www.cs.umd.edu&#x2F;~pugh&#x2F;java&#x2F;memoryModel&#x2F;&lt;&#x2F;li&gt;
&lt;li&gt;http:&#x2F;&#x2F;jeremymanson.blogspot.com&#x2F;2008&#x2F;05&#x2F;double-checked-locking.html&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>疫情封城更新</title>
        <published>2020-04-22T00:00:00+00:00</published>
        <updated>2020-04-22T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://liangliu.me/melbourne-update/"/>
        <id>https://liangliu.me/melbourne-update/</id>
        
        <content type="html" xml:base="https://liangliu.me/melbourne-update/">&lt;p&gt;语言学校的老师昨天早上打电话来了，简单问了一下孩子在家的情况，晚上几点睡觉啊，早上几点起啊，几点开始学习，学校寄过来的学习材料怎么样，孩子学起来困难还是容易，班主任老师还特意问可以跟Mark说话吗，媳妇只能尴尬的说Mark还没有起床...&lt;&#x2F;p&gt;
&lt;p&gt;这是墨尔本封城的第5周了，学校在第一学期假期结束后，从4.15号开始第二学期也开学了，不过都是在家上课，小学都是在线上课，刘沐阳现在还在语言学校，他们还没有开始在线上课，学校寄了一些教材，让在家学习。&lt;&#x2F;p&gt;
&lt;p&gt;我基本上从3月20号开始就在家工作了。这四周来，墨尔本经历了从零星的感染人数，到增长很多，然后封城，维多利亚州长强制大家必须遵守社交距离，禁止2人以上的聚会，不在清单列表里的事项一律不得出门，清单其中包括购买生活必需品，无法在家工作的要出去工作，照顾病人，适当锻炼等几项，但出行必须遵守社交距离，人与人至少1.5m，有警察专门巡逻，有一些人没有遵守的都收到了罚单，$1600还是很疼的。&lt;&#x2F;p&gt;
&lt;p&gt;我基本上每个周末出去采购一周的生活用品，车上备着免洗酒精消毒液，去Costco或者Aldi以及亚洲超市买蛋奶，面包，菜，水果等，买完上车先手消毒，然后再开回来，回来东西先放屋外面凉半天再搬进屋里。超市从最开始的抢购面粉，大米，到一卷卫生纸都没有，然后限购，现在也慢慢恢复了，一切供应都正常，货架也都满满的了，无需担心买不到东西。最开始有些超市限制人数，会有排队现象，不过现在已经好很多了。&lt;&#x2F;p&gt;
&lt;p&gt;现在整个澳洲的总共感染数量大概是6648例，治愈4291例，死亡76例，仍有2286例患者。维多利亚州大概是总共感染数量1336例，治愈1172例，死亡15例，仍有156例，每天增加的新病例大概在个位数或者十几个的样子，可以看到曲线已经非常平了，总体已经趋于稳定状态了，据说在5月11号有可能解除三级响应，逐步恢复。&lt;&#x2F;p&gt;
&lt;p&gt;整个澳洲经济受影响还是相当大的，整个澳洲失业率可能会达到11%，维州GDP下降14%，澳洲第二大航空公司维珍航空宣布由于疫情影响破产，其他中小企业都非常不好过，缩紧支出，停止项目，停止涨薪，降薪，或者裁员，我们咨询公司受影响非常大。政府已经开始发放救助金给失业者，同时也有很多举措来帮助企业渡过难关，公司管理层也每周都发邮件更新最新消息，希望这种局势早点缓解，总体来看形势会慢慢变好，但是整体不容乐观。&lt;&#x2F;p&gt;
&lt;p&gt;目前每天状态就是在家待着，我白天大部分时间在屋里工作，间歇出去跳个绳活动一下。阳阳和妈妈在客厅桌子上学习，完成语言学校作业，这周五就要寄作业回去给老师看了。下午有时候我们4点多出去绕着街道跑一圈，有时候在后院玩一会球，阳阳还要学汉字每天打卡，坚持4个多月了，现在已经学习了500多个字了，今天还看到自己的成就觉得自己很厉害呢，哈哈，现在从图书馆借的带拼音的中文书都能自己看了，每天晚上睡觉之前都自己读一会。还有就是每天打完卡后可以看乐高幻影忍者，他非常痴迷这个动画片，每天一集。&lt;&#x2F;p&gt;
&lt;p&gt;阳阳已经过了5岁生日，又长大了一岁，真是太快了！！！&lt;&#x2F;p&gt;
&lt;p&gt;最近妈妈是很辛苦，又要当老师(主要是当气包，辅导小朋友学习你懂的，气炸了都快)，又要做饭刷碗，点个赞!&lt;&#x2F;p&gt;
&lt;p&gt;鉴于昨天老师打电话来家访，阳阳还没起床，昨天晚上我们早一点上床睡觉了，今早上8点醒来，9点吃完饭接到了老师的电话，阳阳跟老师问好了，还读了一段练习册上的英文练习题，得到了老师的表扬。今晚睡的很早，7点半洗漱完，8点上床，8点40就熄灯了，我迷糊了一会睡不着，又起来看了个电影，写个流水帐日记做为一天的结束吧。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>使用AWS ECS部署应用并自动扩展</title>
        <published>2017-06-01T00:00:00+00:00</published>
        <updated>2017-06-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://liangliu.me/automatic-scaling-with-amazon-ecs/"/>
        <id>https://liangliu.me/automatic-scaling-with-amazon-ecs/</id>
        
        <content type="html" xml:base="https://liangliu.me/automatic-scaling-with-amazon-ecs/">&lt;p&gt;最近在写一些nodejs的东西，尝试用docker去部署，研究了一下如何用AWS ECS做部署和自动扩展，跟传统的使用EC2来做部署使用ASG做扩展还是有一些不同的。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;aws-ecsji-ben-gai-nian&quot;&gt;AWS ECS基本概念&lt;&#x2F;h2&gt;
&lt;p&gt;Amazon EC2 Container Service 是基于docker的具有高可扩展性，快速的容器管理服务，可以让你在EC2 instance集群中简单快速的部署和扩展应用程序。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;ws4.sinaimg.cn&#x2F;large&#x2F;006tKfTcly1fg78xsfn3jj315u0e60w0.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;ECS中有一些基本概念需要先了解一下&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;ECR - Elastic Container Registry 用来建立一个安全，可扩展，高可用的应用程序私有docker registry。&lt;&#x2F;li&gt;
&lt;li&gt;Cluster - 逻辑上的一组由EC2 instance组成的集群，你的container跑在这些集群上面。&lt;&#x2F;li&gt;
&lt;li&gt;Service - 在指定的cluster上来维护和启动一定数量的任务容器实例的服务。&lt;&#x2F;li&gt;
&lt;li&gt;Task Definition - 定义了由service维护的任务，比如image是哪个，CPU和Memory如何分配，端口号如何映射，容器跑起来时候执行哪些命令，有哪些环境变量设置等等。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;ru-he-shi-yong-ecsbu-shu&quot;&gt;如何使用ECS部署&lt;&#x2F;h2&gt;
&lt;p&gt;接下来会介绍如何基于Application Load Balancer和ECS来部署自己的application&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-da-bao-docker-imagebing-pushdao-ecr&quot;&gt;1. 打包docker image并push到ECR&lt;&#x2F;h3&gt;
&lt;p&gt;我们从Dockerfile来打包一个demo的image:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;dockerfile&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-dockerfile &quot;&gt;&lt;code class=&quot;language-dockerfile&quot; data-lang=&quot;dockerfile&quot;&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span&gt; nodejs:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;7.6.0
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;COPY&lt;&#x2F;span&gt;&lt;span&gt; . &#x2F;source
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;COPY&lt;&#x2F;span&gt;&lt;span&gt; .&#x2F;env&#x2F;production.env &#x2F;source&#x2F;.env
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;WORKDIR &lt;&#x2F;span&gt;&lt;span&gt;&#x2F;source
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;RUN &lt;&#x2F;span&gt;&lt;span&gt;yarn install --production
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;EXPOSE &lt;&#x2F;span&gt;&lt;span&gt;3000
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;CMD &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;yarn&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;start&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;执行 &lt;code&gt;docker build -t 12345678.dkr.ecr.ap-northeast-1.amazonaws.com&#x2F;demo:0.0.1 .&lt;&#x2F;code&gt;  来打包image&lt;&#x2F;p&gt;
&lt;p&gt;成功后push到ECR中&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;$ aws ecr get-login --region ap-southeast-1
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;$ docker push 12345678.dkr.ecr.ap-northeast-1.amazonaws.com&#x2F;demo:0.0.1
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;可以自己写个shell脚本把这些都自动化起来&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-shi-yong-cloudformationjian-li-yi-ge-application-load-balancer&quot;&gt;2. 使用CloudFormation建立一个Application Load Balancer&lt;&#x2F;h3&gt;
&lt;p&gt;可以参考AWS CF模板中ALB部分来写，主要是有AWS::ElasticLoadBalancingV2::LoadBalancer， AWS::ElasticLoadBalancingV2::Listener，AWS::ElasticLoadBalancingV2::TargetGroup这些资源，参考这个&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;liul85&#x2F;Autoscale-with-AWS--ECS&#x2F;blob&#x2F;master&#x2F;alb.yml&quot;&gt;demo模板&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;3-li-yong-cloudformation-lai-jian-li-yi-ge-cluster&quot;&gt;3. 利用CloudFormation 来建立一个cluster&lt;&#x2F;h3&gt;
&lt;p&gt;Cluster就是一组运行的EC2 instance，它需要在autoscaling组里面来管理保证cluster是可以随时扩展，所有这部分模板中应该保证有LaunchConfiguration， Cluster，Role，SecurityGroup参考这个&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;liul85&#x2F;Autoscale-with-AWS--ECS&#x2F;blob&#x2F;master&#x2F;cluster.yml&quot;&gt;demo的模板&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;4-jian-li-yi-ge-ecs-service&quot;&gt;4. 建立一个ECS service&lt;&#x2F;h3&gt;
&lt;p&gt;有了cluster之后，我们就可以利用service把容器任务运行起来了，这里我们再建一个CF模板来创建service的资源，&lt;&#x2F;p&gt;
&lt;p&gt;要想用service来管理容器任务，首先定义一个task，在TD里面我们定义了一个container，它有name，cpu，memoery等一些基本属性，注意这里的PortMappings，我们把HostPort设置为0(或者也可以不设置)，这里是为了使用动态端口映射，这样当这个container运行起来之后，ECS会给它随机分配一个主机端口，目前范围是从32768~61000，这样我们就可以在一个instance上同时跑多个container了，具体可以参考&lt;a href=&quot;http:&#x2F;&#x2F;docs.aws.amazon.com&#x2F;AmazonECS&#x2F;latest&#x2F;developerguide&#x2F;task_definition_parameters.html&quot;&gt;这里&lt;&#x2F;a&gt;的portMappings中对hostPort的说明.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;TaskDefinition&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Type&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;AWS::ECS::TaskDefinition
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Properties&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;ContainerDefinitions&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        - &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;!Join &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;-&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;!Ref &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;ServiceName&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;!Ref &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;BuildNumber&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]]
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Cpu&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;512
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Essential&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;true&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Memory&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;512
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;PortMappings&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;            - &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;HostPort&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;0
&lt;&#x2F;span&gt;&lt;span&gt;              &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Protocol&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;tcp
&lt;&#x2F;span&gt;&lt;span&gt;              &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;ContainerPort&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;!Ref &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;ContainerPort&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Image&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;!Join &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;:&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;!Join &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;&#x2F;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;!Ref &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;ECR&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;!Ref &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;ServiceName&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]], &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;!Ref &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;BuildNumber&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;然后我们就可以创建一个service来使用这个task definition，这个service会关联到之前创建的LoadBalancer中，我们使用的是Application Load Balancer，需要在这里指定它关联的TargetGroupArn，这样当容器任务跑起来之后，service会把它注册到这个targetGroup下面。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;ECSService&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Type&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;AWS::ECS::Service
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Properties&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Cluster&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Ref&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;ECSCluster
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;DesiredCount&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Ref&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;DesiredCount
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;LoadBalancers&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        - &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;ContainerName&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;!Join &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;-&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;!Ref &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;ServiceName&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;!Ref &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;BuildNumber&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]]
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;ContainerPort&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;3000
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;TargetGroupArn&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Ref&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;TargetGroup
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;DeploymentConfiguration&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;MaximumPercent&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;200&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;MinimumHealthyPercent&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;50&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;TaskDefinition&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Ref&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;TaskDefinition
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Role&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Ref&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;ECSRole
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;当这三个CF创建的资源全都成功后，你的application就已经在容器中运行了，你可以通过ALB的地址来访问它。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;5-bu-shu-xin-ban-ben&quot;&gt;5. 部署新版本&lt;&#x2F;h3&gt;
&lt;p&gt;当有新的提交之后，我们会有新的版本，就会打包新的image并push到ECR中，这个时候只需要把service中的BuildNumber修改为新版本，然后update这个Stack，service会自动创建新的TaskDefinition并运行起来，然后把老的Task从targetGroup中去掉，这里会有一个draining时间，默认5分钟，&lt;code&gt;MinimumHealthyPercent&lt;&#x2F;code&gt;  参数指定了在部署过程中原来的版本最小百分比，保证在部署过程中业务不会中断，这样就完成了新旧版本的替换。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zi-dong-kuo-zhan&quot;&gt;自动扩展&lt;&#x2F;h2&gt;
&lt;p&gt;上面我们的application已经可以运行在ECS服务中了，但是还没有任何自动扩展功能，当遇到很大的访问量时候就会有问题了，我们需要添加自动扩展，在ECS中自动扩展包括两部分&lt;&#x2F;p&gt;
&lt;h4 id=&quot;cluster-kuo-zhan&quot;&gt;Cluster 扩展&lt;&#x2F;h4&gt;
&lt;p&gt;Cluster扩展主要是通过EC2的autoScalingGroup来完成的，我们定义了ASG，scale up和scale down的policy，以及触发这些policy的alarm就可以完成了。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;service-kuo-zhan&quot;&gt;Service 扩展&lt;&#x2F;h4&gt;
&lt;p&gt;service的扩展我们需要建立ServiceScalingTarget，它指定了最小和最大的capacity，以及哪个service扩展&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;ServiceScalingTarget&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Type&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;AWS::ApplicationAutoScaling::ScalableTarget
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;DependsOn&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;ECSService
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Properties&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;MaxCapacity&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;!Ref &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;ServiceMaxASG&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;MinCapacity&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;!Ref &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;ServiceMinASG&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;ResourceId&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;!Join &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;service&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;!Ref &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;ECSCluster&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;!GetAtt &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;ECSService&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;Name&lt;&#x2F;span&gt;&lt;span&gt;]]]
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;RoleARN&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;!GetAtt &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;ECSAutoscalingRole&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;Arn&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;ScalableDimension&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;ecs:service:DesiredCount
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;ServiceNamespace&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;ecs
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;然后需要创建scale up policy，与serviceScaleTarget关联起来，当需要scale up的时候，会触发这个policy，我们配置的AdjustmentType是&lt;code&gt;ChangeInCapacity&lt;&#x2F;code&gt;同时stepAdjustments里面配置了ScalingAdjustment是1，即policy执行时候会增加一个容器任务做到横向service扩展，详细参考&lt;a href=&quot;http:&#x2F;&#x2F;docs.aws.amazon.com&#x2F;AWSCloudFormation&#x2F;latest&#x2F;UserGuide&#x2F;aws-properties-applicationautoscaling-scalingpolicy-stepscalingpolicyconfiguration.html#cfn-applicationautoscaling-scalingpolicy-stepscalingpolicyconfiguration-stepadjustments&quot;&gt;这里&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;Yaml&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-Yaml &quot;&gt;&lt;code class=&quot;language-Yaml&quot; data-lang=&quot;Yaml&quot;&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;ServiceScalingUpPolicy&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Type&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;AWS::ApplicationAutoScaling::ScalingPolicy
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Properties&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;PolicyName&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;ScaleUpPolicy
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;PolicyType&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;StepScaling
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;ScalingTargetId&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;!Ref &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;ServiceScalingTarget&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;StepScalingPolicyConfiguration&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;AdjustmentType&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;ChangeInCapacity
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Cooldown&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;300
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;MetricAggregationType&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;Average
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;StepAdjustments&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        - &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;MetricIntervalLowerBound&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;0
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;ScalingAdjustment&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;需要一个alarm来触发这个sale up policy，通过cloudWatch中AWS&#x2F;ECS 命名空间下面的以ServiceName为维度的指标来触发，当CPU占用率在5分钟内大于60%即上报告警，触发scale up操作。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;ECSCPUHighAlarm&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Type&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;AWS::CloudWatch::Alarm
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Properties&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;EvaluationPeriods&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;5
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Statistic&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;Average
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Threshold&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;60
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;AlarmDescription&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;Alarm if CPU utilization if great than 60
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Period&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;60
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;AlarmActions&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        - &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Ref&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;ServiceScalingUpPolicy
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Namespace&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;AWS&#x2F;ECS
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Dimensions&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;ServiceName
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Value&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;Ref&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;ECSService
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;ComparisonOperator&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;GreaterThanThreshold
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;MetricName&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;CPUUtilization
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;以上即是在AWS上使用ECS部署应用程序，并配置自动扩展策略的最简单实践。上面3个CloudFormation的模板可以参考&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;liul85&#x2F;Autoscale-with-AWS--ECS&quot;&gt;这里&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;参考：&lt;&#x2F;p&gt;
&lt;p&gt;https:&#x2F;&#x2F;aws.amazon.com&#x2F;blogs&#x2F;compute&#x2F;automatic-scaling-with-amazon-ecs&#x2F;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>函数式编程笔记(2)-Hindley-Milner类型签名</title>
        <published>2017-04-11T00:00:00+00:00</published>
        <updated>2017-04-11T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://liangliu.me/functional-programming-2/"/>
        <id>https://liangliu.me/functional-programming-2/</id>
        
        <content type="html" xml:base="https://liangliu.me/functional-programming-2/">&lt;p&gt;刚接触函数式编程的人总是搞不明白函数类型签名，我记得去年同事有分享过一次FP的session，用Hashkell来做题，其中的类型签名当时就不太搞明白。类型是让不同背景的人高效沟通的元语言，大部分都是以一种叫&lt;code&gt;Hindley Milner&lt;&#x2F;code&gt;系统来表述的。&lt;&#x2F;p&gt;
&lt;p&gt;类型签名的主要作用:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;标明函数输入和输出&lt;&#x2F;li&gt;
&lt;li&gt;让函数保持通用，抽象&lt;&#x2F;li&gt;
&lt;li&gt;可以用于编译时候检查&lt;&#x2F;li&gt;
&lt;li&gt;是代码最好的文档&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h1 id=&quot;hindley-milner-story&quot;&gt;Hindley-Milner story&lt;&#x2F;h1&gt;
&lt;p&gt;其实Hindley Miler并不是一个很复杂的系统，不过我们需要一些学习和练习过程来熟悉和掌握它。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F;  capitalize :: String -&amp;gt; String
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;capitalize &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;s &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;toUpperCase&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;head&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;s&lt;&#x2F;span&gt;&lt;span&gt;)) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;toLowerCase&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;tail&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;s&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F;=&amp;gt; &amp;quot;Smurf&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;capitalize&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;smurf&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这里定义了一个函数capitalize，它接收一个string，然后返回一个string，在HM系统中，函数都写成a -&amp;gt; b这样，所以这个函数的签名&lt;code&gt;capitalize :: String -&amp;gt; String&lt;&#x2F;code&gt;可以理解为一个接受string并返回string的函数。&lt;&#x2F;p&gt;
&lt;p&gt;再来一些例子：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F;  len :: String -&amp;gt; Number
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;len &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;s&lt;&#x2F;span&gt;&lt;span&gt;){
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;s&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;length&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F;  join :: String -&amp;gt; [String] -&amp;gt; String
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;join &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;curry&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;delimiter&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;){
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;data.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;join&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;delimiter&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F;  match :: Regex -&amp;gt; String -&amp;gt; [String]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;match &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;curry&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;reg&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;s&lt;&#x2F;span&gt;&lt;span&gt;){
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;s.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;reg&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F;  replace :: Regex -&amp;gt; String -&amp;gt; String -&amp;gt; String
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;replace &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;curry&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;reg&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;sub&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;s&lt;&#x2F;span&gt;&lt;span&gt;){
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;s.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;replace&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;reg&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;sub&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;对于稍微复杂的函数类型签名，我们开始可以这么理解，把最后一个类型看作是它的返回值，其他的都看作是参数，那么join就是接受一个string和array，然后返回一个string，match就是接受一个正则和string，返回一个array，replace就是接受一个正则，二个string，返回一个string，这样看起来就比较简单啦。&lt;&#x2F;p&gt;
&lt;p&gt;不过这只是帮助我们理解函数签名的开始，下面我们再看一个有意思的理解：&lt;&#x2F;p&gt;
&lt;p&gt;对于join，我们把它的签名分组一下，&lt;code&gt;join :: String -&amp;gt; ([String] -&amp;gt; String)&lt;&#x2F;code&gt;，join接受一个string参数，返回一个接受array，返回string的函数，这样会更容易帮助我们深入理解柯里化的作用。&lt;&#x2F;p&gt;
&lt;p&gt;对于replace，分组一下类型签名， &lt;code&gt;replace :: Regex -&amp;gt; (String -&amp;gt; (String -&amp;gt; String))&lt;&#x2F;code&gt;, 它接受一个正则，返回一个接受一个string，再返回一个接受string，返回string的新函数，这个新函数调用后，又返回一个接受一个string，最后返回一个string的函数，最后一个函数调用后返回最终结果。&lt;&#x2F;p&gt;
&lt;p&gt;因为柯里化就是这样，如果一个函数接受多个参数，那么当它被柯里化之后，如果你调用时候只传入一个参数，那么它将返回一个新的接受剩余参数的柯里化函数，这样从函数签名角度来看，当你每传入一个参数，就弹出类型签名最前面的那个类型，这样就很容易理解了。&lt;&#x2F;p&gt;
&lt;p&gt;当然你也可以一次性传入全部参数，那么将返回最终结果。&lt;&#x2F;p&gt;
&lt;p&gt;在HM系统中，一般约定俗成用a, b类似这样的来代表类型，a引用代表的一定是同一个类型，a和b一定是不同的类型，比如&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;len :: a -&amp;gt; a&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;join :: a -&amp;gt; [a] -&amp;gt; a&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;match :: a -&amp;gt; b -&amp;gt; [b]&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;replace :: a -&amp;gt; b -&amp;gt; b -&amp;gt; b&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;类型签名能够一字一句地告诉我们函数做了什么事情，读懂类型签名是在函数式编程过程中一项非常重要的技能，会让你受益无穷。&lt;&#x2F;p&gt;
&lt;p&gt;再来两个练习试试&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F;  filter :: (a -&amp;gt; Bool) -&amp;gt; [a] -&amp;gt; [a]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;filter &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;curry&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;xs&lt;&#x2F;span&gt;&lt;span&gt;){
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;xs.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;filter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F;  reduce :: (b -&amp;gt; a -&amp;gt; b) -&amp;gt; b -&amp;gt; [a] -&amp;gt; b
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;reduce &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;curry&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;x&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;xs&lt;&#x2F;span&gt;&lt;span&gt;){
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;xs.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;reduce&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;x&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h1 id=&quot;parametricity&quot;&gt;Parametricity&lt;&#x2F;h1&gt;
&lt;p&gt;在上面看到的类型签名中，有各种不同的类型，这就会引入一个程序语言特性叫&lt;a href=&quot;http:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Parametricity&quot;&gt;parametricity&lt;&#x2F;a&gt;。这个特性表明函数将会以一种统一的行为作用于所有的类型。&lt;&#x2F;p&gt;
&lt;p&gt;看一个这样的类型签名： &lt;code&gt;head :: [a] -&amp;gt; a&lt;&#x2F;code&gt;， 从类型签名上我们可以得到这个函数它接受一个元素是类型a的数组，返回一个类型a的元素。类型a可以是任意类型，比如Number，String，Boolean等，那么这个函数对不同的任意类型的作用都是保持统一的，这就是parametricity的含义。这个函数可能返回数组的第一个元素，可能是最后一个，也可能是随机的一个，这时候函数的名字会有更多的信息给我们，不管是哪种情况，在这里类型a的多态性会大幅度缩小函数可能性的范围，保证在多态性的情况下，函数的作用都是统一的。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;free-theorems&quot;&gt;Free theorems&lt;&#x2F;h1&gt;
&lt;p&gt;看个简单例子&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F; head :: [a] -&amp;gt; a
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;compose&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;head&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;compose&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;head&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在这个例子中，左边的操作是，先对数组a进行head操作，得到一个a，然后再对它调用f函数得到结果。右边的操作是，先对数组a所有元素都调用f函数得到一个新数组，然后再对新数组做head操作得到结果。这两个操作最终结果是相等的，只不过左边的效率要高一些。&lt;&#x2F;p&gt;
&lt;p&gt;这种被称为自由定理，可以应用到多态类型的签名上。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;type-constrains&quot;&gt;Type constrains&lt;&#x2F;h1&gt;
&lt;p&gt;类型签名可以把类型限制在一个特定的接口上，&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;reverse &lt;&#x2F;span&gt;&lt;span&gt;:: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;Ord a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;-&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在这里&lt;code&gt;Ord a&lt;&#x2F;code&gt;约束了a必须是一个Ord对象，或者a必须实现Ord接口，这样就限制了函数的作用范围，这样的声明被称作类型约束。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;can-kao&quot;&gt;参考&lt;&#x2F;h4&gt;
&lt;p&gt;https:&#x2F;&#x2F;drboolean.gitbooks.io&#x2F;mostly-adequate-guide&#x2F;content&#x2F;ch7.html&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>函数式编程笔记(1)-基本概念</title>
        <published>2017-04-05T00:00:00+00:00</published>
        <updated>2017-04-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://liangliu.me/functional-programming/"/>
        <id>https://liangliu.me/functional-programming/</id>
        
        <content type="html" xml:base="https://liangliu.me/functional-programming/">&lt;h2 id=&quot;yi-deng-gong-min&quot;&gt;一等公民&lt;&#x2F;h2&gt;
&lt;p&gt;不要把函数理解为特殊的一个类型，他们跟普通数据类型一样，可以被存在数组里，当作参数传递，赋值给变量等等。
把函数当作普通数据类型这样的代码会更容易理解和维护，同时需要正确的为函数命名。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;chun-han-shu&quot;&gt;纯函数&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;纯函数是指对于一个函数，如果有相同的输入，那么输出永远都是相同的，并且没有任何副作用。&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;比如一个map函数&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;double &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;x&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;x.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;n &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;n &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;如果输入一个数组a，就会得到一个double后的新数组，只要a是相同的，那么结果永远都是相同的，并且原来的数组a不会被修改。&lt;&#x2F;p&gt;
&lt;p&gt;在函数式编程中，我们希望的都是这样的纯函数，他们在调用中是:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;可靠的；&lt;&#x2F;li&gt;
&lt;li&gt;适合作cache；&lt;&#x2F;li&gt;
&lt;li&gt;非常容易去测试；&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;调用纯函数会很大程度减少bug的产生几率和你为了找到bug而花费的debug时间。&lt;&#x2F;p&gt;
&lt;p&gt;当被调用的时候，纯函数一定可以被它的计算结果所替换掉，并且不会影响程序的运行结果，这被称为&lt;code&gt;透明引用&lt;&#x2F;code&gt; (&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Referential_transparency&quot;&gt;referential transparency&lt;&#x2F;a&gt;)。&lt;&#x2F;p&gt;
&lt;p&gt;纯函数只依赖于它被调用时候的输入。&lt;&#x2F;p&gt;
&lt;p&gt;来看看几个不属于纯函数的例子：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F;splice change the value of parameter
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;arr &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;arr.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;splice&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F;the result of add depends variable out of it&amp;#39;s scope.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;baseNum &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;8&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;add &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;x&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;x &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;baseNum&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F; impure function producing a side effect
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;showAlert&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;alert&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;This is a side effect!&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;alert triggerd.&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F; impure function calling pure functions procedurally
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;proceduralFn&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;result1 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;pureFnFirst&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;result2 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;pureFnLast&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;`Done with ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;result1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;} and ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;result2&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;}!`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F; impure function that resembles a pure function,
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F; but returns different results given the same inputs
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;getRandomRange&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;min&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;max&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;Math&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;random&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;max &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;min&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;min&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;总结一下纯函数的一些特征：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;纯函数必定要接收参数。&lt;&#x2F;li&gt;
&lt;li&gt;相同的输入参数肯定会得到相同的结果。&lt;&#x2F;li&gt;
&lt;li&gt;纯函数只依赖于自己的scope，并且不会对外部scope的状态做出改变。&lt;&#x2F;li&gt;
&lt;li&gt;纯函数不会有任何副作用。&lt;&#x2F;li&gt;
&lt;li&gt;纯函数不会调用任何非纯函数。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;非纯函数的一些特征：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;会修改它自己scope之外的状态。&lt;&#x2F;li&gt;
&lt;li&gt;没有返回值。&lt;&#x2F;li&gt;
&lt;li&gt;对于相同的输入，输出结果不确定。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;guan-yu-fu-zuo-yong-side-effects&quot;&gt;关于副作用(side effects)&lt;&#x2F;h4&gt;
&lt;p&gt;副作用的关键部分在于“副”，大部分bug都是从这里出来的。对于副作用(side effects)在javascript里有下面一些定义:&lt;&#x2F;p&gt;
&lt;p&gt;如果有函数或者表达式修改了它自己的scope之外的状态，那么这个结果就叫做副作用。一些常见例子有：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;调用API&lt;&#x2F;li&gt;
&lt;li&gt;更改文件系统&lt;&#x2F;li&gt;
&lt;li&gt;操作DOM&lt;&#x2F;li&gt;
&lt;li&gt;弹出alert，或者调用console打印log&lt;&#x2F;li&gt;
&lt;li&gt;写数据库等。&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h1 id=&quot;ke-li-hua-curry&quot;&gt;柯里化(curry)&lt;&#x2F;h1&gt;
&lt;p&gt;在了解了纯函数的概念后，我们肯定都期望后面运用函数式编程写出来的都是纯函数，我们需要一些工具来辅助我们做这些任务，柯里化(curry)就是一个非常重要的工具。&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;柯里化后的函数可以接收一部分参数，返回一个新的函数来接收剩余的参数. 我们可以一次性传所有参数来得到计算结果，也可以顺序传递多次参数，分多次调用得最终到结果。&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;举个例子：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;add &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;R.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;curry&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;x&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;y&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;x &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;y&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F;get 3
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F;get a new function
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;f1 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F;call the new function, get the result 3
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;f1&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;我们定义了一个柯里化后的加法函数，可以直接传递2个参数给这个函数它会立即返回结果。也可以只传递一个参数，它会返回一个新的函数，这个函数接收一个参数，再次调用这个函数会返回最终结果。&lt;&#x2F;p&gt;
&lt;p&gt;再看一个例子：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;formatName &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;R.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;curry&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;firstName&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;middleName&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;lastName&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;`${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;firstName&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;} ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;middleName&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;} ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;lastName&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;}`&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;formatDavid &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;formatName&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;David&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;M&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F;get &amp;#39;David M Xie&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;formatDavid&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;Xie&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F;get &amp;#39;David M Dou&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;formatDavid&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;Dou&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;formatJames &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;formatName&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;James&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F;get &amp;#39;James L Jones&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;formatJames&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;L&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;Jones&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F;get &amp;#39;James R Paul&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;formatJames&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;R&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;Paul&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;会发现代码的reuse非常方便。而且我们特意把变化的数据，放在最后，函数的逻辑跟数据完全没有关系，让reuse会更方便，一会就能看到这样的好处。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;shi-ji-ying-yong&quot;&gt;实际应用&lt;&#x2F;h4&gt;
&lt;p&gt;最近在写一个api通过solr来作搜索查询，需要从用户的requestModel中拿到请求的条件，然后转成solr的query去solr中作search，其中用了不少柯里化，使整个代码看起来清晰不少。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;exports &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;query &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;solr.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;createClient&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;R.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;compose&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;buildRows&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;buildStart&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;buildSearchRange&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;price&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;minPrice&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;maxPrice&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;buildSearchRange&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;area&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;minArea&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;maxArea&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;buildSearchRooms&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;bedroom&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;bedroom&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;buildCustomText&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;buildSortBy&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  )(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;buildRows &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;R.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;curry&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;rows&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;query.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;rows&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;rows&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;query.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;rows&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;query
&lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;buildStart &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;R.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;curry&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;offset&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;query.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;start&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;offset&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;query
&lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;buildCustomText &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;R.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;curry&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;keyword&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;query.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;q&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;keyword&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;df&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;keywords&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;query
&lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;buildSearchRange &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;R.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;curry&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;fqField&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;min&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;max&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;min &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;&amp;amp;&amp;amp; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;max&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;query.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;matchFilter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;fqField&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;`[${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;min&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;} TO ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;max&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;}]`&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;query
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;min&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;query.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;matchFilter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;fqField&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;`[${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;min&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;} TO *]`&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;max&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;query.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;matchFilter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;fqField&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;`[* TO ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;max&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;}]`&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;query
&lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;buildSearchRooms &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;R.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;curry&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;fqField&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;filterValue&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;filterValue&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;query.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;matchFilter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;fqField&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;filterValue&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;query
&lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;buildSortBy &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;query.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;sort&lt;&#x2F;span&gt;&lt;span&gt;({feature: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;desc&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, dorder: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;desc&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;query
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在上面的代码中，主要逻辑是要根据requestModel中的不同field来构造不同的solr query条件，通过几个不同的函数来处理自己的逻辑，
最后调用compose把她们组合起来得到最终的结果, 这几个函数都被柯里化了，在组合的时候只传给他们一部分参数，返回一个新的function，
然后在组合调用最外面把最后一个参数传递进去，经过这一串的函数处理后，便得到了最终计算结果。
其实compose中就是一个pipeline，我更倾向于使用pipe操作。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;zu-he-compose&quot;&gt;组合 (compose)&lt;&#x2F;h1&gt;
&lt;p&gt;组合简单来看就是把多个函数通过组合让他们生成一个新的函数，然后传入数据进行处理得到最后结果。
简单的例子&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;compose &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;g&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;x&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;g&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;x&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这个例子中，f,g都是函数，x是要处理的数据。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F;get &amp;#39;T&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;compose&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;upperCase&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;getFirst&lt;&#x2F;span&gt;&lt;span&gt;)(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;test&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;函数g会先于f执行，因此从调用角度来看，数据&#x27;test&#x27;在一个函数处理pipeline中先进入&lt;code&gt;getFirst&lt;&#x2F;code&gt;, 处理后的结果再进入&lt;code&gt;upperCase&lt;&#x2F;code&gt;, 最后结果返回。
在compose中数据是从右向左的顺序来在函数中流动。&lt;&#x2F;p&gt;
&lt;p&gt;compose的顺序反过来就是pipeline，喜欢用pipe(f1, f2, f3, f4)(data)的方式来处理数据。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;pointfree&quot;&gt;PointFree&lt;&#x2F;h1&gt;
&lt;blockquote&gt;
&lt;p&gt;Pointfree style means never having to say your data&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;永远不要说出你的数据除非你马上就要使用它，即函数式变成你无需提及你将要操作的数据是什么样子的，只有当你要处理这个数据时候它才需要出场。
我们上面的那个构建solr query例子已经很明显，可以再看一个小例子：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F; 非 pointfree，因为提到了数据：data
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;snakeCase &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;name.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;toLowerCase&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;replace&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;-&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;_&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F; pointfree
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;snakeCase &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;compose&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;replace&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;-&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;_&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;), &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;toLowerCase&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F;调用时候才需要数据
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;snakeCase&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;函数式编程和柯里化对于pointfree的实现帮助太大了，通过柯里化和组合或者管道你可以不用关心传进来的数据是什么，只需要关注处理它的规则即可，这也是 &lt;code&gt;Ramda&lt;&#x2F;code&gt;和&lt;code&gt;lodash&lt;&#x2F;code&gt;或者&lt;code&gt;underscore&lt;&#x2F;code&gt;的最大区别。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;can-kao&quot;&gt;参考&lt;&#x2F;h4&gt;
&lt;p&gt;https:&#x2F;&#x2F;drboolean.gitbooks.io&#x2F;mostly-adequate-guide&#x2F;content&#x2F;
https:&#x2F;&#x2F;auth0.com&#x2F;blog&#x2F;glossary-of-modern-javascript-concepts&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Understanding rake task</title>
        <published>2014-05-26T00:00:00+00:00</published>
        <updated>2014-05-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://liangliu.me/understanding-rake-task/"/>
        <id>https://liangliu.me/understanding-rake-task/</id>
        
        <content type="html" xml:base="https://liangliu.me/understanding-rake-task/">&lt;p&gt;在新项目中接触ruby的Rake任务很多，自己还是不太了解Rake，闲了找了一些资料学习一下，做一些笔记。
其实Rake就是ruby中的make，一个用ruby开发的构建工具。Rake真的是一个功能强大，又很贴心的工具，正如它的英文意思&lt;em&gt;耙子&lt;&#x2F;em&gt;--给力的劳动工具。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;rakede-you-dian&quot;&gt;Rake的优点&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;通过Rake我们可以以任务的方式创建和运行脚本。对于大型的应用项目，我们总是会编写很多脚本用来实现自动化，比如数据迁移，清空缓存，清理数据库等。Rake就是一个非常好的任务脚本管理工具，会让你的任务脚本管理非常轻松。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;同时Rake还轻松提供了任务执行之间的依赖关系管理，加入任务1依赖于任务2，那么在执行任务1的时候任务2会自动被调用执行，非常方便。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;an-zhuang-rake&quot;&gt;安装Rake&lt;&#x2F;h3&gt;
&lt;p&gt;非常简单, gem 安装搞定！&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;$ gem install rake
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;xue-xie-rakejiao-ben&quot;&gt;学写Rake脚本&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;简单rake任务&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;创建一个Rakefile，写一个简单的rake任务，从hello world开始吧。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;desc &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;say hello&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;task &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:hello &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;puts &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;hello, world!&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;然后在文件目录下执行 rake -T 就能看到这个任务, rake hello就可以执行rake任务了。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;➜  rake -T
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;rake hello  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;# say hello
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;➜  rake hello
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;hello world!
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;指定默认任务&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;在Rakefile中加入默认任务, 然后执行rake不指定任务名，就执行默认任务。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;task :default =&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt; :hello
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre data-lang=&quot;sh&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;➜  rake
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;hello world!
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;创建具有依赖关系的任务&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;一般我们在执行一些脚本时候，会有一些预制条件，比如跑测试时候，需要提前在数据库中插入一些测试数据。在rake中，可以把预制条件写为一个任务，其他任务依赖于这个时候，可以把它加到依赖关系中&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;desc &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;purchase vegetables&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;task &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:purchase_vegetables &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;puts &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;Buy some potatoes from Walmart!&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;desc &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;cook&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;task &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:cook &lt;&#x2F;span&gt;&lt;span&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:purchase_vegetables &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;puts &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;I am cooking...&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在这个例子中，做饭依赖于买菜，我们把给做饭加一个依赖任务， 这样在执行cook任务时候，purchase_vegetables 任务会被自动执行。&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;命名空间&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;如果在一个项目中有多个模块，由很多rake任务，那么我们可以用命名空间来防止rake任务名重复&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;namespace &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;main&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;	desc &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;build the main programe&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;	task &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:build &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;#build the main programe.
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;namespace &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;module1&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;	desc &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;build the module1&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;	task &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:build &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;#build the module1.
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这样即使同名的任务在一个Rakefile里，由于它们属于不同的namespace，任务之间胡不相干。在调用的时候加上namespace名字即可。namespace还支持嵌套。&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;带参数的rake任务&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;很多情况我们需要rake任务能够接受参数来扩展可用性，在rake 0.8.0 版本后可以支持直接传入参数&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;desc &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;build the programe&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;task &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:build&lt;&#x2F;span&gt;&lt;span&gt;, [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:programename&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:programeversion&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do &lt;&#x2F;span&gt;&lt;span&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;t&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;args&lt;&#x2F;span&gt;&lt;span&gt;|
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;puts &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;building programe &lt;&#x2F;span&gt;&lt;span&gt;#{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;args.programename&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt; with version &lt;&#x2F;span&gt;&lt;span&gt;#{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;args.programeversion&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt; successful!&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;查看rake任务时候可以看到build任务可以以数组的形式接受2个参数&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;➜ rake -T
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;rake build&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;programename,programeversion&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;]  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;# build the programe
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;➜ rake build&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;mysite, 0.1.0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;building programe mysite with version 0.1.0 successful!
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;em&gt;Notes&lt;&#x2F;em&gt;
如果你用的是zsh，那么可能会碰到zsh返回如下:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;zsh: no matches found: build&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;mysite,0.1.0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在zsh下必须用单引号把传递的参数引起来:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;rake build&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;[mysite, 0.1.0]&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;如果不想每次都这么麻烦，可以在~&#x2F;.zshrc中把glob对rake的扩展关掉:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;alias &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;rake&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;noglob rake&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这些都是rake的最基本用法，更多更高级的特性可以从rake&lt;a href=&quot;http:&#x2F;&#x2F;rake.rubyforge.org&#x2F;&quot;&gt;官网&lt;&#x2F;a&gt;学习&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Strong parameters in rails4</title>
        <published>2013-11-10T00:00:00+00:00</published>
        <updated>2013-11-10T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://liangliu.me/strong-parameters-in-rails4/"/>
        <id>https://liangliu.me/strong-parameters-in-rails4/</id>
        
        <content type="html" xml:base="https://liangliu.me/strong-parameters-in-rails4/">&lt;p&gt;在rails中有很多安全相关的特性保证我们的web应用足够安全，今天在学习rails时候碰到了这个，当我试图用一个create方法创建一个新话题的时候，我遇到了麻烦，我的controller如下：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;new
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;@topic &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Topic&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;new
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;create
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Topic&lt;&#x2F;span&gt;&lt;span&gt;.create(params[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:topic&lt;&#x2F;span&gt;&lt;span&gt;])
&lt;&#x2F;span&gt;&lt;span&gt;  redirect_to root_path
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在用户提交新的话题后，rails会报错提示&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;ActiveModel::ForbiddenAttributesError&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;在查阅了railsguides后才知道，这里不能直接使用create(params[:topic])，因为如果这样写，在web应用中这样可以随便让任何参数都传进来从而导致不安全的结果。在这里我们要用的是strong_parameters，我们需要指定controller只允许接受哪几个参数&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;create
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Topic&lt;&#x2F;span&gt;&lt;span&gt;.create(params[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:topic&lt;&#x2F;span&gt;&lt;span&gt;].permit(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:title&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:content&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:node_id&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这样再刷新页面应该就ok了。不过在这个过程中我还遇到了一个问题，我当时只传入了title和content 2个参数，忘了把node_id传过去了，结果刷新页面就报错，怎么也找不到问题，后来尝试把默认传过去的参数打印出来，像这样&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;create
&lt;&#x2F;span&gt;&lt;span&gt;  render &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;text:&lt;&#x2F;span&gt;&lt;span&gt; params[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:topic&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;inspect
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在提交之后就能在浏览器中看到传过去的参数以及取值，再来考虑create方法中应该怎么写了。&lt;&#x2F;p&gt;
&lt;p&gt;但其实上面那个还不是最好的写法，你应该这样写&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;create
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Topic&lt;&#x2F;span&gt;&lt;span&gt;.create(topic_params)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;private
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;topic_params
&lt;&#x2F;span&gt;&lt;span&gt;    params.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;require&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:topic&lt;&#x2F;span&gt;&lt;span&gt;).permit(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:title&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:content&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:node_id&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在这里我们定义一个私有方法&lt;code&gt;topic_params&lt;&#x2F;code&gt;，然后在create里调用这个方法来实现topic所带的参数的传递，这样就避免了通过修改hash传递给model来设置model属性的风险。更多的可以参考&lt;a href=&quot;http:&#x2F;&#x2F;weblog.rubyonrails.org&#x2F;2012&#x2F;3&#x2F;21&#x2F;strong-parameters&#x2F;&quot;&gt;这里&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>block, proc, lambda</title>
        <published>2013-10-27T00:00:00+00:00</published>
        <updated>2013-10-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://liangliu.me/block-proc-lambda/"/>
        <id>https://liangliu.me/block-proc-lambda/</id>
        
        <content type="html" xml:base="https://liangliu.me/block-proc-lambda/">&lt;h2 id=&quot;block&quot;&gt;block&lt;&#x2F;h2&gt;
&lt;p&gt;block其实就是一些可以被执行的代码段，block的语法有2中形式，一种是do...end形式，一种是简写的{}形式，例如：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;array1 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;array1.each &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do &lt;&#x2F;span&gt;&lt;span&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;n&lt;&#x2F;span&gt;&lt;span&gt;|
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;puts&lt;&#x2F;span&gt;&lt;span&gt; n
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;array1.each { |&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;n&lt;&#x2F;span&gt;&lt;span&gt;| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;puts&lt;&#x2F;span&gt;&lt;span&gt; n }
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;block 一般跟times或者each，collect等方法一起使用，可以对某一个hash或者array中的元素进行操作。
用yield方法可以让函数接受一个block，例如：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;block_test
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;puts &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;we are in the method!&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;yield
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;puts &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;we are back in the method!&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;block_test { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;puts &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;we are in the block!&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这样当函数执行到yield语句时候，就会调用传入的block。同时yield还可以处理传入的参数&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;block_test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;puts &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;we are in the method!&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;yield &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;name
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;puts &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;we are back in the method!&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;block_test(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;block&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;) { |&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;puts &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;we are in the &lt;&#x2F;span&gt;&lt;span&gt;#{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;!&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;proc&quot;&gt;proc&lt;&#x2F;h2&gt;
&lt;p&gt;在ruby中“任何都是对象”，但是其实对于blok来讲，它并不是对象，这是ruby中的一个例外，block不能保存到变量中，没有一个真正的对象使用起来那么方便，因此我们需要...proc, 它可以看做是保存成变量的block，这样我们可以给这个block起个名字，并可以重复调用它。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;multiply_of_3 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Proc&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;new do &lt;&#x2F;span&gt;&lt;span&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;n&lt;&#x2F;span&gt;&lt;span&gt;|
&lt;&#x2F;span&gt;&lt;span&gt;  n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;%&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;==&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;0
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;100&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;select&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;multiply_of_3)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;为什么要用proc呢，这里有2点需要理解的&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Procs是一个完全的对象，它具有所有对象的属性。&lt;&#x2F;li&gt;
&lt;li&gt;Procs在创建之后，可以在其他地方被重复调用，而block显然是不行的。&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;我们可以直接使用Ruby的&lt;code&gt;.call&lt;&#x2F;code&gt;方法来调用一个Proc&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Proc&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;new &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;... &lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.call
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;lambda&quot;&gt;Lambda&lt;&#x2F;h2&gt;
&lt;p&gt;与proc一样，lambda也是对象，从一些语法和行为上来看，lambda跟proc在很多地方都是相同的。让我们来看一个lambda的例子吧&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;lambda &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;puts &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;hello&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;与下面这个proc是相同的&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Proc&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;new &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;puts &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;hello&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;我们可以把一个lambda作为一个参数传递给一个函数&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;test_lambda&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;p_lambda&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;puts &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;I am the lambda test method.&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  p_lambda.call
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;test_lambda(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;lambda &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;puts &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;I am the real lambda!&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;从以上例子可以看出来，可以通过以下方式来定义一个lambda
&lt;code&gt;lambda { block }&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;strings &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;Germany&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;Netherland&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;England&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;France&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;symbolize &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;lambda &lt;&#x2F;span&gt;&lt;span&gt;{ |&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;n&lt;&#x2F;span&gt;&lt;span&gt;| n.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;to_sym &lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;symbol &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; strings.collect(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;symbolize)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;lambda和proc的不同点：
lambda会检查传入的参数数量，如果你传入了错误的参数数量，lambda会抛出一个异常，而proc不会检查，但是只会置为nil。
lambda在返回后会把控制权交给调用函数，而proc在返回后不会把控制权交给调用函数。&lt;&#x2F;p&gt;
&lt;p&gt;下面这个proc可以执行一下看下输出：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;batman_ironman_proc
&lt;&#x2F;span&gt;&lt;span&gt;  victor &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Proc&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;new &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;Batman will win!&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;  victor.call
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;Iron man will win!&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;puts&lt;&#x2F;span&gt;&lt;span&gt; batman_ironman_proc
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;我们可以看到结果，在Proc调用之后就结束了&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;Batman&lt;&#x2F;span&gt;&lt;span&gt; will win!
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;再来看一个lambda的例子&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;batman_ironman_lambda
&lt;&#x2F;span&gt;&lt;span&gt;  victor &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;lambda &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;Batman will win!&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  victor.call
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;Iron man will win!&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;我们可以看到结果，在lambda调用之后回到了原来的函数中&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;Iron&lt;&#x2F;span&gt;&lt;span&gt; man will win!
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;block,proc,lambda入门学习就先到这里，希望以后可以深入研究。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Pomodoro time technique</title>
        <published>2013-10-15T00:00:00+00:00</published>
        <updated>2013-10-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://liangliu.me/pomodoro-technique/"/>
        <id>https://liangliu.me/pomodoro-technique/</id>
        
        <content type="html" xml:base="https://liangliu.me/pomodoro-technique/">&lt;p&gt;如果你经常在工作学习的时候总是被各种事情分心，忍不住各种社交应用的诱惑，总是在多个事情之间来回跳转，一上午过去了你突然感觉到自己什么也没完成，番茄工作法应该比较适合你。&lt;&#x2F;p&gt;
&lt;p&gt;如果你经常关注一些提高工作效率或者时间管理类的图书文章或者博客，或许你已经听说过Pomodoro时间管理方法，这是一种非常好的能让人在短时间内集中注意力工作学习，提高效率的方法。我第一次接触Pomodoro方法是在Railscast-china上看KevinWang做&lt;a href=&quot;http:&#x2F;&#x2F;railscasts-china.com&#x2F;episodes&#x2F;kevin-open-class&quot;&gt;live show&lt;&#x2F;a&gt;，他演示了如何用Rails做一个ruby-china的克隆版，在这个视频中他使用了Pomodoro将时间划成多个时间片，在每一个25分钟内给大家演示如何编程，然后在休息的5分钟内跟大家互动答疑，非常好的一个实践，让我在学习rails的同时也获取到了高效工作的时间管理方法。&lt;&#x2F;p&gt;
&lt;p&gt;Pomodoro时间管理方法基本上是这样的&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;确定我们的目标(比如在codecademy学习完一节ruby课程)。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;设定一个25分钟的定时器。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;开始集中精力学习，直到定时器时间到提醒你。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;设定一个休息5分钟的定时器。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;休息时间到了之后，再进行下一个25分钟学习定时器。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;可以选择每4个25分钟后做一个长时间的休息，比如15分钟。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;我觉得这就是在一个短期时间内给自己设定一个目标，然后集中精力去做完它，在这个过程中我们会努力避免一些分心的事情，你会告诉自己等这个番茄钟过后我再做其他事情，这就是番茄工作法有效的原因。试想一下如果你坐在电脑前想花1个小时来写篇博客，你认为你真的可以完成吗？事实上在这个过程中你会发现自己不断的分心，浏览微博，逛汽车论坛等等。而使用番茄钟后你会被某种目标自我督促在这25分钟集中精力完成你的工作。&lt;&#x2F;p&gt;
&lt;p&gt;说说番茄钟工具吧，你可以去网上淘一个真正的番茄钟，或者像kevin那样使用&lt;a href=&quot;http:&#x2F;&#x2F;tomatoi.st&#x2F;&quot;&gt;pomodoro online&lt;&#x2F;a&gt;来做你的番茄钟。&lt;&#x2F;p&gt;
&lt;p&gt;我在chrome中安装了一个叫做&lt;a href=&quot;https:&#x2F;&#x2F;chrome.google.com&#x2F;webstore&#x2F;detail&#x2F;strict-workflow&#x2F;cgmnfnmlficgeijcalkgnnkigkefkbhd?utm_source=plus&quot;&gt;Strict Workflow&lt;&#x2F;a&gt;的pomodoro扩展来做我的番茄钟，非常好用，到点提醒，而且在你工作的25分钟内这个小扩展会自动屏蔽一些网站，比如facebook,youtube,twitter等容易让你分心的网站，只有在休息的5分钟内才能访问，当然屏蔽的网站列表可以自己编辑，可以设为黑名单或者白名单。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Use twitter-bootstrap in a rails project</title>
        <published>2013-10-02T00:00:00+00:00</published>
        <updated>2013-10-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://liangliu.me/use-twitter-bootstrap-in-a-rails-project/"/>
        <id>https://liangliu.me/use-twitter-bootstrap-in-a-rails-project/</id>
        
        <content type="html" xml:base="https://liangliu.me/use-twitter-bootstrap-in-a-rails-project/">&lt;p&gt;昨天在看&lt;a href=&quot;http:&#x2F;&#x2F;railscasts.com&#x2F;&quot;&gt;Railscasts&lt;&#x2F;a&gt;上看到了这个&lt;a href=&quot;http:&#x2F;&#x2F;railscasts.com&#x2F;episodes&#x2F;328-twitter-bootstrap-basics&quot;&gt;视频&lt;&#x2F;a&gt;，于是实践一把，新建一个demo项目&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;liul85:RubyOnRails MAC$ rails new demo
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;create
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;create  README.rdoc
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;create  Rakefile
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;create  config.ru
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;create  .gitignore
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;create  Gemfile
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;create  app
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;create  app&#x2F;assets&#x2F;javascripts&#x2F;application.js
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;create  app&#x2F;assets&#x2F;stylesheets&#x2F;application.css
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;create  app&#x2F;controllers&#x2F;application_controller.rb
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;create  app&#x2F;helpers&#x2F;application_helper.rb
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;create  app&#x2F;views&#x2F;layouts&#x2F;application.html.erb
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;......
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;......
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;Using sdoc (0.3.20&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;Using sqlite3 (1.3.8&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;Using turbolinks (1.3.0&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;Using uglifier (2.2.1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;Your bundle is complete!
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;Use `bundle show &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;gemname&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;` to see where a bundled gem is installed.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;然后编辑Gemfile在其中加入bootstrap，再运行bundle安装gem&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;gem &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;twitter-bootstrap-rails&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;gem &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;therubyracer&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;gem &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;less-rails&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;接下来可以进行自己的项目，比如利用脚手架创建一个简单的微博&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;liul85:demo MAC$ rails g scaffold post title:string content:text --skip-stylesheets
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;invoke  active_record
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;create    db&#x2F;migrate&#x2F;20131002193338_create_posts.rb
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;create    app&#x2F;models&#x2F;post.rb
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;invoke    test_unit
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;create      test&#x2F;models&#x2F;post_test.rb
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;create      test&#x2F;fixtures&#x2F;posts.yml
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;invoke  resource_route
&lt;&#x2F;span&gt;&lt;span&gt;       &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;route    resources :posts
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;invoke  scaffold_controller
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;create    app&#x2F;controllers&#x2F;posts_controller.rb
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;invoke    erb
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;create      app&#x2F;views&#x2F;posts
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;create      app&#x2F;views&#x2F;posts&#x2F;index.html.erb
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;create      app&#x2F;views&#x2F;posts&#x2F;edit.html.erb
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;create      app&#x2F;views&#x2F;posts&#x2F;show.html.erb
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;create      app&#x2F;views&#x2F;posts&#x2F;new.html.erb
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;create      app&#x2F;views&#x2F;posts&#x2F;_form.html.erb
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;invoke    test_unit
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;create      test&#x2F;controllers&#x2F;posts_controller_test.rb
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;invoke    helper
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;create      app&#x2F;helpers&#x2F;posts_helper.rb
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;invoke      test_unit
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;create        test&#x2F;helpers&#x2F;posts_helper_test.rb
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;invoke    jbuilder
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;create      app&#x2F;views&#x2F;posts&#x2F;index.json.jbuilder
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;create      app&#x2F;views&#x2F;posts&#x2F;show.json.jbuilder
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;invoke  assets
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;invoke    coffee
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;create      app&#x2F;assets&#x2F;javascripts&#x2F;posts.js.coffee
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;invoke    scss
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;liul85:demo MAC$
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;别忘了运行dg:migrate来创建迁移&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;liul85:demo MAC$ rake db:migrate
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;=  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;CreatePosts: migrating ====================================================
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;-- create_table(:posts&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;.0019s
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;=  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;CreatePosts: migrated (0.0020s&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;==========================================
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;启动rails服务，你看到的是一个没有任何前端的页面，很土&lt;&#x2F;p&gt;
&lt;p&gt;下来我们在assets中安装bootstrap-rails&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;liul85:demo MAC$ rails g bootstrap:install
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;insert  app&#x2F;assets&#x2F;javascripts&#x2F;application.js
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;create  app&#x2F;assets&#x2F;javascripts&#x2F;bootstrap.js.coffee
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;create  app&#x2F;assets&#x2F;stylesheets&#x2F;bootstrap_and_overrides.css.less
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;create  config&#x2F;locales&#x2F;en.bootstrap.yml
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;gsub  app&#x2F;assets&#x2F;stylesheets&#x2F;application.css
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;gsub  app&#x2F;assets&#x2F;stylesheets&#x2F;application.css
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;liul85:demo MAC$
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在我们的项目中使用全局布局，会提示与rails本身生成的布局视图有冲突，我们选择替换&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;iul85:demo MAC$ rails g bootstrap:layout application fixed
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;conflict  app&#x2F;views&#x2F;layouts&#x2F;application.html.erb
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;Overwrite &#x2F;Users&#x2F;MAC&#x2F;Documents&#x2F;RubyOnRails&#x2F;demo&#x2F;app&#x2F;views&#x2F;layouts&#x2F;application.html.erb&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;?&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt; (enter &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;h&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt; for help&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;[Ynaqdh] y
&lt;&#x2F;span&gt;&lt;span&gt;       &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;force  app&#x2F;views&#x2F;layouts&#x2F;application.html.erb
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;liul85:demo MAC$
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;然后重启rails s，是不是发现立马看到页面显示高端大气上档次了&lt;&#x2F;p&gt;
&lt;p&gt;这样，bootstrap的基本使用就学会了，它提供了很方便的前端效果，对于学习rails的初学者来说很有用。接下来可以再修改bootstrap_and_overides.css.less文件来提升前端效果了。&lt;&#x2F;p&gt;
&lt;p&gt;更多内容可以参考&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;seyhunak&#x2F;twitter-bootstrap-rails&quot;&gt;这里&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Default http method of form_for</title>
        <published>2013-09-29T00:00:00+00:00</published>
        <updated>2013-09-29T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://liangliu.me/default-http-method-of-form-for/"/>
        <id>https://liangliu.me/default-http-method-of-form-for/</id>
        
        <content type="html" xml:base="https://liangliu.me/default-http-method-of-form-for/">&lt;p&gt;在看RubyonRails Guides时候，在posts的new视图里用到了form_for方法来向控制器提交表单，在做了这节之后一直不明白这个表单是在哪里定义提交给控制器中的create动作的，因为我没有看到任何地方写了这个是提交给create动作.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;erb&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-erb &quot;&gt;&lt;code class=&quot;language-erb&quot; data-lang=&quot;erb&quot;&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;&amp;lt;%= form_for :post, url: posts_path &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;|f| %&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;&amp;lt;% &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;@post.errors.any? %&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#83a598;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;div &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;errorExplanation&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#83a598;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#83a598;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;h2&lt;&#x2F;span&gt;&lt;span style=&quot;color:#83a598;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;&amp;lt;%= pluralize(@post.errors.count, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;error&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;) %&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; prohibited
&lt;&#x2F;span&gt;&lt;span&gt;      this post from being saved:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#83a598;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;h2&lt;&#x2F;span&gt;&lt;span style=&quot;color:#83a598;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#83a598;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;ul&lt;&#x2F;span&gt;&lt;span style=&quot;color:#83a598;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;&amp;lt;% @post.errors.full_messages.each &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;|msg| %&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#83a598;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color:#83a598;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;&amp;lt;%= msg %&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#83a598;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color:#83a598;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;&amp;lt;% &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;%&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#83a598;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;ul&lt;&#x2F;span&gt;&lt;span style=&quot;color:#83a598;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#83a598;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span style=&quot;color:#83a598;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;&amp;lt;% &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;%&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#83a598;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color:#83a598;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;&amp;lt;%= f.label :title %&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#83a598;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;br&lt;&#x2F;span&gt;&lt;span style=&quot;color:#83a598;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;&amp;lt;%= f.text_field :title %&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#83a598;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color:#83a598;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt; 
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#83a598;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color:#83a598;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;&amp;lt;%= f.label :text %&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#83a598;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;br&lt;&#x2F;span&gt;&lt;span style=&quot;color:#83a598;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;&amp;lt;%= f.text_area :text %&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#83a598;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color:#83a598;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt; 
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#83a598;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color:#83a598;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;&amp;lt;%= f.submit %&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#83a598;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color:#83a598;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;&amp;lt;% &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;%&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt; 
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;&amp;lt;%= link_to &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;Back&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;, posts_path %&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;当再看到Updating Posts章节的时候，才看到一些解释，原来默认form_for是向POST提交表单，而如果我们用rake routes来看路由设置的话，会看到POST对应的就是控制器中的create方法。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;iul85:blog MAC$ rake routes
&lt;&#x2F;span&gt;&lt;span&gt;   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;Prefix Verb   URI Pattern               Controller#Action
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;posts GET    &#x2F;posts(.:format&lt;&#x2F;span&gt;&lt;span&gt;)          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;posts#index
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;POST   &#x2F;posts(.:format&lt;&#x2F;span&gt;&lt;span&gt;)          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;posts#create
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;new_post GET    &#x2F;posts&#x2F;new(.:format&lt;&#x2F;span&gt;&lt;span&gt;)      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;posts#new
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;edit_post GET    &#x2F;posts&#x2F;:id&#x2F;edit(.:format&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;posts#edit
&lt;&#x2F;span&gt;&lt;span&gt;     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;post GET    &#x2F;posts&#x2F;:id(.:format&lt;&#x2F;span&gt;&lt;span&gt;)      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;posts#show
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;PATCH  &#x2F;posts&#x2F;:id(.:format&lt;&#x2F;span&gt;&lt;span&gt;)      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;posts#update
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;PUT    &#x2F;posts&#x2F;:id(.:format&lt;&#x2F;span&gt;&lt;span&gt;)      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;posts#update
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;DELETE &#x2F;posts&#x2F;:id(.:format&lt;&#x2F;span&gt;&lt;span&gt;)      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;posts#destroy
&lt;&#x2F;span&gt;&lt;span&gt;     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;root GET    &#x2F;                         welcome#index
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;同时也可以在form_for中指定要提交的方法，比如在update的时候，我们是提交给PATCH，转向用户的这条blog，可以这么写，在form_for中添加method，指定提交的方法就可以了，不过要注意method和后面url的对应关系，用update和destroy时候需要指定对象来操作.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;erb&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-erb &quot;&gt;&lt;code class=&quot;language-erb&quot; data-lang=&quot;erb&quot;&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;&amp;lt;%= form_for :post, url: post_path(@post.id) }, method: :patch &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;|f| %&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这个在文档中说明如下，同时可以参考&lt;a href=&quot;http:&#x2F;&#x2F;guides.rubyonrails.org&#x2F;form_helpers.html&quot;&gt;form_helpers&lt;&#x2F;a&gt;文档。&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#282828;color:#fdf4c1aa;&quot;&gt;&lt;code&gt;&lt;span&gt;The method: :patch option tells Rails that we want this form to be submitted via the PATCH HTTP method which is the HTTP method you are expected to use to update resources according to the REST protocol.
&lt;&#x2F;span&gt;&lt;span&gt;By default forms built with the form_for helper are sent via POST.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>The rspec book note 3</title>
        <published>2013-09-14T00:00:00+00:00</published>
        <updated>2013-09-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://liangliu.me/the-rspec-book-note-3/"/>
        <id>https://liangliu.me/the-rspec-book-note-3/</id>
        
        <content type="html" xml:base="https://liangliu.me/the-rspec-book-note-3/">&lt;p&gt;BDD行为驱动开发，一般是指从软件的外在行为出发进行功能描述，从而推动代码开发并最终实现软件交付的过程。首先我们需要从客户的角度去理解需求，去搞清楚他们面临的问题，从他们对期望的软件描述中来获取有价值的信息。其次我们不要只关注主要客户的需求，任何对于我们将要做的项目有浓厚兴趣并提出一些看法的人我们都需要理解他们的意见。&lt;&#x2F;p&gt;
&lt;p&gt;当我们向客户交付有价值的软件的时候，我们需要意识到，我们交付的东西要能够解决客户的问题，但是也不能过度设计。BDD有一系列自己的原则需要我们遵守：&lt;&#x2F;p&gt;
&lt;h2 id=&quot;enough-is-enough&quot;&gt;Enough is enough&lt;&#x2F;h2&gt;
&lt;p&gt;从前期的需求分析，设计开始我们需要多做获取一个好的开始，但是也不能做过了，因为那样是一种浪费，刚刚好就是最好。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;deliver-stakeholder-value&quot;&gt;Deliver stakeholder value&lt;&#x2F;h2&gt;
&lt;p&gt;如果你做的东西既不能给客户带来价值也不能锻炼你交付有价值软件的能力，那么还是歇歇吧，干点别的啥。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;it-s-all-behavior&quot;&gt;It&#x27;s all behavior&lt;&#x2F;h2&gt;
&lt;p&gt;不管是从代码，应用程序，还是更高的层次来讲，我们都可以用相同的思维方式和语言结构从不同的颗粒度去描述我们的软件的外在行为。&lt;&#x2F;p&gt;
&lt;p&gt;BDD中定义任何关心软件的人价值的人都可以作为利益相关者，当他们与BA讨论了他遇到的问题和面临的困境后，会与测试人员来决定故事场景，我理解的故事场景其实越简单越好，就是一个很简单的软件的对外呈现的行为，然后选择出优先级最高的故事，开发人员只需要实现故事中最简单的场景即可，不要做过多的开发工作。在开始写代码之前最重要的的事情就是我们要让这些故事场景全部自动化，我们需要Cucumber。然后我们就可以开始编码，使用rspec进行TDD直到场景实现，然后重构，然后再次迭代直到其他场景也都实现，最终整个故事全部完成。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;yi-ge-gu-shi-ying-gai-shi-shen-me-yang-de&quot;&gt;一个故事应该是什么样的？&lt;&#x2F;h1&gt;
&lt;h3 id=&quot;a-title&quot;&gt;A title&lt;&#x2F;h3&gt;
&lt;p&gt;它应该有个标题，来说明我们做的是什么。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;a-narrative&quot;&gt;A narrative&lt;&#x2F;h3&gt;
&lt;p&gt;一般的描述是作为一个怎样的角色，我需要一个什么样的东西，这样我就能够干什么。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;acceptance-criteria&quot;&gt;Acceptance criteria&lt;&#x2F;h3&gt;
&lt;p&gt;有了验收标准我们才知道什么样的行为就算是ok了。&lt;&#x2F;p&gt;
&lt;p&gt;在软件交付的节奏中我们把需求分解为特性然后再变成故事和一堆场景，我们把它们全都写成自动化测试用例，就可以作为验收用例来保证我们做的软件是我们期望的。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>The rspec book note 2</title>
        <published>2013-09-07T00:00:00+00:00</published>
        <updated>2013-09-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://liangliu.me/the-rspec-book-note-2/"/>
        <id>https://liangliu.me/the-rspec-book-note-2/</id>
        
        <content type="html" xml:base="https://liangliu.me/the-rspec-book-note-2/">&lt;p&gt;在上一节中我们通过cucumber从外部对我们的Codebreaker程序行为进行了描述测试，通过step_definitions来描述了每个场景的步骤，在最后我们遗留了一个失败，我们期望Game向output发送一个消息，但是得到的结果消息是空的。这一节我们要用rspec来更细节的描述Game类的实例对象的行为。&lt;&#x2F;p&gt;
&lt;p&gt;在工程目录下创建一个rspec目录，再创建一个codebreaker子目录，然后创建game_spec.rb文件，加入如下内容：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;require &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;spec_helper&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;module &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Codebreaker
&lt;&#x2F;span&gt;&lt;span&gt;    describe &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;Game &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;	describe &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;#start&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;	    it &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;sends a welcome message&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;	    it &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;prompts for the first guess&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在这个文件我们首先require了一个文件叫spec_helper，这个文件我们创建后保存在spec的目录下，rspec会自动将这个spec目录添加到$LOAD_PATH中。&lt;&#x2F;p&gt;
&lt;p&gt;接下来我们定义了一个叫Codebreakder的module，个人感觉这个模块意义就是指出测试的是哪个代码吧。&lt;&#x2F;p&gt;
&lt;p&gt;describe方法是RSpec的一个API，会返回一个RSpec::Core::ExampleGroup的子类，这里描述的就是一个Game对象的各种行为集合。然后it方法创建了一个行为，来描述具体的一个动作。&lt;&#x2F;p&gt;
&lt;p&gt;接下来我们要做的就是将我们的rspec测试集与我们的代码连接起来，在第二行，我们需要加入&lt;code&gt;require &#x27;game&#x27;&lt;&#x2F;code&gt;，这样将我们的代码引用到了rspec测试集中。&lt;&#x2F;p&gt;
&lt;p&gt;然后在终端执行命令：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;rspec spec&#x2F;codebreaker&#x2F;game_rspec.rb --format doc
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;我们会看到下面的输出：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Codebreaker&lt;&#x2F;span&gt;&lt;span&gt;::Game
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;#start
&lt;&#x2F;span&gt;&lt;span&gt;sends a welcome message (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;PENDING: Not Yet Implemented&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;prompts &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; the first guess (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;PENDING: Not Yet Implemented&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;Pending:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Codebreaker&lt;&#x2F;span&gt;&lt;span&gt;::Game&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;#start sends a welcome message
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;# Not Yet Implemented
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;# .&#x2F;spec&#x2F;codebreaker&#x2F;game_spec.rb:6
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Codebreaker&lt;&#x2F;span&gt;&lt;span&gt;::Game&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;#start prompts for the first guess
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;# Not Yet Implemented
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;# .&#x2F;spec&#x2F;codebreaker&#x2F;game_spec.rb:7
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;--format doc&lt;&#x2F;code&gt;参数让rspec按照我们spec文件中的格式来输出结果。在输出结果中我们可以看到&lt;code&gt;&quot;PENDING: Not yet implemented&quot;&lt;&#x2F;code&gt;，这是因为我们只是写了个测试标题而已，接下来我们补充第一个测试：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;require &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;spec_helper&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;require &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;game&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;module &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Codebreaker
&lt;&#x2F;span&gt;&lt;span&gt;    describe &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;Game &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;	describe &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;#start&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;	    it &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;sends a welcome message&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;		output &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; double(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;output&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;		game &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Game&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;new&lt;&#x2F;span&gt;&lt;span&gt;(output)
&lt;&#x2F;span&gt;&lt;span&gt;		output.should_receive(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:puts&lt;&#x2F;span&gt;&lt;span&gt;).with(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;Welcome to Codebreaker!&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;		game.start
&lt;&#x2F;span&gt;&lt;span&gt;	    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;	    it &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;prompt for the first guess&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这里用到了RSpec中的动态double测试框架，很显然我现在还不知道这个是怎么用的，不过我先跳过。然后创建了一个Game的对象，把output传给它，接下来是期望output能够接收到&lt;code&gt;&#x27;Welcome to Codebreaker!&#x27;&lt;&#x2F;code&gt;的输出消息。最后调用start方法让game跑起来，这样我们就能接收到消息。&lt;&#x2F;p&gt;
&lt;p&gt;现在再跑一下rspec测试，加上--color参数，我们会看到输出显示&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;sends a welcome message (FAILED - 1)&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;很显然我们得到了一个红色的失败的结果，因为我们的代码还没有任何实现。一旦我们的到一个红色，我们就必须让他变绿~&lt;&#x2F;p&gt;
&lt;p&gt;然后我们打开game.rb，来添加代码&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;module &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Codebreaker
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Game
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;initialize&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;output&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;	    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;@output &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; output
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;start
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;@output&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;puts &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;Welcome to Codebreaker!&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;我们创建了initialize和start方法，再进行一次rspec测试，yeah，我们得到了第一个通过的测试，接下来我们应该进行重构，但是由于当前我们代码还没有任何重复，所以先不做。&lt;&#x2F;p&gt;
&lt;p&gt;接着再执行一下上一节遗留的cucumber feature，可以看到Then语句已经通过啦。&lt;&#x2F;p&gt;
&lt;p&gt;第二个测试我们还没有完成，我们需要再修改game_rspec.rb加以下代码&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;it &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;prompts for the first guess&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;    output &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; double(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;output&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    game &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Game&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;new&lt;&#x2F;span&gt;&lt;span&gt;(output)
&lt;&#x2F;span&gt;&lt;span&gt;    output.should_receive(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:puts&lt;&#x2F;span&gt;&lt;span&gt;).with(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;Enter guess:&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    game.start
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;再进行rspec测试，我们会得到第二个红色，我们来修改game.rb代码，在start方法中再加一行输出&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;start
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;@output&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;puts &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;Welcome to Codebreaker!&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;@output&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;puts &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;Enter guess:&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这样我们兴高采烈的再跑一次rspec测试，期待得到2个绿色，结果发现却2个测试全都红了，shit！&lt;&#x2F;p&gt;
&lt;p&gt;让我们来看看到底哪里出了问题，我们发现在2个测试中我们期望输出的是不同的字符，我们程序输出对于2个测试也都是满足的，但是我们发现这2个字符在game.start后是会一起输出的，而我们的2个测试只能识别自己期望的部分，如果多出来的部分就不能识别的，就报失败了。&lt;&#x2F;p&gt;
&lt;p&gt;我们使用RSpec框架中的&lt;code&gt;as_null_object()&lt;&#x2F;code&gt;方法来解决这个问题，使output匹配时候忽略其他字符，在2个测试中的double方法后面再引用&lt;code&gt;as_null_object&lt;&#x2F;code&gt;方法&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span&gt;output &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; double(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;output&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).as_null_object
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这样再跑一次RSpec测试，我们得到了2个绿色~&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Codebreaker&lt;&#x2F;span&gt;&lt;span&gt;::Game
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;#start
&lt;&#x2F;span&gt;&lt;span&gt;    sends a welcome message
&lt;&#x2F;span&gt;&lt;span&gt;    prompt &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; the first guess
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;Finished &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;0.00128&lt;&#x2F;span&gt;&lt;span&gt; seconds
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt; examples, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt; failures
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;Red -&amp;gt; Green -&amp;gt; Refactory&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;zhong-gou&quot;&gt;重构&lt;&#x2F;h2&gt;
&lt;p&gt;很明显，我们要开始考虑重构了，Martin Fowler在他的《重构》这本书里写道：“重构就是在不改变代码外部行为的前提下对内部代码的优化。” 那么我们怎么知道重构后没有改变代码的外在行为呢？这就依靠完整的测试保证，所以完整的测试是进行重构的前提。每当我们重构一处代码，我们跑一下测试得到绿色，说明我们的重构是成功的。&lt;&#x2F;p&gt;
&lt;p&gt;最基本的重构就是消除重复代码，我们来看看game_spec.rb的代码，基本上每一个测试的前2行都是类似的，这也是某种意义上的重复，我们需要修改掉&lt;&#x2F;p&gt;
&lt;h3 id=&quot;before-each&quot;&gt;before(:each)&lt;&#x2F;h3&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;require &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;spec_helper&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;require &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;game&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;module &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Codebreaker
&lt;&#x2F;span&gt;&lt;span&gt;    describe &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;Game &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;    	describe &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;#start&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;    	    before(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:each&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;    	    	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;@output &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; double(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;output&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).as_null_object
&lt;&#x2F;span&gt;&lt;span&gt;    	    	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;@game &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Game&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;new&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;@output&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    	    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;    
&lt;&#x2F;span&gt;&lt;span&gt;    	    it &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;sends a welcome message&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;    	        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;@output&lt;&#x2F;span&gt;&lt;span&gt;.should_receive(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:puts&lt;&#x2F;span&gt;&lt;span&gt;).with(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;Welcome to Codebreaker!&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    	        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;@game&lt;&#x2F;span&gt;&lt;span&gt;.start
&lt;&#x2F;span&gt;&lt;span&gt;    	    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;    
&lt;&#x2F;span&gt;&lt;span&gt;    	    it &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;prompt for the first guess&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;    	        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;@output&lt;&#x2F;span&gt;&lt;span&gt;.should_receive(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:puts&lt;&#x2F;span&gt;&lt;span&gt;).with(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;Enter guess:&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    	        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;@game&lt;&#x2F;span&gt;&lt;span&gt;.start
&lt;&#x2F;span&gt;&lt;span&gt;    	    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;    	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这里我们引入了before方法，把每个测试前面的初始化实例放到了前面，这样在rspec执行每个测试之前，都会执行一下before来创建实例对象。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;let-method&quot;&gt;let(:method)&lt;&#x2F;h3&gt;
&lt;p&gt;一般当before块中的代码只是初始化实例对象和赋值的时候，我们会用RSpec的let(:method)方法，let方法用一个词语来代替要用的方法和代码块&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;require &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;spec_helper&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;require &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;game&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;module &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Codebreaker
&lt;&#x2F;span&gt;&lt;span&gt;    describe &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;Game &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;    	describe &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;#start&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;	    let(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:output&lt;&#x2F;span&gt;&lt;span&gt;) { double(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;output&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).as_null_object }
&lt;&#x2F;span&gt;&lt;span&gt;	    let(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:game&lt;&#x2F;span&gt;&lt;span&gt;)   { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Game&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;new&lt;&#x2F;span&gt;&lt;span&gt;(output) }
&lt;&#x2F;span&gt;&lt;span&gt;    
&lt;&#x2F;span&gt;&lt;span&gt;    	    it &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;sends a welcome message&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;    	        output.should_receive(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:puts&lt;&#x2F;span&gt;&lt;span&gt;).with(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;Welcome to Codebreaker!&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    	        game.start
&lt;&#x2F;span&gt;&lt;span&gt;    	    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;    
&lt;&#x2F;span&gt;&lt;span&gt;    	    it &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;prompt for the first guess&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;    	        output.should_receive(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:puts&lt;&#x2F;span&gt;&lt;span&gt;).with(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;Enter guess:&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    	        game.start
&lt;&#x2F;span&gt;&lt;span&gt;    	    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;    	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;然后我们跑一下cucumber的测试，会看到第一个场景的用例已经通过了。
总结一下：我们从上节遗留的cucumber的失败场景开始，遵循TDD的red，green，refactor的模式用RSpec写了2个测试，学习了按照BDD的cycle从程序外部行为的cucumber测试进入到代码内部的rspec测试。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>The rspec book note 1</title>
        <published>2013-09-05T00:00:00+00:00</published>
        <updated>2013-09-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://liangliu.me/the-rspec-book-note-1/"/>
        <id>https://liangliu.me/the-rspec-book-note-1/</id>
        
        <content type="html" xml:base="https://liangliu.me/the-rspec-book-note-1/">&lt;p&gt;最近在看The rspec book这本书，由于平时工作不是这方面的，自己只是非常有兴趣，而且私下时间也不多，只靠每晚学习看的容易忘记，就在这里写一些笔记做复习。&lt;&#x2F;p&gt;
&lt;p&gt;cucumber主要是由features目录下的多个.feature文件构成BDD场景测试内容，然后针对其中的Given,When,Then,And等语句在featurs目录下的step_definitions子目录中增加ruby语法的行为步骤定义，来实现针对场景的测试执行。&lt;&#x2F;p&gt;
&lt;p&gt;参照书中的例子，逐步进行学习。&lt;&#x2F;p&gt;
&lt;p&gt;首先选择一个有趣的游戏--&amp;gt; Codebreaker&lt;&#x2F;p&gt;
&lt;p&gt;原文对游戏的介绍:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Codebreaker is a logic game in which a code-breaker tries to break a secret code created by a code-maker. The code-maker, which will be played by the application we’re going to write, creates a secret code of four numbers between 1 and 6.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;The code-breaker then gets some number of chances to break the code. In each turn, the code-breaker makes a guess of four numbers (again, 1 to 6). The code-maker then marks the guess with up to four + and - signs.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;A + indicates an exact match: one of the numbers in the guess is the same as one of the numbers in the secret code and in the same position.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;A - indicates a number match: one of the numbers in the guess is the same as one of the numbers in the secret code but in a different position.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;For example, given a secret code 1234, a guess with 4256 would earn a +-. The + is for the 2 in the second position in the guess, which matches the 2 in the secret code in both number and position: an exact match. The - is for the 4 in the first position in the guess, which matches the 4 in the code but not in the same position: a number match.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;简单的说就是计算机给出一个4位的随机数，然后我们输入我们猜想的数字，如果其中有数字和位置都匹配上，那么会返回一个+号，如果只有数字匹配上，那么则返回一个-号。&lt;&#x2F;p&gt;
&lt;p&gt;然后开始我们的迭代一，选定以下story进行交付:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Code-breaker starts game&lt;&#x2F;li&gt;
&lt;li&gt;Code-breaker submits guess&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;接下来就可以写cucumber场景测试，首先在features目录下创建一个codebreaker_starts_game.feature，这里我们要测试的是游戏启动:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#282828;color:#fdf4c1aa;&quot;&gt;&lt;code&gt;&lt;span&gt;Feature: code-breaker starts the game
&lt;&#x2F;span&gt;&lt;span&gt;         As a code-breaker
&lt;&#x2F;span&gt;&lt;span&gt;         I want to start a game
&lt;&#x2F;span&gt;&lt;span&gt;         So that I can break the code
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;         Scenario: start game
&lt;&#x2F;span&gt;&lt;span&gt;	    Given I am not yet playing
&lt;&#x2F;span&gt;&lt;span&gt;	    When I start a new game
&lt;&#x2F;span&gt;&lt;span&gt;	    Then I should see &amp;quot;Welcome to Codebreaker!&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;	    And I should see &amp;quot;Enter guess:&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;再创建一个codebreaker_submits_guess.feature，来测试输入猜想的数字，并看到结果:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#282828;color:#fdf4c1aa;&quot;&gt;&lt;code&gt;&lt;span&gt;Feature: code-breaker submits guess
&lt;&#x2F;span&gt;&lt;span&gt;	In order to play this game
&lt;&#x2F;span&gt;&lt;span&gt;	As a code breaker
&lt;&#x2F;span&gt;&lt;span&gt;	I want to submit a guess
&lt;&#x2F;span&gt;&lt;span&gt;	So that I can try to breake the code
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        Scenario: all exact matches
&lt;&#x2F;span&gt;&lt;span&gt;          Given the secret code is &amp;quot;1234&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;          When I guess &amp;quot;1234&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;          Then the mark should be &amp;quot;++++&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;            
&lt;&#x2F;span&gt;&lt;span&gt;        Scenario: 2 exact matches and 2 number matches
&lt;&#x2F;span&gt;&lt;span&gt;          Given the secret code is &amp;quot;1234&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;          When I guess &amp;quot;1243&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;          Then the mark should be &amp;quot;++--&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;            
&lt;&#x2F;span&gt;&lt;span&gt;        Scenario: 1 exact match and 3 number matches
&lt;&#x2F;span&gt;&lt;span&gt;          Given the secret code is &amp;quot;1234&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;          When I guess &amp;quot;1342&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;          Then the mark should be &amp;quot;+---&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;            
&lt;&#x2F;span&gt;&lt;span&gt;        Scenario: 4 number matches
&lt;&#x2F;span&gt;&lt;span&gt;          Given the secret code is &amp;quot;1234&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;          When I guess &amp;quot;4321&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;          Then the mark should be &amp;quot;----&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这样看来，没写几个测试场景呢，用例就已经很复杂了，而且很重复，DRY，我们可以用Scenario Outline来实现简洁:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#282828;color:#fdf4c1aa;&quot;&gt;&lt;code&gt;&lt;span&gt;Feature: code-breaker submits guess
&lt;&#x2F;span&gt;&lt;span&gt;	In order to play this game
&lt;&#x2F;span&gt;&lt;span&gt;	As a code breaker
&lt;&#x2F;span&gt;&lt;span&gt;	I want to submit a guess
&lt;&#x2F;span&gt;&lt;span&gt;	So that I can try to breake the code
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;	Scenario Outline: submit guess
&lt;&#x2F;span&gt;&lt;span&gt;	    Given the secret code is &amp;quot;&amp;lt;code&amp;gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;	    When I guess &amp;quot;&amp;lt;guess&amp;gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;	    Then the mark should be &amp;quot;&amp;lt;mark&amp;gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;	    Scenarios: no matches
&lt;&#x2F;span&gt;&lt;span&gt;	      | code | guess | mark |
&lt;&#x2F;span&gt;&lt;span&gt;	      | 1234 | 5678  |      |
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;	    Scenarios: 1 number correct
&lt;&#x2F;span&gt;&lt;span&gt;	      | code | guess | mark |
&lt;&#x2F;span&gt;&lt;span&gt;	      | 1234 | 1555  | +    |
&lt;&#x2F;span&gt;&lt;span&gt;	      | 1234 | 2555  | _    |
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;	    Scenarios: 2 number correct
&lt;&#x2F;span&gt;&lt;span&gt;	      | code | guess | mark |
&lt;&#x2F;span&gt;&lt;span&gt;	      | 1234 | 5254  | ++   |
&lt;&#x2F;span&gt;&lt;span&gt;	      | 1234 | 5154  | +-   |
&lt;&#x2F;span&gt;&lt;span&gt;	      | 1234 | 2545  | --   |
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;	    Scenarios: 3 mumber correct
&lt;&#x2F;span&gt;&lt;span&gt;	      | code | guess | mark |
&lt;&#x2F;span&gt;&lt;span&gt;	      | 1234 | 5234  | +++  |
&lt;&#x2F;span&gt;&lt;span&gt;	      | 1234 | 5134  | ++-  |
&lt;&#x2F;span&gt;&lt;span&gt;	      | 1234 | 5124  | +--  |
&lt;&#x2F;span&gt;&lt;span&gt;	      | 1234 | 5123  | ---  |
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;	    Scenarios: all number correct
&lt;&#x2F;span&gt;&lt;span&gt;	      | code | guess | mark |
&lt;&#x2F;span&gt;&lt;span&gt;	      | 1234 | 1234  | ++++ |
&lt;&#x2F;span&gt;&lt;span&gt;	      | 1234 | 1243  | ++-- |
&lt;&#x2F;span&gt;&lt;span&gt;	      | 1234 | 1423  | +--- |
&lt;&#x2F;span&gt;&lt;span&gt;	      | 1234 | 4321  | ---- |
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这样就好多了，容易读也好理解，就算是非技术人员也能看懂了。&lt;&#x2F;p&gt;
&lt;p&gt;接下来我们要定义测试步骤了，在features目录下建立子文件夹step_definitions,再建立codebreaker_steps.rb文件。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;Given &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&#x2F;^I am not yet playing$&#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;When &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&#x2F;^I start a new game$&#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;    game &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Codebreaker&lt;&#x2F;span&gt;&lt;span&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Game&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;new&lt;&#x2F;span&gt;&lt;span&gt;(output)
&lt;&#x2F;span&gt;&lt;span&gt;    game.start
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;Then &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&#x2F;^I should see &amp;quot;([^&amp;quot;]*)&amp;quot;$&#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do &lt;&#x2F;span&gt;&lt;span&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;|
&lt;&#x2F;span&gt;&lt;span&gt;    output.messages.should &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;include&lt;&#x2F;span&gt;&lt;span&gt;(message)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Output
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;messages
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;@messages &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;||= &lt;&#x2F;span&gt;&lt;span&gt;[]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;puts&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;      messages &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;&amp;lt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt; message
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;output
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;@output &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;||= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Output&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;new
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;测试会失败，我们需要写我们game.rb程序了&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;module &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Codebreaker
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Game
&lt;&#x2F;span&gt;&lt;span&gt;    	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;initialize&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;output&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;start
&lt;&#x2F;span&gt;&lt;span&gt;    	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;再进行测试我们会看到提示输出与期望的不一致&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#282828;color:#fdf4c1aa;&quot;&gt;&lt;code&gt;&lt;span&gt;Scenario: start game
&lt;&#x2F;span&gt;&lt;span&gt;        Given I am not yet playing
&lt;&#x2F;span&gt;&lt;span&gt;        When I start a new game
&lt;&#x2F;span&gt;&lt;span&gt;        Then I should see &amp;quot;Welcome to Codebreaker!&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;          expected [] to include &amp;quot;Welcome to Codebreaker!&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;至于这里怎么再修改，我们会在后面进行学习。&lt;&#x2F;p&gt;
&lt;p&gt;总结一下：这里我们了解了cucumber的基本目录结构，学习了gherkin语法，包括feature, Scenario, Given, When, Then以及Scenario Outline的用法，到此我们已经从Codebreaker游戏的行为出发点来用cucumber语法进行描述测试，下一节我们会学习使用rspec对每个独立对象的行为进行分析。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>happy Koln -&gt; Boen -&gt; königswinter</title>
        <published>2013-08-12T00:00:00+00:00</published>
        <updated>2013-08-12T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://liangliu.me/koln-boen-knigswinter/"/>
        <id>https://liangliu.me/koln-boen-knigswinter/</id>
        
        <content type="html" xml:base="https://liangliu.me/koln-boen-knigswinter/">&lt;p&gt;周末到了，又该计划出游了，这周我们打算去科隆逛逛，刚好凑够5个人，于是可以买周末的5人天票，非常便宜，德国境内火车随便坐，当然除了ICE和CE外。出去玩总得先做一下功课，貌似我们几个人没人愿意做功课，大家都想跟组织一路溜达玩，当然包括我也是这样，前几周出去玩都是跟着其他人，来了快1个月了，也有点混熟了，我就决定自己研究路线，也是为我自己后面好几个月的周游欧洲列国做准备吧。&lt;&#x2F;p&gt;
&lt;p&gt;同事有给一些穷游上找的材料，我看了一下，一天时间去科隆绰绰有余，最好可以再跑其他地方看看，于是就敲定了这样一个路线，科隆-&amp;gt;波恩-&amp;gt;科尼格斯温特尔，一天估计刚好，科隆不必多说，去波恩是因为也比较近，可以去看看波恩大学和贝多芬故居，克尼格斯文格尔是在波恩旁边的一个小镇，龙居住的地方，去参观不太有名的Schloss Drachenburg，龙堡。&lt;&#x2F;p&gt;
&lt;p&gt;早上在杜塞火车站坐9点40的车，经过30多分钟便到了科隆，在火车进站时候，便能看到科隆大教堂的宏伟
&lt;img src=&quot;http:&#x2F;&#x2F;farm6.staticflickr.com&#x2F;5506&#x2F;9494460591_7a78ff5f7b.jpg&quot; alt=&quot;大教堂&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;走出火车站，看到的是大教堂的侧面，光看到这个我和我的小伙伴们都震惊了，原地观望了好久&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm8.staticflickr.com&#x2F;7396&#x2F;9494981581_d43d776279.jpg&quot; alt=&quot;教堂侧面&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;来到正面，这是科隆地标性建筑，从13世纪中期就可以修建，一直到19世纪末才完工，近600多年才修建好，直到今日修缮仍然不断，我们去得时候，左边这个钟楼还在修缮中，它完美结合了中世纪哥特式建筑和装饰元素，第一次见绝对令人震撼。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm3.staticflickr.com&#x2F;2818&#x2F;9483266367_0226392a79.jpg&quot; alt=&quot;教堂正面&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;教堂内是可以免费进入，我们去的时候正赶上唱诗&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm8.staticflickr.com&#x2F;7419&#x2F;9494624139_b3b0af4d31.jpg&quot; alt=&quot;唱诗&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;听了一会我们也听不懂，就出来了，教堂里有珍宝厅，还可以登顶，我们穷游就没有去，在周围广场晃了晃，然后去城里找&lt;a href=&quot;http:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;4711&quot;&gt;4711&lt;&#x2F;a&gt;了&lt;&#x2F;p&gt;
&lt;p&gt;这就是著名的古龙水发源地，来到4711 house前面,只可惜我们去的时候是周日，商店没有开门，在这里买不到古龙水了，只在门口拍了几张照片，后来他们在火车站的一个小店里买了几瓶。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm6.staticflickr.com&#x2F;5521&#x2F;9498028504_23c3691466.jpg&quot; alt=&quot;4711-1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm6.staticflickr.com&#x2F;5471&#x2F;9497949896_09c969e184.jpg&quot; alt=&quot;4711-2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm8.staticflickr.com&#x2F;7339&#x2F;9495152429_cfe92d5e8d.jpg&quot; alt=&quot;4711-3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;下午1点离开科隆前往波恩，到达波恩，下了火车3.5欧的土耳其肉夹馍便饱了。开始在波恩晃悠，发现这里也不大，很难想象这是一个当年西德的近50年首都，直到今天德国好几个国家政治机构还在这里，他是一个很安静的小城，波恩大学就坐落在城中&lt;&#x2F;p&gt;
&lt;p&gt;前面黄色的楼就是波恩大学的主教学楼&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm4.staticflickr.com&#x2F;3763&#x2F;9498046914_9e824dc81f.jpg&quot; alt=&quot;波恩1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm8.staticflickr.com&#x2F;7349&#x2F;9498053030_2060c9ae8c.jpg&quot; alt=&quot;波恩2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这里的学校不会有明显的校门标志，我们只能看到一个牌子，整个学校就在城中，与城市融为一体。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm8.staticflickr.com&#x2F;7385&#x2F;9497417212_8a5c5dab06.jpg&quot; alt=&quot;波恩大学&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;波恩大学中的大草坪与主教学楼&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm4.staticflickr.com&#x2F;3826&#x2F;9494441161_9280b683b6.jpg&quot; alt=&quot;波恩大学&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;离开波恩大学，寻找贝多芬故居，这里是全世界音乐爱好者的圣地啊&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm8.staticflickr.com&#x2F;7434&#x2F;9497271266_e803b1e082.jpg&quot; alt=&quot;贝多芬图&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;故居大门&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm6.staticflickr.com&#x2F;5541&#x2F;9497236542_981e3652ae.jpg&quot; alt=&quot;贝多芬故居1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;我也有幸来到了音乐圣地，不知道可否叩开音乐大门啊&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm4.staticflickr.com&#x2F;3690&#x2F;9495361119_24d25800d7.jpg&quot; alt=&quot;敲门&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;1770年贝多芬就出生在这里，一直到22岁，这里的小楼里陈列着150多件贝多芬当年的原物，很古老的大钢琴，手稿，肖像画等，屋里不让拍照。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm8.staticflickr.com&#x2F;7284&#x2F;9494430667_66038d9a8c.jpg&quot; alt=&quot;贝多芬故居2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;准备离开波恩，去科尼格斯温特尔，旅途永远都是美好的，身在旅途，你永远都是自由的。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm4.staticflickr.com&#x2F;3815&#x2F;9485361585_1cf92b80bd.jpg&quot; alt=&quot;波恩车站&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;20分钟火车到达konigswinter小镇&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm6.staticflickr.com&#x2F;5498&#x2F;9495429407_1cd8d48af0.jpg&quot; alt=&quot;Konigswinter&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;看到远处山上的城堡了吗，那就是我们要去的地方~，龙堡没有被发掘为著名旅游景点，但其并不亚于天鹅堡&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;fmn.rrfmn.com&#x2F;fmn058&#x2F;20121103&#x2F;0035&#x2F;b_large_d5hY_2a0800000a2e1262.jpg&quot; alt=&quot;龙堡&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm6.staticflickr.com&#x2F;5546&#x2F;9495434449_433f6e0ee3.jpg&quot; alt=&quot;龙堡&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;火车站旁边的这个小镇特别漂亮，鲜花绿草地，彩色房子&lt;&#x2F;p&gt;
&lt;p&gt;来到了山脚下，我们花10欧坐了齿轮火车到了山顶&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm6.staticflickr.com&#x2F;5467&#x2F;9494412749_bdb008bc35.jpg&quot; alt=&quot;齿轮火车&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;在这里，视野特别开阔，莱茵河从脚下流过&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm4.staticflickr.com&#x2F;3786&#x2F;9497338144_06f0288f72_b.jpg&quot; alt=&quot;全景&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;欧洲大陆真是太美了&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm6.staticflickr.com&#x2F;5523&#x2F;9495482133_3f05d2e48e.jpg&quot; alt=&quot;null&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm8.staticflickr.com&#x2F;7413&#x2F;9488180592_9ed7230d96.jpg&quot; alt=&quot;刘亮&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;不过可惜的是我们去的时候已经17点了，在山顶溜达了一圈等去龙堡时候已经17:30了，城堡18:00就关门了，所以我们没法买票进去了，只在门口拍了个照片留作纪念了&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm4.staticflickr.com&#x2F;3759&#x2F;9495501683_a2613c929e.jpg&quot; alt=&quot;龙堡1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;我在小商店里拍到了一只特别憨的小龙&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm8.staticflickr.com&#x2F;7409&#x2F;9498298170_edefd1a772.jpg&quot; alt=&quot;憨龙&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;乘坐火车下山回杜塞了，充实的一天。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm6.staticflickr.com&#x2F;5518&#x2F;9498332058_2c666fe199.jpg&quot; alt=&quot;下山火车&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;PS: Instagram分享图片真的很好用啊。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>杜塞街景</title>
        <published>2013-08-02T00:00:00+00:00</published>
        <updated>2013-08-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://liangliu.me/dusseldorf-view/"/>
        <id>https://liangliu.me/dusseldorf-view/</id>
        
        <content type="html" xml:base="https://liangliu.me/dusseldorf-view/">&lt;p&gt;每次一个人到一个陌生的地方，我都喜欢徒步走遍每条街道，现在在欧洲最大的村里-&amp;gt;杜村这个再适合不过了，周六周日都出去走走看看，天气很不错，空气清新，天空湛蓝。
清晨阳光从这里把你叫醒&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm4.staticflickr.com&#x2F;3776&#x2F;9327344607_0db1d09311.jpg&quot; alt=&quot;sunrise&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;出去街上走走，有没有感觉到清新的空气迎面扑来&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm8.staticflickr.com&#x2F;7399&#x2F;9327099561_73397d7100.jpg&quot; alt=&quot;GreenRoad&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;多么美丽的草坪&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm8.staticflickr.com&#x2F;7284&#x2F;9327032653_e93e9d1be4.jpg&quot; alt=&quot;草坪&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;欧式风格建筑&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm6.staticflickr.com&#x2F;5540&#x2F;9327168181_57bcdfe742.jpg&quot; alt=&quot;建筑&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;穿过草坪，来到了博物馆，有没有觉得像蒙古包&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm8.staticflickr.com&#x2F;7429&#x2F;9329783654_a4f98285d8.jpg&quot; alt=&quot;博物馆&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这里已经到了莱茵河边，视野好阔&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm4.staticflickr.com&#x2F;3709&#x2F;9326959969_b098897564.jpg&quot; alt=&quot;莱茵河边&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;走上大桥，旁边还有城铁呼呼开过，整个桥都在震动，桥上行人很多，还有很多骑行的，看看河对岸的草地&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm8.staticflickr.com&#x2F;7437&#x2F;9329654196_9f57603eea.jpg&quot; alt=&quot;草地&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这2天有个移动游乐场在这里营业，周末人们都带着孩子过来玩，车站人特别多。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm4.staticflickr.com&#x2F;3823&#x2F;9329620600_56a56c02b2.jpg&quot; alt=&quot;游乐场&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm6.staticflickr.com&#x2F;5332&#x2F;9326848179_99734e7325.jpg&quot; alt=&quot;游乐场2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;街上的教堂&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm8.staticflickr.com&#x2F;7406&#x2F;9326697455_b105f50ab2.jpg&quot; alt=&quot;教堂&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这个双塔教堂绝对霸气&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm4.staticflickr.com&#x2F;3740&#x2F;9329415636_d35d8cd512.jpg&quot; alt=&quot;双塔教堂&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;准备着时刻去旅行&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm8.staticflickr.com&#x2F;7435&#x2F;9330157308_3c00546345.jpg&quot; alt=&quot;Station&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Lost in Germany</title>
        <published>2013-07-21T00:00:00+00:00</published>
        <updated>2013-07-21T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://liangliu.me/lost-in-germany/"/>
        <id>https://liangliu.me/lost-in-germany/</id>
        
        <content type="html" xml:base="https://liangliu.me/lost-in-germany/">&lt;p&gt;到杜塞已经4天了，时差终于倒的差不多了，以前感觉倒时差没这么明显的，这次不知道怎么回事，刚来的头2天，下午下班从客户那里回到宿舍就已经困的不行了，基本上脑袋一沾床就不知觉了，早上4点又睡不着了，第三天晚上终于熬啊熬啊，期间打盹无数次，熬到了11点睡觉，太不容易了，这里晚上10点，才基本有点天黑的意思。天一黑也就意味着要睡觉了，我觉得挺好的，符合日落而息的规律，日出而作就先不考虑了，哈哈&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm6.staticflickr.com&#x2F;5464&#x2F;9327308393_66a87dc90e.jpg&quot; alt=&quot;sunset&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;xian-shuo-shuo-xing&quot;&gt;先说说行&lt;&#x2F;h3&gt;
&lt;p&gt;刚来这里，最深切的感受就是不管是人还是车都非常严格的遵守交规，只要是红灯，不管有没有车，行人都会停下来等绿灯，绿灯亮了再通过，这里的十字路口红绿灯特别复杂，基本每个口都有十几个灯指示到不同的方向，部分是给行人看，部分是给司机看，还有一部分是给轻轨火车，我到现在还没搞懂，不过作为行人红灯停，绿灯行就够了。如果让我做司机，不晓得能不能看到灯，交通灯相对国内，比较小，没有那么醒目。在没有红绿灯的小路口，也是汽车会让着行人。这里的汽车开的都非常快，目测市内正常速度都在60左右，基本上绿灯一亮，大家都是地板油冲出去，刚下飞机上了大奔出租就感受到了这一点，不过这丝毫不影响交通安全，该停的时候还是停的很稳。而且最明显的一点，你在大街上很少能听到汽车鸣喇叭，通常听到的都是改装过的排气声。&lt;&#x2F;p&gt;
&lt;p&gt;在这里大部分车都是两厢车和旅行版，在国内没见过的很多旅行版在马路上都能看到，奔驰，宝马，奥迪，VW，OPEL，SKODA比较多，还有新福克斯的旅行版，好车不少，当然路边也停着很多有年头的老车，很多车都是手动挡，敞篷也是亮点啊，在欧洲开敞篷才是享受，这个时间正舒服，温度26度，天气非常好，出去兜兜风很爽。想想在国内开敞篷的大爷们，开着篷吸着PM2.5，就图个回头率，没啥意思了。路上Golf也比较多，随手拍拍。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm8.staticflickr.com&#x2F;7427&#x2F;9327241437_370228b165.jpg&quot; alt=&quot;Golf5&quot; title=&quot;第五代Golf&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm4.staticflickr.com&#x2F;3773&#x2F;9327189471_bc2898c6ca.jpg&quot; alt=&quot;Porsche&quot; title=&quot;Porsche 911&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm3.staticflickr.com&#x2F;2880&#x2F;9326643413_c76d204087.jpg&quot; alt=&quot;OPEL&quot; title=&quot;OPEL 国内是英朗XT&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;我住的地方离DB火车站很近，每天早上8点20从宿舍出发，穿过2个路口就到了火车站，火车很准点，我坐的车20分钟逢7就有一趟，我坐的是8点37的，经过4站，9分钟的车程就到了客户那。在这里坐火车特别方便，买了月票后，每天就直接到站台上车，就连上下班高峰时段，车上的人也不多，基本每天都有座。如果你有月票，还可以带一个朋友一起坐车，上周我们回来时候我们三个人一起走，同事有月票，我们以为可以带2个人，结果运气不好，碰到了查票的大妈，大妈说只能带一个人，还好隔壁座的国际友人出手相助，用他的月票带了我们一个人，非常nice。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm8.staticflickr.com&#x2F;7435&#x2F;9330157308_3c00546345_z.jpg&quot; alt=&quot;RailwayStation&quot; title=&quot;车站等车中…&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm8.staticflickr.com&#x2F;7388&#x2F;9330178498_93c7282213.jpg&quot; alt=&quot;Ontheway&quot; title=&quot;去客户办公室的路上&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;zai-shuo-shuo-chi&quot;&gt;再说说吃&lt;&#x2F;h3&gt;
&lt;p&gt;来欧洲吃饭绝对是个难题，贵就一个字，我来之前踌躇满志，特意去超市买了油盐酱醋，各种大料，豆瓣酱等，来了发现宿舍的这个厨房太坑爹，没有像样的锅，没有抽油烟机，要想炒菜基本很难，只有一个微波炉，今天去超市溜达了一圈，发现蔬菜好贵啊，当然是换算成CNY了，牛奶很便宜，买了袋通心粉，回来发现没有酱，我想到了带过来的豆瓣酱，于是乎出现了下面这个饭，意大利面与苦瓜和豆瓣酱的搭配是不是很诡异，不过味道还不错哦&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;farm6.staticflickr.com&#x2F;5501&#x2F;9327255591_a92068aaf9.jpg&quot; alt=&quot;spagette&quot; title=&quot;通心粉&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;今天从超市还买了一盒鸡腿鸡翅，640g，2.85欧，没有烤箱，我研究研究怎么用微波炉把他弄熟，或者用那个非常难用的平底锅来个煎鸡翅。很是羡慕zhaocc同学在瑞士还有烤箱的日子啊。&lt;&#x2F;p&gt;
&lt;p&gt;如果想要改善伙食去外面吃，基本上一顿都得十几欧，还是有些奢侈，补助的钱根本不够吃饭啊。由于自己做饭上班带饭的梦破灭了，我就在公司订了中午饭，每顿饭5欧，有蔬菜有肉，一般是会有青菜，蔬菜沙拉，鸡肉，鱼肉，或者大虾，还有汤，骨头汤，牛肚汤，番茄汤等。还有水果，一般是苹果，猕猴桃，小西红柿，有时候还有盒酸奶。米饭随便吃，还有小馒头也随便吃，一般吃完饭我会再带几个小馒头回家晚饭吃，哈哈&lt;&#x2F;p&gt;
&lt;p&gt;晚饭一般是回宿舍吃，我会熬个粥，弄点菜，吃小馒头。多熬些粥，第二天早上也可以喝，今天买了牛奶，麦片，以后早上也可以冲着喝。不过今晚就先研究下怎么搞鸡翅啦。&lt;&#x2F;p&gt;
&lt;p&gt;另外，还有一点让我感受特别深，在这里的超市，人们去购物的时候都会提个大袋子，你猜里面装的是啥？ 告诉你，里面装的是喝完的空饮料瓶子，带到超市可以把瓶子换给超市，超市会付你钱，这样起到了回收再利用的效果，同时你回家时候也可以用这个袋子装买的东西，就可以不使用塑料袋了，减少白色污染。&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
