<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title><![CDATA[好麻煩部落格]]></title>
  <link href="http://gogojimmy.net/atom.xml" rel="self"/>
  <link href="http://gogojimmy.net/"/>
  <updated>2014-08-02T00:35:02+08:00</updated>
  <id>http://gogojimmy.net/</id>
  <author>
    <name><![CDATA[Jimmy Kuo]]></name>
    
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html"><![CDATA[講個秘訣，如何處理在 Rails 網址中的斜線(/)]]></title>
    <link href="http://gogojimmy.net/2014/08/02/handle-slash-in-routing-in-rails/"/>
    <updated>2014-08-02T00:06:03+08:00</updated>
    <id>http://gogojimmy.net/2014/08/02/handle-slash-in-routing-in-rails</id>
    <content type="html"><![CDATA[<p>之前在一個暗黑社群中，有位朋友發問了一個問題，是關於如何處理 Rails 中網址的斜線，像是 <code>/a/b/c/d/e</code>，因為 Rails 在一般情況下會以為這是你的 <code>Resource</code>，但為了 SEO 的需求，我們常會把原本預設的 <code>:id</code> 改為 <code>Resource</code> 的標題，但標題中如果有 <code>/</code> 的時候該怎麼辦呢？今天剛好遇到這個情況，想了一下後其實也沒那麼難：</p>

<figure class='code'><figcaption><span>config/routes.rb</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">get</span> <span class="s1">&#39;articles/*title&#39;</span><span class="p">,</span> <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;articles#show&#39;</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>articles_controller.rb</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">def</span> <span class="nf">show</span>
</span><span class='line'>  <span class="vi">@article</span> <span class="o">=</span> <span class="no">Article</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:title</span><span class="o">]</span><span class="p">)</span> <span class="c1"># 存取 article/a/b/c/d/e params[:title] 就會變成 &quot;/a/b/c/d/e&quot;</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[給 Rails Developer 的基本SEO]]></title>
    <link href="http://gogojimmy.net/2013/09/26/basic-seo-for-rails-developer/"/>
    <updated>2013-09-26T17:03:00+08:00</updated>
    <id>http://gogojimmy.net/2013/09/26/basic-seo-for-rails-developer</id>
    <content type="html"><![CDATA[<p>做過那麼多的Projects幾乎每個老闆都會要求SEO，幾個專案下來我也整理了一些SEO基本佈置，在這邊就分享一下，這裡並沒有太高深的SEO技巧，都是基本SEO所需要的配置，但因為是基本，所以非常重要，如果你平常寫筆記有在重點部分畫星星的習慣，請把這篇印下來在上面畫上一個銀河。</p>

<!-- more -->


<h2>搞定Meta Tags 及 Meta Data</h2>

<p>第一步也是最基本的一步也就是搞定在<code>head</code>裡面的那些Meta Tag以及一些給特殊網站使用的Meta Data，下面是你會需要加入的東西：</p>

<ul>
<li>Title Tag：每一頁的標題，英文長度最好是70個字內，中文大約40字內，關鍵字盡量往前放，每一頁的<code>Title Tag</code>不要重複。</li>
<li>Meta Description Tag：每一頁的敘述，包含標點符號在內英文大約150字內，中文12字內，關鍵字最好在前10個字就出現，會出現在Google搜尋結果，因此文案好壞普遍來說會影響點擊率，每一頁的<code>Decription Tag</code>也盡量不要重複。</li>
<li>Canonical Url：告訴搜尋引擎關於這個頁面的標準網址，有時候我們一個網頁會因為參數而產生不同的網址，這標籤能告訴搜尋引擎只需要收錄哪個網址即可。</li>
<li>Facebook Open Graph：專門給Facebook抓的，可以用來調整你的文章分享到Facebook後的的縮圖、標題、敘述，搭配其他或是自定義的Open Graph可以做更多應用。</li>
<li>Twitter Cards：專門給Twitter抓的，可以用來調整你的文章分享到Twitter後的樣式。</li>
</ul>


<p>以上這些<code>Meta Tag</code>我都使用<a href="https://github.com/kpumuk/meta-tags">meta-tag</a>來完成，例如：</p>

<figure class='code'><figcaption><span>Title Tag</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">set_meta_tags</span> <span class="ss">title</span><span class="p">:</span> <span class="s1">&#39;給Rails Developer的基本SEO&#39;</span>
</span><span class='line'><span class="c1"># &lt;title&gt;給Rails Developer的基本SEO&lt;/title&gt;</span>
</span><span class='line'><span class="n">set_meta_tags</span> <span class="ss">site</span><span class="p">:</span> <span class="s1">&#39;好麻煩部落格&#39;</span><span class="p">,</span> <span class="ss">title</span><span class="p">:</span> <span class="s1">&#39;給Rails Developer的基本SEO&#39;</span>
</span><span class='line'><span class="c1"># &lt;title&gt;好麻煩部落格 | 給Rails Developer的基本SEO&lt;/title&gt;</span>
</span><span class='line'><span class="n">set_meta_tags</span> <span class="ss">site</span><span class="p">:</span> <span class="o">=&gt;</span> <span class="s1">&#39;好麻煩部落格&#39;</span><span class="p">,</span> <span class="ss">title</span><span class="p">:</span> <span class="s1">&#39;給Rails Developer的基本SEO&#39;</span><span class="p">,</span> <span class="ss">reverse</span><span class="p">:</span> <span class="kp">true</span>
</span><span class='line'><span class="c1"># &lt;title&gt;給Rails Developer的基本SEO | 好麻煩部落格&lt;/title&gt; &lt;= 建議作法</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>Description Tag</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">set_meta_tags</span> <span class="ss">description</span><span class="p">:</span> <span class="s1">&#39;教你怎麼在Rails裡做好基本的SEO&#39;</span>
</span><span class='line'><span class="c1"># &lt;meta name=&quot;description&quot; content=&quot;教你怎麼在Rails裡做好基本的SEO&quot; /&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<ul>
<li><a href="https://support.google.com/webmasters/answer/35624?hl=zh-Hant&amp;ref_topic=2371375">更多關於Meta Tag</a></li>
</ul>


<figure class='code'><figcaption><span>Canonical Url</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">set_meta_tags</span> <span class="ss">canonical</span><span class="p">:</span> <span class="s1">&#39;http://gogojimmy.net&#39;</span>
</span><span class='line'><span class="c1"># &lt;link rel=&quot;canonical&quot; href=&quot;http://gogojimmy.net&quot; /&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<ul>
<li><a href="https://support.google.com/webmasters/answer/139394?hl=zh-Hant&amp;ref_topic=2371375">更多關於Canonical Url</a></li>
</ul>


<figure class='code'><figcaption><span>Facebook Open Graph</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">set_meta_tags</span> <span class="ss">og</span><span class="p">:</span> <span class="p">{</span>
</span><span class='line'>  <span class="ss">title</span><span class="p">:</span> <span class="s1">&#39;教你怎麼在Rails裡做好基本的SEO&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="n">site_name</span><span class="p">:</span> <span class="s1">&#39;好麻煩部落格&#39;</span>
</span><span class='line'>  <span class="ss">type</span><span class="p">:</span> <span class="s1">&#39;website&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="ss">url</span><span class="p">:</span> <span class="s1">&#39;http://gogojimmy.net&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="ss">image</span><span class="p">:</span> <span class="s1">&#39;https://dl.dropboxusercontent.com/u/1390253/gogojimmy.jpg&#39;</span><span class="p">,</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'><span class="c1"># &lt;meta property=&quot;og:title&quot; content=&quot;教你怎麼在Rails裡做好基本的SEO&quot;/&gt;</span>
</span><span class='line'><span class="c1"># &lt;meta property=&quot;og:type&quot; content=&quot;website&quot;/&gt;</span>
</span><span class='line'><span class="c1"># &lt;meta property=&quot;og:site_name&quot; content=&quot;好麻煩部落格&quot;/&gt;</span>
</span><span class='line'><span class="c1"># &lt;meta property=&quot;og:url&quot; content=&quot;http://gogojimmy.net&quot;/&gt;</span>
</span><span class='line'><span class="c1"># &lt;meta property=&quot;og:image&quot; content=&quot;https://dl.dropboxusercontent.com/u/1390253/gogojimmy.jpg&quot;/&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>Facebook Open Graph可以做出很多應用，例如你可以申請一個Facebook APP之後自己定義新的動作來出現在Facebook上，國內像是<a href="http://kkbox.com">KKBOX</a>，<a href="http://icook.tw">icook 愛料理</a>都有使用。</p>

<ul>
<li><a href="http://developers.facebook.com/docs/opengraph/">更多關於Facebook Open Graph</a></li>
</ul>


<figure class='code'><figcaption><span>Twitter Card</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">set_meta_tags</span> <span class="ss">twitter</span><span class="p">:</span> <span class="p">{</span>
</span><span class='line'>  <span class="ss">card</span><span class="p">:</span> <span class="o">=&gt;</span> <span class="s2">&quot;summary&quot;</span><span class="p">,</span>
</span><span class='line'>  <span class="ss">site</span><span class="p">:</span> <span class="o">=&gt;</span> <span class="s2">&quot;@gogojimmy&quot;</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'><span class="c1"># &lt;meta property=&quot;twitter:card&quot; content=&quot;summary&quot;/&gt;</span>
</span><span class='line'><span class="c1"># &lt;meta property=&quot;twitter:site&quot; content=&quot;@gogojimmy&quot;/&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>Twitter Card目前支援七種Card，像是<code>Summary</code>、<code>Photo</code>、<code>Gallery</code>、<code>Product</code>之類的基本上如果你有設定Facebook Open Graph的話，你可以不用設定Twitter Card，因為Twitter抓不到Twitter Card的時候會尋找Open Graph的標籤。</p>

<ul>
<li><a href="https://dev.twitter.com/docs/cards/">更多關於Twitter Card</a></li>
</ul>


<p>另外還有Google+ Authorship以及Google+ Publisher、前者是給一般個人使用、後者是企業公司品牌使用，這樣搜尋引擎出現的內容會出現你的頭像以及讓Google知道這篇文章的正確作者是誰：</p>

<figure class='code'><figcaption><span>Google+ Authorship</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">link_to</span> <span class="s1">&#39;Google&#39;</span><span class="p">,</span> <span class="s2">&quot;</span><span class="si">#{</span><span class="err">你的</span><span class="n">google_profile_url</span><span class="si">}</span><span class="s2">?rel=author&quot;</span>
</span><span class='line'><span class="c1"># &lt;a href=&quot;你的google_profile_url?rel=author&quot;&gt;Google&lt;/a&gt;</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>Google+ Publisher</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">link_to</span> <span class="s1">&#39;Google&#39;</span><span class="p">,</span> <span class="s2">&quot;</span><span class="si">#{</span><span class="err">你的</span><span class="n">google_page_profile_url</span><span class="si">}</span><span class="s2">?rel=publisher&quot;</span>
</span><span class='line'><span class="c1"># &lt;a href=&quot;你的google_page_profile_url?rel=author&quot;&gt;Google&lt;/a&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>注意以上兩者皆需要在你的個人/官方的Google+ Profile裡面新增你的網站才會生效。</p>

<ul>
<li><a href="https://support.google.com/webmasters/answer/2539557">更多關於Google+ Authorship</a></li>
<li><a href="https://support.google.com/webmasters/answer/1708844">更多關於Google+ Publisher</a></li>
</ul>


<h2>搞定連結</h2>

<p>連結大概分三種，純文字的連結、圖片連結以及你不想讓搜尋引擎作為SEO計算的nofollow連結(像是留言裡的垃圾回應或是付費連結)</p>

<figure class='code'><figcaption><span>純文字連結</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">link_to</span> <span class="s1">&#39;關鍵字&#39;</span><span class="p">,</span> <span class="n">some_path</span>
</span><span class='line'><span class="c1"># 錨點文字就是關鍵字</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>圖片連結</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">link_to</span> <span class="n">some_path</span> <span class="k">do</span>
</span><span class='line'>  <span class="n">image_tag</span><span class="p">(</span><span class="s1">&#39;關鍵字.jpg&#39;</span><span class="p">,</span> <span class="ss">alt</span><span class="p">:</span> <span class="s1">&#39;關鍵字&#39;</span><span class="p">)</span>
</span><span class='line'><span class="k">end</span>
</span><span class='line'><span class="c1"># 對圖片連結來說，alt的用途就是錨點文字，檔案名稱也要符合關鍵字。</span>
</span><span class='line'><span class="c1"># 沒意義的圖片不要做SEO的強化，像是網站內自己的素材。</span>
</span><span class='line'><span class="c1"># 圖片附近最好有描述圖片的上下文，因為這會出現在搜尋結果中(請針對自己網站的設計做調整)。</span>
</span><span class='line'><span class="c1"># 如果圖片有多種尺寸，利用Robots.txt等方式讓搜尋引擎只收錄一個尺寸，簡單來說就是圖片不要重複。</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>nofollow連結</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">link_to</span> <span class="s1">&#39;某個不想被follow的地方&#39;</span><span class="p">,</span> <span class="n">some_path</span><span class="p">,</span> <span class="ss">rel</span><span class="p">:</span> <span class="s1">&#39;nofollow&#39;</span>
</span><span class='line'><span class="c1"># nofollow會讓搜尋引擎不會前往這個連結，通常運用在留言、付費連結，</span>
</span><span class='line'><span class="c1"># 以及像是登入登出這種沒有必要讓搜尋引擎收錄的地方。</span>
</span></code></pre></td></tr></table></div></figure>


<ul>
<li><a href="https://support.google.com/webmasters/answer/114016?hl=zh-Hant&amp;ref_topic=2370565">更多關於圖片連結</a></li>
<li><a href="https://support.google.com/webmasters/answer/96569?hl=zh-Hant">更多關於nofollow</a></li>
</ul>


<h2>搞定網址</h2>

<p>良好的網址對於SEO非常重要，一般在Rails裡面使用<code>[:id]</code>的方法我們可以使用<a href="https://github.com/norman/friendly_id">friendly_id</a>這套Gem來幫助我們，你可以把像是<code>http://example.com/products/123</code>變成<code>http://example.com/products/好商品不買嗎</code>這樣的網址，除此之外他最好用的功能就是可以記錄你的網址記錄，例如說你原先的網址已經被搜尋引擎收錄，但今天你有需求要把網址改掉，這時候搜尋引擎點進來的人就會404找不到網頁，但是<a href="https://github.com/norman/friendly_id">friendly_id</a>可以幫我們把原先的網址用301轉址到新的網址，對SEO有非常重要的幫助，<a href="https://github.com/norman/friendly_id">friendly_id</a>使用上很簡單，你看完他的<a href="https://github.com/norman/friendly_id">ReadMe</a>就沒問題了。</p>

<p>其他關於URL的建議：</p>

<ul>
<li>網址內要有關鍵字，越前面越好。</li>
<li>注意因為不同參數所產生同樣的網頁，使用Canonical Url的方式來避免重複內容，除非你是搜尋結果頁。</li>
<li>將內容放在網域下第一層路徑的效果比弄成子網域好，因為可以增加網域的權威性，例如<code>http://example.com/blog</code>會比<code>http://blog.example.com</code>來的好，因為你的內容可以增加<code>http://example.com</code>的權威性。</li>
</ul>


<h2>搞定爬蟲</h2>

<p>robots.txt這隻檔案是用來告訴搜尋引擎什麼東西不要收錄，在Rails中預設是放在<code>public/robots.txt</code>，你可以直接修改這隻檔案，或是說如果你有多個測試環境(像是staging)，那麼你大概不想讓Google可以收錄staging的內容，因此這時候你需要可以針對Rails環境來動態產生的<code>Robots.txt</code>，我是參考<a href="https://gist.github.com/timcheadle/3761844">這支Gist的作法</a>。</p>

<ul>
<li><a href="http://www.robotstxt.org/robotstxt.html">更多關於Robot.txt</a></li>
</ul>


<h2>搞定Sitemap</h2>

<p>Sitemap可以直接告訴搜尋引擎你的網站結構，幫助搜尋引擎更容易了解你的網站內容，通常我的習慣是會在每次有新內容產生的時候就會向搜尋引擎提交一個新的Sitemap，我是使用<a href="https://github.com/kjvarga/sitemap_generator">sitemap_generator</a>這套Gem來實作的，這個Gem在使用上也很簡單，把<a href="https://github.com/kjvarga/sitemap_generator">ReadMe</a>看一看就行了，他有提供<code>rake task</code>讓你可以在新內容產生的時候跑<code>rake</code>去提交新的sitemap到所有的搜尋引擎。</p>

<p>更多關於Sitemap的說明與建議：</p>

<ul>
<li><a href="https://support.google.com/webmasters/answer/183668?hl=zh-Hant&amp;ref_topic=8476">Sitemap的寫法</a></li>
<li>可以針對不同的Content Type來做不同的Sitemap，像是影片、圖片、手機版，<a href="https://github.com/kjvarga/sitemap_generator">sitemap_generator</a>這套Gem你可以輕鬆做好。</li>
</ul>


<h2>搞定語系與國家</h2>

<p>別忘了在你的html tag中加入語系</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='html'><span class='line'><span class="nt">&lt;html</span> <span class="na">lang=</span><span class="s">&quot;zh&quot;</span><span class="nt">&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>你可以在head tag中加入所有語系以及國家，可以使用<a href="https://github.com/kpumuk/meta-tags">meta-tag</a>來幫助你完成</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='html'><span class='line'><span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">&quot;alternate&quot;</span> <span class="na">hreflang=</span><span class="s">&quot;x-default&quot;</span> <span class="na">href=</span><span class="s">&quot;http://www.example.com/&quot;</span> <span class="nt">/&gt;</span> (提供自動導向或是讓使用者可以自己選擇語言或地區的)
</span><span class='line'><span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">&quot;alternate&quot;</span> <span class="na">hreflang=</span><span class="s">&quot;en&quot;</span> <span class="na">href=</span><span class="s">&quot;http://example.com/en/&quot;</span> <span class="nt">/&gt;</span> (英文版本的入口)
</span><span class='line'><span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">&quot;alternate&quot;</span> <span class="na">hreflang=</span><span class="s">&quot;en-ca&quot;</span> <span class="na">href=</span><span class="s">&quot;http://example.com/en-ca/&quot;</span> <span class="nt">/&gt;</span> (針對加拿大地區使用的英文版本)
</span></code></pre></td></tr></table></div></figure>


<p>你也可以使用Sitemap來產生語系，請參考<a href="https://support.google.com/webmasters/answer/183668?hl=zh-Hant&amp;ref_topic=8476">Sitemap的寫法</a>，基本上面那種方法（<code>&lt;link rel="alternate" hreflang=""</code>）與Sitemap的作法擇一就可以了。</p>

<h2>其他建議</h2>

<p>可以的話，請盡量提供行動裝置的網站，無論是Responsive Design或是建立另外一個網站，Google在他們的<a href="https://developers.google.com/webmasters/smartphone-sites/">Building Mobile-Optimized Websites</a>裡面有提到Responsive對Google來說最方便，因為這樣子對他們來說內容都一樣，但是並不是說Responsive就是你唯一的標準，如果你的網站內容很複雜的話，建議還是拆成不同的網頁來呈現，無論是不是同個網址。</p>

<ul>
<li>參考<a href="http://seo.dns.com.tw/?p=10633">網站優化應該使用Responsive Web Design嗎?</a></li>
</ul>


<p>有什麼錯誤都歡迎來信或留言罵我，謝謝</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[[Rails佈署實戰教學]使用Chef-Solo一鍵安裝機器]]></title>
    <link href="http://gogojimmy.net/2013/06/01/Chef-Solo-Basic-with-Vagrant/"/>
    <updated>2013-06-01T03:53:00+08:00</updated>
    <id>http://gogojimmy.net/2013/06/01/Chef-Solo-Basic-with-Vagrant</id>
    <content type="html"><![CDATA[<p>這大概會是連續800篇的連載文章，當然就是循序漸進的從安裝一台server到安裝50000台server這麼複雜，首先我想先從最基本的裝機器開始說，因為在以前他一直是我的軟肋。</p>

<!-- more -->


<p>首先要來跟大家說一個秘密，就是我從來沒有一次就從安裝機器到 cap deploy一氣呵成安裝好的紀錄，總是在每次改東改西後，滿懷著希望輸入<code>cap deploy</code>後，機器跑阿跑的，然後吐給我一個Rollback告訴我哪裡出問題，然後我就摸摸鼻子改了改重來，就這樣修修改改到Deploy成功，這樣的步驟不知道陪了我幾個夜晚。</p>

<p>什麼？你說我沒有把記錄抄下來？你犯傻的嗎怎麼可能沒有，但有時候科學不能解釋所有事情，莫非定律告訴我們如果事情可能會出錯，那麼他一定會出錯，但今天我要告訴你，這一切都將成為過去，一切的故事，都是從與<a href="http://www.opscode.com/chef/">Chef</a>開始，當我跟他交上朋友後，我的Deploy再也沒出過問題，所有關於機器的問題都交給他幫我處理，今天我就要介紹我這位朋友給你認識。</p>

<h2>關於Chef</h2>

<p>如果你英文跟我一樣頂呱呱的話，你應該知道Chef就是大廚的意思，Chef有兩種，分別是<a href="http://docs.opscode.com/chef_solo.html">Chef-Solo</a>跟<a href="http://docs.opscode.com/chef_overview_server.html">Chef-Server</a>，Chef-Solo顧名思義基本上就是單機作業，專屬於你個人的機器上，依照你的指示去管理你的機器，，這位大廚依照食譜集(cookbooks)裡的各種食譜(recipe)去幫你管理菜色(你的機器所需要的套件)，你需要做的就是告訴這位大廚你想要什麼食譜(套件)，這些食譜裡各種菜色的樣式(套件的設定)想要怎樣，而Chef-Server則是一個真正的大大大大廚，他需要自己生活在一台機器裡，因為他是個大大大大廚，跟Chef-Solo一樣他管理著所有的食譜，管理著旗下所有的機器(node)，你說跟Chef-Solo有什麼不同，當你的機器數量是動態不固定的時候你就知道他的好處了，因為今天要跟大家介紹的是Chef-Solo這位朋友，關於Chef-Server的故事我們下次再提，你只要知道我們今天請來的這位大廚，我們要怎麼去跟他溝通來幫我們管機器就好了。</p>

<h2>跟Capistrano有什麼不同？</h2>

<p>Capistrano其實簡單來說是將你設定好的指令透過ssh來執行，因此基本上只要機器能讓你ssh進去，而又有權限的情況下其實你也可以用Capistrano來做到與Chef-Solo一樣的功能，例如<a href="https://github.com/webficient/capistrano-recipes">capistrano-recipes</a>這個gem就是在做這件事，但這樣會有一個情況是程式的佈署設定很容易與機器的設定混在一起，日子久了當你用的東西越來越多時就很容易變亂，這樣當進行下一個專案的時候又要開始重寫Capistrano設定檔，因此這樣目標就變得很清楚了，我們希望能將佈署時做的事情與機器設定兩件事情分開，在這個前提下，Chef就是你的好幫手，因此我們並沒有捨棄Capistrano，而是將裝機器的事情交給了另一位專業人士，在設定上的模式會是這樣的：</p>

<ol>
<li>用Capistrano在機器上安裝好Chef-Solo</li>
<li>將食譜上傳到機器</li>
<li>用Capistrano執行Chef-Solo</li>
</ol>


<p>在這樣的模式下，我們只要將機器環境的設定集中在cookbooks就好了，capistrano只要專心做佈署程式碼上去時該做的事情就好，讓大家各司其職，在這樣的結構下，你的設定便會變得乾淨許多，你甚至可以把cookbooks獨立出一個repository讓以後的專案都可以重複利用，反正每次裝機器都是裝那些東西，就開始使用Chef，不要再浪費生命裝機器了</p>

<h2>安裝Chef-Solo</h2>

<p>這邊會以Vagrant作為示範，如果你不知道怎麼使用Vagrant，可以參考鍵人矬作<a href="http://gogojimmy.net/2013/05/26/vagrant-tutorial/">使用Vagrant練習環境佈署</a>，讓我們<code>vagrant up</code>一台什麼東西都還沒有裝的VM，並且裝上Chef-Solo，Chef-Solo需要安裝Ruby環境以及一些有的沒有的，你可以使用我的<a href="https://gist.github.com/gogojimmy/5523985">gist</a>來安裝，先登入我們的VM後切換到root：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="nv">$ </span>vagrant up
</span><span class='line'><span class="nv">$ </span>vagrant ssh
</span><span class='line'><span class="nv">$ </span>sudo su
</span><span class='line'><span class="nv">$ </span>curl -L https://gist.github.com/gogojimmy/5523985/raw/b9d777bc380ee791c2f4534e9261b4b99289ed9f/bootstrap-chef-solo.sh | sh
</span></code></pre></td></tr></table></div></figure>


<p>安裝好後，執行<code>chef-solo -v</code>應該可以看到chef-solo已安裝成功並印出版本號，接下來我們要開始寫我們的食譜了</p>

<h2>Cookbook</h2>

<p>如同剛剛所說，Cookbook是個食譜集，裡面包含了我們想要的所有菜色，我們先來建立我們第一道菜，就來安裝Nginx吧，先建立出chef的資料夾</p>

<blockquote><p>注意：以下操作都是在VM上執行</p></blockquote>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>mkdir -p /etc/chef/coobooks/nginx/recipes
</span></code></pre></td></tr></table></div></figure>


<blockquote><p><code>/etc/chef</code>這個結構是固定的，不能亂放其他地方&hellip;.</p></blockquote>

<p>注意到了嗎，不管是哪道菜色的資料結構其實是有規則的，在cookbooks的資料夾下面都是{套件名稱}/{recipes}，而在每個recipes的目錄下都會有個<code>default.rb</code>的設定檔，這個設定檔就是讓我們去設定怎麼安裝及啟動套件的地方，現在就讓我們建立`default.rb&#8217;這支檔案並寫下對nginx的設定：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">package</span> <span class="s1">&#39;nginx&#39;</span>
</span></code></pre></td></tr></table></div></figure>


<p><code>package</code> 這個方法是讓Chef-Solo去呼叫你作業系統的套件管理指令進行安裝，例如以Ubuntu來說就是<code>apt</code>、Fedora的話就是<code>yum</code>，因此當我們下了<code>package 'nginx'</code>之後，chef就會根據你的作業系統去搜尋套件進行安裝，nginx的食譜寫好後，再來就是要告訴chef要去哪邊找食譜來安裝，我們在這邊就要建立一個json格式的設定檔來完成，這個設定檔主要就是針對這台機器的設定，告訴chef說這台機器要安裝哪些套件，機器在chef的概念裡叫做<code>node</code>，因此我們在根目錄下建立一個node.json的檔案，裡面寫好告訴Chef要安裝哪些套件的設定</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='json'><span class='line'><span class="p">{</span> <span class="nt">&quot;run_list&quot;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&quot;recipe[nginx]&quot;</span><span class="p">]</span> <span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p><code>run_list</code>這個key指向的array就是告訴Chef要去執行哪些recipe，我們剛剛新增了Nginx的recipe因此這邊理所當然的就加上去了，如果你還有很多其他的recipe就是去擴充這個array就好了，最後一件要做的事情，就是建立一支<code>solo.rb</code>的檔案，這個檔案的目的要告訴Chef說關於哪些套件要進行安裝的<code>node.json</code>檔案以及cookbooks在哪邊的設定，因此我們一樣在chef的根目錄下建立這支檔案</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">cookbook_path</span> <span class="no">File</span><span class="o">.</span><span class="n">expand_path</span><span class="p">(</span><span class="s2">&quot;../cookbooks&quot;</span><span class="p">,</span> <span class="bp">__FILE__</span><span class="p">)</span>
</span><span class='line'><span class="n">json_attribs</span> <span class="no">File</span><span class="o">.</span><span class="n">expand_path</span><span class="p">(</span><span class="s2">&quot;../node.json&quot;</span><span class="p">,</span> <span class="bp">__FILE__</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>最後我們執行chef-solo來看看</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="nv">$ </span>chef-solo solo.rb
</span><span class='line'>Starting Chef Client, version 11.4.0
</span><span class='line'>Compiling Cookbooks...
</span><span class='line'>Converging 1 resources
</span><span class='line'>Recipe: nginx::default
</span><span class='line'>  * package<span class="o">[</span>nginx<span class="o">]</span> action install
</span><span class='line'>    - install version 1.1.19-1ubuntu0.2 of package nginx
</span><span class='line'>
</span><span class='line'>Chef Client finished, 1 resources updated
</span><span class='line'>root@vagrant-ubuntu-precise-64:/etc/chef# which nginx
</span><span class='line'>/usr/sbin/nginx
</span></code></pre></td></tr></table></div></figure>


<p>噹噹！我們成功的做好一個nginx的recipe，並且讓chef安裝成功，往後你每次再執行的時候，Chef都會檢查如果該套件已經安裝過的話就略過安裝，簡單的說，就是我們已經確保了<code>run_list</code>裡面所設定的東西在你每次執行後都會安裝好了！這裡介紹的是package最基本的用法，詳細其實package還支援了很多其他的功能，像是你可以用直接用<code>version</code>設定版本，用<code>opions</code>來指定安裝時的選項像是<code>--with-http_ssl_module</code>，也可以指定<code>action</code>，<code>action</code>預設動作為<code>install</code>，你可以指定為像是<code>upgrade</code>、<code>remove</code>之類的，詳細的設定請參考<a href="http://docs.opscode.com/resource_package.html">Opscode文件中的說明</a></p>

<h2>運用Chef安裝套件</h2>

<p>你知道我知道裝機器不是把套件裝起來就結束了，以剛剛的Nginx為例，你裝完以後至少也要讓他啟動吧，我們在原先nginx的設定檔中加入下面這幾行：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">package</span> <span class="s1">&#39;nginx&#39;</span>
</span><span class='line'>
</span><span class='line'><span class="n">service</span> <span class="s1">&#39;nginx&#39;</span> <span class="k">do</span>
</span><span class='line'>  <span class="n">supports</span> <span class="o">[</span><span class="ss">:status</span><span class="p">,</span> <span class="ss">:restart</span><span class="p">,</span> <span class="ss">:reload</span><span class="o">]</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">action</span> <span class="ss">:start</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>在你使用<code>package</code>這個resource安裝完nginx的同時，套件本身也幫你增加了一份script到<code>/etc/init.d/</code>底下，一般來說我們這時候可以去執行這個script來啟動nginx，但這樣就太遜了，在Chef裡面我們使用<code>service</code>這個resource並且指定為nginx的service，預設情況下Chef會去找與你指定service同樣name的process來確定這個服務是否存活，但我們這邊可以使用<code>supports</code>來告訴Chef他可以使用<code>status</code>這個指令來取代，並且另外支援<code>restart</code>與<code>reload</code>的指令，最後用<code>action</code>來指定其動作是<code>start</code>，因此當我們再次執行我們的<code>solo.rb</code>，就會有不同的變化了：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="nv">$ </span>chef-solo solo.rb
</span><span class='line'>Starting Chef Client, version 11.4.0
</span><span class='line'>Compiling Cookbooks...
</span><span class='line'>Converging 2 resources
</span><span class='line'>Recipe: nginx::default
</span><span class='line'>  * package<span class="o">[</span>nginx<span class="o">]</span> action install <span class="o">(</span>up to date<span class="o">)</span>
</span><span class='line'>  * service<span class="o">[</span>nginx<span class="o">]</span> action start
</span><span class='line'>    - start service service<span class="o">[</span>nginx<span class="o">]</span>
</span><span class='line'>
</span><span class='line'>Chef Client finished, 1 resources updated
</span></code></pre></td></tr></table></div></figure>


<p>現在在我們安裝好Nginx的同時，我們也一併啟動了nginx，如果你現在開啟你的瀏覽器來連線到你的VM位置的話，你就會看到你的Nginx已經跑起來了！更多關於Service的設定你可以參考<a href="http://docs.opscode.com/resource_service.html">Opscode文件中的說明</a></p>

<h2>運用Chef安裝deploy使用者</h2>

<p>Chef本身提供了<code>user</code>這個resource讓你可以用來管理使用者，為了有架構的管理我們的cookbook，我們現在在cookbooks底下新增一個user的cookbook，與剛剛Nginx的部份一樣我們在cookbook裡新增一個default的資料夾，並且在裡面新增<code>default.rb</code>的檔案用來讓我們設定user</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">user</span> <span class="s1">&#39;gogojimmy&#39;</span> <span class="k">do</span>
</span><span class='line'>  <span class="n">password</span> <span class="s2">&quot;$1$OxPMgmAb$bLlr2pIPTetowdIPOuCw20&quot;</span>
</span><span class='line'>  <span class="n">gid</span> <span class="s2">&quot;admin&quot;</span>
</span><span class='line'>  <span class="n">home</span> <span class="s2">&quot;/home/gogojimmy&quot;</span>
</span><span class='line'>  <span class="n">supports</span> <span class="n">manage_home</span><span class="p">:</span> <span class="kp">true</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>在這邊我使用<code>user</code>這個resource建立一個名為gogojimmy的user，指定為admin的group、指定家目錄，並且提昇家目錄管理權限，其中password欄位不是直接輸入密碼而是使用<code>openssl</code>加密後的hash字串，再怎麼說設定檔會放在版本控制系統中，加個密總是比較安全，你可以參考<a href="http://docs.opscode.com/resource_user.html#password-shadow-hash">Opscode的官方說明</a>做進一步的了解，在這邊產生密碼你可以使用openssl這套工具：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">openssl</span> <span class="o">-</span><span class="n">l</span> <span class="s1">&#39;password&#39;</span>
</span></code></pre></td></tr></table></div></figure>


<p>再來我們要做的事情就是把我們剛剛做好的recipe加入到<code>node.json</code>的<code>run_list</code>中：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='json'><span class='line'><span class="p">{</span>
</span><span class='line'>  <span class="nt">&quot;run_list&quot;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&quot;recipe[nginx]&quot;</span><span class="p">,</span> <span class="s2">&quot;recipe[user]&quot;</span><span class="p">]</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>再次執行後<code>chef-solo</code>你就會看到chef幫你建立好剛剛你所設定的user了：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="nv">$ </span>chef-solo solo.rb
</span><span class='line'>Starting Chef Client, version 11.4.0
</span><span class='line'>Compiling Cookbooks...
</span><span class='line'>Converging 3 resources
</span><span class='line'>Recipe: nginx::default
</span><span class='line'>  * package<span class="o">[</span>nginx<span class="o">]</span> action install <span class="o">(</span>up to date<span class="o">)</span>
</span><span class='line'>  * service<span class="o">[</span>nginx<span class="o">]</span> action start <span class="o">(</span>up to date<span class="o">)</span>
</span><span class='line'>Recipe: user::default
</span><span class='line'>  * user<span class="o">[</span>gogojimmy<span class="o">]</span> action create
</span><span class='line'>    - create user user<span class="o">[</span>gogojimmy<span class="o">]</span>
</span></code></pre></td></tr></table></div></figure>


<p>其實像是user的名稱寫死在recipe中也不太對勁，recipe重點是執行resouce的動作，因此我們其實可以把像是使用者名稱，資料夾位置等一些設定直接在我們的<code>node.json</code>檔案中寫好，讓recipe去存取就可以了，概念上你可以想像就像是Rails的i18n，現在讓我們把user的設定部分搬到<code>node.json</code>中，記得放在nginx的前面，Chef會按照run_list的順序執行，因為等等我們在Nginx的設定部分會需要到這個user，所以我們必須讓他在Nginx前面執行：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='json'><span class='line'><span class="p">{</span>
</span><span class='line'>  <span class="nt">&quot;run_list&quot;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&quot;recipe[user]&quot;</span><span class="p">,</span> <span class="s2">&quot;recipe[nginx]&quot;</span><span class="p">],</span>
</span><span class='line'>  <span class="nt">&quot;user&quot;</span><span class="p">:</span> <span class="p">{</span>
</span><span class='line'>    <span class="nt">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;gogojimmy&quot;</span><span class="p">,</span>
</span><span class='line'>    <span class="nt">&quot;password&quot;</span><span class="p">:</span> <span class="s2">&quot;$1$OxPMgmAb$bLlr2pIPTetowdIPOuCw20&quot;</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>而原先recipe的部份我們也要做修改：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">user</span> <span class="n">node</span><span class="o">[</span><span class="ss">:user</span><span class="o">][</span><span class="ss">:name</span><span class="o">]</span> <span class="k">do</span>
</span><span class='line'>  <span class="n">password</span> <span class="n">node</span><span class="o">[</span><span class="ss">:user</span><span class="o">][</span><span class="ss">:password</span><span class="o">]</span>
</span><span class='line'>  <span class="n">gid</span> <span class="s2">&quot;admin&quot;</span>
</span><span class='line'>  <span class="n">home</span> <span class="s2">&quot;/home/</span><span class="si">#{</span><span class="n">node</span><span class="o">[</span><span class="ss">:user</span><span class="o">][</span><span class="ss">:name</span><span class="o">]</span><span class="si">}</span><span class="s2">&quot;</span>
</span><span class='line'>  <span class="n">supports</span> <span class="n">manage_home</span><span class="p">:</span> <span class="kp">true</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>再次執行<code>chef-solo</code>：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="nv">$ </span>chef-solo solo.rb
</span><span class='line'>Starting Chef Client, version 11.4.0
</span><span class='line'>Compiling Cookbooks...
</span><span class='line'>Converging 3 resources
</span><span class='line'>Recipe: nginx::default
</span><span class='line'>  * package<span class="o">[</span>nginx<span class="o">]</span> action install <span class="o">(</span>up to date<span class="o">)</span>
</span><span class='line'>  * service<span class="o">[</span>nginx<span class="o">]</span> action start <span class="o">(</span>up to date<span class="o">)</span>
</span><span class='line'>Recipe: user::default
</span><span class='line'>  * user<span class="o">[</span>gogojimmy<span class="o">]</span> action create <span class="o">(</span>up to date<span class="o">)</span>
</span><span class='line'>Chef Client finished, 0 resources updated
</span></code></pre></td></tr></table></div></figure>


<p>一樣ok！只是因為我們剛剛已經新增過這個user了，因此他就不會在另外新增一個。</p>

<h2>善用template整理設定</h2>

<p>我們安裝的套件常常也需要我們編寫另外的設定檔，例如MySQL的my.cnf，或是針對網站本身的Nginx設定檔*.conf，在Chef中我們可以使用template來建立一份我們需要的設定檔，template是一個<code>erb</code>的檔案，意味著你可以在這個template中用變數及運算，例如我們現在先來新增一個Nginx的設定檔，我們在nginx的資料夾下新增一個templates的資料夾，templates下你要建立一個根據node作業系統的版本的資料夾用來存放針對不同作業系統的template，如果Chef找不到對應的作業系統時他就會去找有沒有default的template，因此我們在這邊新增default的資料夾就好，並且在default資料夾下新增一個<code>nginx.conf.erb</code>的檔案</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">server</span> <span class="p">{</span>
</span><span class='line'>  <span class="n">server_name</span> <span class="mi">33</span><span class="o">.</span><span class="mi">33</span><span class="o">.</span><span class="mi">33</span><span class="o">.</span><span class="mi">10</span><span class="p">;</span>
</span><span class='line'>  <span class="n">root</span> <span class="sr">/home/</span><span class="o">&lt;%=</span> <span class="n">node</span><span class="o">[</span><span class="ss">:user</span><span class="o">][</span><span class="ss">:name</span><span class="o">]</span> <span class="o">%&gt;</span><span class="sr">/demo;</span>
</span><span class='line'><span class="sr">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>我們很簡單的設定nginx建立一個host在user底下的demo站台的設定檔，再來我們回到剛剛Nginx的recipe中去告訴Chef使用這個template：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">template</span> <span class="s2">&quot;/etc/nginx/sites-enabled/nginx.conf&quot;</span> <span class="k">do</span>
</span><span class='line'>  <span class="n">source</span> <span class="s1">&#39;nginx.conf.erb&#39;</span>
</span><span class='line'>  <span class="n">notifies</span> <span class="ss">:restart</span><span class="p">,</span> <span class="s1">&#39;service[nginx]&#39;</span><span class="p">,</span> <span class="ss">:immediately</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>我們使用template這個resource，後面告訴Chef這個檔案要被存在哪個地方，以上面為例就是<code>/etc/nginx/sites-available/nginx.conf</code>，最後使用source的指令告訴Chef要去哪邊找這支template來用，<code>notifies</code>這個指令是一個callback，用來告訴Chef當這邊執行完畢的時候要去呼叫resource執行動作，並且指派等級是<code>immediately</code>，預設等級會是<code>delayed</code>，會在Chef執行完所有的工作後才執行，現在萬事具備，我們再來只要在user底下建立我們需要的demo資料夾以及一個簡單的‘index.html`檔案就可以了，在這邊我們也可以使用Chef來完成，為求迅速我直接將這部分寫在nginx的recipe中，現在這個reicipe的全貌會是這樣：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">package</span> <span class="s1">&#39;nginx&#39;</span>
</span><span class='line'>
</span><span class='line'><span class="n">service</span> <span class="s1">&#39;nginx&#39;</span> <span class="k">do</span>
</span><span class='line'>  <span class="n">supports</span> <span class="o">[</span><span class="ss">:status</span><span class="p">,</span> <span class="ss">:restart</span><span class="p">,</span> <span class="ss">:reload</span><span class="o">]</span>
</span><span class='line'>  <span class="n">action</span> <span class="ss">:start</span>
</span><span class='line'><span class="k">end</span>
</span><span class='line'>
</span><span class='line'><span class="n">directory</span> <span class="s2">&quot;/home/</span><span class="si">#{</span><span class="n">node</span><span class="o">[</span><span class="ss">:user</span><span class="o">][</span><span class="ss">:name</span><span class="o">]</span><span class="si">}</span><span class="s2">/demo&quot;</span> <span class="k">do</span>
</span><span class='line'>  <span class="n">owner</span> <span class="n">node</span><span class="o">[</span><span class="ss">:user</span><span class="o">][</span><span class="ss">:name</span><span class="o">]</span>
</span><span class='line'><span class="k">end</span>
</span><span class='line'>
</span><span class='line'><span class="n">file</span> <span class="s2">&quot;/home/</span><span class="si">#{</span><span class="n">node</span><span class="o">[</span><span class="ss">:user</span><span class="o">][</span><span class="ss">:name</span><span class="o">]</span><span class="si">}</span><span class="s2">/demo/index.html&quot;</span> <span class="k">do</span>
</span><span class='line'>  <span class="n">owner</span> <span class="n">node</span><span class="o">[</span><span class="ss">:user</span><span class="o">][</span><span class="ss">:name</span><span class="o">]</span>
</span><span class='line'>  <span class="n">content</span> <span class="s2">&quot;&lt;h1&gt;Hello gogojimmy!&lt;/h1&gt;&quot;</span>
</span><span class='line'><span class="k">end</span>
</span><span class='line'>
</span><span class='line'><span class="n">template</span> <span class="s2">&quot;/etc/nginx/sites-enabled/nginx.conf&quot;</span> <span class="k">do</span>
</span><span class='line'>  <span class="n">source</span> <span class="s1">&#39;nginx.conf.erb&#39;</span>
</span><span class='line'>  <span class="n">notifies</span> <span class="ss">:restart</span><span class="p">,</span> <span class="s1">&#39;service[nginx]&#39;</span><span class="p">,</span> <span class="ss">:immediately</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>這邊不多做解釋了，原理都是一樣的，再來我們就直接給他執行下去：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="nv">$ </span>chef-solo solo.rb
</span><span class='line'>Starting Chef Client, version 11.4.0
</span><span class='line'>Compiling Cookbooks...
</span><span class='line'>Converging 6 resources
</span><span class='line'>Recipe: nginx::default
</span><span class='line'>  * package<span class="o">[</span>nginx<span class="o">]</span> action install <span class="o">(</span>up to date<span class="o">)</span>
</span><span class='line'>  * service<span class="o">[</span>nginx<span class="o">]</span> action start <span class="o">(</span>up to date<span class="o">)</span>
</span><span class='line'>  * template<span class="o">[</span>/etc/nginx/sites-available/nginx.conf<span class="o">]</span> action create <span class="o">(</span>up to date<span class="o">)</span>
</span><span class='line'>  * directory<span class="o">[</span>/home/gogojimmy/demo<span class="o">]</span> action create
</span><span class='line'>    - create new directory /home/gogojimmy/demo
</span><span class='line'>    - change owner from <span class="s1">&#39;&#39;</span> to <span class="s1">&#39;gogojimmy&#39;</span>
</span><span class='line'>server <span class="o">{</span>
</span><span class='line'>
</span><span class='line'>  * file<span class="o">[</span>/home/gogojimmy/demo/index.html<span class="o">]</span> action create
</span><span class='line'>    - create new file /home/gogojimmy/demo/index.html with content checksum 375690
</span><span class='line'>        --- /tmp/chef-tempfile20130530-5037-1gsrh3h     2013-05-30 16:11:00.741313182 +0000
</span><span class='line'>        +++ /tmp/chef-diff20130530-5037-h4yu39     2013-05-30 16:11:00.741313182 +0000
</span><span class='line'>        @@ -0,0 +1 @@
</span><span class='line'>        +&lt;h1&gt;Hello World!&lt;/h1&gt;
</span><span class='line'>
</span><span class='line'>Recipe: user::default
</span><span class='line'>  * user<span class="o">[</span>gogojimmy<span class="o">]</span> action create <span class="o">(</span>up to date<span class="o">)</span>
</span><span class='line'>Chef Client finished, 2 resources updated
</span></code></pre></td></tr></table></div></figure>


<p>接下來在你host機器上的瀏覽器重新整理剛剛的網頁，你應該就能看到我們的結果了！至此為止，你已經完整的設定了一台機器，接下來你可以複製這些cookbooks下來，重新開一台新的VM然後灌好Chef-Solo，接著把複製下來的cookbooks丟上去後執行，Chef-Solo就會一氣呵成的幫你完成你剛剛做的那些作業，這輩子再也不用擔心裝機器的步驟了。</p>

<h2>搭配Knife讓Chef更好用</h2>

<p>即使Chef是這麼好用的工具，在使用上還是會有些讓人覺得麻煩的地方，例如說明明就是一個固定的資料結構但你得一直新增資料夾，在你想要安裝機器前你還必須手動先安裝Chef-Solo，剛剛所有的操作都在VM上，實務上如果我們使用VPS的時候，一直與VPS溝通耗時又耗力，為了解決這些問題，你就必須給Chef一把Knife，Knife是Opscode推出的一套command line的Chef管理工具，你可以在你的workingstation的機器使用Knife來管理VPS上的Chef，你甚至不用手動去幫機器安裝Chef-Solo，讓我們看看Knife可以做什麼，在我們的workingstation的機器上，讓我們先安裝好knife，針對我們所使用的是Chef-Solo，因此我們要安裝的是Knife-Solo：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="nv">$ </span>gem install knife-solo
</span></code></pre></td></tr></table></div></figure>


<p>裝好了<code>knife-solo</code>以後，讓我們使用<code>knife solo init</code> 來開啟一個新專案</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="nv">$ </span>knife solo init knife-solo-demo
</span><span class='line'><span class="nv">$ </span><span class="nb">cd </span>knife-solo-demo
</span><span class='line'><span class="nv">$ </span>tree
</span><span class='line'>.
</span><span class='line'>├── cookbooks
</span><span class='line'>├── data_bags
</span><span class='line'>├── nodes
</span><span class='line'>├── roles
</span><span class='line'>├── site-cookbooks
</span><span class='line'>└── solo.rb
</span></code></pre></td></tr></table></div></figure>


<p>沒錯！只要一個指令他就幫你把Chef基本的專案結構建立完成了！再來我們進行下一步之前我們先產生一個chef的設定檔，基本上這個設定檔可有可無，但是如果沒有的話每次你打指令的時候他會一直叫警告實在很惱人，因此我們還是先來產生一個設定檔：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="nv">$ </span>knife configure -r . --defaults
</span></code></pre></td></tr></table></div></figure>


<p>讓我們準備一台全新的VM，建立好後請指定好他的IP位置，不知道怎麼建立跟設定VM的請參考鍵人矬作<a href="http://gogojimmy.net/2013/05/26/vagrant-tutorial/">使用Vagrant練習環境佈署</a>，再來就是厲害的地方了：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="nv">$ </span>knife solo bootstrap vagrant@33.33.33.10
</span><span class='line'>Bootstrapping Chef...
</span><span class='line'>
</span><span class='line'>  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
</span><span class='line'>                                 Dload  Upload   Total   Spent    Left  Speed
</span><span class='line'>100  6510  100  6510    0     0   2649      0  0:00:02  0:00:02 --:--:--  4389
</span><span class='line'>Downloading Chef  <span class="k">for </span>ubuntu...
</span><span class='line'>Installing Chef
</span><span class='line'>Selecting previously unselected package chef.
</span><span class='line'><span class="o">(</span>Reading database ... 62744 files and directories currently installed.<span class="o">)</span>
</span><span class='line'>Unpacking chef <span class="o">(</span>from .../tmp.scDzMEID/chef__amd64.deb<span class="o">)</span> ...
</span><span class='line'>Setting up chef <span class="o">(</span>11.4.4-2.ubuntu.11.04<span class="o">)</span> ...
</span><span class='line'>Thank you <span class="k">for </span>installing Chef!
</span><span class='line'>Generating node config <span class="s1">&#39;nodes/33.33.33.10.json&#39;</span>...
</span><span class='line'>vagrant@33.33.33.10<span class="err">&#39;</span>s password:
</span><span class='line'>Starting Chef Client, version 11.4.0
</span><span class='line'>Compiling Cookbooks...
</span><span class='line'>Converging 0 resources
</span><span class='line'>Chef Client finished, 0 resources updated
</span></code></pre></td></tr></table></div></figure>


<p>看到沒有！<code>knife solo bootstrap</code>這個指令直接幫我們連到VM去安裝好Chef並且還上傳了我們的VM，其實<code>knife solo bootstrap</code>是<code>knife solo prepare</code>與<code>knife solo cook</code>兩個指令的組合，前面那個指令會幫我們的機器下載安裝Chef，而後面的指令是幫我們上傳本地端的cookbooks以及執行<code>chef-solo</code>，所以這個指令也只會用在第一次，再來以後我們都只要<code>cook</code>、<code>cook</code>、<code>cook</code>就對了！</p>

<p>再來讓我們跟剛剛一樣建立一個Nginx的設定吧，這次我們不用再慢慢的手工打造，讓我們直接用knife幫我們建立：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="nv">$ </span>knife cookbook create nginx
</span><span class='line'>** Creating cookbook nginx
</span><span class='line'>** Creating README <span class="k">for </span>cookbook: nginx
</span><span class='line'>** Creating CHANGELOG <span class="k">for </span>cookbook: nginx
</span><span class='line'>** Creating metadata <span class="k">for </span>cookbook: nginx
</span></code></pre></td></tr></table></div></figure>


<p>就跟剛剛一樣，knife就會幫我們在cookbooks下面建立好nginx的cookbook的基本專案結構，並且附上<code>default.rb</code>的基本設定檔等著我們去使用了，現在讓我們把剛剛學到的建立user跟nginx的相關設定都給他複製上去，過程就不贅述了只是複製貼上的工作我相信你行的，唯一要注意的是原先我們使用的<code>node.json</code>這支檔案現在不在根目錄下，而是在<code>nodes</code>這個資料夾下幫我們建立了一個以你設定的VM ip位置為檔名的<code>json</code>檔，記得把你的<code>run_list</code>以及參數的設定寫在這支檔案中，寫完後我們再<code>cook</code>一次</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="nv">$ </span>knife solo cook vagrant@33.33.33.10
</span><span class='line'>Checking Chef version...
</span><span class='line'><span class="o">[</span>2013-05-31T19:03:31+00:00<span class="o">]</span> INFO: *** Chef 10.12.0 ***
</span><span class='line'><span class="o">[</span>2013-05-31T19:03:32+00:00<span class="o">]</span> INFO: Setting the run_list to <span class="o">[</span><span class="s2">&quot;recipe[user]&quot;</span>, <span class="s2">&quot;recipe[nginx]&quot;</span><span class="o">]</span> from JSON
</span><span class='line'><span class="o">[</span>2013-05-31T19:03:32+00:00<span class="o">]</span> INFO: Run List is <span class="o">[</span>recipe<span class="o">[</span>user<span class="o">]</span>, recipe<span class="o">[</span>nginx<span class="o">]]</span>
</span><span class='line'><span class="o">[</span>2013-05-31T19:03:32+00:00<span class="o">]</span> INFO: Run List expands to <span class="o">[</span>user, nginx<span class="o">]</span>
</span><span class='line'><span class="o">[</span>2013-05-31T19:03:32+00:00<span class="o">]</span> INFO: Starting Chef Run <span class="k">for </span>vagrant-ubuntu-quantal-64
</span><span class='line'><span class="o">[</span>2013-05-31T19:03:32+00:00<span class="o">]</span> INFO: Running start handlers
</span><span class='line'><span class="o">[</span>2013-05-31T19:03:32+00:00<span class="o">]</span> INFO: Start handlers complete.
</span><span class='line'><span class="o">[</span>2013-05-31T19:03:32+00:00<span class="o">]</span> INFO: Processing user<span class="o">[</span>gogojimmy<span class="o">]</span> action create <span class="o">(</span>user::default line 10<span class="o">)</span>
</span><span class='line'><span class="o">[</span>2013-05-31T19:03:32+00:00<span class="o">]</span> INFO: Processing package<span class="o">[</span>nginx<span class="o">]</span> action install <span class="o">(</span>nginx::default line 10<span class="o">)</span>
</span><span class='line'><span class="o">[</span>2013-05-31T19:03:32+00:00<span class="o">]</span> INFO: Processing service<span class="o">[</span>nginx<span class="o">]</span> action start <span class="o">(</span>nginx::default line 12<span class="o">)</span>
</span><span class='line'><span class="o">[</span>2013-05-31T19:03:32+00:00<span class="o">]</span> INFO: service<span class="o">[</span>nginx<span class="o">]</span> started
</span><span class='line'><span class="o">[</span>2013-05-31T19:03:32+00:00<span class="o">]</span> INFO: Processing directory<span class="o">[</span>/home/gogojimmy/demo<span class="o">]</span> action create <span class="o">(</span>nginx::default line 17<span class="o">)</span>
</span><span class='line'><span class="o">[</span>2013-05-31T19:03:32+00:00<span class="o">]</span> INFO: Processing file<span class="o">[</span>/home/gogojimmy/demo/index.html<span class="o">]</span> action create <span class="o">(</span>nginx::default line 21<span class="o">)</span>
</span><span class='line'><span class="o">[</span>2013-05-31T19:03:32+00:00<span class="o">]</span> INFO: Processing template<span class="o">[</span>/etc/nginx/sites-enabled/nginx.conf<span class="o">]</span> action create <span class="o">(</span>nginx::default line 26<span class="o">)</span>
</span><span class='line'><span class="o">[</span>2013-05-31T19:03:32+00:00<span class="o">]</span> INFO: Chef Run <span class="nb">complete </span>in 0.425268589 seconds
</span><span class='line'><span class="o">[</span>2013-05-31T19:03:32+00:00<span class="o">]</span> INFO: Running report handlers
</span><span class='line'><span class="o">[</span>2013-05-31T19:03:32+00:00<span class="o">]</span> INFO: Report handlers <span class="nb">complete</span>
</span></code></pre></td></tr></table></div></figure>


<p>這時候你在打開瀏覽器連線，你會發現已經成功上線了，一切都是那麼美好，第一次做完的時候我興奮的立馬把VM殺掉直接重新跑<code>knife solo bootstrap</code>，結果就會是這樣：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
<span class='line-number'>55</span>
<span class='line-number'>56</span>
<span class='line-number'>57</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="nv">$ </span>knife solo bootstrap vagrant@33.33.33.10
</span><span class='line'>Bootstrapping Chef...
</span><span class='line'>Enter the password <span class="k">for </span>vagrant@33.33.33.10:
</span><span class='line'>  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
</span><span class='line'>                                 Dload  Upload   Total   Spent    Left  Speed
</span><span class='line'>100  6510  100  6510    0     0   6755      0 --:--:-- --:--:-- --:--:-- 10742
</span><span class='line'>Downloading Chef  <span class="k">for </span>ubuntu...
</span><span class='line'>Installing Chef
</span><span class='line'><span class="o">(</span>Reading database ... 63748 files and directories currently installed.<span class="o">)</span>
</span><span class='line'>Preparing to replace chef 10.12.0-2 <span class="o">(</span>using .../tmp.EMBKgQMR/chef__amd64.deb<span class="o">)</span> ...
</span><span class='line'> * Stopping chef-client chef-client                                      <span class="o">[</span> OK <span class="o">]</span>
</span><span class='line'>Unpacking replacement chef ...
</span><span class='line'>dpkg: warning: unable to delete old directory <span class="s1">&#39;/var/log/chef&#39;</span>: Directory not empty
</span><span class='line'>dpkg: warning: unable to delete old directory <span class="s1">&#39;/etc/chef&#39;</span>: Directory not empty
</span><span class='line'>Setting up chef <span class="o">(</span>11.4.4-2.ubuntu.11.04<span class="o">)</span> ...
</span><span class='line'>Thank you <span class="k">for </span>installing Chef!
</span><span class='line'>Processing triggers <span class="k">for </span>man-db ...
</span><span class='line'>Processing triggers <span class="k">for </span>ureadahead ...
</span><span class='line'>Starting Chef Client, version 11.4.4
</span><span class='line'>Compiling Cookbooks...
</span><span class='line'>Converging 6 resources
</span><span class='line'>Recipe: user::default
</span><span class='line'>  * user<span class="o">[</span>gogojimmy<span class="o">]</span> action create
</span><span class='line'>    - create user user<span class="o">[</span>gogojimmy<span class="o">]</span>
</span><span class='line'>
</span><span class='line'>Recipe: nginx::default
</span><span class='line'>  * package<span class="o">[</span>nginx<span class="o">]</span> action install
</span><span class='line'>    - install version 1.2.1-2.2ubuntu0.1 of package nginx
</span><span class='line'>
</span><span class='line'>  * service<span class="o">[</span>nginx<span class="o">]</span> action start
</span><span class='line'>    - start service service<span class="o">[</span>nginx<span class="o">]</span>
</span><span class='line'>
</span><span class='line'>  * directory<span class="o">[</span>/home/gogojimmy/demo<span class="o">]</span> action create
</span><span class='line'>    - create new directory /home/gogojimmy/demo
</span><span class='line'>    - change owner from <span class="s1">&#39;&#39;</span> to <span class="s1">&#39;gogojimmy&#39;</span>
</span><span class='line'>
</span><span class='line'>  * file<span class="o">[</span>/home/gogojimmy/demo/index.html<span class="o">]</span> action create
</span><span class='line'>    - create new file /home/gogojimmy/demo/index.html with content checksum 60ce4d
</span><span class='line'>        --- /tmp/chef-tempfile20130531-3432-c8tneh     2013-05-31 19:14:01.334694350 +0000
</span><span class='line'>        +++ /tmp/chef-diff20130531-3432-1q1fx4z     2013-05-31 19:14:01.334694350 +0000
</span><span class='line'>        @@ -0,0 +1 @@
</span><span class='line'>        +&lt;h1&gt;Hello gogojimmy!&lt;/h1&gt;
</span><span class='line'>
</span><span class='line'>  * template<span class="o">[</span>/etc/nginx/sites-enabled/nginx.conf<span class="o">]</span> action create
</span><span class='line'>    - create template<span class="o">[</span>/etc/nginx/sites-enabled/nginx.conf<span class="o">]</span>
</span><span class='line'>        --- /tmp/chef-tempfile20130531-3432-18v20jr     2013-05-31 19:14:01.418694347 +0000
</span><span class='line'>        +++ /tmp/chef-rendered-template20130531-3432-w0bdgr     2013-05-31 19:14:01.414694348 +0000
</span><span class='line'>        @@ -0,0 +1,4 @@
</span><span class='line'>        +server <span class="o">{</span>
</span><span class='line'>        +  server_name 33.33.33.10;
</span><span class='line'>        +  root /home/gogojimmy/demo;
</span><span class='line'>        +<span class="o">}</span>
</span><span class='line'>
</span><span class='line'>  * service<span class="o">[</span>nginx<span class="o">]</span> action restart
</span><span class='line'>    - restart service service<span class="o">[</span>nginx<span class="o">]</span>
</span><span class='line'>
</span><span class='line'>Chef Client finished, 7 resources updated
</span></code></pre></td></tr></table></div></figure>


<p>一鍵完成阿！！！！看到這你能不流淚嗎？想到過往裝機器而逝去的那些青春，我就不禁鼻酸啊(擦眼角</p>

<p>最後，其實Opscode本身的社群有非常豐富的資源，有很多其他人已經寫好的<code>cookbook</code>你可以直接下載使用，畢竟我們就是不喜歡造輪子，你可以在<a href="http://community.opscode.com/cookbooks">這裡</a>找到這些cookbook，想像就像是Ruby的<a href="ruby-toolbox.com">ruby-toolbox</a>吧！再來在下篇要跟大家分享的是如何使用Chef搭配Capistrano進行佈署的策略，現在我把螢幕切還給你們，各位同學可以自己練習看看，有什麼不懂的，可以在下面提問。</p>

<h2>Q&amp;A</h2>

<p>Q：Chef裡面那些Cookbooks啊..Recipe啊…這些名詞好容易搞混，可以解釋一下嗎？</p>

<p>A：關於這個問題，當初我在看文件的時候也覺得這些洋人裝什麼生活化，再怎麼生活化人家看我們還不是一堆宅宅嗎？Anyway我也還寫不出一套Chef，所以我們還是遷就一下洋人，不過我可以為這些名次跟你做些解釋：</p>

<ul>
<li><code>node</code>：指的就是你要安裝的機器</li>
<li><code>workstation</code>：指的就是你用來編輯食譜的機器，也就是你用來上PTT跟Facebook跟寫程式的這台電腦</li>
<li><code>kitchen</code>：指的就是你的chef專案所在地，有<code>solo.rb</code>所在的地方</li>
<li><code>recipe</code>：指的就是你用Ruby DSL語法寫成用來指示<code>node</code>該怎麼進行安裝設定的文件</li>
<li><code>cookbook</code>：不只存放<code>recipe</code>，同時也存放了像是<code>template</code>、<code>attributes</code>等resource的地方，就是一個含有安裝步驟、設定、樣式的食譜集</li>
<li><code>resource</code>：一個抽象的概念，通常就是指&#8221;套件&#8221;、&#8221;使用者&#8221;這種跟平台是什麼無關的抽象名詞。</li>
<li><code>data bag</code>：就是你用來設定<code>node</code>一些參數的JSON檔</li>
</ul>


<p>Q：既然<code>knife-solo</code>這麼方便，為什麼不一開始跟大家說這個就好了呢，幹嘛花那麼多時間在這上面，<code>mkdir</code>打多了也是很煩人的</p>

<p>A：難道你以為我打字有比較輕鬆嗎？我這都是為了讓你更能仔細了解Chef的檔案結構啊，如此用心良苦你是不是應該移動你的滑鼠去下面幫我點個廣告呢？</p>

<p>Q：你這篇文章實在太長了，要看完實在很累，下次可以寫短一點嗎？</p>

<p>A：我很貼心的準備了<a href="http://bit.ly/chef-solo-basic">PDF版本下載</a>，讓你可以下載到iPad慢慢看。</p>

<h2>實用連結及參考資訊</h2>

<ul>
<li><a href="http://matschaffer.github.io/knife-solo/">knife-solo</a></li>
<li><a href="http://devops.mashion.net/2011/08/04/chef-solo-basics/">chef-solo-basic</a></li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[[教學]使用Vagrant練習環境佈署]]></title>
    <link href="http://gogojimmy.net/2013/05/26/vagrant-tutorial/"/>
    <updated>2013-05-26T15:09:00+08:00</updated>
    <id>http://gogojimmy.net/2013/05/26/vagrant-tutorial</id>
    <content type="html"><![CDATA[<p>最近對Rails的佈署有更深一層的體悟，打算花點時間將佈署心得整理成文章，預計大概會在2050年前完成這部大作，這邊決定先發布序章，就是教你怎麼使用Vagrant來打造自己的測試機器。</p>

<!-- more -->


<h2>為什麼要用Vagrant</h2>

<p>答案很簡單，因為開遠端機器練習佈署或是機器架構又慢又麻煩又要錢，而且玩壞了或是環境髒了又得重灌又很慢，如果你想我一樣最近在玩Chef-Server，實驗多機器環境架構，例如一台Web Load Balancer、5台Application Server、1台Master Database + 1台Slave Database、1台Data Analytics Server、1台 Service Monitor Server，1台Redis Server，聽起來有沒有很牛，但這一切都只要在你那台用來開發的電腦就做的到，告訴我為何你還不用Vagrant呢？</p>

<h2>Vagrant Basic</h2>

<h3>下載安裝Vagrant與Virtual Box</h3>

<p>Vagrant背後用的是Virtual Box作為虛擬機器，Vagrant只是一個讓你可以方面做設定來開你想要的虛擬機器的方便工具，所以你必須先安裝Vagrant和Virtual Box，Virtual Box你可以在<a href="https://www.virtualbox.org/">Virtual Box官網</a>下載適合你平台的版本，而Vagrant你可以在<a href="http://www.vagrantup.com/">Vagrant官網</a>下載打包好的版本，或是如果你跟我一樣是個玉樹臨風的Rubist，你可以打開我們最愛的小黑視窗輸入</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>$ gem install vagrant</span></code></pre></td></tr></table></div></figure>


<blockquote><p>Vagrant 1.1後已經不支援使用Gem來安裝了，據說是因為Dependecy太多他受不了了，詳細可以參考他們的<a href="http://mitchellh.com/abandoning-rubygems">說明</a>，感謝ihower的提示</p></blockquote>

<h2>開始使用Vagrant 新增作業系統</h2>

<p>當你已經安裝好Virtual Box以及Vagrant後，你要開始思考你想要在你的VM上使用什麼作業系統，一個打包好的作業系統環境在Vagrant稱之為Box，也就是說每個Box都是一個打包好的作業系統環境，當然網路上什麼都有，你不用自己去找作業系統，<a href="vagrantbox.es">vagrantbox.es</a>上面就有許多大家熟知且已經打包好的作業系統，你只需要下載就可以了，為你的Vagrant增加一個Box很簡單</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>$ vagrant box add {你想要的Box名稱} {下載網址}</span></code></pre></td></tr></table></div></figure>


<p>例如我想要下載我最愛的<del>也只會這個的</del>Ubuntu，我只要在<a href="vagrantbox.es">vagrantbox.es</a>挑好我想要的版本，一樣在我們的小黑視窗輸入</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>$ vagrant box add {Ubuntu-12-10} {http://cloud-images.ubuntu.com/quantal/current/quantal-server-cloudimg-vagrant-amd64-disk1.box}</span></code></pre></td></tr></table></div></figure>


<p>Vagrant就會開始下載這個Box，你可以用<code>vagrant box list</code>這個指令看到你所擁有的所有Box，想像就是你的書架上多了一片Ubuntu 12.10的安裝光碟，以後要安裝機器就是用這的安裝就可以了，有了Box以後，我們要產生一個設定檔來設定我們的虛擬機器，這個檔案可以透過指令<code>vagrant init Box名稱</code>來產生，你可以在你的專案中或是另外開個練習用的資料夾輸入，這時候你的資料夾終究會有一個名稱為<code>vagrantfile</code>的檔案，這個檔案就是所有魔法的開始</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>~/Dropbox/Projects/Personal/vagrant » vagrant init ubuntu-12-10                                                                                             gogojimmy@MBP
</span><span class='line'>A `vagrantfile` has been placed in this directory. You are now
</span><span class='line'>ready to `vagrant up` your first virtual environment! Please read
</span><span class='line'>the comments in the vagrantfile as well as documentation on
</span><span class='line'>`vagrantup.com` for more information on using Vagrant.</span></code></pre></td></tr></table></div></figure>


<h3>讓VM動起來</h3>

<p>我們晚一點再提設定檔的部份，讓我們先把VM跑起來，要讓VM跑起來的指令是<code>vagrant up</code></p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>$ vagrant up                                                                                                      gogojimmy@MBP
</span><span class='line'>[default] VM already created. Booting if it's not already running...
</span><span class='line'>[default] Clearing any previously set forwarded ports...
</span><span class='line'>[default] Forwarding ports...
</span><span class='line'>[default] -- 22 =&gt; 2222 (adapter 1)
</span><span class='line'>[default] Creating shared folders metadata...
</span><span class='line'>[default] Clearing any previously set network interfaces...
</span><span class='line'>[default] Booting VM...
</span><span class='line'>[default] Waiting for VM to boot. This can take a few minutes.
</span><span class='line'>[default] VM booted and ready for use!
</span><span class='line'>[default] The guest additions on this VM do not match the install version of
</span><span class='line'>VirtualBox! This may cause things such as forwarded ports, shared
</span><span class='line'>folders, and more to not work properly. If any of those things fail on
</span><span class='line'>this machine, please update the guest additions and repackage the
</span><span class='line'>box.
</span><span class='line'>
</span><span class='line'>Guest Additions Version: 4.1.18
</span><span class='line'>VirtualBox Version: 4.2.12
</span><span class='line'>[default] Mounting shared folders...
</span><span class='line'>[default] -- v-root: /vagrant</span></code></pre></td></tr></table></div></figure>


<p>看到以上這段指令跑完後，讓我們用<code>ssh</code>連線到機器</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>$ vagrant ssh</span></code></pre></td></tr></table></div></figure>


<p>現在你已經在虛擬機器上了，登入的用戶是預設的<code>vagrant</code>，現在你可以開始玩壞你的機器了，在VM中有個<code>/vagrant</code>的資料夾會與你<code>host</code>機器的vagrant設定檔所在的資料夾共享資料，現在你可以照你的習慣把機器的環境安裝起來，例如以我來說我會先安裝Ruby以及一些基本的系統套件，你可以在我的<a href="https://gist.github.com/gogojimmy/5523985">Gist</a>上看到這個裝機步驟，等我一下，讓我先把我的環境先安裝好，你可以先去玩一下Candy Crush或是看一下Facebook</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>vagrant@vagrant-ubuntu-quantal-64:~$ curl -L https://gist.github.com/gogojimmy/5523985/raw/b9d777bc380ee791c2f4534e9261b4b99289ed9f/bootstrap-chef-solo.sh | sh</span></code></pre></td></tr></table></div></figure>


<h3>將習慣的環境打包成Box</h3>

<p>我的環境大概要裝15分鐘左右，有人說一個程式設計師的人生花最多時間的一件事情就是等，這個說法真是一點也不為過，每次裝機都從環境重新開始Build也不是辦法，我們要讓人生有更多的時間去處理更多的事情，所以我們可以把一個已經Build好的環境打包成一個我們自己的Box，以後我們只要直接使用這個打包好的版本就可以了，因此讓我們幫自己的人生省點時間，速速登出VM來打包這個Box</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>vagrant@vagrant-ubuntu-quantal-64:~$ exit
</span><span class='line'>logout
</span><span class='line'>Connection to 127.0.0.1 closed.
</span><span class='line'>------------------------------------------------------------
</span><span class='line'>$ vagrant package                                                                                                 gogojimmy@JoycetekiMacBook-Pro
</span><span class='line'>[default] Attempting graceful shutdown of VM...
</span><span class='line'>[default] Clearing any previously set forwarded ports...
</span><span class='line'>[default] Creating temporary directory for export...
</span><span class='line'>[default] Exporting VM...
</span><span class='line'>[default] Compressing package to: /Users/gogojimmy/Dropbox/Projects/Personal/vagrant/package.box</span></code></pre></td></tr></table></div></figure>


<p><code>vagrant package</code>這個指令會在你目前的資料夾下建立一個<code>vagrant.box</code>的Box檔案，這時候我們跟剛剛一樣把它加入到我們的Box List中，以後我們就可以快速使用這個Box就好了！除此之外，可以自定Box的意義還有讓你的團隊都能用VM來擁有自己的Staging環境，例如在Rails專案中我們也可以建立一個Vagrant的設定檔來做一個給開發人員測試用的Staging環境，這時候你就可以指定好你自定的機器設定，確保每個開發人員都能擁有一樣的環境來進行開發。</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>$ vagrant box add gogojimmy-ubuntu-12-10 package.box
</span><span class='line'>$ vagrant list</span></code></pre></td></tr></table></div></figure>


<h3>Vagrant基本設定</h3>

<h4>設定VM的名稱及記憶體</h4>

<p>用你最喜歡的編輯器打開<code>vagrantfile</code>，<code>vagrantfile</code>是個有著詳細解釋的設定檔，非常建議妳好好看過一遍來了解每個設定，當然我也知道今天你會來看這篇文章就表示你根本懶得看英文，就讓我來幫你一步步走過，在這個檔案中所有的設定都被<code>Vagrant::Config.run</code>的Block包起來，在一開始只會有box的設定：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">config</span><span class="o">.</span><span class="n">vm</span><span class="o">.</span><span class="n">box</span> <span class="o">=</span> <span class="s2">&quot;gogojimmy-ubuntu-12-10&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<p>這告訴了Vagrant要去套用哪個Box作為環境，也就是你一開始輸入<code>varant init Box名稱</code>時所指定的Box，如果沒有輸入Box名稱的話就會是預設的<code>base</code>，Virtual Box本身提供了<code>VBoxManage</code>這個command line tool讓你可以設定你的VM，用<code>modifyvm</code>這個指令讓你可以設定VM的名稱及記憶體大小等等，這裡說的名稱指的是在Virtual Box中顯示的名稱，我們也可以在<code>vagrantfile</code>中進行設定，在你的<code>vagrantfile</code>中加入這行</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">config</span><span class="o">.</span><span class="n">vm</span><span class="o">.</span><span class="n">customize</span> <span class="o">[</span><span class="s2">&quot;modifyvm&quot;</span><span class="p">,</span> <span class="ss">:id</span><span class="p">,</span> <span class="s2">&quot;--name&quot;</span><span class="p">,</span> <span class="s2">&quot;gogojimmy&quot;</span><span class="p">,</span> <span class="s2">&quot;--memory&quot;</span><span class="p">,</span> <span class="s2">&quot;512&quot;</span><span class="o">]</span>
</span></code></pre></td></tr></table></div></figure>


<p>這行設定檔意思就是呼叫VBoxManage的<code>modifyvm</code>的指令，設定VM的名稱為<code>gogojimmy</code>，而設定VM的記憶體大小為512MB，你可以照這這種作法為你的VM設定好不同的設定。</p>

<h4>設定Hostname以及Port forward</h4>

<p>設定<code>hostname</code>非常簡單，設定中加入下面這行就好</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">config</span><span class="o">.</span><span class="n">vm</span><span class="o">.</span><span class="n">host_name</span> <span class="o">=</span> <span class="s2">&quot;gogojimmy-app&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<p>設定<code>hostname</code>非常重要，有很多服務都仰賴著<code>hostname</code>來做為辨識，例如<code>Puppet</code>或是<code>Chef</code>，一般一些監控服務像是New Relic之類的也都是以<code>hostname</code>來做為辨識，設定那麼簡單，就別偷懶吧，再來看到下面這行</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">config</span><span class="o">.</span><span class="n">vm</span><span class="o">.</span><span class="n">forward_port</span> <span class="mi">80</span><span class="p">,</span> <span class="mi">8080</span>
</span></code></pre></td></tr></table></div></figure>


<p>這個設定非常的厲害，這行的意思就是把Host機器上8080 port傳來的東西<code>forward</code>到VM跑的 80 port的服務，例如當你練習Deploy到VM上時，在瀏覽器打開<a href="http://localhost:8080%E6%99%82%E5%B0%B1%E6%9C%83%E5%82%B3%E5%85%A5VM%E8%A3%A1%E8%B7%9180">http://localhost:8080%E6%99%82%E5%B0%B1%E6%9C%83%E5%82%B3%E5%85%A5VM%E8%A3%A1%E8%B7%9180</a> port的服務像是Nginx或是Apache，因此我們可以透過這個設定來幫助我們去設定Host與VM間，或是VM與VM間的溝通，像是MySQL通常跑3306之類的</p>

<h4>設定網路橋接方式</h4>

<p>Vagrant有兩種橋接方式是，一種是host only，意思是說在你電腦同個區網中的其他電腦是看不到你的VM的，只有你一個人自High，另一種是Bridge，當然就是說VM會跟你區網的router去要一組IP，區網中的其他電腦也都能看到他，一般來說因為開VM的情況都是自High居多，因此我們在設定上都是設定host only：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">config</span><span class="o">.</span><span class="n">vm</span><span class="o">.</span><span class="n">network</span> <span class="ss">:hostonly</span><span class="p">,</span> <span class="s2">&quot;192.168.33.10&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<p>這邊將網路設定成<code>hostonly'，並且指定一組IP位址，IP位址的設定會建議不要使用</code>192.168.<em>.</em><code>的設定，因為很有可能會跟你區網的IP衝突，你可以改使用像是</code>33.33.<em>.</em>`的設定。</p>

<blockquote><p>更改vagrantfile的設定後，記得要用<code>vagrant reload</code>的指令重開VM讓VM可以用新的設定檔跑起來</p></blockquote>

<h3>讓我們開始打造多機器環境</h3>

<p>重頭戲來了，前面的一切都是為了今天鋪陳，現在我們要建立多個VM跑起來，並且讓他們互相溝通，有人跑Application、有人跑DB、有人跑Memcached，這一切在Vagrant中非常簡單，跟剛剛的設定都一樣，你只需要指定好機器的角色就可以了，讓我們再次打開我們的設定檔來設定一台APP Server加上一台DB Server：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">config</span><span class="o">.</span><span class="n">vm</span><span class="o">.</span><span class="n">define</span> <span class="ss">:app</span> <span class="k">do</span> <span class="o">|</span><span class="n">app_config</span><span class="o">|</span>
</span><span class='line'>    <span class="n">app_config</span><span class="o">.</span><span class="n">vm</span><span class="o">.</span><span class="n">customize</span> <span class="o">[</span><span class="s2">&quot;modifyvm&quot;</span><span class="p">,</span> <span class="ss">:id</span><span class="p">,</span> <span class="s2">&quot;--name&quot;</span><span class="p">,</span> <span class="s2">&quot;app&quot;</span><span class="p">,</span> <span class="s2">&quot;--memory&quot;</span><span class="p">,</span> <span class="s2">&quot;512&quot;</span><span class="o">]</span>
</span><span class='line'>    <span class="n">app_config</span><span class="o">.</span><span class="n">vm</span><span class="o">.</span><span class="n">box</span> <span class="o">=</span> <span class="s2">&quot;ubuntu-12-10&quot;</span>
</span><span class='line'>    <span class="n">app_config</span><span class="o">.</span><span class="n">vm</span><span class="o">.</span><span class="n">host_name</span> <span class="o">=</span> <span class="s2">&quot;app&quot;</span>
</span><span class='line'>    <span class="n">app_config</span><span class="o">.</span><span class="n">vm</span><span class="o">.</span><span class="n">network</span> <span class="ss">:hostonly</span><span class="p">,</span> <span class="s2">&quot;33.33.13.10&quot;</span>
</span><span class='line'><span class="k">end</span>
</span><span class='line'><span class="n">config</span><span class="o">.</span><span class="n">vm</span><span class="o">.</span><span class="n">define</span> <span class="ss">:db</span> <span class="k">do</span> <span class="o">|</span><span class="n">db_config</span><span class="o">|</span>
</span><span class='line'>  <span class="n">db_config</span><span class="o">.</span><span class="n">vm</span><span class="o">.</span><span class="n">customize</span> <span class="o">[</span><span class="s2">&quot;modifyvm&quot;</span><span class="p">,</span> <span class="ss">:id</span><span class="p">,</span> <span class="s2">&quot;--name&quot;</span><span class="p">,</span> <span class="s2">&quot;db&quot;</span><span class="p">,</span> <span class="s2">&quot;--memory&quot;</span><span class="p">,</span> <span class="s2">&quot;512&quot;</span><span class="o">]</span>
</span><span class='line'>  <span class="n">db_config</span><span class="o">.</span><span class="n">vm</span><span class="o">.</span><span class="n">box</span> <span class="o">=</span> <span class="s2">&quot;ubuntu-12-10&quot;</span>
</span><span class='line'>  <span class="n">db_config</span><span class="o">.</span><span class="n">vm</span><span class="o">.</span><span class="n">host_name</span> <span class="o">=</span> <span class="s2">&quot;db&quot;</span>
</span><span class='line'>  <span class="n">db_config</span><span class="o">.</span><span class="n">vm</span><span class="o">.</span><span class="n">network</span> <span class="ss">:hostonly</span><span class="p">,</span> <span class="s2">&quot;33.33.13.11&quot;</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>這邊的設定就像是剛剛在設定的部份教的一樣，只是我們使用了<code>:app</code>以及<code>:db</code>分別做了兩個VM的設定，並且給予不同的<code>hostname</code>和IP，設定好了以後再使用<code>vagrant up</code>將機器跑起來：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="err">$</span> <span class="n">vagrant</span> <span class="n">up</span>
</span><span class='line'><span class="o">[</span><span class="n">app</span><span class="o">]</span> <span class="no">Importing</span> <span class="n">base</span> <span class="n">box</span> <span class="s1">&#39;ubuntu-12-10&#39;</span><span class="o">.</span><span class="n">.</span><span class="o">.</span>
</span><span class='line'><span class="o">[</span><span class="n">app</span><span class="o">]</span> <span class="no">Matching</span> <span class="no">MAC</span> <span class="n">address</span> <span class="k">for</span> <span class="no">NAT</span> <span class="n">networking</span><span class="o">.</span><span class="n">.</span><span class="o">.</span>
</span><span class='line'><span class="o">[</span><span class="n">app</span><span class="o">]</span> <span class="no">Clearing</span> <span class="n">any</span> <span class="n">previously</span> <span class="n">set</span> <span class="n">forwarded</span> <span class="n">ports</span><span class="o">.</span><span class="n">.</span><span class="o">.</span>
</span><span class='line'><span class="o">[</span><span class="n">app</span><span class="o">]</span> <span class="no">Forwarding</span> <span class="n">ports</span><span class="o">.</span><span class="n">.</span><span class="o">.</span>
</span><span class='line'><span class="o">[</span><span class="n">app</span><span class="o">]</span> <span class="o">--</span> <span class="mi">22</span> <span class="o">=&gt;</span> <span class="mi">2222</span> <span class="p">(</span><span class="n">adapter</span> <span class="mi">1</span><span class="p">)</span>
</span><span class='line'><span class="o">[</span><span class="n">app</span><span class="o">]</span> <span class="o">--</span> <span class="mi">80</span> <span class="o">=&gt;</span> <span class="mi">8080</span> <span class="p">(</span><span class="n">adapter</span> <span class="mi">1</span><span class="p">)</span>
</span><span class='line'><span class="o">[</span><span class="n">app</span><span class="o">]</span> <span class="no">Creating</span> <span class="n">shared</span> <span class="n">folders</span> <span class="n">metadata</span><span class="o">.</span><span class="n">.</span><span class="o">.</span>
</span><span class='line'><span class="o">[</span><span class="n">app</span><span class="o">]</span> <span class="no">Clearing</span> <span class="n">any</span> <span class="n">previously</span> <span class="n">set</span> <span class="n">network</span> <span class="n">interfaces</span><span class="o">.</span><span class="n">.</span><span class="o">.</span>
</span><span class='line'><span class="o">[</span><span class="n">app</span><span class="o">]</span> <span class="no">Preparing</span> <span class="n">network</span> <span class="n">interfaces</span> <span class="n">based</span> <span class="n">on</span> <span class="n">configuration</span><span class="o">.</span><span class="n">.</span><span class="o">.</span>
</span><span class='line'><span class="o">[</span><span class="n">app</span><span class="o">]</span> <span class="no">Running</span> <span class="n">any</span> <span class="no">VM</span> <span class="n">customizations</span><span class="o">.</span><span class="n">.</span><span class="o">.</span>
</span><span class='line'><span class="o">[</span><span class="n">app</span><span class="o">]</span> <span class="no">Booting</span> <span class="no">VM</span><span class="o">.</span><span class="n">.</span><span class="o">.</span>
</span><span class='line'><span class="o">[</span><span class="n">app</span><span class="o">]</span> <span class="no">Waiting</span> <span class="k">for</span> <span class="no">VM</span> <span class="n">to</span> <span class="n">boot</span><span class="o">.</span> <span class="no">This</span> <span class="n">can</span> <span class="n">take</span> <span class="n">a</span> <span class="n">few</span> <span class="n">minutes</span><span class="o">.</span>
</span><span class='line'><span class="o">[</span><span class="n">app</span><span class="o">]</span> <span class="no">VM</span> <span class="n">booted</span> <span class="ow">and</span> <span class="n">ready</span> <span class="k">for</span> <span class="n">use!</span>
</span><span class='line'><span class="o">[</span><span class="n">app</span><span class="o">]</span> <span class="no">Configuring</span> <span class="ow">and</span> <span class="n">enabling</span> <span class="n">network</span> <span class="n">interfaces</span><span class="o">.</span><span class="n">.</span><span class="o">.</span>
</span><span class='line'><span class="o">[</span><span class="n">app</span><span class="o">]</span> <span class="no">Setting</span> <span class="n">host</span> <span class="nb">name</span><span class="o">.</span><span class="n">.</span><span class="o">.</span>
</span><span class='line'><span class="o">[</span><span class="n">app</span><span class="o">]</span> <span class="no">Mounting</span> <span class="n">shared</span> <span class="n">folders</span><span class="o">.</span><span class="n">.</span><span class="o">.</span>
</span><span class='line'><span class="o">[</span><span class="n">app</span><span class="o">]</span> <span class="o">--</span> <span class="n">v</span><span class="o">-</span><span class="n">root</span><span class="p">:</span> <span class="sr">/vagrant</span>
</span><span class='line'><span class="sr">[db] Importing base box &#39;ubuntu-12-10&#39;...</span>
</span><span class='line'><span class="sr">[db] Matching MAC address for NAT networking...</span>
</span><span class='line'><span class="sr">[db] Clearing any previously set forwarded ports...</span>
</span><span class='line'><span class="sr">[db] Fixed port collision for 22 =&gt; 2222. Now on port 2200.</span>
</span><span class='line'><span class="sr">[db] Fixed port collision for 22 =&gt; 2222. Now on port 2201.</span>
</span><span class='line'><span class="sr">[db] Forwarding ports...</span>
</span><span class='line'><span class="sr">[db] -- 22 =&gt; 2201 (adapter 1)</span>
</span><span class='line'><span class="sr">[db] Creating shared folders metadata...</span>
</span><span class='line'><span class="sr">[db] Clearing any previously set network interfaces...</span>
</span><span class='line'><span class="sr">[db] Preparing network interfaces based on configuration...</span>
</span><span class='line'><span class="sr">[db] Running any VM customizations...</span>
</span><span class='line'><span class="sr">[db] Booting VM...</span>
</span><span class='line'><span class="sr">[db] Waiting for VM to boot. This can take a few minutes.</span>
</span><span class='line'><span class="sr">[db] VM booted and ready for use!</span>
</span><span class='line'><span class="sr">[db] Configuring and enabling network interfaces...</span>
</span><span class='line'><span class="sr">[db] Setting host name...</span>
</span><span class='line'><span class="sr">[db] Mounting shared folders...</span>
</span><span class='line'><span class="sr">[db] -- v-root: /</span><span class="n">vagrant</span>
</span></code></pre></td></tr></table></div></figure>


<p>看到上面的訊息跑完後，你就可以跟剛剛一樣使用<code>ssh</code>連到VM裡，但這次不同的是你要加上你所指定的角色告訴你要連線的機器是哪一台：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="err">$</span> <span class="n">vagrant</span> <span class="n">ssh</span> <span class="n">app</span>
</span><span class='line'><span class="n">vagrant</span><span class="vi">@app</span><span class="ss">:~</span><span class="err">$</span>
</span><span class='line'>
</span><span class='line'><span class="err">$</span> <span class="n">vagrant</span> <span class="n">ssh</span> <span class="n">db</span>
</span><span class='line'><span class="n">vagrant</span><span class="vi">@db</span><span class="ss">:~</span><span class="err">$</span>
</span></code></pre></td></tr></table></div></figure>


<p>是不是很酷！！再來我們來驗證一下VM之間的連線，讓我們使用<code>ssh</code>登入<code>db</code>的機器，然後在<code>db</code>的機器上使用<code>ssh</code>來連線到<code>app</code>的機器(預設密碼就是vagrant)：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="err">$</span> <span class="n">vagrant</span> <span class="n">ssh</span> <span class="n">db</span>
</span><span class='line'>
</span><span class='line'><span class="n">vagrant</span><span class="vi">@db</span><span class="ss">:~</span><span class="err">$</span> <span class="n">ssh</span> <span class="mi">33</span><span class="o">.</span><span class="mi">33</span><span class="o">.</span><span class="mi">13</span><span class="o">.</span><span class="mi">10</span>
</span><span class='line'><span class="no">The</span> <span class="n">authenticity</span> <span class="n">of</span> <span class="n">host</span> <span class="s1">&#39;33.33.13.10 (33.33.13.10)&#39;</span> <span class="n">can</span><span class="s1">&#39;t be established.</span>
</span><span class='line'><span class="s1">ECDSA key fingerprint is a7:71:36:4c:01:4a:38:a2:fc:fa:ea:d7:67:63:3c:40.</span>
</span><span class='line'><span class="s1">Are you sure you want to continue connecting (yes/no)? yes</span>
</span><span class='line'><span class="s1">Warning: Permanently added &#39;</span><span class="mi">33</span><span class="o">.</span><span class="mi">33</span><span class="o">.</span><span class="mi">13</span><span class="o">.</span><span class="mi">10</span><span class="s1">&#39; (ECDSA) to the list of known hosts.</span>
</span><span class='line'><span class="s1">vagrant@33.33.13.10&#39;</span><span class="n">s</span> <span class="n">password</span><span class="p">:</span>
</span><span class='line'>
</span><span class='line'><span class="n">vagrant</span><span class="vi">@app</span><span class="ss">:~</span><span class="err">$</span>
</span></code></pre></td></tr></table></div></figure>


<p>看到了嗎，VM之間的溝通也是沒有問題的！，你現在可以開始好好思考你偉大的<code>Infrastructure</code>，讓你的程式跑在多機器的環境中，如果你對於Infrastructure不熟悉，Amazon有提供了不少<a href="http://aws.amazon.com/architecture/">範例</a>可以參考，想像力就是你的超能力，現在唯一侷限你的只會是你的電腦記憶體了，不要開到跑不動都不會有事的，今天就開始用Vagrant練習你的機器佈署吧！</p>

<blockquote><p>記得設定多台機器的時候，剛剛原先在單台機器的設定必須先清除或是註解掉，如果剛剛的VM還在跑，記得要用<code>vagrant halt</code>先關機</p></blockquote>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[[掃雷]在Mac OSX 10.8.2 上安裝 Rmagick 失敗]]></title>
    <link href="http://gogojimmy.net/2013/02/04/install-rmagick-failed-on-mac-10-dot-8-2/"/>
    <updated>2013-02-04T00:14:00+08:00</updated>
    <id>http://gogojimmy.net/2013/02/04/install-rmagick-failed-on-mac-10-dot-8-2</id>
    <content type="html"><![CDATA[<p><a href="https://github.com/rmagick/rmagick">Rmagick</a>是一個用Ruby寫成與<a href="http://www.imagemagick.org/script/index.php">ImageMagick</a>溝通的界面，基本上只要有寫上傳圖片的功能就少不了他啊，今天在安裝一個新環境時照著平常以往的步驟安裝竟然一直失敗，奮力掃雷後來分享一下&hellip;</p>

<!-- more -->


<p>失敗的步驟：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>brew install ImageMagick
</span><span class='line'>gem install rmagick
</span><span class='line'>uilding native extensions.  This could take a while...
</span><span class='line'>ERROR:  Error installing rmagick:
</span><span class='line'>  ERROR: Failed to build gem native extension.
</span><span class='line'>
</span><span class='line'>        /Users/lifejimmy/.rvm/rubies/ruby-1.9.3-p374/bin/ruby extconf.rb
</span><span class='line'>checking for Ruby version &gt;= 1.8.5... yes
</span><span class='line'>extconf.rb:128: Use RbConfig instead of obsolete and deprecated Config.
</span><span class='line'>checking for /usr/local/bin/gcc-4.2... yes
</span><span class='line'>checking for Magick-config... yes
</span><span class='line'>checking for ImageMagick version &gt;= 6.4.9... yes
</span><span class='line'>checking for HDRI disabled version of ImageMagick... yes
</span><span class='line'>checking for stdint.h... yes
</span><span class='line'>checking for sys/types.h... yes
</span><span class='line'>checking for wand/MagickWand.h... yes
</span><span class='line'>checking for InitializeMagick() in -lMagickCore... no
</span><span class='line'>checking for InitializeMagick() in -lMagick... no
</span><span class='line'>checking for InitializeMagick() in -lMagick++... no
</span><span class='line'>Can't install RMagick 2.13.1. Can't find the ImageMagick library or one of the dependent libraries. Check the mkmf.log file for more detailed information.
</span><span class='line'>
</span><span class='line'>*** extconf.rb failed ***
</span><span class='line'>Could not create Makefile due to some reason, probably lack of
</span><span class='line'>necessary libraries and/or headers.  Check the mkmf.log file for more
</span><span class='line'>details.  You may need configuration options.
</span><span class='line'>
</span><span class='line'>Provided configuration options:
</span><span class='line'>  --with-opt-dir
</span><span class='line'>  --with-opt-include
</span><span class='line'>  --without-opt-include=${opt-dir}/include
</span><span class='line'>  --with-opt-lib
</span><span class='line'>  --without-opt-lib=${opt-dir}/lib
</span><span class='line'>  --with-make-prog
</span><span class='line'>  --without-make-prog
</span><span class='line'>  --srcdir=.
</span><span class='line'>  --curdir
</span><span class='line'>  --ruby=/Users/lifejimmy/.rvm/rubies/ruby-1.9.3-p374/bin/ruby
</span><span class='line'>  --with-MagickCorelib
</span><span class='line'>  --without-MagickCorelib
</span><span class='line'>  --with-Magicklib
</span><span class='line'>  --without-Magicklib
</span><span class='line'>  --with-Magick++lib
</span><span class='line'>  --without-Magick++lib
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>Gem files will remain installed in /Users/lifejimmy/.rvm/gems/ruby-1.9.3-p374/gems/rmagick-2.13.1 for inspection.
</span><span class='line'>Results logged to /Users/lifejimmy/.rvm/gems/ruby-1.9.3-p374/gems/rmagick-2.13.1/ext/RMagick/gem_make.out</span></code></pre></td></tr></table></div></figure>


<p>這問題的發生原因是由於<a href="https://github.com/rmagick/rmagick">Rmagick</a>還不完整支援最新的<a href="http://www.imagemagick.org/script/index.php">ImageMagick</a>，因此你必須手動fix:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>$ cd /usr/local/Cellar/imagemagick/6.8.0-10/lib
</span><span class='line'>$ ln -s libMagick++-Q16.7.dylib   libMagick++.dylib
</span><span class='line'>$ ln -s libMagickCore-Q16.7.dylib libMagickCore.dylib
</span><span class='line'>$ ln -s libMagickWand-Q16.7.dylib libMagickWand.dylib</span></code></pre></td></tr></table></div></figure>


<p>考慮到<a href="https://github.com/rmagick/rmagick">Rmagick</a>上次更新是兩年前的事情，或許現在開始可以考慮改用<a href="https://github.com/minimagick/minimagick">MiniMagick</a>了。</p>

<p>參考：
<a href="http://stackoverflow.com/questions/13942443/error-installing-rmagick-on-mountain-lion/13960185#13960185">Error installing Rmagick on Mountain Lion</a></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Google Analytics 自定報表、Dashboard、進階區隔整理]]></title>
    <link href="http://gogojimmy.net/2012/12/25/google-analytics/"/>
    <updated>2012-12-25T17:47:00+08:00</updated>
    <id>http://gogojimmy.net/2012/12/25/google-analytics</id>
    <content type="html"><![CDATA[<p>每天GA一直看也不是辦法，時間寶貴，因此把一些常用的東西拉出來看比較快，下面是收集而來跟自己製作的一些自定報表以及Dashboard，在沒有很多時間看的情況下可以快速的查看一些關鍵資料。</p>

<!--more-->


<h2>訪客分析報表</h2>

<ul>
<li>維度：城市、語言</li>
<li>指標：造訪、單次造訪頁數、跳出率、收益、單次造訪價值、電子商務轉換率</li>
<li>維度深入分析：地區 > 關鍵字</li>
</ul>


<p>追蹤來自最多城市、語系的訪客流量，並找出他們目標關鍵字以及所造成的貢獻及轉化</p>

<p><a href="https://www.google.com/analytics/web/permalink?uid=IBpsM0nURwm0rTCgkJpdZw">加到GA</a></p>

<h2>內容分析報表</h2>

<p>了解透過關鍵字搜尋的熱門網頁是否符合關鍵字需求</p>

<h3>參與內容：</h3>

<ul>
<li>指標：造訪、單次造訪頁面、社交動作、跳出率、目標轉換率、目標達成</li>
<li>維度深入分析：到達網頁 > 關鍵字</li>
</ul>


<h3>問題網頁：</h3>

<ul>
<li>指標：造訪、離開百分比、跳出率、網頁停留時間、目標轉換率、目標達成</li>
<li>維度深入分析：到達網頁 > 關鍵字</li>
</ul>


<p>透過社交活動指標可以觀察是否對內容的流量及轉化有所影響</p>

<h2>關鍵字分析報表</h2>

<h3>關鍵字目標：</h3>

<ul>
<li>維度：網頁標題、關鍵字</li>
<li>指標：不重複訪客、目標達成、目標轉換率、網頁載入平均時間</li>
</ul>


<p>找出透過哪些關鍵字進來了哪些網頁，且這些網頁是否有帶來轉化，可以用來了解該關鍵字對內容是否正確</p>

<h3>參與程度：</h3>

<ul>
<li>指標：不重複瀏覽量、單次造訪頁數、平均網頁停留時間、跳出率、離開百分比、目標轉換率</li>
<li>維度深入分析：網頁標題 > 關鍵字</li>
</ul>


<p>類似於內容分析報表，但可以找出參與程度高的使用者透過哪些關鍵字到達你的網站後做了什麼</p>

<h3>收益：</h3>

<ul>
<li>維度：關鍵字</li>
<li>指標：交易次數、收益、單次造訪價值、電子商務轉換率</li>
</ul>


<p>找出關鍵字對你的收益影響</p>

<p><a href="https://www.google.com/analytics/web/permalink?uid=0FlAi-xVQSu05ggTOe-ywQ">加到GA</a></p>

<h2>連結分析報表</h2>

<p>找出對你貢獻最大的外部網站連結，這份報表幫你找出哪些外部網站來的流量帶來了最好的參與度，你可以更進一步透過URL的客製化進一步追蹤單一URL細節</p>

<ul>
<li>維度：來源、到達網頁</li>
<li>指標：造訪、目標達成、單次造訪頁數、跳出率、新造訪</li>
</ul>


<p><a href="https://www.google.com/analytics/web/permalink?uid=78FqNeU5T56KjKgt7cr9_g">加到GA</a></p>

<h2>PPC 內容分析：</h2>

<p>PPC是一個用來測試 Landing Page 與關鍵字關係很好的工具，這報表就是要幫你找出你的關鍵字是否有下對，你的 Landing Page 是否是使用者正在尋找的目標</p>

<ul>
<li>維度：到達網頁、關鍵字</li>
<li>指標：造訪、交易次數、收益、電子商務轉換率、單次造訪價值</li>
</ul>


<p><a href="https://www.google.com/analytics/web/permalink?uid=qt9c-g7NToiVC7d64afp4g">加到GA</a></p>

<h2>PPC 關鍵字分析</h2>

<p>找出表現優良的付費關鍵字，並找出關鍵字成本</p>

<ul>
<li>維度：關鍵字、廣告群組</li>
<li>指標：造訪、單次點擊出價、目標達成、單次轉換出價</li>
</ul>


<p><a href="https://www.google.com/analytics/web/permalink?uid=BiO6IhVuTxW8NWxRtaIkpA">加到GA</a></p>

<h2>社群媒體分析</h2>

<p>篩選出來自於主流社群媒體的流量，並對你網站所造成的參與程度，你可以找出最多人喜歡、分享的內容。也能找出最有幫助的媒體來源</p>

<ul>
<li>維度：到達網頁、來源</li>
<li>指標：造訪、社交動作、目標達成、目標轉換率、目標價值</li>
</ul>


<p><a href="https://www.google.com/analytics/web/permalink?uid=z9kjbwReRSWrMODpEHVjlg">加到GA</a></p>

<h2>SEO成效Dashboard</h2>

<ul>
<li>自然搜尋關鍵字：找出用戶搜尋哪些關鍵字進入網站</li>
<li>造訪/頁數 by 城市：不同城市的用戶的瀏覽次數</li>
<li>自然搜尋造訪流量：來自搜尋引擎的流量變化</li>
<li>Visits and Pages / Visit by Landing Page：找出來自搜尋引擎的流量中最大量的Landing Page以及使用者所瀏覽的頁數</li>
<li>自然搜尋造訪媒介：查看來自哪些搜尋媒介</li>
<li>目標達成：搜尋流量的目標達成狀況</li>
</ul>


<p><a href="https://www.google.com/analytics/web/permalink?uid=14xnY2UyQwySIbcdYmDikw">加到GA</a></p>

<h2>PPC成效Dashboard</h2>

<p><a href="https://www.google.com/analytics/web/permalink?uid=ug63Lyb8TWqDzTIRebg1Dw">加到GA</a></p>

<h2>社群分析Dashboard</h2>

<ul>
<li>來自社交網路的新造訪(%)</li>
<li>來自社交網站的流量</li>
<li>社交活動 vs 流量</li>
<li>網路上的社交活動</li>
<li>社交網路造訪價值</li>
<li>最多分享的內容</li>
<li>來自社交網路並且使用手機造訪</li>
<li>從推薦連結造訪的價值</li>
<li>從社交網路造訪所達成的收益</li>
<li>來自社交網路並且至少瀏覽一個頁面</li>
</ul>


<p><a href="https://www.google.com/analytics/web/permalink?uid=8t-t1BWWR46xjRWf6x0qzg">加到GA</a></p>

<h2>網站效能主控台</h2>

<ul>
<li>網頁平均載入時間</li>
<li>伺服器平均反應時間</li>
<li>網域查詢平均反應時間</li>
<li>訪客快取資訊</li>
<li>手機平均讀取時間</li>
<li>頁面讀取時間 by 瀏覽器</li>
<li>熱門頁面平均載入時間</li>
<li>造訪 by 國家</li>
<li>造訪速度 by 國家</li>
<li>重新導向時間 by 國家</li>
</ul>


<p><a href="https://www.google.com/analytics/web/permalink?uid=UpfzzUysRyqXIjlTFg88bA">加到GA</a></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[[Ruby 重構]Refactoring from Good to Great 影片心得]]></title>
    <link href="http://gogojimmy.net/2012/11/13/note-for-refactoring-from-good-to-great/"/>
    <updated>2012-11-13T22:53:00+08:00</updated>
    <id>http://gogojimmy.net/2012/11/13/note-for-refactoring-from-good-to-great</id>
    <content type="html"><![CDATA[<p>今天看了一支<em>Ruby Conf 2012</em>的影片，標題是<a href="http://www.confreaks.com/videos/1233-aloharuby2012-refactoring-from-good-to-great">Refactoring from Good to Great</a>，主講人是<a href="http://www.confreaks.com/presenters/780-ben-orenstein">Ben Orenstein</a>，非常強烈推薦去看一下影片，裡面所提到重構技巧非常實用，以下是為影片做的一些筆記：</p>

<!--more-->


<p>首先我們先來看看這段程式：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">class</span> <span class="nc">OrdersReport</span>
</span><span class='line'>  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">orders</span><span class="p">,</span> <span class="n">start_date</span><span class="p">,</span> <span class="n">end_date</span><span class="p">)</span>
</span><span class='line'>    <span class="vi">@orders</span> <span class="o">=</span> <span class="n">orders</span>
</span><span class='line'>    <span class="vi">@start_date</span> <span class="o">=</span> <span class="n">start_date</span>
</span><span class='line'>    <span class="vi">@end_date</span> <span class="o">=</span> <span class="n">end_date</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">def</span> <span class="nf">total_sales_within_date_range</span>
</span><span class='line'>    <span class="n">orders_within_range</span> <span class="o">=</span>
</span><span class='line'>    <span class="vi">@orders</span><span class="o">.</span><span class="n">select</span> <span class="p">{</span> <span class="o">|</span><span class="n">order</span><span class="o">|</span> <span class="n">order</span><span class="o">.</span><span class="n">placed_at</span> <span class="o">&gt;=</span> <span class="vi">@start_date</span> <span class="o">&amp;&amp;</span>
</span><span class='line'>      <span class="n">order</span><span class="o">.</span><span class="n">placed_at</span> <span class="o">&lt;=</span> <span class="vi">@end_date</span> <span class="p">}</span>
</span><span class='line'>    <span class="n">orders_within_range</span><span class="o">.</span>
</span><span class='line'>    <span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:amount</span><span class="p">)</span><span class="o">.</span><span class="n">inject</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">sum</span><span class="p">,</span> <span class="n">amount</span><span class="o">|</span> <span class="n">amount</span> <span class="o">+</span> <span class="n">sum</span> <span class="p">}</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span><span class='line'>
</span><span class='line'><span class="k">def</span> <span class="nf">Order</span> <span class="o">&lt;</span> <span class="no">OpenStruct</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>我們有一個<code>Order</code>以及作為報表的<code>OrderReport</code>兩支<em>Class</em>，在初始化一個<code>OrderReport</code>物件時必須傳入<code>orders</code>、<code>start_time</code>及<code>end_time</code>三個參數，其中<code>total_sales_within_date_range</code>這個方法會計算出給定的時間區間中所有的訂單總數。</p>

<p>看起來很簡單，程式也運作正常，但這出了什麼問題？首先我們把目光放到<code>total_sales_within_date_range</code>這個方法：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">def</span> <span class="nf">total_sales_within_date_range</span>
</span><span class='line'>  <span class="n">orders_within_range</span> <span class="o">=</span>
</span><span class='line'>  <span class="vi">@orders</span><span class="o">.</span><span class="n">select</span> <span class="p">{</span> <span class="o">|</span><span class="n">order</span><span class="o">|</span> <span class="n">order</span><span class="o">.</span><span class="n">placed_at</span> <span class="o">&gt;=</span> <span class="vi">@start_date</span> <span class="o">&amp;&amp;</span>
</span><span class='line'>    <span class="n">order</span><span class="o">.</span><span class="n">placed_at</span> <span class="o">&lt;=</span> <span class="vi">@end_date</span> <span class="p">}</span>
</span><span class='line'>    <span class="n">orders_within_range</span><span class="o">.</span>
</span><span class='line'>    <span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:amount</span><span class="p">)</span><span class="o">.</span><span class="n">inject</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">sum</span><span class="p">,</span> <span class="n">amount</span><span class="o">|</span> <span class="n">amount</span> <span class="o">+</span> <span class="n">sum</span> <span class="p">}</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>在這個方法中，定義了一個<code>orders_within_range</code>的變數，用<code>select</code>方法篩選出了區間的訂單，再來將這些訂單的數量做加總的動作，你會發現在這個方法中我們擁有了很多邏輯上的實作，這不是件好事，不好在過兩個月後你來看你的程式，或是你的同事來看你的程式時，他無法馬上看懂你在做什麼，以一個<em>Class</em>的<em>public</em>方法而言他擁有了太多實作的細節，比較下面這個版本：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">def</span> <span class="nf">total_sales_within_date_range</span>
</span><span class='line'>  <span class="n">orders_within_range</span><span class="o">.</span>
</span><span class='line'>  <span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:amount</span><span class="p">)</span><span class="o">.</span><span class="n">inject</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">sum</span><span class="p">,</span> <span class="n">amount</span><span class="o">|</span> <span class="n">amount</span> <span class="o">+</span> <span class="n">sum</span> <span class="p">}</span>
</span><span class='line'><span class="k">end</span>
</span><span class='line'>
</span><span class='line'><span class="kp">private</span>
</span><span class='line'>
</span><span class='line'><span class="k">def</span> <span class="nf">orders_within_range</span>
</span><span class='line'>  <span class="vi">@orders</span><span class="o">.</span><span class="n">select</span> <span class="p">{</span> <span class="o">|</span><span class="n">order</span><span class="o">|</span> <span class="n">order</span><span class="o">.</span><span class="n">placed_at</span> <span class="o">&gt;=</span> <span class="vi">@start_date</span> <span class="o">&amp;&amp;</span>
</span><span class='line'>    <span class="n">order</span><span class="o">.</span><span class="n">placed_at</span> <span class="o">&lt;=</span> <span class="vi">@end_date</span> <span class="p">}</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>這個版本我們將<code>orders_within_range</code>拉出去作為一個<em>private</em>的方法，這樣有什麼好處？作為一個<em>Class</em>的<em>public</em>方法原則就是越簡單越好，細節越少越好，因為這個是外面的其他人會去使用的方法，在物件導向中稱之為最小知識原則(<em>Least Knowledge Principle</em>)，對於外面使用<em>API</em>的人他沒有必要知道邏輯細節的實作，因此我們把它藏成<em>private</em>的方法，再者，你現在回頭看原先<code>total_sales_within_date_range</code>這個方法，是不是比原先的寫法容易理解許多，我們從字面上可以知道<code>orders_within_range</code>這個變數會幫我取得訂單，然後我再去做運算即可，非常好，我們完成了第一步，也是很重要的一步。</p>

<p>再來我們看到我們剛剛被拉出去做成<em>private</em>的<code>orders_within_range</code>這個方法，在這邊我們用<code>&gt;=</code>及<code>&lt;=</code>來比對日期區間與訂單日期以求得訂單，問題在於這樣的寫法還不夠好，除了不夠直覺之外，對於篩選出某個區間的訂單這個方法似乎不該屬於報表類別的責任，我們可以試著改寫成下面的版本：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">def</span> <span class="nf">orders_within_range</span>
</span><span class='line'>  <span class="vi">@orders</span><span class="o">.</span><span class="n">select</span> <span class="p">{</span> <span class="o">|</span><span class="n">order</span><span class="o">|</span> <span class="n">order</span><span class="o">.</span><span class="n">placed_between?</span><span class="p">(</span><span class="vi">@start_date</span><span class="p">,</span> <span class="vi">@end_date</span><span class="p">)</span>
</span><span class='line'><span class="k">end</span>
</span><span class='line'>
</span><span class='line'><span class="k">def</span> <span class="nf">Order</span> <span class="o">&lt;</span> <span class="no">OpenStruct</span>
</span><span class='line'>  <span class="k">def</span> <span class="nf">placed_between?</span><span class="p">(</span><span class="n">start_date</span><span class="p">,</span> <span class="n">end_date</span><span class="p">)</span>
</span><span class='line'>    <span class="n">placed_at</span> <span class="o">&gt;=</span> <span class="n">start_date</span> <span class="o">&amp;&amp;</span> <span class="n">placed_at</span> <span class="o">&lt;=</span> <span class="n">end_date</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>這個版本中我們將原先的比對拉到了<code>Order</code>類別去做，找出一個範圍的訂單這件事情本來就應該屬於訂單的責任而不是報表，我們包裝成一個簡單的<code>placed_between？</code>的方法來進行比對，因此在原先的<code>orders_within_range</code>方法中就可以呼叫這個方法來使用，這樣看起來是不是比原先的程式看起來又更直覺了些？</p>

<p>下一步我們回頭看看我們全部的程式，你會發現到處都有著<code>start_date</code>以及<code>end_date</code>，而且兩者還缺一不可，這告訴我們這兩個是著高度依賴性必須同時存在的變數，這個情形稱之為<a href="http://sourcemaking.com/refactoring/data-clumps">Data Clumps</a>，這時候你應該要把這兩個或是多個變數抽出來成為一個物件來處理，我們試著改寫這段程式：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">class</span> <span class="nc">OrdersReport</span>
</span><span class='line'>  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">orders</span><span class="p">,</span> <span class="n">date_range</span><span class="p">)</span>
</span><span class='line'>    <span class="vi">@orders</span> <span class="o">=</span> <span class="n">orders</span>
</span><span class='line'>    <span class="vi">@date_range</span> <span class="o">=</span> <span class="n">date_range</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">def</span> <span class="nf">total_sales_within_date_range</span>
</span><span class='line'>    <span class="n">orders_within_range</span><span class="o">.</span>
</span><span class='line'>    <span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:amount</span><span class="p">)</span><span class="o">.</span><span class="n">inject</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">sum</span><span class="p">,</span> <span class="n">amount</span><span class="o">|</span> <span class="n">amount</span> <span class="o">+</span> <span class="n">sum</span> <span class="p">}</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="kp">private</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">def</span> <span class="nf">orders_wihin_range</span>
</span><span class='line'>    <span class="vi">@orders</span><span class="o">.</span><span class="n">select</span> <span class="p">{</span> <span class="o">|</span><span class="n">order</span><span class="o">|</span> <span class="n">order</span><span class="o">.</span><span class="n">placed_between?</span><span class="p">(</span><span class="vi">@date_range</span><span class="p">)</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span><span class='line'>
</span><span class='line'><span class="k">class</span> <span class="nc">DateRange</span> <span class="o">&lt;</span> <span class="no">Struct</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="ss">:start_date</span><span class="p">,</span> <span class="ss">:end_date</span><span class="p">)</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>你看到這邊最重要的是，我們宣告了一個簡單的<code>Struct</code>類別包含了<code>start_date</code>以及<code>end_date</code>，原本的<code>OrderReport</code>類別中<code>initialize</code>方法需要傳入的參數從三個減少成兩個，所有原先需要<code>start_date</code>以及<code>end_date</code>的地方現在全部都只有一個<code>date_range</code>物件來取代，最簡單來說，我們用了一個有意義的名字來代表了一些複雜的事情，另一個好處在於我們再降低了<code>OrderReport</code>以及<code>DateRange</code>的耦合性，一個只需要傳入兩個參數的類別可能造成的耦合一定比要傳入三個參數的類別來的小，如同上面所說，一個類別所需要知道的內容越少，他的耦合度就越低，彈性也就越高。再者，被封裝成物件的<code>date_range我們現在還可以去為他定義一些行為，我們現在回頭看看</code>Order<code>這個類別的</code>placed_between?<code>這個方法，其中有著日期比對的判斷，這理所當然要屬於</code>DateRange`應該去做的事情，因此我們再改寫一下我們的程式：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">def</span> <span class="nf">DateRange</span> <span class="o">&lt;</span> <span class="no">Struct</span><span class="p">(</span><span class="ss">:start_date</span><span class="p">,</span> <span class="ss">:end_date</span><span class="p">)</span>
</span><span class='line'>  <span class="k">def</span> <span class="nf">include?</span><span class="p">(</span><span class="n">date</span><span class="p">)</span>
</span><span class='line'>    <span class="p">(</span><span class="n">start_date</span><span class="o">.</span><span class="n">.end_date</span><span class="p">)</span><span class="o">.</span><span class="n">cover?</span><span class="p">(</span><span class="n">date</span><span class="p">)</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span><span class='line'>
</span><span class='line'><span class="k">def</span> <span class="nf">Order</span> <span class="o">&lt;</span> <span class="no">OpenStruct</span>
</span><span class='line'>  <span class="k">def</span> <span class="nf">placed_between?</span><span class="p">(</span><span class="n">date_range</span><span class="p">)</span>
</span><span class='line'>    <span class="n">date_range</span><span class="o">.</span><span class="n">include?</span><span class="p">(</span><span class="n">placed_at</span><span class="p">)</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>我們將日期判斷這件事情移到了<code>DateRange</code>裡面去做，在這邊我們改使用了<code>cover</code>方法來判斷是否符合，<code>cover</code>方法不同於<code>include</code>方法會將整個陣列元素取出到記憶體，<code>cover</code>僅找出陣列的頭尾元素並加以判斷，這樣程式看起來就分工分的很好。</p>

<p>現在我們來重構最後一個部分，我們來看看<code>total_sales_within_date_range</code>這個方法，在這方法我們先取出了<code>orders_within_range</code>，然後計算出總數，看起來已經不錯了，但我們還可以再做一步，試著將計算的部份也抽出來看看會有什麼結果：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">class</span> <span class="nc">OrdersReport</span>
</span><span class='line'>  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">orders</span><span class="p">,</span> <span class="n">date_range</span><span class="p">)</span>
</span><span class='line'>    <span class="vi">@orders</span> <span class="o">=</span> <span class="n">orders</span>
</span><span class='line'>    <span class="vi">@date_range</span> <span class="o">=</span> <span class="n">date_range</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">def</span> <span class="nf">total_sales_within_date_range</span>
</span><span class='line'>    <span class="n">total_sales</span><span class="p">(</span><span class="n">orders_within_range</span><span class="p">)</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="kp">private</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">def</span> <span class="nf">total_sales</span><span class="p">(</span><span class="n">orders</span><span class="p">)</span>
</span><span class='line'>    <span class="n">orders</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:amount</span><span class="p">)</span><span class="o">.</span><span class="n">inject</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="ss">:+</span><span class="p">)</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">def</span> <span class="nf">orders_wihin_range</span>
</span><span class='line'>    <span class="vi">@orders</span><span class="o">.</span><span class="n">select</span> <span class="p">{</span> <span class="o">|</span><span class="n">order</span><span class="o">|</span> <span class="n">order</span><span class="o">.</span><span class="n">placed_between?</span><span class="p">(</span><span class="vi">@date_range</span><span class="p">)</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>你看到了我們將計算的方法也拆成一個<code>private</code>的方法，比對原來的部分我們的<code>public</code>方法只剩下了<code>total_sales_within_date_range</code>你一眼就可以看出來這個<em>public</em>的內容在做什麼，而裡面的相關邏輯實作並不是最重要的且並不是我們想知道的，因此我們把它封裝成<code>private</code>，你的<code>public</code>方法現在看起來非常乾淨且簡單，並不存在當初的複雜邏輯，<code>public</code>的部份是與外部溝通的，它本來就必須看起來很簡單，取用它的人並不需要知道你的邏輯細節實作，因此我們才將它封裝成`private，現在這看起來就是一個非常易懂且實用的類別！</p>

<p>再來我們看到下一個範例，一個<code>JobSite</code>類別，其中有著必填的<code>location</code>參數以及選填的<code>contact</code>：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">class</span> <span class="nc">JobSite</span>
</span><span class='line'>  <span class="kp">attr_reader</span> <span class="ss">:contact</span>
</span><span class='line'>  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">location</span><span class="p">,</span> <span class="n">contact</span><span class="p">)</span>
</span><span class='line'>    <span class="vi">@location</span> <span class="o">=</span> <span class="n">location</span>
</span><span class='line'>    <span class="vi">@contact</span> <span class="o">=</span> <span class="n">contact</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">def</span> <span class="nf">contact_name</span>
</span><span class='line'>    <span class="k">if</span> <span class="n">contact</span>
</span><span class='line'>      <span class="n">contact</span><span class="o">.</span><span class="n">name</span>
</span><span class='line'>    <span class="k">else</span>
</span><span class='line'>      <span class="s2">&quot;no name&quot;</span>
</span><span class='line'>    <span class="k">end</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">def</span> <span class="nf">contact_phone</span>
</span><span class='line'>    <span class="k">if</span> <span class="n">contact</span>
</span><span class='line'>      <span class="n">contact</span><span class="o">.</span><span class="n">phone</span>
</span><span class='line'>    <span class="k">else</span>
</span><span class='line'>      <span class="s2">&quot;no phone&quot;</span>
</span><span class='line'>    <span class="k">end</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">def</span> <span class="nf">email_to_contact</span><span class="p">(</span><span class="n">contact</span><span class="p">)</span>
</span><span class='line'>    <span class="n">contact</span><span class="o">.</span><span class="n">deliver_personalized_email</span><span class="p">(</span><span class="n">email_body</span><span class="p">)</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span><span class='line'>
</span><span class='line'><span class="k">class</span> <span class="nc">Contact</span> <span class="o">&lt;</span> <span class="no">OpenStruct</span>
</span><span class='line'>  <span class="k">def</span> <span class="nf">deliver_personalized_email</span><span class="p">(</span><span class="n">email</span><span class="p">)</span>
</span><span class='line'>    <span class="n">email</span><span class="o">.</span><span class="n">deliver</span><span class="p">(</span><span class="nb">name</span><span class="p">)</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>因為<code>contact</code>不是必填，因此我們定義了一些方法當<code>contact</code>沒有傳入的時候則回傳&#8221;no xxx&#8221;，你會發現整個<code>Class</code>每次去要資料的時候都會對<code>contact</code>做一次是否存在的判斷，我們可以加以改善：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">class</span> <span class="nc">JobSite</span>
</span><span class='line'>  <span class="kp">attr_reader</span> <span class="ss">:contact</span>
</span><span class='line'>  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">location</span><span class="p">,</span> <span class="n">contact</span><span class="p">)</span>
</span><span class='line'>    <span class="vi">@location</span> <span class="o">=</span> <span class="n">location</span>
</span><span class='line'>    <span class="vi">@contact</span> <span class="o">=</span> <span class="n">contact</span> <span class="o">||</span> <span class="no">NullContact</span><span class="o">.</span><span class="n">new</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">def</span> <span class="nf">contact_name</span>
</span><span class='line'>    <span class="n">contact</span><span class="o">.</span><span class="n">name</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">def</span> <span class="nf">contact_phone</span>
</span><span class='line'>    <span class="n">contact</span><span class="o">.</span><span class="n">phone</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">def</span> <span class="nf">email_to_contact</span><span class="p">(</span><span class="n">contact</span><span class="p">)</span>
</span><span class='line'>    <span class="n">contact</span><span class="o">.</span><span class="n">deliver_personalized_email</span><span class="p">(</span><span class="n">email_body</span><span class="p">)</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span><span class='line'>
</span><span class='line'><span class="k">class</span> <span class="nc">NullContact</span>
</span><span class='line'>  <span class="k">def</span> <span class="nf">name</span>
</span><span class='line'>    <span class="s2">&quot;no name&quot;</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">def</span> <span class="nf">phone</span>
</span><span class='line'>    <span class="s2">&quot;no phone&quot;</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">def</span> <span class="nf">deliver_personalized_email</span><span class="p">(</span><span class="n">body</span><span class="p">)</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span><span class='line'>
</span><span class='line'><span class="k">class</span> <span class="nc">Contact</span> <span class="o">&lt;</span> <span class="no">OpenStruct</span>
</span><span class='line'>  <span class="k">def</span> <span class="nf">deliver_personalized_email</span><span class="p">(</span><span class="n">email</span><span class="p">)</span>
</span><span class='line'>    <span class="n">email</span><span class="o">.</span><span class="n">deliver</span><span class="p">(</span><span class="nb">name</span><span class="p">)</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>看出來了嗎？我們當<code>initialize</code>方法中的<code>contact</code>沒有傳入的時候，我們製造一個新的<code>NullContact</code>物件，這個物件擁有與<code>Contact</code>一樣的方法，但是會回傳所有的預設值，這時候我們就可以將原先程式中那些擾人的判斷給一一砍掉，多美妙阿，我們在呼叫的時候再也不需要知道現在這個<code>contact</code>是否存在，伴隨而來的壞處是，現在你必須讓<code>Contact</code>與<code>NullContact</code>兩個<em>API</em>保持同步，不然就會出錯。</p>

<p>再來我們看到最後一個範例，<em>User</em>使用一些第三方金流進行付款或退款時的處理：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">class</span> <span class="nc">User</span>
</span><span class='line'>  <span class="no">SUBSCRIPTION_AMOUNT</span> <span class="o">=</span> <span class="mi">10</span><span class="o">.</span><span class="n">to_money</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">def</span> <span class="nf">charge_for_subscription</span>
</span><span class='line'>    <span class="n">braintree_id</span> <span class="o">=</span> <span class="no">BraintreeGem</span><span class="o">.</span><span class="n">find_user</span><span class="p">(</span><span class="n">email</span><span class="p">)</span><span class="o">.</span><span class="n">braintree_id</span>
</span><span class='line'>    <span class="no">BraintreeGem</span><span class="o">.</span><span class="n">charge</span><span class="p">(</span><span class="n">braintree_id</span><span class="p">,</span> <span class="no">SUBSCRIPTION_AMOUNT</span><span class="p">)</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">def</span> <span class="nf">create_as_customer</span>
</span><span class='line'>    <span class="no">BraintreeGem</span><span class="o">.</span><span class="n">create_customer</span><span class="p">(</span><span class="n">email</span><span class="p">)</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span><span class='line'>
</span><span class='line'><span class="k">class</span> <span class="nc">Refund</span>
</span><span class='line'>  <span class="k">def</span> <span class="nf">process!</span>
</span><span class='line'>    <span class="n">transaction_id</span> <span class="o">=</span> <span class="no">BraintreeGem</span><span class="o">.</span><span class="n">find_transaction</span><span class="p">(</span><span class="n">order</span><span class="o">.</span><span class="n">braintree_id</span><span class="p">)</span>
</span><span class='line'>    <span class="no">BraintreeGem</span><span class="o">.</span><span class="n">refund</span><span class="p">(</span><span class="n">transaction_id</span><span class="p">,</span> <span class="n">amount</span><span class="p">)</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>在<code>User</code>中使用了<code>BraintreeGem</code>的<em>API</em>來做付款動作，你會發現我們必須在<code>User</code>中做一些與<code>User</code>不相關的事情，例如你必須找出<code>braintree_id</code>、<code>transaction_id</code>然後對他進行charge的動作，這樣對嗎？<code>User</code>不應該去知道這些細節的實作，再者，如果今天在付款方式有所更動，我們想要除了<code>BraintreeGem</code>之外另外增加付款方式，而這時要去更改<code>User</code>不是很奇怪的事情嗎？因此我們應該將它放到應該放的地方：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">class</span> <span class="nc">PaymentGateway</span>
</span><span class='line'>  <span class="no">SUBSCRIPTION_AMOUNT</span> <span class="o">=</span> <span class="mi">10</span><span class="o">.</span><span class="n">to_money</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">gateway</span> <span class="o">=</span> <span class="no">BraintreeGem</span><span class="p">)</span>
</span><span class='line'>    <span class="vi">@gateway</span> <span class="o">=</span> <span class="n">gateway</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">def</span> <span class="nf">charge_for_subscription</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span><span class='line'>    <span class="n">braintree_id</span> <span class="o">=</span> <span class="no">BraintreeGem</span><span class="o">.</span><span class="n">find_user</span><span class="p">(</span><span class="n">email</span><span class="p">)</span><span class="o">.</span><span class="n">braintree_id</span>
</span><span class='line'>    <span class="vi">@gateway</span><span class="o">.</span><span class="n">charge</span><span class="p">(</span><span class="n">braintree_id</span><span class="p">,</span> <span class="no">SUBSCRIPTION_AMOUNT</span><span class="p">)</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">def</span> <span class="nf">create_as_customer</span>
</span><span class='line'>    <span class="vi">@gateway</span><span class="o">.</span><span class="n">create_customer</span><span class="p">(</span><span class="n">email</span><span class="p">)</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">def</span> <span class="nf">refund</span><span class="p">(</span><span class="n">refund_model</span><span class="p">)</span>
</span><span class='line'>    <span class="n">transaction_id</span> <span class="o">=</span> <span class="vi">@gateway</span><span class="o">.</span><span class="n">find_transaction</span><span class="p">(</span><span class="n">order</span><span class="o">.</span><span class="n">braintree_id</span><span class="p">)</span>
</span><span class='line'>    <span class="vi">@gateway</span><span class="o">.</span><span class="n">refund</span><span class="p">(</span><span class="n">transaction_id</span><span class="p">,</span> <span class="n">amount</span><span class="p">)</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span><span class='line'>
</span><span class='line'><span class="k">class</span> <span class="nc">User</span>
</span><span class='line'>  <span class="k">def</span> <span class="nf">charge_for_subscription</span>
</span><span class='line'>    <span class="no">PaymentGateway</span><span class="o">.</span><span class="n">new</span><span class="o">.</span><span class="n">charge_for_subscription</span><span class="p">(</span><span class="nb">self</span><span class="p">)</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">def</span> <span class="nf">create_as_customer</span>
</span><span class='line'>    <span class="no">PaymentGateway</span><span class="o">.</span><span class="n">new</span><span class="o">.</span><span class="n">create_as_customer</span><span class="p">(</span><span class="nb">self</span><span class="p">)</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span><span class='line'>
</span><span class='line'><span class="k">class</span> <span class="nc">Refund</span>
</span><span class='line'>  <span class="k">def</span> <span class="nf">process!</span>
</span><span class='line'>    <span class="no">PaymentGateway</span><span class="o">.</span><span class="n">new</span><span class="o">.</span><span class="n">refund</span><span class="p">(</span><span class="nb">self</span><span class="p">)</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>我們新建立了一個<code>PaymentGateway</code>物件，用來處理所有跟交易有關的事務，並且將預設的<em>gateway</em>指定為<code>BraintreeGem</code>，如此我們的<code>User</code>現在不需要知道那些實作的細節，也不用知道到底是用什麼付款機制，它所需要做的就是呼叫付款的<em>API</em>，這多麼美好，這讓我們更好去測試所有我們想要測試的行為，因為所有的行為經過如此的封裝後更為簡單單純，<em>User</em>的部份我們只需測是否有呼叫到<em>API</em>，而在<code>PaymentGateway</code>的部份就測試相關的付款行為，不會再糾結在一起，這就是一個好設計。</p>

<h2>什麼時候該進行重構？</h2>

<p>幾個作為判斷的提示：</p>

<ol>
<li>找出你的<em>God Object</em>，幾乎所有的<em>App</em>都會有兩個最重要的<em>Class</em>，其中一個幾乎都是<em>User</em>，另一個就是你的<em>App</em>的主題，例如以電子商務來說就像是<em>Order</em>或是<em>Product</em>。找出<em>God Object</em>有兩種方式，找出所有<em>Model</em>的行數，最多行的那幾個肯定需要重構。</li>
<li>找出更動最頻繁的那些<em>Model</em>(High churn files)，更動頻繁的檔案意味著你的<em>API</em>寫的不夠好因此頻繁的被更改，<a href="https://github.com/danmayer/churn">churn</a>這個<em>Gem</em>能幫助你找出這些檔案。</li>
<li>第三就是找出很常發生<em>Bug</em>的部分，那表示那邊有太複雜的邏輯因此你才會出現<em>Bug</em>。</li>
</ol>


<p>最後<a href="http://www.confreaks.com/presenters/780-ben-orenstein">Ben Orenstein</a>推薦了三本重構必讀聖經：</p>

<ol>
<li><a href="http://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882">Clean Code: A Handbook of Agile Software Craftsmanship (Paperback)</a>:這是寫出簡單直覺且乾淨的程式碼的入門書。</li>
<li><a href="http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672/ref=sr_1_1?s=books&amp;ie=UTF8&amp;qid=1352823235&amp;sr=1-1&amp;keywords=refactoring+improving+the+design+of+existing+code">Refactoring: Improving the Design of Existing Code (Hardcover)</a>:必買，這是聖經。</li>
<li><a href="http://www.amazon.com/Growing-Object-Oriented-Software-Guided-Tests/dp/0321503627/ref=sr_1_1?s=books&amp;ie=UTF8&amp;qid=1352823314&amp;sr=1-1&amp;keywords=growing+object-oriented+software+guided+by+tests">Growing Object-Oriented Software, Guided by Tests (Paperback)</a>裡頭提到了非常多實用的<em>OO</em>觀念以及<em>TDD</em>技巧。</li>
</ol>


<p>最後，如果有時間，建議妳可以看看影片：</p>

<div class="embed-video-container"><iframe src="http://www.youtube.com/embed/DC-pQPq0acs "></iframe></div>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[安裝 Moutain Lion 後，一些掃雷步驟(Homebrew)]]></title>
    <link href="http://gogojimmy.net/2012/07/27/homebrew-on-moutain-lion/"/>
    <updated>2012-07-27T22:11:00+08:00</updated>
    <id>http://gogojimmy.net/2012/07/27/homebrew-on-moutain-lion</id>
    <content type="html"><![CDATA[<p>身為一個勇者，升級作業系統永遠都是搶第一的，當然免不了遇到地雷炸一下，大概掃了一下雷後，分享一下我的山獅更新步驟。</p>

<!--more-->


<ol>
<li>從 App Store 下載安裝山獅。</li>
<li>從 App Store 更新你的 Xcode，更新完畢後在設定 > 下載 > 安裝 Command Line Tools</li>
<li>因為<a href="http://support.apple.com/kb/HT5293?viewlocale=en_US&amp;locale=en_US">山獅已經移除了 X11</a>，請下載安裝 <a href="http://xquartz.macosforge.org/landing/">XQuartz</a></li>
<li>有些人安裝了 Xcode 的 Command Line Tools 後一樣找不到 GCC (知道原因的拜託告訴我)，你可以下載安裝人家打包好的 <a href="https://github.com/kennethreitz/osx-gcc-installer/">GCC for OSX</a></li>
<li>更新升級 Homebrew 中的套件 <code>brew update &amp;&amp; brew upgrade</code></li>
</ol>


<p>這樣應該就能正常使用了</p>

<p>其他：
* 你可能會看到會出現這樣的訊息 <code>WARNING: Nokogiri was built against LibXML version 2.8.0, but has dynamically loaded 2.7.8</code></p>

<p>在 StackOverFlow 找到的<a href="http://stackoverflow.com/questions/11452380/warning-nokogiri-was-built-against-libxml-version-2-7-3-but-has-dynamically-lo">解法</a>：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>gem uninstall nokogiri libxml-ruby
</span><span class='line'>
</span><span class='line'>brew uninstall libxml2
</span><span class='line'>brew install libxml2 --with-xml2-config
</span><span class='line'>brew link libxml2
</span><span class='line'>
</span><span class='line'>brew install libxslt
</span><span class='line'>brew unlink libxslt
</span><span class='line'>
</span><span class='line'>gem install nokogiri -- --with-xml2-include=/usr/local/Cellar/libxml2/2.8.0/include/libxml2/ --with-xml2-lib=/usr/local/Cellar/libxml2/2.8.0/lib/ --with-xslt-dir=/usr/local/Cellar/libxslt/1.1.26/</span></code></pre></td></tr></table></div></figure>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[WordPress Basic]]></title>
    <link href="http://gogojimmy.net/2012/07/10/wordpress-basic/"/>
    <updated>2012-07-10T18:19:00+08:00</updated>
    <id>http://gogojimmy.net/2012/07/10/wordpress-basic</id>
    <content type="html"><![CDATA[<h1>基本架設步驟：</h1>

<p>要架設一個 WordPress 站台，基本有四個步驟，所有步驟可能會產生的資料，不論像是註冊網域時的資料，買空間給你的資料，請務必記得備份：</p>

<!--more-->


<ul>
<li>空間(Hosting)

<ol>
<li>申請一個空間或是 VPS。</li>
<li>將他給你的資料全部先抄下來備份。</li>
</ol>
</li>
<li>網址(Domain/URL) 使用虛擬主題提供的免費網址可省去這一步

<ol>
<li>在網域註冊商註冊一個網址。</li>
<li>找到網址商設定 Name Server 的地方將你申請的空間所提供給你的 Name Server 設定好。</li>
</ol>
</li>
<li>資料庫(Database)

<ol>
<li>在虛擬主機操作介面(通常是 cpanel) 上建立一個 MySQL 的資料庫。</li>
<li>把帳號密碼記起來。</li>
</ol>
</li>
<li>安裝(Install WordPress)

<ol>
<li>虛擬主機上大多有提供一鍵安裝，你可以直接使用。</li>
<li>使用 FTP 上傳 你想要的 WordPress 版本。</li>
<li>照著 WordPress 的安裝引導一步一步安裝。</li>
</ol>
</li>
</ul>


<p>以下提供四個步驟的安裝時會需要的資源</p>

<h2>申請網址</h2>

<ul>
<li>國內

<ul>
<li><a href="http://myname.pchome.com.tw/">PChome</a></li>
<li><a href="http://www.twnic.net.tw/index4.php">TWNIC</a></li>
<li><a href="http://hosting.url.com.tw/buyurl.html">智邦</a></li>
<li><a href="http://www.net-chinese.com.tw/">網路中文</a></li>
<li><a href="http://www.domain.club.tw/">台灣網域名稱俱樂部</a></li>
<li><a href="http://domain.hinet.net/">Hinet</a></li>
</ul>
</li>
<li>國外

<ul>
<li><a href="www.godaddy.com/">Godaddy</a></li>
</ul>
</li>
</ul>


<h2>申請虛擬主機 或 VPS</h2>

<h3>虛擬主機好處：</h3>

<ol>
<li>便宜(甚至免費)</li>
<li>專人維護(也可能算是壞處)</li>
<li>操作較為簡單</li>
</ol>


<h3>虛擬主機壞處</h3>

<ol>
<li>壞鄰居</li>
<li>專人維護</li>
<li>功能以及軟體限制</li>
<li>容易有廣告</li>
</ol>


<h3>VPS 好處</h3>

<ol>
<li>軟體彈性大</li>
<li>穩定</li>
<li>全球彈性佈署</li>
</ol>


<h3>VPS 壞處</h3>

<ol>
<li>技術成分高</li>
<li><p>價格較高</p></li>
<li><p>國內虛擬主機：</p>

<ul>
<li><a href="http://host.waterworld.tw/">水世界</a></li>
<li><a href="http://www.nss.com.tw/index.php">戰國策</a></li>
<li><a href="http://hosting.url.com.tw/products.html">智邦</a></li>
<li><a href="http://www.website.com.tw/">威普</a></li>
</ul>
</li>
<li>國外虛擬主機：

<ul>
<li>國外虛擬主機排行評比 <a href="http://www.free-webhosts.com/">free-webhosts.com</a></li>
<li><a href="http://www.bluehost.com/">Bluehost</a></li>
<li><a href="http://www.byethost.com/">Bytehost</a></li>
<li><a href="http://www.host1free.com/">Host1Free</a></li>
<li><a href="http://www.host1plus.com/">Host1Plus</a></li>
</ul>
</li>
</ol>


<h3>VPS</h3>

<ul>
<li><a href="http://linode.com">Linode</a></li>
<li><a href="http://aws.amazon.com/ec2/pricing/">Amazon EC2</a></li>
</ul>


<h2>WordPress 的教學資源</h2>

<ul>
<li><a href="http://wpchina.org/">WPChina.org</a></li>
<li><a href="http://www.wpcourse.com/">WordPress教程網</a></li>
<li><a href="http://www.wheatv.com/wordpress">玩轉WP</a></li>
<li><a href="http://fairyfish.net/">我愛水煮魚</a></li>
<li><a href="http://sofree.cc/">香腸炒魷魚</a></li>
<li><a href="https://www.facebook.com/groups/wordpresstw/">Facebook WordPress 社團</a></li>
</ul>


<h2>WordPress 的佈景主題</h2>

<ul>
<li><a href="http://www.wpthemesfree.com/">WordPress themes</a></li>
<li><a href="http://themeforest.net/">themeforest</a></li>
<li><a href="http://www.rockettheme.com/">RocketTheme</a></li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[一次搞懂 Assets Pipeline]]></title>
    <link href="http://gogojimmy.net/2012/07/03/understand-assets-pipline/"/>
    <updated>2012-07-03T23:22:00+08:00</updated>
    <id>http://gogojimmy.net/2012/07/03/understand-assets-pipline</id>
    <content type="html"><![CDATA[<p>Assets Pipeline 是 Rails 3.1 一個重要的功能，一直並沒有很去了解其特性，但因為最近都在寫前端的東西在 assets pipeline 的東西上跌跌撞撞了不少次(尤其在 deploy 上 production 後常爆炸，爆到我無處自容)，這篇就是好好研究後的心得以及筆記。</p>

<!--more-->


<h2>Assets Pipeline 有什麼好處，不用會怎樣?</h2>

<p>不用當然不會怎樣，你可以在 <code>confing/application.rb</code> 中把他關掉：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">config</span><span class="o">.</span><span class="n">assets</span><span class="o">.</span><span class="n">enabled</span> <span class="o">=</span> <span class="kp">false</span>
</span></code></pre></td></tr></table></div></figure>


<p>但是 Assets Pipeline 有著許多優良的好處，幫助你處理的過去一些需要由第三方元件來處理的事情，像是：</p>

<ul>
<li>將所有的 js 或是 css 壓縮打包成單一檔案，減少 http request 的大小與數量，增加你網站的效能及速度。</li>
<li>支援像是 SCSS 及 CoffeeScript 這樣的 high-lever 語言，你可以用更簡單更棒的方式來寫 css 及 js。</li>
<li>取代原先不可靠的 query string 改用 MD5 的 fingerprint，query string 的用意在於當檔案內容更動的時候也會一併更動檔案的 query string，這樣可以分辨檔案是否有更動過，因此客戶端可以保留快取並比對自己擁有的版本以及伺服器上的版本是否一致，減少每次的 request，無奈 query string 的作法還是有些問題，像是在部分 CDN 上根本不會快取、在多伺服器的環境中檔案名稱可能會變動、以及許多無效的 cache 問題，因此在 Rails 3.1 改使用 MD5 的 Fingerprinting 來解決了這個問題。</li>
</ul>


<p>Assets Pipeline 的功能主要由兩個重要的元件提供：<a href="https://github.com/sstephenson/sprockets">Sprockets</a> 以及 <a href="https://github.com/rtomayko/tilt">Tilt</a>。Sprockets 用來從你的 assets 路徑中打包壓縮你所有的 assets 後包裝成一個檔案，然後放到你目的地路徑(<code>public/assets</code>)，而 Tilt 主要是一個樣板引擎，用來讓 Sprockets 可以去解析像是 SCSS、CoffeeScript 或是 ERB 等各種樣板，你可以參考 <a href="https://github.com/rtomayko/tilt/blob/master/README.md">Tilt 的 Readme</a> 來了解支援哪些樣板。</p>

<h2>Assets 的結構</h2>

<p>首先必須了解 Assets 的結構，在 Rails 的目錄結構中有三個地方：</p>

<ul>
<li><code>app/assets</code>(通常放置我們自己為了自己的程式所寫的 js、css 或是 images)</li>
<li><code>lib/assets</code>(通常是我們所使用的套件中去用到的 assets)</li>
<li><code>vendor/assets</code>(通常是放一些我們從別的地方借用的 assets，例如說一些 jQuery 的套件)</li>
</ul>


<p>這三個目錄，在預設情況下這三個資料夾的東西是共通的(因為都會被打包成一個檔案)，你可以把你的 rails app 跑起來後在 <code>http://localhost:3000/assets/application.js</code> 中看到你所有的 js 都在這支檔案中，css 同理亦然，你可以在 terminal 中輸入 <code>Rails.application.config.assets.paths</code> 來查看所有的 assets 路徑。你可以發現，除了原本我們剛剛說的三個 assets 目錄之外，還出現了包含在我們 GemFile 中的 jquery，這代表你的 assets 現在也可以包成 gem 來用，如果你有很多個 projects 常重複使用一些共通的 assets，不妨考慮包成 gem 來使用，方便又愉快。</p>

<h2>Assets 的載入</h2>

<p>再來是 assets 目錄下的檔案 import 方式，以 <code>app/asset/javascripts/application.js</code> 這支檔案為例，這是一支 manifest 檔案，主要用來告訴 Sprockets 說哪些檔案是要被載入最後要被包起來壓縮的，最後這支檔案裡面所有的東西就會被包成 <code>application.js</code> 這支檔案，也是我們 <code>layout/application.html.erb</code> 中的 <code>javascript_include_tag 'application'</code> 中的檔案，打開這支檔案除了上面的說明外只有這三行：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="c1">//= require jquery</span>
</span><span class='line'><span class="c1">//= require jquery_ujs</span>
</span><span class='line'><span class="c1">//= require_tree .</span>
</span></code></pre></td></tr></table></div></figure>


<p>上面兩行很明顯的就是要載入 jquery 以及 jquery_ujs 這兩支檔案，這兩支檔案剛剛有提到他其實是被包含在我們所使用的 Gem 中，而下面那行 <code>require_tree .</code> 表示是把三個 <code>assets/javascript</code> 目錄下的檔案或是子目錄內的檔案全部都包進來，這時候你一定會想問如果有些 js 或是 css 我只想在某些特定頁面中使用的話該怎麼辦，例如說假設我們今天有個 admin_functions.js 的檔案只想在我們的後台使用，有兩種方法可以使用：</p>

<ol>
<li>你可以將 <code>require_tree</code> 的目錄改成其他目錄，例如在 <code>app/assets/javascript</code> 目錄下建個 common 資料夾，把 <code>require_tree .</code> 改成 <code>require_tree ./common</code>，這樣子所產生的 application.js 這支檔案就不會用到 admin_functions.js 這支檔案。</li>
<li>你可以建立一個新的資料夾來放你不想要被 application.js 載入的檔案，例如我們在 <code>app/assets/javascript</code> 下建立一個 admin 資料夾把剛剛的 admin_functions.js 檔案放進去，然後把原先 application.js 中的 <code>require_tree .</code> 改為 <code>require_directory .</code> 這樣子只會抓與 apllication.js 檔案同目錄底下的所有檔案而不會去載入子目錄中的檔案。</li>
</ol>


<p>最後再建立另外一支 manifest 用來 import 那些我們要獨立出來的 assets，例如我們建立一支 admin.js 的檔案用來載入其他功能，一樣使用 <code>require_tree</code> 或是 <code>require_directory</code> 的方式來載入，然後在你需要用到的頁面中使用 <code>javascript_include_tag 'admin'</code> 來存取。</p>

<blockquote><p>千千萬萬要記得，當你使用 application.js 以外的 manifest 檔案時，一定要在你環境設定檔中將這支檔案加入 precompile 的清單，否則上了 staging 或是 production 時你就會收到一堆 500 Error XXXX isn&rsquo;t precompiled，加入的位置在環境設定檔像是 production.rb 中的 <code>config.assets.precompile += %w( search.js )</code> 中。</p></blockquote>

<p>除了 <code>require_tree</code> 及 <code>require_directory</code> 之外，還有其他的用法，你都可以使用絕對或是相對路徑來指定檔案位置，副檔名可有可無：</p>

<ul>
<li><code>require</code> [路徑] 載入某支特定檔案，如果這支檔案被載入多次，Sprockets 也會很聰明的只幫你載入一次。</li>
<li><code>include</code> [路徑] 與 <code>require</code> 一樣，差別在即使是被載入過的檔案也會再被載入。</li>
<li><code>require_directory</code> [路徑] 將路徑下不包含子目錄的檔案按照字母順序依次載入。</li>
<li><code>require_tree</code> [路徑] 會將路徑下包含子目錄的檔案全部載入。</li>
<li><code>require_self</code> [路徑] 告訴 Sprockets 再載入其他的檔案前，先將自己的內容插入。</li>
<li><code>depend_on</code> [路徑] 宣告依賴於某支 js，在需要通知某支快取的 assets 過期時非常實用。</li>
<li><code>stub</code> [路徑] 將路徑中的 assets 加入黑名單，所有其他的 require 都不會將他載入。</li>
</ul>


<p>你可以看 <a href="https://github.com/sstephenson/sprockets">Sprockets 的 Readme</a> 來獲得更多的資訊。</p>

<h2>Preprocessing</h2>

<p>另外就是 Sprockets 在 Tilt 的協助下有 preprocessing 的功能，例如你可以使用像是 <code>something.js.coffee.erb</code> 這樣的檔名，Sprockets 會從檔名的最後面一直解析回去成最後的檔案，因此你可以在 js 中使用 CoffeeScript 的寫法來寫 js，並在裡面寫 ruby code 來產生你想要的東西，例如：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='coffeescript'><span class='line'><span class="nx">jQuery</span> <span class="o">-&gt;</span>
</span><span class='line'>  <span class="nv">number = </span><span class="o">&lt;%=</span> <span class="mi">1</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">%&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>不用我說我想你也知道會有什麼結果。</p>

<h2>Helper</h2>

<p>Assets 提供了很多路徑 helper 來讓你指向你的 assets:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='coffeescript'><span class='line'><span class="nx">audio_path</span><span class="p">(</span><span class="s">&quot;horse.wav&quot;</span><span class="p">)</span>   <span class="c1"># =&gt; /audios/horse.wav</span>
</span><span class='line'><span class="nx">audio_tag</span><span class="p">(</span><span class="s">&quot;sound&quot;</span><span class="p">)</span>        <span class="c1"># =&gt; &lt;audio src=&quot;/audios/sound&quot; /&gt;</span>
</span><span class='line'><span class="nx">font_path</span><span class="p">(</span><span class="s">&quot;font.ttf&quot;</span><span class="p">)</span>     <span class="c1"># =&gt; /fonts/font.ttf</span>
</span><span class='line'><span class="nx">image_path</span><span class="p">(</span><span class="s">&quot;edit.png&quot;</span><span class="p">)</span>    <span class="c1"># =&gt; &quot;/images/edit.png&quot;</span>
</span><span class='line'><span class="nx">image_tag</span><span class="p">(</span><span class="s">&quot;icon.png&quot;</span><span class="p">)</span>     <span class="c1"># =&gt; &lt;img src=&quot;/images/icon.png&quot; alt=&quot;Icon&quot; /&gt;</span>
</span><span class='line'><span class="nx">video_path</span><span class="p">(</span><span class="s">&quot;hd.avi&quot;</span><span class="p">)</span>      <span class="c1"># =&gt; /videos/hd.avi</span>
</span><span class='line'><span class="nx">video_tag</span><span class="p">(</span><span class="s">&quot;trailer.ogg&quot;</span><span class="p">)</span>  <span class="c1"># =&gt; &lt;video src=&quot;/videos/trailer.ogg&quot; /&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>Sass 還提供了像是 <code>-url</code> 和 <code>-path</code> 這樣的 helper 來協助你，因此你也可以這樣使用：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='coffeescript'><span class='line'><span class="nx">image</span><span class="o">-</span><span class="nx">url</span><span class="p">(</span><span class="s">&quot;rails.png&quot;</span><span class="p">)</span>         <span class="c1"># =&gt; url(/assets/rails.png)</span>
</span><span class='line'><span class="nx">image</span><span class="o">-</span><span class="nx">path</span><span class="p">(</span><span class="s">&quot;rails.png&quot;</span><span class="p">)</span>        <span class="c1"># =&gt; &quot;/assets/rails.png&quot;.</span>
</span><span class='line'><span class="nx">asset</span><span class="o">-</span><span class="nx">url</span><span class="p">(</span><span class="s">&quot;rails.png&quot;</span><span class="p">,</span> <span class="nx">image</span><span class="p">)</span>  <span class="c1"># =&gt; url(/assets/rails.png)</span>
</span><span class='line'><span class="nx">asset</span><span class="o">-</span><span class="nx">path</span><span class="p">(</span><span class="s">&quot;rails.png&quot;</span><span class="p">,</span> <span class="nx">image</span><span class="p">)</span> <span class="c1"># =&gt; &quot;/assets/rails.png&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<h2>Production</h2>

<p>在還不熟悉的情況下，很容易上了 production 環境後發現原本在本機好好的東西全部炸掉了，因此我們必須了解一下在 production 運作時的情形，如果你直接在 console 中打 <code>rails s -e production</code> 來啟動 production 環境時，你會發現馬上就噴錯誤 <code>application.css isn't precompiled</code>，這是因為在 production 的環境下我們的 assets 是必須被 compile 過後存在 <code>public/assets</code> 底下的，你可以在 console 中打 <code>rake assets:precompile</code>，Rails 會幫你把所有的 assets 檔案依照你 manifests 以及環境設定打包壓縮成單一的檔案後放在 <code>public/assets</code> 目錄底下，所有的檔案名稱並會加入 MD5 的 fingerprinting 用來表示其內容供快取，這些 assets 會被 Rack Cache middleware 自動的被快取，如果你想要使用自己的 server 來取代 middleware 的功能你也可以自己 precompile 後上傳。</p>

<p>在你 precompile 後你再打開 localhost:3000 會發現這時已經沒有錯誤，但是網站看起來就是沒有 css 的感覺，這時候查看 log 你會看到 Rails 找不到 assets 檔案的錯誤，這是因為在 production 環境中是不處理靜態檔案的，因此你必須先在 production.rb 中將 <code>config.serve_static_assets = false</code> 改成 true，這時候重開一次就會看到一切正常，我們已經順利在本機上跑起 production 環境了，你就可以在本機上測試你的 assets pipeline 是否正常，如果你去檢視 precompile 後的檔案你會發現它們都加上了我們之前提到的 MD5 digest，用來辨認其檔案內容是否有所更動，因此你可以將你的 Server 設定中的 expires 時間調到最長，因為在檔案沒有更動的情況下他都會保持相同的檔名，讓使用者可以達到最快的效能。</p>

<h2>Deploy 的小技巧</h2>

<h3>在本機 precompile 來節省在 server 上 precompile 的記憶體使用量</h3>

<p>一般我們都會使用 Capistrano 來 Deploy，記得要將 Capfile 中的 <code>load 'deploy/assets'</code> 的註解取消，Deploy 的過程中會使用 production server 來 compile 你的 assets，如果你很在意 production server 的效能，你可以在本機先 compile 後再上傳到 Server，我們只需要覆寫原本 Capistrano 所提供的 <code>assets:precompile</code> 功能，在你的 <code>deploy.rb</code> 中加入下面的 Code：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">namespace</span> <span class="ss">:deploy</span> <span class="k">do</span>
</span><span class='line'>  <span class="n">namespace</span> <span class="ss">:assets</span> <span class="k">do</span>
</span><span class='line'>    <span class="n">desc</span> <span class="s2">&quot;Precompile assets on local machine and upload them to the server.&quot;</span>
</span><span class='line'>    <span class="n">task</span> <span class="ss">:precompile</span><span class="p">,</span> <span class="ss">:roles</span> <span class="o">=&gt;</span> <span class="n">web</span><span class="p">,</span> <span class="ss">:except</span> <span class="o">=&gt;</span> <span class="p">{</span><span class="ss">:no_release</span> <span class="o">=&gt;</span> <span class="kp">true</span><span class="p">}</span> <span class="k">do</span>
</span><span class='line'>      <span class="n">run_locally</span> <span class="s2">&quot;bundle exec rake assets:precompile&quot;</span>
</span><span class='line'>      <span class="n">find_servers_for_task</span><span class="p">(</span><span class="n">current_task</span><span class="p">)</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">server</span><span class="o">|</span>
</span><span class='line'>        <span class="n">run_locally</span> <span class="s2">&quot;rsync -vr --exclude=&#39;.DS_Store&#39; public/assets </span><span class="si">#{</span><span class="n">user</span><span class="si">}</span><span class="s2">@</span><span class="si">#{</span><span class="n">server</span><span class="o">.</span><span class="n">host</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="n">shared_path</span><span class="si">}</span><span class="s2">/&quot;</span>
</span><span class='line'>      <span class="k">end</span>
</span><span class='line'>    <span class="k">end</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>這樣會在本機 precompile 後使用 rsync 將檔案上傳上去，如果你有使用 Git 的話別忘了把 <code>public/assets</code> 加到 <code>.gitignore</code> 中。</p>

<h3>如果 assets 沒有更新時，就不要跑 precompile</h3>

<p>precompile 應該是整個 deploy 過程中最漫長的一段時間，即使你沒有更新 assets 時也免不了給你跑一下，你可以一樣覆寫 <code>assets:precompile</code> 來判斷是否有更新 assets，如果沒有才執行 precompile：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">namespace</span> <span class="ss">:deploy</span> <span class="k">do</span>
</span><span class='line'>  <span class="n">namespace</span> <span class="ss">:assets</span> <span class="k">do</span>
</span><span class='line'>    <span class="n">task</span> <span class="ss">:precompile</span><span class="p">,</span> <span class="ss">:roles</span> <span class="o">=&gt;</span> <span class="ss">:web</span><span class="p">,</span> <span class="ss">:except</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="ss">:no_release</span> <span class="o">=&gt;</span> <span class="kp">true</span> <span class="p">}</span> <span class="k">do</span>
</span><span class='line'>      <span class="n">from</span> <span class="o">=</span> <span class="n">source</span><span class="o">.</span><span class="n">next_revision</span><span class="p">(</span><span class="n">current_revision</span><span class="p">)</span>
</span><span class='line'>      <span class="k">if</span> <span class="n">capture</span><span class="p">(</span><span class="s2">&quot;cd </span><span class="si">#{</span><span class="n">latest_release</span><span class="si">}</span><span class="s2"> &amp;&amp; </span><span class="si">#{</span><span class="n">source</span><span class="o">.</span><span class="n">local</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="n">from</span><span class="p">)</span><span class="si">}</span><span class="s2"> vendor/assets/ app/assets/ | wc -l&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">to_i</span> <span class="o">&gt;</span> <span class="mi">0</span>
</span><span class='line'>        <span class="n">run</span> <span class="sx">%Q{cd </span><span class="si">#{</span><span class="n">latest_release</span><span class="si">}</span><span class="sx"> &amp;&amp; </span><span class="si">#{</span><span class="n">rake</span><span class="si">}</span><span class="sx"> RAILS_ENV=</span><span class="si">#{</span><span class="n">rails_env</span><span class="si">}</span><span class="sx"> </span><span class="si">#{</span><span class="n">asset_env</span><span class="si">}</span><span class="sx"> assets:precompile}</span>
</span><span class='line'>      <span class="k">else</span>
</span><span class='line'>        <span class="n">logger</span><span class="o">.</span><span class="n">info</span> <span class="s2">&quot;Skipping asset pre-compilation because there were no asset changes&quot;</span>
</span><span class='line'>      <span class="k">end</span>
</span><span class='line'>    <span class="k">end</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>當然你可以將上面兩段 Code 合而為一，有更新 assets 的情況就在本機 precompile 後才上傳到伺服器：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">namespace</span> <span class="ss">:deploy</span> <span class="k">do</span>
</span><span class='line'>  <span class="n">namespace</span> <span class="ss">:assets</span> <span class="k">do</span>
</span><span class='line'>    <span class="n">task</span> <span class="ss">:precompile</span><span class="p">,</span> <span class="ss">:roles</span> <span class="o">=&gt;</span> <span class="ss">:web</span><span class="p">,</span> <span class="ss">:except</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="ss">:no_release</span> <span class="o">=&gt;</span> <span class="kp">true</span> <span class="p">}</span> <span class="k">do</span>
</span><span class='line'>      <span class="n">from</span> <span class="o">=</span> <span class="n">source</span><span class="o">.</span><span class="n">next_revision</span><span class="p">(</span><span class="n">current_revision</span><span class="p">)</span>
</span><span class='line'>      <span class="k">if</span> <span class="n">capture</span><span class="p">(</span><span class="s2">&quot;cd </span><span class="si">#{</span><span class="n">latest_release</span><span class="si">}</span><span class="s2"> &amp;&amp; </span><span class="si">#{</span><span class="n">source</span><span class="o">.</span><span class="n">local</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="n">from</span><span class="p">)</span><span class="si">}</span><span class="s2"> vendor/assets/ app/assets/ | wc -l&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">to_i</span> <span class="o">&gt;</span> <span class="mi">0</span>
</span><span class='line'>        <span class="n">run_locally</span> <span class="s2">&quot;bundle exec rake assets:precompile&quot;</span>
</span><span class='line'>        <span class="n">find_servers_for_task</span><span class="p">(</span><span class="n">current_task</span><span class="p">)</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">server</span><span class="o">|</span>
</span><span class='line'>          <span class="n">run_locally</span> <span class="s2">&quot;rsync -vr --exclude=&#39;.DS_Store&#39; public/assets </span><span class="si">#{</span><span class="n">user</span><span class="si">}</span><span class="s2">@</span><span class="si">#{</span><span class="n">server</span><span class="o">.</span><span class="n">host</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="n">shared_path</span><span class="si">}</span><span class="s2">/&quot;</span>
</span><span class='line'>        <span class="k">end</span>
</span><span class='line'>      <span class="k">else</span>
</span><span class='line'>        <span class="n">logger</span><span class="o">.</span><span class="n">info</span> <span class="s2">&quot;Skipping asset pre-compilation because there were no asset changes&quot;</span>
</span><span class='line'>      <span class="k">end</span>
</span><span class='line'>    <span class="k">end</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>參考資料：</p>

<ul>
<li><a href="http://guides.rubyonrails.org/asset_pipeline.html">RailsGuides Asset Pipeline</a></li>
<li><a href="http://coderberry.me/blog/2012/04/24/asset-pipeline-for-dummies/">Asset Pipeline for Dummies</a></li>
<li><a href="http://railscasts.com/episodes/279-understanding-the-asset-pipeline">#279 Understanding the Asset Pipeline</a></li>
<li><a href="http://railscasts.com/episodes/341-asset-pipeline-in-production">#341 Asset Pipeline in Production</a></li>
<li><a href="http://stackoverflow.com/questions/9016002/speed-up-assetsprecompile-with-rails-3-1-3-2-capistrano-deployment">Speed up assets:precompile with Rails 3.1/3.2 Capistrano deployment</a></li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[寫程式該選 Mac 還是 PC ?]]></title>
    <link href="http://gogojimmy.net/2012/04/07/why-programmer-should-use-mac/"/>
    <updated>2012-04-07T15:29:00+08:00</updated>
    <id>http://gogojimmy.net/2012/04/07/why-programmer-should-use-mac</id>
    <content type="html"><![CDATA[<p>『有人的地方，就有江湖』&mdash; 徐克。笑傲江湖。</p>

<h2>序</h2>

<p>一個競爭的市場，就會有對立的產生，這世界存在著很多不同的領域，領域好比是個江湖的縮影，因此就有許多門派的紛爭，例如說瀏覽器領域有著最大宗的 IE 派，門派成長速度飛快，武功版號跳的跟台灣物價指數一樣快的 Chrome 門，不斷被模仿，一直被超越的 Opera 派；韌性極強，一直對抗幾大勢力的 Firefox 派等等，程式語言也有自己的領域紛爭，社群網站也有社群網站領域的戰爭，這根本就是一個戰火永不停止的戰國時代，但是其中持續最久，衝突最大最激烈，當然以作業系統這塊領域為最，因此，讓我們來談談這個江湖。</p>

<!-- more -->


<p>首先我想講講這個武林的歷史，不想看的人可以直接跳到最下面<a href="#link">我的看法</a>。</p>

<h2>作業系統的上古時代</h2>

<p>在很久以前，這塊江湖的地位是侷限在於少數被選上的人才可以踏入的，這些人就是工程師，因為要掌握這個領域的武功不是一件容易的事情，因為在這個領域的武功基本心法就是學習一堆複雜的武功招式(文字介面的程式指令)，這些被選上的人必須學習很多很多的招式，然後搭配使用這些招式進而來操作作業系統，因此這門領域的武功學習曲線是很高的，不是隨便阿貓阿狗可以學習的，在這段時間中，掌握了這些招式你就能去使用作業系統中的各式各樣的武器(軟體)，在這段時間內，你可以透過作業系統本身提供的基本招式來控制作業系統，你可以透過武器提供的招式來使用武器，你甚至可以自己發明招式(自己寫script)來控制作業系統和武器，因此當時所有的工程師都有自己打造的一套最適合自己的武功，沒有了他們武功會大打折扣，但是在他們使用自己打造的武功時能發揮出他們最強大的威力。</p>

<h2>百家爭鳴</h2>

<p>但是這世界後來起了變化，當時某人將一個『連猴子也能學會用電腦』的概念帶進了這個領域，讓這個領域不再被少數人寡佔，這個概念就是 GUI (圖形化介面)，GUI 的產生讓作業系統看起來更容易理解，大幅降低了這個領域武功的學習曲線，將這個概念發揚光大的有幾個大門派，一個是目前最大的微軟 派，這個門派非常可怕，勢力非常之龐大，他們以作業系統這個領域起家，不單單如此還將觸角伸向非常多不同的領域，幾乎是跟電腦有關的領域都參一腳，不難理解是一個以征服世界為終極目標的門派，掌門人是 Bill Gates，據說他靠著賣出這些武功所賺的錢蟬聯了 13 年的世界首富，是個極為可怕的角色，所有能力值大概都是 ?????，微軟派的武功非常多，但都是以 Windows NT 這個核心心法所延伸出來的，像是大家熟知的 Windows XP，Windows 7 等等。</p>

<p>另外一個是由 Steve Jobs 建立的蘋果教，教主 Steve Jobs 是一代傳奇人物，蘋果教早期曾經發生過一次內亂，這次內亂迫使當時教主 Steve Jobs 遠走他鄉，但是強者天生就是不平凡，在教主離開蘋果教的這段期間，教主看出了當時武功心法的一些通病，為了矯正這些錯誤他窮盡心力費盡苦思的想出一套曠世奇功 &mdash; NeXTStep，而反觀在這期間蘋果教在新教主阿貓一號的帶領下反而流失越來越多的教徒，整個蘋果教呈現一個日暮西山的狀況，因此在其他長老的強烈要求下他們將 Steve Jobs 迎回了蘋果教繼續擔任教主，當然教主也帶回了他所創的那套曠世奇功 NeXTStep 作為日後蘋果橫行天下的武功 Mac OS 的基礎，如果你有研究過 Mac 的心法你不難發現在他們的招式中常常會有 NS 的 prefix 像是 <code>NSString</code>，這個 NS 所指的就是繼承於 NeXTStep，在教主回歸蘋果教後，經過了幾年的發展再次將蘋果教推向了新的高峰，推出的許多劃時代的神兵利器像是 iPod、iPhone、iPad，蘋果教利用這些神兵利器打下了一片大大的江山，成為微軟派最大的勁敵，但在作業系統領域上的唯一一套武功就是 Mac OS，也就是這個門派最基本的心法武功。</p>

<p>其他門派像是 Linux 門也是一股勢力，這個門派的武功與蘋果教的 Mac OS 同樣都是由一個上古時代就存在的門派 Unix 演變而來，這個上古時代也就是本文一開始提到在還沒有『連猴子也能學會用電腦』這個概念出現前，那個被少數人所寡佔的世界，這個門派心法的特性就像是我們前面提到的工程師可以利用各種由作業系統本身或是應用程式本身所提供的招式，加上自己的想法組合出最適合自己的武器，因此 Linux 及 Mac OS 皆繼承了這個傳統，但是 Linux 門情況比較特殊的是，雖然他是一個門派但是本身有非常多不同的勢力，這些勢力自己每天互相打來打去不亦樂乎，造成無法將槍口一致對外，大家自己閉門造車一直重複打造別人已經有的武功，因此發展始終無法達到一個高峰，但是一直以來他在伺服器領域的令人驚艷，也讓 Linux 在這塊領域有著他不可取代的地位。</p>

<h2>回到武功心法</h2>

<p>前面說到，這個世界的改變是由於『連猴子也能學會用電腦』這個概念的帶入，但是這個概念在每個門派的實作方式是不一樣的，例如說微軟派為了將這個心法發揮到極致，將自己的心法改造的與上古時代完全不同，拋棄了那些使用指令操縱一切的心法，取而代之的是使用許多不同的武器(軟體)來應付不同的對象(檔案)，因此基本心法(作業系統)不再是重點，而是一個隨時可以替換掉的東西(隨時可以重灌)，你只要保留你的檔案，重灌你的作業系統後再把這些武器裝上去就可以用了，這個過程不知葬送了多少宅男的青春，但也讓他們有當好人幫正妹修電腦的機會，但是因為如此，微軟派的心法失去了原本上古流傳下來的傳統，你無法使用招式(指令)來操作各種不同的武器(軟體、程序)，因為這些武器現在開始彼此是不相往來，GUI 上的所有程序無法互相溝通。</p>

<p>你可能會問這有什麼影響？我告訴你影響可大了，人使用電腦的目的就是提高工作效率，在程式無法互相溝通的情況下，程式必須仰賴人的操作才有辦法工作，因為你無法寫一些綜合巨集來完成複雜大量的工作，以微軟的例子來說，會使用 VBA 腳本的人工作效率會是一般人的 n 倍，因為你可以完全的操控你的作業系統，就像你使用 VBA 可以完全的自動化 Excel 一樣，但是作業系統想要做到這個層次，必須仰賴著一個統一的執行環境來管理這些程式，這個目標也是之前程式語言領域的 Sun 想要在他們的工作站做到的事情，也就是 Java，但是那是另一段故事我們先暫且不談，這樣的影響對工程師來說，就是 &ldquo;什麼都看不到&#8221;，現在微軟派武功心法全部都蒙上了一層馬賽克，因為所有的東西都是被包裝過的，相對的工程師們無法去真正的了解這套武功心法，你想要做的事情如果超出了武器(軟體)能應付的範圍，你只能放棄，也是因為包裝的太好，在發生問題想要 Debug 的時候你根本看不到作業系統層面的問題，只能按下那個回報給微軟的按鈕，因此在微軟派以這樣的概念全力發展 GUI 的歷史後，取而代之所捨棄的就是這個 &#8220;問題&#8221;，我把問題用雙引號包起來的原因是因為，對一般使用者來說這並不算是個問題，因為他們感受不到，我相信這或許也是微軟的成功原因之一。</p>

<p>Linux 門則是把原先上古時代的傳統發揮的淋漓盡致，但就像我前面提到的，Linux 門自己的內戰打的不亦樂乎，當你踏入了 Linux 門之後，你會發現常常當你想使用 Linux 上面所需要的武器 A 的時，他會告訴你你必須先有武器 B 的 1.13 版，然後武器 B 的 1.13 版必須先有裝備 C 的 2.01 版，然後裝備 C 的 2.01 版需要 Linux 心法的 1.03 版，然後結果你發現你在用的是 Linux 心法 2.0 版，因此雖然 Linux 門繼承了上古 Unix 門的良好傳統，讓你可以像高手般的打造自己的武功，也可以完全的掌控整個作業系統，但是常會因為這些武器的相依性給搞瘋，還沒用到武器，就先被武器給弄死了。</p>

<h2>再談 GUI</h2>

<p>所以你大概可以體會，GUI 開始算是一個作業系統的分水嶺，他讓每個門派的心法看起來變得很不一樣，微軟派的設計讓你看不到作業系統層面的東西，因為東西幾乎都是被包裝好的，但是你可以在微軟派的 windows 系列武功上使用相對良好的 GUI，但是由於微軟派的 GUI 設計是跟底層綁在一起的，因此有時候 GUI 的崩潰也讓系統一起崩潰了，就出現了傳說中的藍白畫面，Linux 門的設計非常開放，你可以隨時取用串接你想用的心法，但是在 GUI 方面就差強人意，大部分是一些看起來就像是山寨微軟派或蘋果教的產物，當然現在來說有些表現較為優異的像是 Ubuntu 之類的，但用過就知道還是有差，而蘋果教正好是取其兩家之長，一個擁有高度 usability 的 GUI 介面加上可以自行設計的武器系統，Mac OS 感覺就像是上古流傳下來的神器再用對武器施法的卷軸衝到 +10 那樣強大，在 Mac OS 上使用繼承於教主所開發的心法 NeXTStep 的進化版本 Cocoa，這套心法提供了一個統一的作業系統執行環境，所有基於 Cocoa 心法所研發出來的新武器都有了統一的介面，這個統一的介面讓你可以 100% 的控制所有的程序，而且 Mac OS 本身就已經提供了完整的開發環境給所有的工程師，反觀在 Linux 門缺乏這個在 GUI 上統一的介面，因此他們的 GUI 看起來就挺杯具，而微軟派的 Windows 系列在從 Vista 以後希望打造出一個完全建構於新心法 .NET 框架的環境來達成像是統一介面的環境，但是由於其設計哲學上的差異他們還是有差異，而且 .NET 心法的一個致命傷就是仰賴著使用者必須自行去更新 .NET 版本，如果他沒有更新成適合的版本那你所開發的武器可能他就沒有辦法使用。</p>

<h2><a id="link"></a>後記</h2>

<p>其實是簡短的故事整理，對於使用 Mac 或是 PC 的來寫程式比較好，其實兩派各有說法，個人淺見只是文化上的差異，一個最簡單考慮的重點就是你想開發哪個平台上面的軟體，那麼你最好就使用該平台來開發，你想開發 Windows 上面的應用程式，那麼趕快安裝 Visual Studio.NET，你想開發 Mac 上面的程式那就趕快買台 Mac 安裝好你的 XCode，當然，你也可以考慮 Java 來開發跨平台的程式，但是用過都知道在 GUI 的顯示在不同平台上還是會有差異的，那如果是開發 Web 的程式來說，個人淺見如果你是使用 PHP、Ruby、Python 等程式語言，那麼 Unix-base 的環境還是比較適合你，因為你必須常常去追蹤你的 Application 在系統的運行狀況來做效能的調教或是 Debug，而這是在 Windows 平台上的相對弱項。</p>

<p>已經看過太多的戰文在討論 unix 好還是 windows 好，其實就像我說的是文化上的差異，微軟的文化是以一般使用者為出發點而設計的平台，如果是由一個 Unix 的工程師來設計一個軟體，他可能會開始先設計一個文字介面的核心，然後才加上一層 GUI 的皮，這樣這支程式才可以為其他程式設計師來自動化操作，因為 Unix 的哲學是以程式設計師為出發點，反觀 Windows 工程師會傾向於先做出一個使用者操作的畫面，然後才去寫核心部分，對於 99.999999999% 的使用者不寫程式的 Windows 這是很正常的事。另外 Windows 跟 Unix 最大的差異還是在資訊透明度，Windows 上核心底層的東西必須仰賴微軟更新後才會修復，因為你很難去看到底層的原始碼，而 Unix 由於開放的關係你可以隨時進入底層去找出錯誤來修正他，但這也不算是 Unix 就比較好的觀點，因為這單純是設計理念的不同，Windows 傾向於我幫你處理所有底層的問題，你專心寫你的程式就好，而 Unix 則是我讓你看光光，你不開心就改沒關係，因此兩派信徒互相嘲笑對方，其實只是設計理念上的差異罷了。</p>

<p>雖然我用 Mac，但我相信比爾蓋茲在 Windows 上寫程式還是會把我電假的，所以我從不嘲笑 Windows 的工程師，所以請依照你的需求挑選你的平台就好，如果要開發微軟的程式，那麼用微軟的環境還是最無痛的！</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[懷念阿桑]]></title>
    <link href="http://gogojimmy.net/2012/04/06/huai-nian-a-sang/"/>
    <updated>2012-04-06T18:20:00+08:00</updated>
    <id>http://gogojimmy.net/2012/04/06/huai-nian-a-sang</id>
    <content type="html"><![CDATA[<p>我這個年代的人，應該沒有人沒聽過阿桑的葉子</p>

<!--more-->




<iframe width="420" height="315" src="http://www.youtube.com/embed/Gk2oPC7o1aw" frameborder="0" allowfullscreen></iframe>


<p>我想每個人在問到自己的興趣時，大部分人都會回答音樂，我想人們之所以會喜歡音樂，因為音樂的某一段弦律，某一段歌詞，再加上歌手歌聲的感染力，觸發了我們心中某一段故事，而引起共鳴。</p>

<p>阿桑，就是這樣一位有感染力的歌手。</p>

<p>他是在一個商業環境下，一個最不商業的歌手，阿桑略帶沙啞的嗓音為歌曲詮釋出那種酸、苦的感覺能挑起你的情緒，在你腦海中喚起那段回憶，阿桑的聲音就是這種層次。</p>

<iframe width="420" height="315" src="http://www.youtube.com/embed/psEnkuP4oOM" frameborder="0" allowfullscreen></iframe>


<p>2008 年的今天，阿桑變成了一片葉子，而離開了我們，僅以此篇紀念一個曾經感動我的歌手，謝謝你的歌聲陪伴了我的回憶，希望你在天堂能過的開心。</p>

<iframe width="420" height="315" src="http://www.youtube.com/embed/fu_8Hz8M0MY" frameborder="0" allowfullscreen></iframe>




<iframe width="560" height="315" src="http://www.youtube.com/embed/bCgalIf78tU" frameborder="0" allowfullscreen></iframe>




<iframe width="560" height="315" src="http://www.youtube.com/embed/HFFn7H_7ZWM" frameborder="0" allowfullscreen></iframe>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Git 情境劇]]></title>
    <link href="http://gogojimmy.net/2012/02/29/git-scenario/"/>
    <updated>2012-02-29T00:14:00+08:00</updated>
    <id>http://gogojimmy.net/2012/02/29/git-scenario</id>
    <content type="html"><![CDATA[<h2>Git 情境劇</h2>

<p>這篇主要是給自己做個記錄，因為 Git 指令實在太多了&hellip;</p>

<!-- more -->


<ol>
<li><a href="http://blog.gogojimmy.net/2012/01/17/how-to-use-git-1-git-basic/">Git 教學(1)：Git的基本使用</a></li>
<li><a href="http://blog.gogojimmy.net/2012/01/21/how-to-use-git-2-basic-usage-and-worflow/">Git 教學(2)：Git Branch 的操作與基本工作流程</a></li>
<li><p><a href="http://blog.gogojimmy.net/2012/02/29/git-scenario/">Git 情境劇：告訴你使用 Git 時什麼情況該下什麼指令</a></p></li>
<li><p>如何安裝 Git</p>

<ul>
<li><p>Mac : 安裝 <a href="http://mxcl.github.com/homebrew/">Homebrew</a></p>

<pre><code>  brew install git
</code></pre></li>
<li>Linux(Debian) : <code>apt-get install git-core</code></li>
<li>Linux(Fedora) : <code>yum install git-core</code></li>
<li>Windows : 下載安裝 <a href="http://code.google.com/p/msysgit">msysGit</a></li>
</ul>
</li>
<li><p>如何設定 Git</p>

<ul>
<li>Mac : <a href="http://help.github.com/mac-set-up-git/">Set Up Git on Mac</a></li>
<li>Linux : <a href="http://help.github.com/linux-set-up-git/">Set Up Git on Linux</a></li>
<li>Windows : <a href="http://help.github.com/win-set-up-git/">Set up Git on Windows</a></li>
</ul>
</li>
<li><p>如何開始一個 Git Respository</p>

<ul>
<li>在專案底下使用 <code>git init</code> 開始一個新的 Git repo.</li>
<li>使用 <code>git clone</code> 複製一個專案</li>
</ul>
</li>
<li><p>如何將檔案加入 Stage</p>

<ul>
<li>使用 <code>git add</code> 將想要的檔案加入 Stage.</li>
<li><code>git add .</code> 會將所有編修過的檔案加入 Stage (新增但還沒 Commit 過的檔案並不會加入)</li>
</ul>
</li>
<li><p>如何將檔案從 Stage 中移除(取消add)</p>

<ul>
<li><code>git reset HEAD 檔案名稱</code></li>
</ul>
</li>
<li><p>如何將檔案提交(commit)</p>

<ul>
<li>使用 <code>git commit</code>會將 Stage 狀態的檔案做 Commit 動作</li>
<li><code>git commit -m "commit訊息"</code> 可以略過編輯器直接輸入 commit 訊息完成提交。</li>
<li><code>git commit -am "commit訊息"</code> 等同於先<code>git add .</code>後略過編輯器提交 commit。</li>
</ul>
</li>
<li><p>如何修改/取消上一次的 commit</p>

<ul>
<li><code>git commit --amend</code> 修改上一次的 commit 訊息。</li>
<li><code>git commit --amend 檔案1 檔案2...</code> 將檔案1、檔案2加入上一次的 commit。</li>
<li><code>git reset HEAD^ --soft</code> 取消剛剛的 commit，但保留修改過的檔案。</li>
<li><code>git reset HEAD^ --hard</code> 取消剛剛的 commit，回到再上一次 commit的 乾淨狀態。</li>
</ul>
</li>
<li><p>分支基本操作(branch)</p>

<ul>
<li><code>git branch</code> 列出所有本地端的 branch。</li>
<li><code>git branch -r</code> 列出所有遠端的 branch。</li>
<li><code>git branch -a</code> 列出所有本地及遠端的 branch。</li>
<li><code>git branch "branch名稱"</code> 建立一個新的 branch。</li>
<li><code>git checkout -b "branch名稱"</code> 建立一個新的 branch 並切換到該 branch。</li>
<li><code>git branch branch名稱 起始點</code> 以起始點作為基準建立一個新的 branch，起始點可以是一個 tag，branch 或是 commit。</li>
<li><code>git branch --track branch名稱 遠端branch</code> 建立一個 tracking 遠端 branch 的 branch，這樣以後 push/pull都會直接對應到該遠端的branch。</li>
<li><code>git branch --set-upstream branch 遠端branch</code> 將一個已存在的 branch 設定成 tracking 遠端的branch。</li>
<li><code>git branch -d "branch 名稱"</code> 刪除 branch。</li>
<li><code>git -r -d 遠端branch</code> 刪除一個 tracking 的遠端 branch，例如<code>git branch -r -d wycats/master</code></li>
<li><code>git push repository名稱 :遠端branch</code> 刪除一個 repository 的 branch，通常用在刪除遠端的 branch，例如<code>git push origin :old_branch_to_be_deleted</code>。</li>
<li><code>git checkout branch名稱</code> 切換到另一個 branch(所有修改過程會被保留)。</li>
</ul>
</li>
<li><p>遠端操作(remote)</p>

<ul>
<li><code>git remote add remote名稱 remote網址</code> 加入一個 remote repository，例如 <code>git remote add github git://github.com/gogojimmy/test.git</code></li>
<li><code>git push remote名稱 :branch名稱</code> 刪除遠端 branch，例如 <code>git push origin :somebranch</code>。</li>
<li><code>git pull remote名稱 branch名稱</code> 下載一個遠端的 branch 並合併(注意是下載遠端的 branch 合併到目前本地端所在的 branch)。</li>
<li><code>git push</code> 類似於 pull 操作，將本地端的 branch 上傳到遠端。</li>
</ul>
</li>
<li><p>合併操作(merge)</p>

<ul>
<li><code>git merge branch名稱</code> 合併指定的 branch 到目前的 branch。</li>
<li><code>git merge branch名稱 --no-commit</code> 合併指定的 branch 到目前的 branch 但是不會產生合併的 commit。</li>
<li><code>git cherry-pick SHA</code> 將某一個 commit 的內容合併到目前 branch，指定 commit 是使用該 commit 的 SHA 值，例如 <code>git cherry-pick 7300a6130d9447e18a931e898b64eefedea19544</code>。</li>
</ul>
</li>
<li><p>暫存操作(stash)</p>

<ul>
<li><code>git stash</code> 將目前所做的修改都暫存起來。</li>
<li><code>git stash apply</code> 取出最新一次的暫存。</li>
<li><code>git stash pop</code> 取出最新一次的暫存並將他從暫存清單中移除。</li>
<li><code>git stash list</code> 顯示出所有的暫存清單。</li>
<li><code>git stash clear</code> 清除所有暫存。</li>
</ul>
</li>
<li><p>常見問題：</p>

<ul>
<li>我的 code 改爛了我想全部重來，我要如何快速回到乾淨的目錄?

<ul>
<li><code>git reset --hard</code> 這指令會清除所有與最近一次 commit 不同的修改。</li>
</ul>
</li>
<li>merge 過程中發生 confict 我想放棄 merge，要如何取消 merge？

<ul>
<li>一樣使用 <code>git reset --hard</code> 可以取消這次的 merge。</li>
</ul>
</li>
<li>如何取消這次的 merge 回到 merge 前的狀態?

<ul>
<li><code>git reset --hard ORIG_HEAD</code> 這指令會取消最近一次成功的 merge 以及所有你在這次 merge 後所做的修改。</li>
</ul>
</li>
<li>如何回復單獨檔案到原本 commit 的狀態?

<ul>
<li><code>git checkout 檔案名稱</code> 這指令會將已經被修改過的檔案回復到最近一次 commit 的樣子。</li>
</ul>
</li>
</ul>
</li>
<li><p>其他連結：</p></li>
<li><p><a href="http://blog.gogojimmy.net/2012/01/17/how-to-use-git-1-git-basic/">Git 教學(1)：Git的基本使用</a></p></li>
<li><a href="http://blog.gogojimmy.net/2012/01/21/how-to-use-git-2-basic-usage-and-worflow/">Git 教學(2)：Git Branch 的操作與基本工作流程</a></li>
<li><a href="http://blog.gogojimmy.net/2012/02/29/git-scenario/">Git 情境劇：告訴你使用 Git 時什麼情況該下什麼指令</a></li>
</ol>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[[掃雷]在 Mac 上使用 RVM 安裝 Ruby 1.9.3 時錯誤]]></title>
    <link href="http://gogojimmy.net/2012/02/20/install-ruby-1-dot-9-3-via-rvm-on-mac-problem/"/>
    <updated>2012-02-20T01:54:00+08:00</updated>
    <id>http://gogojimmy.net/2012/02/20/install-ruby-1-dot-9-3-via-rvm-on-mac-problem</id>
    <content type="html"><![CDATA[<!--more-->


<h2>環境：</h2>

<ul>
<li>OS:Mac OSX 10.7</li>
<li>RVM:1.8.2</li>
<li>Ruby:1.9.3-p125</li>
<li>Xcode:4.2</li>
</ul>


<h2>過程：</h2>

<p>自從有了 RVM 後其實已經不太記得原本的 Ruby 怎麼裝了，某天想在公司的電腦安裝 1.9.3 時，一如往常的輸入 <code>rvm install 1.9.3</code> ，過程也一如往常的：</p>

<pre>
Installing Ruby from source to: /Users/Jimmy/.rvm/rubies/ruby-1.9.3-p125, this may take a while depending on your cpu(s)...

ruby-1.9.3-p125 - #fetching
ruby-1.9.3-p125 - #extracted to /Users/Jimmy/.rvm/src/ruby-1.9.3-p125 (already extracted)
Fetching yaml-0.1.4.tar.gz to /Users/Jimmy/.rvm/archives
Extracting yaml-0.1.4.tar.gz to /Users/Jimmy/.rvm/src
Configuring yaml in /Users/Jimmy/.rvm/src/yaml-0.1.4.
Compiling yaml in /Users/Jimmy/.rvm/src/yaml-0.1.4.
Installing yaml to /Users/Jimmy/.rvm/usr
ruby-1.9.3-p125 - #configuring
ERROR: Error running ' ./configure --prefix=/Users/Jimmy/.rvm/rubies/ruby-1.9.3-p125 --enable-shared --disable-install-doc --with-libyaml-dir=/Users/Jimmy/.rvm/usr ', please read /Users/Jimmy/.rvm/log/ruby-1.9.3-p125/configure.log
ERROR: There has been an error while running configure. Halting the installation.
</pre>


<p>幹這是哪招！ 拜求 Google 大神後找到的解法：</p>

<pre>
$ rvm reinstall 1.9.3 --with-gcc=clang
</pre>


<p>一樣無效！！</p>

<h2>解法</h2>

<p>這問題主要是出在 Xcode 4.2 以後 gcc 的位置就改了。</p>

<p>重新下載編譯 <a href="https://github.com/kennethreitz/osx-gcc-installer/downloads">GCC GCC Installer for OSX 10.7+, Version 2</a></p>

<p>然後就可以輕鬆的 <code>rvm install 1.9.3-p125</code> 了。</p>

<p><img src="https://lh3.googleusercontent.com/-6T1CNuz-D2Q/T0FDdB75zhI/AAAAAAAABEM/6H_FdBLZZUU/s800/14869308.jpg" alt="Success" /></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[在 Rails 中使用 Solr 做全文搜尋]]></title>
    <link href="http://gogojimmy.net/2012/01/25/full-text-search-in-rails-with-solr/"/>
    <updated>2012-01-25T22:02:00+08:00</updated>
    <id>http://gogojimmy.net/2012/01/25/full-text-search-in-rails-with-solr</id>
    <content type="html"><![CDATA[<p>做網站常免不了使用全文搜尋的功能，很多時候我們會選擇方便快速的 <a href="http://www.google.com/cse/">Google Custom Search</a> ，但搜尋的結果差強人意，因此我們會更希望能擁有自己的搜尋引擎。</p>

<h2>Solr &amp; Sunspot</h2>

<p>Solr 是一套建立在 <a href="http://lucene.apache.org/java/docs/">Lucene</a> 搜尋引擎的工具，不用煩惱如何去操作複雜的 Lucene ，只需要透過 HTTP Request 來跟 Solr 溝通就好，非常的簡單易用，在 Rails 使用比簡單還要更簡單，因為我們有 <a href="https://github.com/outoftime/sunspot">sunspot_rails</a> ，你不需要下載 Slor 來編譯執行他，因為 <a href="https://github.com/outoftime/sunspot">sunspot_rails</a> 這套 gem 已經幫你包在裡面了，你只需要如同往常的將他加到 Gemfile ，然後 bundle install ，最後啟動它就完成了他所需要的動作，簡單到覺得寫篇文章來講彷彿都侮辱了他。</p>

<!--more-->


<h2>設定及啟動 Solr：</h2>

<p>使用 sunspot_rails 非常簡單：</p>

<p>在你的 Gemfile：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">gem</span> <span class="s1">&#39;sunspot_rails&#39;</span>
</span></code></pre></td></tr></table></div></figure>


<p>然後：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="err">$</span> <span class="n">bundle</span> <span class="n">install</span>
</span></code></pre></td></tr></table></div></figure>


<p>Solr 本身跑在一個獨立的 proccess ，因此我們需要啟動它：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="err">$</span> <span class="n">rake</span> <span class="n">sunspot</span><span class="ss">:solr:start</span>
</span></code></pre></td></tr></table></div></figure>


<p>當然你也可以停止它，將 start 換成 stop 就好：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="err">$</span> <span class="n">rake</span> <span class="n">sunspot</span><span class="ss">:solr:stop</span>
</span></code></pre></td></tr></table></div></figure>


<p>當你啟動了 Solr Server ，你會發現 solr 複製了一些檔案到你 Rails 下一個 Solr 的目錄，這是因為 Solr 將搜尋的 index 都放在這個檔案目錄中，這樣一來就算你重新啟動 Server 也仍然會保留你的 index 。</p>

<p><img src="https://lh5.googleusercontent.com/-Cqnr58uBvzA/Tx_TPxKel9I/AAAAAAAAA_0/vGOBrLGT-NE/s640/2012-01-25_1801.jpg" alt="solr" /></p>

<h2>建立搜尋</h2>

<p>假設我今天要在部落格使用 Solr ，在我的 Rails 專案中我建立一個 scaffold 的 post，有 title 與 content 的 fields：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">rails</span> <span class="n">g</span> <span class="n">scaffold</span> <span class="n">post</span> <span class="n">title</span><span class="ss">:string</span> <span class="n">text</span><span class="ss">:content</span>
</span><span class='line'><span class="n">rake</span> <span class="n">db</span><span class="ss">:migrate</span>
</span></code></pre></td></tr></table></div></figure>


<p>再來我們要在 Post 的 model 中設定讓 Solr 對 Title 做 index：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">class</span> <span class="nc">Post</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
</span><span class='line'>  <span class="n">validates_presence_of</span> <span class="ss">:title</span><span class="p">,</span> <span class="ss">:content</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">searchable</span> <span class="k">do</span>
</span><span class='line'>    <span class="n">text</span> <span class="ss">:title</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p><strong>在 Solr 中只有 text 欄位是做全文搜尋，其他如 time、integer、boolean&hellip;等其他欄位都只能用來作排序/過濾/限制的選項而已。</strong></p>

<p>在 controller 中我們也需要建立一個 search 的 Action 讓人可以做搜尋：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'>  <span class="k">def</span> <span class="nf">search</span>
</span><span class='line'>    <span class="vi">@posts</span> <span class="o">=</span> <span class="no">Post</span><span class="o">.</span><span class="n">search</span> <span class="k">do</span>
</span><span class='line'>      <span class="n">keywords</span> <span class="n">params</span><span class="o">[</span><span class="ss">:query</span><span class="o">]</span>
</span><span class='line'>    <span class="k">end</span><span class="o">.</span><span class="n">results</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">respond_to</span> <span class="k">do</span> <span class="o">|</span><span class="nb">format</span><span class="o">|</span>
</span><span class='line'>      <span class="nb">format</span><span class="o">.</span><span class="n">html</span> <span class="p">{</span> <span class="n">render</span> <span class="ss">:action</span> <span class="o">=&gt;</span> <span class="s2">&quot;index&quot;</span> <span class="p">}</span>
</span><span class='line'>      <span class="nb">format</span><span class="o">.</span><span class="n">xml</span> <span class="p">{</span> <span class="n">render</span> <span class="ss">:xml</span> <span class="o">=&gt;</span> <span class="vi">@posts</span> <span class="p">}</span>
</span><span class='line'>    <span class="k">end</span>
</span><span class='line'>  <span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>在 View 中我們也加入一個搜尋的欄位：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="o">&lt;</span><span class="sx">%= form_tag search_posts_path, :method =</span><span class="o">&gt;</span> <span class="ss">:get</span> <span class="k">do</span> <span class="sx">%&gt;</span>
</span><span class='line'><span class="sx">  &lt;p&gt;</span>
</span><span class='line'>    <span class="o">&lt;</span><span class="sx">%= text_field_tag :query, params[:query] %&gt; &lt;%=</span> <span class="n">submit_tag</span> <span class="s2">&quot;搜尋&quot;</span> <span class="sx">%&gt;</span>
</span><span class='line'><span class="sx">  &lt;/p&gt;</span>
</span><span class='line'><span class="o">&lt;</span><span class="sx">% end </span><span class="o">%&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>來看看成果：</p>

<p>搜尋前：</p>

<p><img src="https://lh3.googleusercontent.com/-TrA6x4o7AKQ/Tx_aDjidaCI/AAAAAAAABAM/0OQA4i-QQ7s/s400/2012-01-25_1831.jpg" alt="solr index" /></p>

<p>搜尋後：</p>

<p><img src="https://lh3.googleusercontent.com/-gCyODSBp3wo/Tx_ZJIDD8sI/AAAAAAAABAA/H6qKqWsQho4/s400/2012-01-25_1827.jpg" alt="solr results" /></p>

<p>超簡單的吧！</p>

<h2>加入現有的內容並 Reindex</h2>

<p>若是我們想要加入一個 field 也變為搜尋的內容該怎麼辦？ Solr 提供你一個 reindex 的功能可以方便你達成，首先先將你想要加入搜尋的 field 加到 model 中：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">class</span> <span class="nc">Post</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
</span><span class='line'>  <span class="n">validates_presence_of</span> <span class="ss">:title</span><span class="p">,</span> <span class="ss">:content</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">searchable</span> <span class="k">do</span>
</span><span class='line'>    <span class="n">text</span> <span class="ss">:title</span><span class="p">,</span> <span class="ss">:content</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>這時候 Solr 就會將未來所有的 content 也加入 index 的範圍內，但我們還需要將已經產生的 content 也做 index，因此我們執行：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="err">$</span> <span class="n">rake</span> <span class="n">sunspot</span><span class="ss">:reindex</span>
</span></code></pre></td></tr></table></div></figure>


<p>Solr 便會自動幫我們將所有內容做 index ，我們現在搜尋便會加入了 content 的內容：</p>

<p><img src="https://lh5.googleusercontent.com/-PJyi15c9xL8/Tx_cN-GOPCI/AAAAAAAABAY/wR3E_ZSz9OY/s800/2012-01-25_1840.jpg" alt="solr content search" /></p>

<p>真是太方便了！！</p>

<h2>斷字系統</h2>

<p>只要是碰到搜尋免不了就有語言的問題，也會碰到斷字的問題，像剛剛的搜尋中，搜尋 pen 會有結果，但是搜尋 &lsquo;pe&rsquo; 或是 &lsquo;en&rsquo; 就會看不到任何結果，這個問題我們只要簡單的設定一下 Solr 就可以解決了，還記得在你啟動 Solr 的時候在你 Rails 的目錄下也建立了 Solr 的資料夾，現在我們要去設定 RAILS_ROOT/solr/conf/schema.xml 這支檔案，找到下面這個片段(大約在62行)：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='xml'><span class='line'><span class="nt">&lt;fieldType</span> <span class="na">name=</span><span class="s">&quot;text&quot;</span> <span class="na">class=</span><span class="s">&quot;solr.TextField&quot;</span> <span class="na">omitNorms=</span><span class="s">&quot;false&quot;</span><span class="nt">&gt;</span>
</span><span class='line'>  <span class="nt">&lt;analyzer&gt;</span>
</span><span class='line'>    <span class="nt">&lt;tokenizer</span> <span class="na">class=</span><span class="s">&quot;solr.StandardTokenizerFactory&quot;</span><span class="nt">/&gt;</span>
</span><span class='line'>    <span class="nt">&lt;filter</span> <span class="na">class=</span><span class="s">&quot;solr.StandardFilterFactory&quot;</span><span class="nt">/&gt;</span>
</span><span class='line'>    <span class="nt">&lt;filter</span> <span class="na">class=</span><span class="s">&quot;solr.LowerCaseFilterFactory&quot;</span><span class="nt">/&gt;</span>
</span><span class='line'>  <span class="nt">&lt;/analyzer&gt;</span>
</span><span class='line'><span class="nt">&lt;/fieldType&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>改成：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
</pre></td><td class='code'><pre><code class='xml'><span class='line'><span class="nt">&lt;fieldType</span> <span class="na">name=</span><span class="s">&quot;text&quot;</span> <span class="na">class=</span><span class="s">&quot;solr.TextField&quot;</span> <span class="na">omitNorms=</span><span class="s">&quot;false&quot;</span><span class="nt">&gt;</span>
</span><span class='line'>  <span class="nt">&lt;analyzer&gt;</span>
</span><span class='line'>    <span class="nt">&lt;tokenizer</span> <span class="na">class=</span><span class="s">&quot;solr.StandardTokenizerFactory&quot;</span><span class="nt">/&gt;</span>
</span><span class='line'>    <span class="nt">&lt;filter</span> <span class="na">class=</span><span class="s">&quot;solr.StandardFilterFactory&quot;</span><span class="nt">/&gt;</span>
</span><span class='line'>    <span class="nt">&lt;filter</span> <span class="na">class=</span><span class="s">&quot;solr.LowerCaseFilterFactory&quot;</span><span class="nt">/&gt;</span>
</span><span class='line'>    <span class="nt">&lt;filter</span> <span class="na">class=</span><span class="s">&quot;solr.NGramFilterFactory&quot;</span> <span class="na">minGramSize=</span><span class="s">&quot;2&quot;</span> <span class="na">maxGramSize=</span><span class="s">&quot;15&quot;</span><span class="nt">/&gt;</span>
</span><span class='line'>  <span class="nt">&lt;/analyzer&gt;</span>
</span><span class='line'>
</span><span class='line'>  <span class="nt">&lt;analyzer</span> <span class="na">type=</span><span class="s">&quot;query&quot;</span><span class="nt">&gt;</span>
</span><span class='line'>    <span class="nt">&lt;tokenizer</span> <span class="na">class=</span><span class="s">&quot;solr.StandardTokenizerFactory&quot;</span><span class="nt">/&gt;</span>
</span><span class='line'>    <span class="nt">&lt;filter</span> <span class="na">class=</span><span class="s">&quot;solr.StandardFilterFactory&quot;</span><span class="nt">/&gt;</span>
</span><span class='line'>    <span class="nt">&lt;filter</span> <span class="na">class=</span><span class="s">&quot;solr.LowerCaseFilterFactory&quot;</span><span class="nt">/&gt;</span>
</span><span class='line'>  <span class="nt">&lt;/analyzer&gt;</span>
</span><span class='line'><span class="nt">&lt;/fieldType&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>在這裡我們設定讓 Solr 對最少2個字元最大15個字元做 index，例如以單字 Character 來說就會對 &lsquo;Ch&#8217;、&#8217;ar&#8217;、&#8217;ra&#8217;、&#8217;Cha&rsquo;&hellip;都做 index，這時重新啟動你的 Solr 並重新 index:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='xml'><span class='line'>rake sunspot:solr:stop
</span><span class='line'>rake sunspot:solr:start
</span><span class='line'>rake sunspot:reindex
</span></code></pre></td></tr></table></div></figure>


<p>這時候你的搜尋結果就會依照我們剛剛的設定生效了：</p>

<p><img src="https://lh6.googleusercontent.com/-p0ET4YWSMxE/Tx_j6y5H79I/AAAAAAAABAk/eYw9PXu6fwM/s800/2012-01-25_1913.jpg" alt="solr result" /></p>

<p>如果你想要讓所有 index 只從字首開始算，那麼將剛剛設定中的 <code>solr.NGramFilterFactory</code> 改為 <code>solr.EdgeNGramFilterFactory</code> 就可以了，更多的斷字設定建議妳可以參考 Solr 的 <a href="http://wiki.apache.org/solr/AnalyzersTokenizersTokenFilters">Wiki</a></p>

<p>如果想讓你的 Solr 支援中文也很簡單，你只要將設定中的 tokenizer 替換成你想要使用的分詞系統就可以了， Solr 本身內建簡單的 CJK 分詞系統，你只要將原先的 StandardTokenizerFactory 替換掉：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='xml'><span class='line'><span class="nt">&lt;tokenizer</span> <span class="na">class=</span><span class="s">&quot;solr.StandardTokenizerFactory&quot;</span><span class="nt">/&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>換成：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='xml'><span class='line'><span class="nt">&lt;tokenizer</span> <span class="na">class=</span><span class="s">&quot;solr.CJKTokenizerFactory&quot;</span><span class="nt">/&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>一樣照上面的流程重新啟動 Solr 並做 reindex 的動作就可以看到結果了：</p>

<p><img src="https://lh4.googleusercontent.com/-NH94Y8INkrc/Tx_wuj2-R7I/AAAAAAAABAw/bFYCWrbcxDE/s800/2012-01-25_2007.jpg" alt="Solr CJK" /></p>

<p>Solr 非常強大卻又非常簡單，有很多的特異功能你可以去使用，建議你可以將它的 <a href="http://wiki.apache.org/solr/">Wiki</a>好好看過，快速的設定也可以參考 sunspot_rails 的 Github 上的<a href="https://github.com/outoftime/sunspot_rails">說明</a>。</p>

<p>參考：</p>

<ul>
<li><a href="https://github.com/outoftime/sunspot_rails">Github: sunspot_rails</a></li>
<li><a href="http://wiki.apache.org/solr/">Solr Wiki</a></li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Git 教學(2)：Git Branch 的操作與基本工作流程]]></title>
    <link href="http://gogojimmy.net/2012/01/21/how-to-use-git-2-basic-usage-and-worflow/"/>
    <updated>2012-01-21T17:46:00+08:00</updated>
    <id>http://gogojimmy.net/2012/01/21/how-to-use-git-2-basic-usage-and-worflow</id>
    <content type="html"><![CDATA[<p>這是一篇系列教學文：</p>

<ol>
<li><a href="http://blog.gogojimmy.net/2012/01/17/how-to-use-git-1-git-basic/">Git 教學(1)：Git的基本使用</a></li>
<li><a href="http://blog.gogojimmy.net/2012/01/21/how-to-use-git-2-basic-usage-and-worflow/">Git 教學(2)：Git Branch 的操作與基本工作流程</a></li>
<li><a href="http://blog.gogojimmy.net/2012/02/29/git-scenario/">Git 情境劇：告訴你使用 Git 時什麼情況該下什麼指令</a></li>
</ol>


<h2>Git branch</h2>

<p>branch (分支)應該是 Git 最重要的技能了，在一個多人專案的開發過程中我們有時候要開發新功能，有時候是要修正某個Bug，有時候想要測試某個特異功能能不能 work ，這時候我們通常都會從主 branch 再開出一條新的 branch 來做，這支新開的 branch 會帶著你的主 branch 目前的最新狀態，當你完成你所要開發的新功能/ Bug 修正後確認沒問題就再把它 merge(合併)回主 Branch ，如此便完成了新功能的開發或是 Bug 的修正，因此每個人都可以從主 branch 拉一條新的 branch 來做自己想做的事，再來我們好好了解一下 branch 的使用。</p>

<!--more-->


<p>了解 branch 最好的方法就是有圖像可以看，你可以用任何你已安裝的GUI來查看，但在你安裝 Git 的時候其實同時也安裝了最基本的 Git GUI 叫做 gitk，你可以使用 <code>gitk --all</code> 這個指令來呼叫他，此時你應該能看到以下的畫面：</p>

<p><img src="https://lh3.googleusercontent.com/-0KUqcz4fH_I/TxMcZZU2VsI/AAAAAAAAA8Q/bNlXOjlN3NU/s640/2012-01-16_0233.jpg" alt="gitk" /></p>

<p>點擊左上方區塊的 commit 節點你可以看到當次 commit 的詳細資料，例如作者以及他的更新記錄，但你會發現這時候終端機是無法輸入的，因此請你先關掉 gitk ，改打指令 <code>gitk --all &amp;</code> 來讓 gitk 在背景執行。</p>

<p><code>git branch</code> 這個指令可以列出所有的 branch 並告訴你目前正在哪個 branch：</p>

<pre>
$ git br
* master
  develop
  feature/test
</pre>


<p>上面的訊息告訴我們在這個 Git repository裡有3支 branch ，而你目前正在 master branch 上。假設我們現在要開一支新的 branch 叫做 cat ，使用 <code>git branch</code> 來幫助你開一支新的 branch</p>

<pre>
$ git branch cat
$ git branch
  cat
* master
</pre>


<p>上面我們開了一支新的 branch 叫做 cat ，使用 <code>git branch</code> 再查看一次發現已經多了這支新的 branch了，這時候你去查看你的 gitk 的圖像狀態會發現像下圖一樣，新的 branch cat 與 master 在同一條水平線上，表示目前他們的狀態是一模一樣的。</p>

<p><img src="https://lh3.googleusercontent.com/-191UMSCyUtA/TxMjm_lkvbI/AAAAAAAAA9E/Fod45Hxj0gs/s800/2012-01-16_0305.jpg" alt="git branch" /></p>

<p>你應該也有發現，雖然我們建立了一個 cat 的 branch ，但其實我們所在的 branch 還是在 master branch，因此我們現在還需要切換過去，因此我們使用 <code>git checkout</code> 來切換：</p>

<pre>
$ git checkout cat
Switched to branch 'cat'
</pre>


<p>這樣就會從原本的 mater branch 切換到 cat branch 了。</p>

<p>接下來假設我正在 cat 這支 branch 做開發，因此新增一個檔案，加上一些內容，將它 add 到 stage 後再 commit 它。</p>

<pre>
$ vim lib/cat.rb
$ git status
# On branch cat
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   lib/cat.rb
nothing added to commit but untracked files present (use "git add" to track)
$ git add lib/cat.rb
$ git commit -m "Add Cat.rb"
[cat ea7d309] Add Cat.rb
 1 files changed, 3 insertions(+), 0 deletions(-)
 create mode 100644 lib/cat.rb
</pre>


<p><img src="https://lh4.googleusercontent.com/-OHOBrffzDXM/TxSumAE7wII/AAAAAAAAA9Q/pjQaLiDStVw/s640/2012-01-17_0709.jpg" alt="cat branch" /></p>

<p>上面的流程你已經很熟悉了，接下來我們再切換到原本的 master branch ，這時候你會發現剛剛在 cat branch 新增的 cat.rb 檔案已經不見了。</p>

<pre>
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 1 commit.
</pre>


<p><img src="https://lh6.googleusercontent.com/-_cHj5HPuUk4/TxSvXFQl-mI/AAAAAAAAA9Y/ZPKBHSfcBrM/s640/2012-01-17_0714.jpg" alt="master branch" /></p>

<p>Git 在我們切換 branch 的同時就會很聰明的會把我們的工作目錄更動成那個 branch 該有的狀態，如果你這時候切換到 GUI 去看，你會發現到與剛剛 cat 和 master branch 在同一條水平線上不同，cat branch現在已經比 master branch 再多出了一個 commit 的內容。</p>

<p><img src="https://lh6.googleusercontent.com/-WpmDiD9pCOw/TxSw6lRD7iI/AAAAAAAAA9k/YHaM6PuPJs4/s800/2012-01-17_0720.jpg" alt="gitk" /></p>

<p>現在我在切換到 cat branch 去增加更多的內容，一樣再將它 add 到 stage 後，再 commit 它。</p>

<pre>
$ git checkout cat
Switched to branch 'cat'
$ mvim lib/cat.rb
$ git add lib/cat.rb
$ git commit -m "Add initializer"
[cat a3bce42] Add initializer
 1 files changed, 3 insertions(+), 1 deletions(-)
</pre>


<p><img src="https://lh4.googleusercontent.com/-ShoeQxd0BXU/TxSyncgOuII/AAAAAAAAA9w/2zRc_kzBswY/s640/2012-01-17_0728.jpg" alt="add initializer" /></p>

<p>切到 GUI 來看的話你會發現現在 cat 這支 branch 比 master branch 要在多上兩個 commit 的內容。</p>

<p><img src="https://lh4.googleusercontent.com/-msnyGox6oqY/TxSzlC6TE2I/AAAAAAAAA94/agwGQVkMQQg/s800/2012-01-17_0732.jpg" alt="gitk" /></p>

<p>如果這時候我們在 master 上繼續開發會發生什麼事呢？ 我們現在切換到 master branch 並增加一個檔案及內容，照慣例 add 後 commit。</p>

<pre>
$ git co master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 1 commit.
$ git add spec/animal_spec.rb
$ git commit -m "Another spec"
[master 7d72927] Another spec
 1 files changed, 2 insertions(+), 0 deletions(-)
</pre>


<p><img src="https://lh5.googleusercontent.com/-bI3T4UXtwzc/TxS96YN_C7I/AAAAAAAAA-E/BoVEpL0RZ9I/s640/2012-01-17_0816.jpg" alt="master another spec" /></p>

<p>我在 master branch 的 animal_spec.rb 增加了一些內容，把它 commit 之後我們切換到 GUI 來看。</p>

<p><img src="https://lh4.googleusercontent.com/-qYJ-rOokSco/TxS_8bxidzI/AAAAAAAAA-Q/u8b2EXKfFng/s800/2012-01-17_0825.jpg" alt="master spec" /></p>

<p>我們發現現在 master branch 與 cat branch 已經產生分歧了，因為兩支 branch 都有了各自往後開發的 commit ，而且由於 master branch 最後一次的 commit 時間較新因此排列在最前面。</p>

<h3>Git rebase 整理現在的 branch</h3>

<p>假設我們現在在 cat branch 的開發動作已經完畢，通常我們現在要做的事情會是將 cat branch 合併回 master branch，在開發流程上， master branch 就像是一個主要的 branch ，每個開發人員都是從 master branch checkout 出去一支新的 branch 做開發，在開發完畢後就再將開發完的 branch 合併回 master branch，因此 master branch 都會保有最新的開發好的狀態，一般在 Git 教學中會教你現在使用 <code>git merge</code> 這個指令來將兩個 branch 合併，但這邊我要先教你的是 <code>git rebase</code> 這個指令。</p>

<p>與 <code>git merge</code> 不同的是， <code>git rebase</code> 不單單只是將兩個不同的 branch 合併起來，而是將<strong>某一支 branch 基於另一支 branch 的內容合併起來</strong>，這是什麼意思？ 以我們的例子來說，我們在 cat branch 開發完了以後，這時候我們的 master branch 也有了其他開發者所合併回去的內容，換句話說現在的 master branch 與我們當初 checkout 出去的時候的狀態已經不同了，但我們會希望我們現在這支 cat branch 的內容就像是剛剛從 master branch checkout出來一樣乾淨，也就是說讓 cat branch 中保有 master branch 最新的狀態， <code>git rebase</code> 會基於 master branch 目前最後一次的 commit 內容再往後把你在 cat branch 上commit 的內容加上去，我們現在在 cat branch 輸入 <code>git rebase master</code> 來將 cat branch 基於 master branch 做 rebase。</p>

<pre>
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: Add Cat.rb
Applying: Add initializer
</pre>


<p>過程中沒有發生衝突，這時候我們到 GUI 看看現在的結果。</p>

<p><img src="https://lh4.googleusercontent.com/-esENJUTDCjU/TxTGtd1yeLI/AAAAAAAAA-c/VXTyBX9B7EI/s800/2012-01-17_0848.jpg" alt="git rebase" /></p>

<p>原先 cat branch 上的兩個 commit (Add Cat.rb 和 Add initializer) 已經合併到 master branch 最新的 commit (Another spec)，換句話說目前 cat branch 的內容就像是剛從 master branch 所 checkout 出來然後再加上自己的 commit，因此不同於 <code>git merge</code> 的線圖會把 cat branch 合併到 master branch ， 而是把原本的 cat branch 接到 master branch 因此只有一條線，當一個專案有很多的 branch 再做開發的時候會避免很多 branch 的線接來接去難以辨認。</p>

<p><strong>開發過程中，若你在開發的 branch 功能比較多， commit 的量也比較多時，建議使用rebase將你現在的 branch 整理過再合併回主幹，這樣會產生較漂亮的線圖</strong></p>

<p>若你想要看看目前的 branch 與其他 branch 有哪些差異，你可以使用 <code>git diff</code> 的指令去觀察，例如我現在想要看 master 跟 cat 這兩個 branch 的差異，我只要下：</p>

<pre>
$ git diff cat master
diff --git a/lib/cat.rb b/lib/cat.rb
deleted file mode 100644
index 1227d26..0000000
--- a/lib/cat.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class Cat < Animal
-  def initialize
-    super
-  end
-end
</pre>


<p>你就可以看到現在的 cat branch 跟 master branch 的差異在哪了。</p>

<p>如果我們開發完畢時，我們會把開發好的東西合併回 master 很自然的我們通常都會使用 <code>git merge</code> 這個指令來合併兩個branch</p>

<pre>
$ git merge cat
Merge made by recursive.
 lib/cat.rb |    3 +++
 1 files changed, 3 insertions(+), 0 deletions(-)
 create mode 100644 lib/cat.rb
</pre>


<p>這時候我們看圖的話會是這樣：</p>

<p><img src="https://lh4.googleusercontent.com/-JriNDcS61UI/TxmrJsBo7wI/AAAAAAAAA-s/o2_A-ju4zrE/s800/2012-01-21_0156.jpg" alt="git merge" /></p>

<p>可以看到我剛剛在 master branch 下了 <code>git merge cat</code> 這個指令來告訴 git 要 merge cat 到現在所在的 branch ，因此在圖上就看到了 cat branch 拉一條線回來合併到了 master 這個 branch 了，解釋這張圖的意思就是， cat branch 從 master branch 的 Another spec 這一次的 commit 分支出來後，自己產生了三次的 commit (Add Cat.rb、Add initializer、Rever &ldquo;Add initializer&rdquo;) 然後合併到 master。</p>

<h3>Confict: 處理 Git 合併時的衝突：</h3>

<p>很常發生的情況是再 merge 或是 rebase 的圖中產生了 convict (衝突)，這時候 Git 會停下來請你去處理，例如我們在 cat 和 master 的 branch 都對 lib/cat.rb 這支檔案做編輯，然後我們將他們 merge：</p>

<pre>
$ git merge cat
Auto-merging lib/cat.rb
CONFLICT (content): Merge conflict in lib/cat.rb
Automatic merge failed; fix conflicts and then commit the result.
</pre>


<p>你會看到 Git 告訴你在你合併的過程中在 lib/cat.rb 這支檔案發生了衝突，Git 不知道該怎麼處理因此要你去處理它，這時候我們打開這支檔案會看到這樣的情況：</p>

<p><img src="https://lh6.googleusercontent.com/-ZrCt7H7xBJ8/TxpugjL11TI/AAAAAAAAA_E/eN35v4t7qXE/s800/2012-01-21_1550.jpg" alt="git confict" /></p>

<p>&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD 到 ========== 的中間區域是目前你所在 branch 的 commit 內容，而從 =========== 到 >>>>>>>>>>> cat 則是你要合併的 cat branch 的內容，你必須做出決定看是要兩個都留下，或是選一個，或是改成你想要的內容，改好後記得要將 Git 自動產生的 &lt;&lt;&lt; 、 =====、 &lt;&lt;&lt; 的內容都刪除，修改完畢後存檔，將剛剛修改過的檔案再使用 <code>git add</code> 加入 stage ，將所有的衝突都修正完畢後就使用 <code>git commit</code> 提交一次 commit，由於這次的 commit 是在處理 merge 時的衝突，因此 Git 很聰明的已經幫我們加上了一些預設的訊息 &ldquo;Merge branch &lsquo;cat&rsquo;&#8221;， commit 提交後就會看到合併成功的訊息了。</p>

<pre>
$ git add lib/cat.rb
$ git commit
[master c37c9e3] Merge branch 'cat'
</pre>


<p><strong>發生 confict 時的處理步驟</strong></p>

<ol>
<li><strong>將發生 confict 的檔案打開，處理內容( 別忘了刪除&lt;&lt;&lt;、===、>>> )。</strong></li>
<li><strong>使用 <code>git add</code> 將處理好的檔案加入 stage。</strong></li>
<li><strong>反覆步驟 1~2 直到所有 confict 處理完畢。</strong></li>
<li><strong><code>git commit</code> 提交合併訊息。</strong></li>
<li><strong>完成</strong></li>
</ol>


<p><strong>講到這裡我們再來整理一下工作流程，讓大家再複習一下 git 的使用:</strong></p>

<ol>
<li><strong>在專案中會有一條主 branch 是大家將開發好或是修好的東西合併回去的對象，所有要開發的新功能或是修 bug 都是從主 branch 拉出一條新的 branch 去工作。</strong></li>
<li><strong>當你完成一個階段性的任務時，將你剛剛 所新增的內容使用 <code>git add</code> 加入到 stage 的狀態，並且使用 <code>git commit</code> 加上 commit 的訊息來提交一次的 commit。</strong></li>
<li><strong>反覆動作 2 直到你完成這支 branch 的主要目的(新功能/修 bug )，若這時你離主 branch 已經有一段時間，或是確定主 branch 上已經有新的 commit ，使用 <code>git rebase</code> 將自己的分支整理然後使用 <code>git merge</code> 合併回主 branch，反之則是直接使用 <code>git merge</code> 將自己 branch 的內容合併回去。</strong></li>
</ol>


<h2>Git reset 取消上一次的操作</h2>

<h3>取消 merge</h3>

<p>版本控制最大的好處之一就是讓你永遠可以後悔，因此我們常會希望把已暫存的檔案、已提交的 commit 或是已合併的 branch 取消修改，這時候我們可以使用 <code>git reset</code> 這個指令來幫助我們，像現在我若是想要取消剛剛的 merge 動作，我只要下：</p>

<pre>
$ git reset --hard ORIG_HEAD
HEAD is now at c126ff9 Config initialze
</pre>


<p>這時候再回去看圖你會發現已經回到合併前的樣子了：</p>

<p><img src="https://lh5.googleusercontent.com/-2tyXpiUJKmU/Txm0r4vgeDI/AAAAAAAAA-4/k2s_JJlyj1E/s800/2012-01-21_0238.jpg" alt="git reset" /></p>

<h3>取消已暫存的檔案</h3>

<p>有時候手殘不小心將還沒修改完的檔案使用 <code>git add</code> 加入了 stage ，這時候可以使用 <code>git reset HEAD &lt;file&gt;</code> 來將這支檔案取消 stage：</p>

<p><img src="https://lh5.googleusercontent.com/-LI6SAuanFeQ/Txp07PZatSI/AAAAAAAAA_Q/eXB_6EVU1Ck/s800/2012-01-21_1618.jpg" alt="git reset" /></p>

<p>你可以看到我使用 <code>git add</code> 將檔案加入 stage 後，在我的 status 狀態顯示 lib/cat.rb 這支檔案現在已經準備好被 commit ，但這時我使用了 <code>git reset HEAD</code> 將這支檔案取消 stage，再使用 status 查看時它就變回一支還沒加入 stage 的檔案了。</p>

<h3>取消修改過的檔案</h3>

<p>連續剛剛的情況，若是我想完全放棄這次修改 (將檔案狀態回復到最新的一次 commit 時的狀態)，我可以使用 <code>git checkout -- &lt;file&gt;</code> 來回復這支檔案：</p>

<pre>$ git checkout -- lib/cat.rb</pre>


<p>取消變更不會有任何訊息，但這時你去看檔案會發現他已經回復成沒修改過時的模樣了。</p>

<h3>修改上一次的commit</h3>

<p>手誤打太快， commit 訊息打錯時，我們可以使用 <code>git commit --amend</code> 來幫助我們重新修改：</p>

<p><img src="https://lh5.googleusercontent.com/-EWew34dGcNs/Txp4qkIcZwI/AAAAAAAAA_c/cAgGz7WWS5k/s800/2012-01-21_1634.jpg" alt="git reset" />
<img src="https://lh5.googleusercontent.com/-f2T7R1Bq7oM/TxqEN_EfY9I/AAAAAAAAA_o/IHlCkmOX1vk/s800/2012-01-21_1723.jpg" alt="git reset" /></p>

<p>在上面我想要修改打錯字的 commit 訊息 &ldquo;Cat initiae&#8221;，因此我使用 <code>git commit --amend</code> 來修改成正確的訊息。</p>

<h3>強制回復到上一次 commit 的版本</h3>

<p>有時候我們想要放棄所有修改回到 commit 時的狀態，這時候我們可以下 <code>git reset --hard HEAD</code> 來回復，HEAD 參數可以加上一些變化，例如 HEAD^ 表示目前版本的上一個版本 HEAD~2 則是再上一個，因此你可以自由的跳回去之前的狀態。</p>

<h3>git reset, hard 與 soft 的差異</h3>

<p>你可能會在這邊感到疑惑，在使用 <code>git reset</code> 的時候都會看到一個 soft 或是 hard 的參數，這代表什麼樣的意義？基本上在使用 <code>git reset</code> 的時候，都會把目前狀態回復到你想回復的版本，但若是不加參數的情況，會把你做過的修改仍然保留，但是，若是加上 &mdash;soft 參數，則會把做過的修改加入 stage ，若是加上 hard 參數的話則是把做過的修改完全刪除，回到那個版本原本的樣子。</p>

<ul>
<li><p>其他連結：</p></li>
<li><p><a href="http://blog.gogojimmy.net/2012/01/17/how-to-use-git-1-git-basic/">Git 教學(1)：Git的基本使用</a></p></li>
<li><a href="http://blog.gogojimmy.net/2012/01/21/how-to-use-git-2-basic-usage-and-worflow/">Git 教學(2)：Git Branch 的操作與基本工作流程</a></li>
<li><a href="http://blog.gogojimmy.net/2012/02/29/git-scenario/">Git 情境劇：告訴你使用 Git 時什麼情況該下什麼指令</a></li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Git 教學(1) : Git 的基本使用]]></title>
    <link href="http://gogojimmy.net/2012/01/17/how-to-use-git-1-git-basic/"/>
    <updated>2012-01-17T23:11:00+08:00</updated>
    <id>http://gogojimmy.net/2012/01/17/how-to-use-git-1-git-basic</id>
    <content type="html"><![CDATA[<p>這是一篇系列教學文：</p>

<ol>
<li><a href="http://blog.gogojimmy.net/2012/01/17/how-to-use-git-1-git-basic/">Git 教學(1)：Git的基本使用</a></li>
<li><a href="http://blog.gogojimmy.net/2012/01/21/how-to-use-git-2-basic-usage-and-worflow/">Git 教學(2)：Git Branch 的操作與基本工作流程</a></li>
<li><a href="http://blog.gogojimmy.net/2012/02/29/git-scenario/">Git 情境劇：告訴你使用 Git 時什麼情況該下什麼指令</a></li>
</ol>


<h2>前言</h2>

<p>Git 是一套分散式的版本控制系統，版本控制是一個開發團隊中不可或缺的工具，Git 最強大的一個特點就是可以無窮無盡的開 branch (分支)，好處就是今天不論是修 Bug ，開發新功能，或是研究 feature 都非常的方便，學 Git 到現在大概三個月的時間讓我體會到&#8221; Git 用的好，產品開發沒煩惱!!&ldquo; ，搭配 Github (一個以 Git 作為基礎的程式碼社群服務，上面有非常多的資源)使用更是天下無敵，團隊開發怎麼能少的了用 Git 呢！！！！</p>

<!--more-->


<p>坦白說 Git 還是需要一些時間去學習， Workflow 對於使用 Git 有非常大的影響，一個好的 Workflow 會更幫助你學會如何去操作 Git 及培養好的團隊開發技巧，因此這篇就是要來幫助剛學習 Git 的人如何快速上手 Git 及一個開發的 Workflow ，以下是給完全沒接觸過的新手的入門文，如果你想要快速的了解基本操作，你可以直接看這裡。</p>

<h2>安裝Git</h2>

<p>網路上已經有很多教學了，善用 Google 我相信你可以找到非常多的資源，的我建議可以參考 Github 上的教學(請先建立一個 Github 的帳號，因為他會連 Github 的設定一起設定完成)：</p>

<ul>
<li><a href="http://help.github.com/mac-set-up-git/">Mac setup git</a></li>
<li><a href="http://help.github.com/win-set-up-git/">Windows setup git</a></li>
<li><a href="http://help.github.com/linux-set-up-git/">Linux setup git</a></li>
</ul>


<p>若你想看中文的推薦你 Git 的權威教材 <a href="http://progit.org/book/zh/ch1-4.html">Pro Git</a>。</p>

<p>Git 也有很多的 GUI 工具，免費的像是 <a href="http://brotherbard.com/blog/2010/03/experimental-gitx-fork/">GITX</a>、 <a href="http://itunes.apple.com/tw/app/sourcetree-git-hg/id411678673?mt=12">SourceTree</a>、<a href="http://www.syntevo.com/smartgit/index.html">SmartGit</a> (我沒用過但是是2011評價最好的 Git GUI)，付費的像是 <a href="http://www.git-tower.com/">Tower</a> 都是不錯的選擇，但我會建議先從基本的 Git 指令開始學習，當你對基本的指令熟悉後再去使用各種不同的 GUI 就會更加的得心應手了！</p>

<h2>設定你的Git</h2>

<p>在每一次的 Git commit (提交，我們稍後會提到) 都會記錄作者的訊息像是 name 及 email ，因此我們使用下面的指令來設定：</p>

<pre>
$ git config --global user.name "Jimmy Kuo"
$ git config --global user.email "jimmy@gogojimmy.net"
</pre>


<p>加上 <code>--global</code> 表示是全域的設定。你可以使用 <code>git config --list</code> 這個指令來看你的 Git 設定內容：</p>

<pre>
$ git config --list
    user.name=Jimmy Kuo
    user.email=jimmy@gogojimmy.net
</pre>


<p>或是其實 Git 的設定檔是儲存在你的家目錄的.gitconfig 隱藏檔中，你可以使用編輯器將他打開</p>

<pre>$ cat ~/.gitconfig
    [user]
        name = Jimmy Kuo
        email = jimmy@gogojimmy.net
</pre>


<p>使用終端機來操作 Git 常會讓人覺得一直打指令很繁瑣，因此 Git 也有提供 alias 的功能，例如你可以將 <code>git status</code> 縮寫為 <code>git st</code>，<code>git checkout</code> 縮寫為 <code>git co</code> 等，你只要這樣設定：</p>

<pre>
$ git config --global alias.st status
</pre>


<p>這樣一來只要打 <code>git st</code> 就等同於打 <code>git status</code> 了。</p>

<p>空白對有些語言是有影響的(像是Ruby)，因此我們會希望 Git 去忽略空白的變化，這時候你需要在你的設定加入：</p>

<pre>
$ git config --global apply.whitespace nowarn
</pre>


<p>如此一來 Git 對於空白的變化便會忽略不計。</p>

<p>Git 預設輸出是沒有顏色的，我們可以讓他在輸出時加上顏色讓我們更容易閱讀：</p>

<pre>
$ git config --global color.ui true
</pre>


<h2>開始使用Git (init, clone)</h2>

<p>要開始使用 Git 你必須先建立一個 Git 的 Repository，你可以把它想做是一個資料庫的意思，有兩種方法可以建立一個 Git 的 Repository：</p>

<ul>
<li><h3>自己建立一個新的 Repository</h3></li>
</ul>


<p>例如我現在有個叫做 Animal 專案資料夾，我現在想要開始使用 Git 開始管理，因此我先將目錄切換到 Animal 底下後輸入<code>git init</code></p>

<pre>
$ git init
Initialized empty Git repository in /Users/Jimmy/Projects/Animal/.git/
</pre>


<p>這時你就會看到 Git 告訴你說已經在這邊建立好一個新的 Git Repository。</p>

<ul>
<li><h3>Clone(複製)別人的 Repository</h3>

<p>  例如我們在 Github 上面看到人家的程式碼想要抓下來自己修改，或是團隊中別人的程式碼，這時候他們通常會有一個 Git 的檔案位置像是在 Github 的話你就會看到像下面的地方可以讓你複製 git 的 clone 位置：</p></li>
</ul>


<p><img src="https://lh5.googleusercontent.com/-dCT8HRsIZ20/TxMeU1pW18I/AAAAAAAAA84/pglAoT3kplE/s640/2012-01-16_0242.jpg" alt="git clone" /></p>

<p>將他複製起來後到你的目錄下輸入 <code>git clone</code></p>

<pre><code>$ git clone https://gogojimmy@github.com/gogojimmy/Animal.git
</code></pre>

<p>如此便會將這個 Git Repository下載到我們的資料夾， <code>git clone</code> 預設會將下載的 git 存成一樣檔名的資料夾，如果你要更改成別的名稱的話只需要在網址後面加上你想要更改的名稱即可，像是：</p>

<pre><code>$ git clone https://gogojimmy@github.com/gogojimmy/Animal.git monkey
</code></pre>

<p>這樣子下載下來的 Repository 的名稱就會從原本的 Animal 變成 monkey 了。</p>

<h2>Git的基本功(status, add, commit, log, .gitignore)</h2>

<p>在一個 Git 的 Repository 中你可以輸入<code>git status</code>來檢查目前 Git 的狀態</p>

<pre>
$ git status
# On branch master
#
# Initial commit
#
nothing to commit (create/copy files and use "git add" to track)
</pre>


<p>這代表目前是一個乾淨的目錄狀態，沒有未被追蹤或是被修改的檔案， On branch master 表示你目前正在名為 master 的 branch 上，等等會再說明 branch 的功用。我們現在在這個目錄新增一個 test 檔案後，再使用<code>git status</code> 來查看：</p>

<pre>
$ git status
# On branch master
#
# Initial commit
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   test.rb
nothing added to commit but untracked files present (use "git add" to track)
</pre>


<p>這時候你會看到我剛剛新增的 test.rb 檔案變成在 Untracked files (未被追蹤的檔案)，表示過去在這個 Git Repository 中從未有這支檔案，是一支未被追蹤的檔案，我們要把這個檔案讓 Git 能夠追蹤的話，我們使用 <code>git add</code> 這個指令來把它加入追蹤：</p>

<pre>
$ git add test.rb
$ git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#   new file:   test.rb
#
</pre>


<p>你可以看到原本 test.rb 還在 Untracked files 中，經過我們使用 <code>git add test.rb</code> 後他就變成了 Changes to commit，通常我們稱這個狀態叫做 stage ，修改過但還沒使用 <code>git add</code> 的檔案稱為 unstage 。</p>

<h3>Tips: 一次加入全部的檔案</h3>

<p><strong>如果你一次修改了很多檔案，懶得一個一個輸入 <code>git add</code> 的話，你可以輸入 <code>git add .</code> 這會幫你將所有剛剛修改過或新增加的檔案一次 Add 進 stage 狀態，但強烈不推薦這樣的作法，這樣的方法雖然方便但很容易會不小心加入一些其他不必要的檔案，一個正確的觀念是你必須隨時都很清楚你的檔案狀態，因此最好是手動將你確定要加入的檔案使用 <code>git add</code> 來加入，有一個更好的作法是使用互動模式 <code>git add -i</code> ，在互動模式下你可以方便的選擇你要加入的檔案，或是移除剛剛不小心加入的檔案 (revert)。</strong></p>

<p>已經在 stage 狀態的檔案的下一步就是準備提交( commit )，commit 是寫程式時一個很重要的動作，一個 commit 在 Git 中就是一個節點，這些 commit 的節點就是未來你可以回朔及追蹤的參考，你可以想像就像是電玩遊戲時的存檔，每一個 commit 就是一次存檔，讓我們未來在需要的時候都可以回到這些存檔時的狀態。因此我們將剛剛做的修改提交：</p>

<pre>$ git commit</pre>


<p>此時你會看到畫面跳到你在 git 中設定的編輯器畫面：</p>

<pre>
#
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
#
# Initial commit
#
# Changes to be committed:
#  (use "git rm --cached <file>…" to unstage)
#
#       new file: test.rb
#
</pre>


<p>這個視窗中最上面的空行是要給你寫下你這次 commit 的訊息，例如在這裡寫下 &ldquo;Add test.rb file to test git function&#8221;來表示這一次提交的目的，<strong>強烈建議在 commit 的時候要盡量清楚表達這次 commit 的內容為何</strong>，因為這會讓你未來要回頭看 code 的時候能讓你快速的找到你想要找的內容，也能對團隊中其他成員了解你在做什麼。</p>

<p>在 commit 完後會顯出出你這次 commit 的更動：</p>

<pre>
[master 5f76371] add ignore files
 2 files changed, 8 insertions(+), 0 deletions(-)
 create mode 100644 .gitignore
 create mode 100644 test.rb
</pre>


<p>如果你覺得每次這樣跳出編輯器很麻煩，你也可以在 commit 時加上 <code>-m</code> 的參數來快速提交：</p>

<pre>$ git commit -m "Add test.rb to test git function"</pre>


<p>若使用 <code>-am</code> 的話還能將所有未被 add 的檔案一併 add 進來(<strong><em> 更新：如果是第一次新增還沒有被 add 的檔案是不會一起加入的，只有之前已經被 add 過 commit 的檔案才會被加入 </em></strong>)，但就像前面所說這樣的作法並不推薦，你應該清楚的加入你應該加入的檔案就好：</p>

<pre>$ git commit -am "Add test.rb to test git function"</pre>


<p>若使用 <code>-v</code> 的話會列出更動的紀錄：</p>

<pre>

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   new file:   .gitignore
#   new file:   test.rb
#
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..61521a9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+.DS_Store
+*.swp
+log/*.log
diff --git a/test.rb b/test.rb
new file mode 100644
index 0000000..23df1c1
--- /dev/null
+++ b/test.rb
@@ -0,0 +1,5 @@
+class Test
+  def test
+    puts "This is a test file"
+  end
+end
</pre>


<p>加號(+)代表增加的部份，減號(&ndash;)代表刪除的部份。</p>

<p>我們可以使用 <code>git log</code> 的指令查看過去 commit 的紀錄：</p>

<pre>
$ git log
5f76371 - (HEAD, master) add ignore files (3 minutes ago) <Jimmy Kuo>
4ca268d - (origin/master) First commit (33 minutes ago) <Jimmy Kuo>
</pre>


<p>前面的亂碼代表的是當次 commit 的版號，後面的是 commit 的訊息、時間以及 commit的作者，你可以使用 <code>--stat</code> 參數看到更詳盡的訊息：</p>

<pre>
$ git log --stat
5f76371 - (HEAD, master) add ignore files (7 minutes ago) <Jimmy Kuo>
 .gitignore |    3 +++
 test.rb    |    5 +++++
 2 files changed, 8 insertions(+), 0 deletions(-)

4ca268d - (origin/master) First commit (37 minutes ago) <Jimmy Kuo>
 lib/animal.rb       |    5 +++++
 spec/animal_spec.rb |    5 +++++
 2 files changed, 10 insertions(+), 0 deletions(-)
</pre>


<p>如果你想看到檔案更詳細的變更內容，你可以加上 <code>-p</code> 的參數：</p>

<pre>
$ git log -p
5f76371 - (HEAD, master) add ignore files (8 minutes ago) <Jimmy Kuo>
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..61521a9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+.DS_Store
+*.swp
+log/*.log
diff --git a/test.rb b/test.rb
new file mode 100644
index 0000000..23df1c1
--- /dev/null
+++ b/test.rb
@@ -0,0 +1,5 @@
+class Test
+  def test
+    puts "This is a test file"
+  end
+end
</pre>


<p>因此我們稍微做一下流程的整理：</p>

<p><strong>修改檔案 => 加入 stage (git add) => 提交( git commit )=> 繼續修改其他檔案</strong></p>

<h3>Tips: commit 的最佳時機？ 什麼時候該 commit ?</h3>

<p><strong>什麼時候才是 commit 的最好時機並沒有一個定論，大部分人會告訴你通常你完成了一個階段性的小工作就做一次的 commit ，我的感覺是</strong>當你想要記錄目前的狀態的時候<strong>就是你 commit 的最佳時機，例如說剛完成某個page，某個任務需求而你想要做個記錄的時候。</strong></p>

<p>有些檔案我們不希望加入版本控制的追蹤，例如說Database的schema或是一些log檔，這時候你可以將他們加入 <code>.gitignore</code> 中來讓 Git 忽略他們，使用編輯器來打開你的 <code>.gitignore</code> 檔案。</p>

<pre>
$ vim .gitignore
</pre>


<p>在這邊假設我想將 Mac 自動產生的.DS_Store， vim 的 swp 暫存檔及 log 檔忽略，我便分行加入：</p>

<pre>
.DS_Store
*.swp
log/*.log
</pre>


<p>如此一來便會將這些檔案從git追蹤忽略了。</p>

<h3>Tips: 被加入 gitignore 的檔案一樣出現在 status 中？</h3>

<p><strong>有時候你會發現即時你將檔案加入了 <code>.gitignore</code> 卻一樣會出現在 Git 的追蹤狀態中，這是由於你想要忽略的檔案之前已經被 Git 追蹤了，因此你現在想要讓 Git 忽略他的話只能先將他刪除然後再 commit 一次，再來這支檔案再出現就不會再追蹤這支檔案了。</strong></p>

<ul>
<li><p>其他連結：</p></li>
<li><p><a href="http://blog.gogojimmy.net/2012/01/17/how-to-use-git-1-git-basic/">Git 教學(1)：Git的基本使用</a></p></li>
<li><a href="http://blog.gogojimmy.net/2012/01/21/how-to-use-git-2-basic-usage-and-worflow/">Git 教學(2)：Git Branch 的操作與基本工作流程</a></li>
<li><a href="http://blog.gogojimmy.net/2012/02/29/git-scenario/">Git 情境劇：告訴你使用 Git 時什麼情況該下什麼指令</a></li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[在Rails中使用Path或是Url的時機]]></title>
    <link href="http://gogojimmy.net/2011/12/02/when-to-use-url-or-path-in-rails/"/>
    <updated>2011-12-02T23:17:00+08:00</updated>
    <id>http://gogojimmy.net/2011/12/02/when-to-use-url-or-path-in-rails</id>
    <content type="html"><![CDATA[<p>剛開始寫Rails的時候，一個很基本的問題就是Path跟Url的分別，其實差別在於一個回傳的是相對路徑，而一個是絕對路徑而已，例如說假設今天我們有個叫做Product的Resource</p>

<!--more-->




<figure class='code'><figcaption><span>routes.rb</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">resources</span> <span class="ss">:products</span>
</span></code></pre></td></tr></table></div></figure>


<p>當你使用path的時候，像是這樣</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">link_to</span> <span class="s2">&quot;產品首頁&quot;</span><span class="p">,</span> <span class="n">products_path</span>
</span></code></pre></td></tr></table></div></figure>


<p>這段程式碼會產生：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='html'><span class='line'><span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">&quot;/products&quot;</span><span class="nt">&gt;</span>產品首頁<span class="nt">&lt;a&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>而使用url的時候，像是這樣</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">link_to</span> <span class="s2">&quot;產品首頁&quot;</span><span class="p">,</span> <span class="n">products_url</span>
</span></code></pre></td></tr></table></div></figure>


<p>這段程式碼會產生：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='html'><span class='line'><span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">&quot;http://www.example.com/products&quot;</span><span class="nt">&gt;</span>產品首頁<span class="nt">&lt;a&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>那什麼時候該用url,什麼時候又該用path呢?</p>

<p>一般來說在View的時候我們會用path，而在Controller的時候我們會用url，因為在View的時候使用path連到我們自己domain底下的相對網址會比較節省時間也提昇些許效能，而url通常我們會使用在redirect_to的3xx轉向，這種情況就得使用絕對路徑因此我們會使用url，還有種情況你必須使用url，就是在有SSL的網址與沒有SSL的網址間互傳的時候，你必須使用絕對路徑，因此請使用url。</p>

<p>不過為了一些SEO的理由，使用絕對路徑會比使用相對路徑來的優秀，例如說root_url會比root_path來的好。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[新Blog開張，Octopress]]></title>
    <link href="http://gogojimmy.net/2011/11/28/new-blog-on-octopress/"/>
    <updated>2011-11-28T16:31:00+08:00</updated>
    <id>http://gogojimmy.net/2011/11/28/new-blog-on-octopress</id>
    <content type="html"><![CDATA[<p>之前一直使用Wordpress架在DreamHost，但現在覺得身為一個Rails的愛好者這樣做實在不太像是個Developer，也看到了國外很多Hacker（或是Geek?）正在使用octopress來當自己的Blog，趕流行，所以我就換了。</p>

<p>但是竟然沒備份好舊的Wordpress文章，真是敗筆，只好當個完全的新開始了。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[在linode上架設Ubuntu+Nginx+Passenger+Rails環境]]></title>
    <link href="http://gogojimmy.net/2011/11/28/build-a-enviroment-for-rails-plus-nginx-plus-passenger-plus-ubuntu-on-linode/"/>
    <updated>2011-11-28T02:01:00+08:00</updated>
    <id>http://gogojimmy.net/2011/11/28/build-a-enviroment-for-rails-plus-nginx-plus-passenger-plus-ubuntu-on-linode</id>
    <content type="html"><![CDATA[<p>這是我租用了Linode的VPS後，在上面架設一個deploy用的Rails環境的一些筆記，為什麼使用Ubuntu 10.04 LTS的版本單純是因為之前用過，覺得沒什麼特別問題因此使用。</p>

<!-- more -->


<h3>環境</h3>

<hr />

<p><strong>OS</strong>: Ubuntu 10.04 LTS</p>

<p><strong>HTTP Server</strong>: nginx</p>

<p><strong>Ruby</strong>: 1.9.2-p290</p>

<p><strong>Rails</strong>: 3.1.3</p>

<h3>更新系統</h3>

<hr />

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>$ apt-get update
</span><span class='line'>$ apt-get upgrade</span></code></pre></td></tr></table></div></figure>


<h3>設定Hostname</h3>

<hr />

<pre>
$ echo "gogojimmy" > /etc/hostname      #將gogojimmy替換成你想要的hostname
$ hostname -F /etc/hostname
</pre>


<h3>更新/etc/hosts</h3>

<pre>
$ vi /etc/hosts
</pre>


<p>在127.0.0.1下面加上自己的網域</p>

<pre>
127.0.0.1        localhost.localdomain    localhost
12.34.56.78      gogojimmy.net            gogojimmy
</pre>


<h3>設定時區</h3>

<hr />

<pre>
$ dpkg-reconfigure tzdata
</pre>


<p>使用date指令若可以看到回應出現在時間的話表示你設定成功</p>

<h3>建立deploy用的帳號並提升權限</h3>

<hr />

<pre>
$ useradd deployer                  #建立deploy用帳號deployer
$ passwd deployer                   #建立deployer的密碼
$ mkdir /home/deployer              #建立deployer的家目錄
$ chown -R deployer /home/deployer  #為deployer設定家目錄權限
$ visudo                            #設定sudoer
</pre>


<p>在root ALL=(ALL) ALL下面加入這一行，可以讓deployer這個帳號使用sudo指令來獲得root的所有權限</p>

<pre>deployer ALL=(ALL) ALL</pre>


<h3>設定SSH</h3>

<hr />

<pre>
$ su deployer               #切換帳號成deployer
$ ssh-keygen                #產生ssh key pair
$ more ~/.ssh/id_rsa.pub    #這個key的內容日後用來貼到你要deploy專案的deploy key
</pre>


<p><strong><em>Note: 下面這一步若你本機上還沒有ssh key pair的話請先使用ssh-keygen先產生</em></strong></p>

<pre>
$ vi ~/.ssh/authorized_keys         #將你本機的id_rsa.pub內容貼入authorized_keys
$ chmod 711 ~/.ssh
$ chmod 644 ~/.ssh/authorized_keys
</pre>


<p>這樣以後就可以免密碼登入ssh了。</p>

<h3>安裝系統環境套件</h3>

<hr />

<pre>
$ sudo apt-get install gcc
$ sudo apt-get install build-essential
$ sudo apt-get install bison openssl libreadline6 libreadline6-dev
$ sudo apt-get install curl git-core zlib1g zlib1g-dev libssl-dev libyaml-dev
$ sudo apt-get install libsqlite3-0 libsqlite3-dev sqlite3 libxml2-dev
$ sudo apt-get install libxslt-dev autoconf libc6-dev
</pre>


<h3>安裝mysql</h3>

<hr />

<pre>
$ sudo apt-get install mysql-server libmysqlclient15-dev
</pre>


<h3>安裝nginx</h3>

<hr />

<p>將nginx的source貼到/etc/apt/sources.list:</p>

<pre>
$ sudo deb http://nginx.org/packages/ubuntu/ lucid nginx
$ sudo ddeb-src http://nginx.org/packages/ubuntu/ lucid nginx
$ sudo -s
$ sudo dnginx=stable
$ sudo dadd-apt-repository ppa:nginx/$nginx
$ sudo dapt-get update
$ sudo dapt-get install nginx
$ nginx -v                          #看的到版本編號的話表示已安裝成功
$ sudo /etc/init.d/nginx start      #啟動nginx
</pre>


<h3>安裝Ruby</h3>

<hr />

<pre>
$ wget http://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.2-p290.tar.gz
$ tar -xzvf ruby-1.9.2-p290.tar.gz
$ cd ruby-1.9.2-p290
$ ./configure
$ make
$ make test
$ make install
$ ruby -v           #看的到版本編號表示已經安裝成功
</pre>


<h3>Rubygems</h3>

<hr />

<p>Ruby 1.9.2內建的Rubygems版本較低，因此我們先將他升級到最新版本</p>

<pre>
$ sudo gem update --system
</pre>


<p>將以下內容貼到~/.gemrc，讓gem安裝時不要安裝ri跟rdoc文件以節省安裝時間</p>

<pre>
:sources:
- http://gems.rubyforge.org
- http://gems.github.com
gem: --no-ri --no-rdoc
</pre>


<h3>安裝Rails</h3>

<hr />

<pre>
$ sudo gem install Rails
</pre>


<h3>安裝ImageMagick與RMagick</h3>

<hr />

<p>先上  <a href="http://imagemagick.org">Imagemagick.org</a>上找到最新的版本，使用wget下載</p>

<pre>
$ wget ftp://ftp.kddlabs.co.jp/graphics/ImageMagick/ImageMagick-6.6.9-10.tar.gz
$ tar xvfz ImageMagick-6.6.9-10.tar.gz
$ cd ImageMagick-6.6.9-10
$ ./configure
$ make
$ sudo make install
</pre>


<p>安裝RMagick</p>

<pre>
$ sudo gem install rmagick
</pre>


<h3>安裝Passenger</h3>

<pre>
$ sudo /usr/local/bin/passenger-install-nginx-module
</pre>


<p>安裝時選擇1預設安裝就可以了。</p>

<h3>測試環境，使用octopress</h3>

<hr />

<p>我們來安裝octopress來測試剛剛的環境是否OK，請先參考<a href="http://octopress.org/docs/setup/">Octopress Setup</a>來建立你的octopress，設定好後使用下面指令來上傳到你的linode。</p>

<pre>
$ rake generate
$ rake deploy
</pre>


<p>再來我們去更改一下nginx內的設定</p>

<pre>
$ sudo vi /etc/nginx/nginx.conf
</pre>


<p>在中間加上這段Server敘述：</p>

<pre>
server {
    listen   80; ## listen for ipv4
    listen   [::]:80 default ipv6only=on; ## listen for ipv6

    server_name  example.com www.example.com;
    access_log  /var/log/nginx/example.access.log;

    location / {
        root   /path/to/deployed/files;
    }
}
</pre>


<p>然後重新啟動你的nginx，你就可以在你的網址上看到你的Octopress了！</p>

<pre>
sudo /etc/init.d/nginx restart
</pre>


<p><strong>NOTE</strong>:如果在重新啟動nginx時看到nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)的錯誤訊息，那是因為nginx重複重新啟動佔用了port的關係，使用下面指令殺掉後再啟動就可以了。</p>

<pre>
sudo killall -9 nginx
</pre>


<p>有什麼問題都歡迎留言討論！</p>
]]></content>
  </entry>
  
</feed>
