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

  <title><![CDATA[Essentail Rails Design Pattern]]></title>
  <link href="http://erp-book.heroku.com/atom.xml" rel="self"/>
  <link href="http://erp-book.heroku.com/"/>
  <updated>2012-01-24T02:40:56+08:00</updated>
  <id>http://erp-book.heroku.com/</id>
  <author>
    <name><![CDATA[xdite]]></name>
    <email><![CDATA[xuite.joke@gmail.com]]></email>
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html"><![CDATA[學習寫出好的 Rails code]]></title>
    <link href="http://erp-book.heroku.com/ch1-intro/"/>
    <updated>2012-01-24T02:16:00+08:00</updated>
    <id>http://erp-book.heroku.com/ch1-intro</id>
    <content type="html"><![CDATA[<p>我剛開始接觸 Rails 是在 2007 年。Rails 在世界剛竄紅。當時遑論說要一個 Developer 寫出容易維護的程式碼，基本上，在那個時代能夠使用 Rails 「兜」*(註）出一個堪用的網站，你都算一個高手了。</p>

<p>經過了四年，這個框架一路發展到現在。能夠自在的使用 Rails 的 Developer 在台灣卻也都還不算多。其中有很多原因，除了本地語言的文件不夠多之外，文件和 API 更迭過速導致開發環境破碎，也對後進者造成很大程度的學習障礙。</p>

<p>這一行可以說大家都是邊做專案，邊熬夜翻網路文件，跌跌撞撞把網站「拼」出來的。</p>

<h2>維護擴充性的重要性</h2>

<p>有些人，幸運的只是接案維生的 Developer，在練功「拼」完專案之後，就不需要再維護這些急就章的程式碼。</p>

<p>但在這個世界上，絕大多數的人，還是屬於服務於企業中，需要持久維護一個專案的 Developer。</p>

<p>這兩種人，我都當過。</p>

<p>因為浸淫在 Rails 的開發世界夠長，本身培育過 Developer，也開發過不少專案。在經歷維護專案，並持續升級 Rails 版本後，我漸漸能體悟到寫出好維護的程式碼是多麼重要的一件事。</p>

<p>如果程式碼亂七八糟：</p>

<ul>
<li>你根本無法進行網站下一次的改版擴充動作，即便是功能上的小改版</li>
<li>你根本無法進行 Rails 版本升級的動作，因為你會被過去造成的錯誤（不管是自己或者是夥伴）不斷懲罰，導致寸步難行。</li>
</ul>


<p>接著業務拓展和工作進度就會開始被這些程式碼開始扯到後腿…</p>

<p>寫程式變成了一件不快樂的事。</p>

<h2>沒有書教你怎麼寫出好的（ Rails ）程式碼</h2>

<p>但話說回來，寫出這些亂七八糟的程式碼，也不是開發者本身故意的。</p>

<p>畢竟 Rails 是一個進化很快的 Framework，要能跟上這個 Framework API 與架構的最新進度，已經很吃力了。很難奢求一般的 Developer 去追求所謂的 Rails Best Practices。</p>

<p>而大多數的狀況是，坊間的書籍與網站往往只教你：</p>

<ul>
<li><p>如何快速把開發環境建置起來，開發一個示範站台，利用 plugins 建構出簡單的 feature。 &#8212; 看完了這本書只學會 CRUD。</p></li>
<li><p>另外一個極端，將所有 API 全數羅列出來， &#8212; 翻的時候只想睡，翻完之後不知如何應用在自己 project 之內</p></li>
</ul>


<p>其他時候，只能全憑自己經驗或碰運氣看網路上找來的文件。功能可以勉強在時程內湊出來就萬幸，更別提寫出「好」的程式碼了。</p>

<h2>This book is for you</h2>

<p>網路上不乏許多關於 Rails 的 Best Practices，Antipattern 的演講。比如 ihower 的 [Rails Best Practices] 就是一篇對於寫出高品質 Rails code 很棒的指南。</p>

<p>但在實戰和平日社群交流中，我發覺大家需要的卻不是 Best Practices。Best Practices 離大家的生活太遙遠。大家所需要的是 「Practices」：</p>

<ul>
<li>如何寫出容易維護的程式碼</li>
<li>如何避免寫出的糟糕程式碼</li>
<li>如何遵循 Rails 架構，寫出標準好維護的程式碼</li>
<li>如何透過 Rails 內建機制整理，經過專案積累而日益肥大的 Code Base</li>
<li>如何搭配 RubyGems 讓自己的程式碼更乾淨、整潔，更易讀。</li>
</ul>


<p>在開發功能，專案更迭中，自然持續的維持著專案的整潔。保持著可以繼續高速前進的心情與步調，愉快的工作。</p>

<p>這才是寫 Ruby / Rails 的初衷不是嗎？</p>

<h2>本書架構</h2>

<p>在接觸過無數的專案之後，我發現許多讓人無法繼續維護的 Rails code。除去開發者偷懶的因素，剩下的原因幾乎都是肇因對於 Ruby / Rails 基礎 API 以及架構不了解，所寫出的問題代碼。</p>

<p>所以這本書的順序將會是這樣的：</p>

<ol>
<li>介紹 Ruby 通用的的基本 Coding Style，包含程式碼的寫作慣例</li>
<li>介紹 Rails 容易被誤用，但在開發中相當重要的基本工具。</li>
<li>Application 的 AntiPattern，什麼是絕對錯誤的架構設計</li>
<li>利用 Rails 原生架構實現的程式碼整理術</li>
<li>利用 3rd Party Gem 實現的程式整理術</li>
</ol>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[More about RESTful on Rails]]></title>
    <link href="http://erp-book.heroku.com/ch3-restful-intro-3/"/>
    <updated>2012-01-23T16:37:00+08:00</updated>
    <id>http://erp-book.heroku.com/ch3-restful-intro-3</id>
    <content type="html"><![CDATA[<p>有時候開發中也會出現超過這七種 action 之外的動作。那要如何整合進 RESTful 的 route 中呢？</p>

<h2>Collection &amp; Member</h2>

<h3>Member</h3>

<p>宣告這個動作是屬於單個資源的 URI。可以透過 <code>/photos/1/preview</code> 進行 GET。有 <code>preview_photo_path(photo)</code> 或 <code>preview_photo_url(photo)</code>  這樣的 Url 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>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">resources</span> <span class="ss">:photos</span> <span class="k">do</span>
</span><span class='line'>  <span class="n">member</span> <span class="k">do</span>
</span><span class='line'>    <span class="n">get</span> <span class="s1">&#39;preview&#39;</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>

<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='ruby'><span class='line'><span class="n">resources</span> <span class="ss">:photos</span> <span class="k">do</span>
</span><span class='line'>  <span class="n">get</span> <span class="s1">&#39;preview&#39;</span><span class="p">,</span> <span class="ss">:on</span> <span class="o">=&gt;</span> <span class="ss">:member</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<h3>Collection</h3>

<p>宣告這個動作是屬於一組資源的 URI。可以透過 <code>/photos/search</code> 進行 GET。有 <code>search_photos_path</code> 或 <code>search_photos_url</code> 這樣的 Url 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>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">resources</span> <span class="ss">:photos</span> <span class="k">do</span>
</span><span class='line'>  <span class="n">collection</span> <span class="k">do</span>
</span><span class='line'>    <span class="n">get</span> <span class="s1">&#39;search&#39;</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>

<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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">resources</span> <span class="ss">:photos</span> <span class="k">do</span>
</span><span class='line'>  <span class="n">get</span> <span class="s1">&#39;search&#39;</span><span class="p">,</span> <span class="ss">:on</span> <span class="o">=&gt;</span> <span class="ss">:collection</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<h2>Nested Resources</h2>

<p>有時候我們會需要使用雙重 resources 來表示一組資源。比如說 Post 與 Comment ：</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='ruby'><span class='line'><span class="n">resources</span> <span class="ss">:posts</span> <span class="k">do</span>
</span><span class='line'>  <span class="n">resources</span> <span class="ss">:comments</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>可以透過 <code>/posts/123/comments</code> 進行 GET。有 <code>post_comments_path(post)</code> 或 <code>post_comments_url(post)</code> 這樣的 Url Helper 可以用。</p>

<p>而這樣的 URL 也很清楚的表明，這組資源就是用來存取 編號 123 的 post 下所有的 comments。</p>

<p>而要拿取 Post 的 id 可以透過這樣的方式取得：</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='ruby'><span class='line'><span class="k">class</span> <span class="nc">CommentsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">before_filter</span> <span class="ss">:find_post</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">def</span> <span class="nf">index</span>
</span><span class='line'>    <span class="vi">@comments</span> <span class="o">=</span> <span class="vi">@post</span><span class="o">.</span><span class="n">comments</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="kp">protected</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">def</span> <span class="nf">find_post</span>
</span><span class='line'>    <span class="vi">@post</span> <span class="o">=</span> <span class="no">Post</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">:post_id</span><span class="o">]</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>


<h2>Namespace Resources</h2>

<p>但是像 /admin/posts/1/edit 這種 URL，用 Nested Resources 應該造不出來吧？沒錯，這樣的 URL 應該要使用 Namespace 去建構：</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="n">namespace</span> <span class="ss">:admin</span> <span class="k">do</span>
</span><span class='line'>   <span class="c1"># Directs /admin/products/* to Admin::PostsController</span>
</span><span class='line'>   <span class="c1"># (app/controllers/admin/posts_controller.rb)</span>
</span><span class='line'>   <span class="n">resources</span> <span class="ss">:posts</span>
</span><span class='line'> <span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>可以透過 <code>/admin/posts/123/edit</code> 進行 GET。有 <code>edit_admin_post_path(post)</code> 或 <code>edit_admin_post_url(post)</code> 這樣的 Url Helper 可以用。</p>

<h2>延伸閱讀</h2>

<ul>
<li><a href="http://guides.rubyonrails.org/routing.html">Rails Guide: Rails Routing from the Outside In</a></li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[RESTful on Rails]]></title>
    <link href="http://erp-book.heroku.com/ch3-restful-intro-2/"/>
    <updated>2012-01-23T04:35:00+08:00</updated>
    <id>http://erp-book.heroku.com/ch3-restful-intro-2</id>
    <content type="html"><![CDATA[<p>Representational State Transfer，簡稱 REST。是 Roy Fielding 博士在2000年他的博士論文中提出來的一種軟體架構風格。目前是一種相當風行的 Web Services 實現手法，因為 REST 風格的 Web Services 遠比傳統的 SOAP 與 XML-RPC 來的簡潔。在近年，幾乎所有各大主流網站的 API 都已採用此種風格進行設計。</p>

<h2>What is REST?</h2>

<p>REST 提出了一些設計概念和準則：</p>

<ol>
<li>網路上的所有事物都被將被抽象成資源 (resource)</li>
<li>每個資源對應一個唯一的 resource identifier</li>
<li>通過通用的介面 (generic connector interface) 對資源進行操作</li>
<li>對資源的各種操作不會改變 resource idetifier</li>
<li>所有的操作都是無態 (stateless) 的</li>
</ol>


<p>對照到 web services 上來說：</p>

<ul>
<li>resource indentifier 是 URI</li>
<li>generic connector interface 是 HTTP</li>
</ul>


<h3>RESTful Web Services</h3>

<p>RESTful Web Services 是使用 HTTP 並遵循 REST 設計原則的 Web Services。它從以下三個方面資源進行定義：</p>

<ul>
<li>URI，比如：http://example.com/resources/。</li>
<li>Web Services accept 與 return 的 media type，如：JSON，XML ，YAML 等。</li>
<li>Web Services 在該 resources 所支援的一系列 request methid : 如：POST，GET，PUT 或 DELETE。</li>
</ul>


<p>以下列出在實現 RESTful Web 服務時HTTP請求方法的典型用途。</p>

<table>
<thead>
<tr>
<th>資源                                              </th>
<th> GET </th>
</tr>
</thead>
<tbody>
<tr>
<td>一組資源的URI，比如http://example.com/resources/    </td>
<td> 使用給定的一組資源替換當前整組資源。</td>
</tr>
<tr>
<td>單個資源的URI，比如http://example.com/resources/142 </td>
<td> 獲取 指定的資源的詳細信息，格式可以自選一個合適的 media type（如：XML、JSON等）</td>
</tr>
</tbody>
</table>


<br><hr><br>


<table>
<thead>
<tr>
<th>資源                                              </th>
<th> PUT</th>
</tr>
</thead>
<tbody>
<tr>
<td>一組資源的URI，比如http://example.com/resources/    </td>
<td> 列出 URI，以及該資源組中每個資源的詳細信息（後者可選）。</td>
</tr>
<tr>
<td>單個資源的URI，比如http://example.com/resources/142 </td>
<td> 替換/創建 指定的資源。並將其追加到相應的資源組中。</td>
</tr>
</tbody>
</table>


<br><hr><br>


<table>
<thead>
<tr>
<th>資源                                              </th>
<th> POST</th>
</tr>
</thead>
<tbody>
<tr>
<td>一組資源的URI，比如http://example.com/resources/    </td>
<td> 在本組資源中創建/追加一個新的資源。 該操作往往返回新資源的URL。</td>
</tr>
<tr>
<td>單個資源的URI，比如http://example.com/resources/142 </td>
<td> 把指定的資源當做一個資源組，並在其下創建/追加一個新的元素，使其隸屬於當前資源。</td>
</tr>
</tbody>
</table>


<br><hr><br>


<table>
<thead>
<tr>
<th>資源                                              </th>
<th> DELETE</th>
</tr>
</thead>
<tbody>
<tr>
<td>一組資源的URI，比如http://example.com/resources/    </td>
<td> 刪除 整組資源。</td>
</tr>
<tr>
<td>單個資源的URI，比如http://example.com/resources/142 </td>
<td> 刪除 指定的元素。</td>
</tr>
</tbody>
</table>


<br><br>


<h2>制約即解放</h2>

<p>DHH 在 <a href="http://www.slideshare.net/vishnu/discovering-a-world-of-resources-on-rails">Discovering a world of Resources on Rails</a> 提到一個核心概念：Constraints are liberating 。</p>

<p>很多剛踏入 Rails 這個生態圈的開發者，對於 REST 總有股強烈的反抗心態，認為 REST 是一個討厭的限制。但事實上，DHH 卻認為引入 REST 的制約卻反為 Rais 開發帶來了更大的解放，而這也是他引入這個設計的初衷。</p>

<h3>維護性：解決了程式碼上的風格不一</h3>

<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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">def</span> <span class="nf">add_friend</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">remove_friend</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_post</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">delete_post</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>透過 REST 的包裝（對於 resource 的操作），可以變得更簡潔直觀，且具有統一的寫法。</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="k">class</span> <span class="nc">FriendShipController</span>
</span><span class='line'>   <span class="k">def</span> <span class="nf">create</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">destroy</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">PostController</span>
</span><span class='line'>  <span class="k">def</span> <span class="nf">create</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">destroy</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>


<h3>介面統一：Action as Resource</h3>

<p>大眾最常對 REST 產生的一個誤解，就是以為 resource 只有指的是 data。其實 resource 指的是：data + representation (表現形式，如 html, xml, json 等)。</p>

<p>在網頁開發中，網站所需要的表現格式，不只有 HTML 而已，有時候也需要提供 json 或者 xml。Rails 透過 responder 實現了</p>

<p>在 Resource Oriented Architecture (ROA，一組 REST 架構實現 guideline) 中有一條： <code>A resource can use file extension in the URI, instead of Content-Type negotiation</code>。</p>

<p>Rails 透過 responder 的設計，讓一個 action 可以以 file extension ( .json, .xml, .csv &#8230;etc.) 的形式，提供不同類型的 representation。</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">class</span> <span class="nc">PostsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span><span class='line'>  <span class="c1"># GET /posts</span>
</span><span class='line'>  <span class="c1"># GET /posts.xml</span>
</span><span class='line'>  <span class="k">def</span> <span class="nf">index</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">all</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="c1"># index.html.erb</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>


<h3>開發速度：透過 CRUD 七個 action + HTTP 四個 verb + 表單 Helper 與 URL Helper 達到高速開發</h3>

<p>REST 之所以能簡化開發，是因為其所引入的架構約束。Rails 中的 REST implementation 將 controller 的 method 限制在七個：</p>

<ul>
<li>index</li>
<li>show</li>
<li>new</li>
<li>edit</li>
<li>create</li>
<li>update</li>
<li>destroy</li>
</ul>


<p>實際上就是整個 CRUD。而在實務上，web services 的需要的操作行為，其實也不脫這七種。Rails 透過 HTTP 作為 generic connector interface，使用 HTTP 的四種 verb ： GET、POST、PUT、DELETE 對資源進行操作。</p>

<h4>GET</h4>

<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="o">&lt;</span><span class="sx">%= link_to(&quot;List&quot;, posts_path) %&gt;</span>
</span><span class='line'><span class="sx">&lt;%=</span> <span class="n">link_to</span><span class="p">(</span><span class="s2">&quot;Show&quot;</span><span class="p">,</span> <span class="n">post_path</span><span class="p">(</span><span class="n">post</span><span class="p">))</span> <span class="sx">%&gt;</span>
</span><span class='line'><span class="sx">&lt;%= link_to(&quot;New&quot;, new_post_path) %&gt;</span>
</span><span class='line'><span class="o">&lt;%=</span> <span class="n">link_to</span><span class="p">(</span><span class="s2">&quot;Edit&quot;</span><span class="p">,</span> <span class="n">edit_post_path</span><span class="p">(</span><span class="n">post</span><span class="p">))</span> <span class="o">%&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<h4>POST</h4>

<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="o">&lt;</span><span class="sx">%= form_for @post , :url =</span><span class="o">&gt;</span> <span class="n">posts_path</span> <span class="p">,</span> <span class="ss">:html</span> <span class="o">=&gt;</span> <span class="p">{</span><span class="ss">:method</span> <span class="o">=&gt;</span> <span class="ss">:post</span><span class="p">}</span> <span class="k">do</span> <span class="o">|</span><span class="n">f</span><span class="o">|</span> <span class="o">%&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p></p>

<h4>PUT</h4>

<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="o">&lt;</span><span class="sx">%= form_for @post , :url =</span><span class="o">&gt;</span> <span class="n">post_path</span><span class="p">(</span><span class="vi">@post</span><span class="p">)</span> <span class="p">,</span> <span class="ss">:html</span> <span class="o">=&gt;</span> <span class="p">{</span><span class="ss">:method</span> <span class="o">=&gt;</span> <span class="ss">:put</span><span class="p">}</span> <span class="k">do</span> <span class="o">|</span><span class="n">f</span><span class="o">|</span> <span class="o">%&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p></p>

<h4>Destroy</h4>

<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="o">&lt;</span><span class="sx">%= link_to(&quot;Destroy&quot;, post_path(@post), :method =</span><span class="o">&gt;</span> <span class="ss">:delete</span> <span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p></p>

<h4>Form 綁定 Model Attribute 的設計</h4>

<p>Rails 的表單欄位，是對應 Model Attribute 的：</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="o">&lt;</span><span class="n">h1</span><span class="o">&gt;</span><span class="no">New</span> <span class="n">post</span><span class="o">&lt;</span><span class="sr">/h1&gt;</span>
</span><span class='line'>
</span><span class='line'><span class="sr">&lt;%= form_for @post , :url =&gt; posts_path do |f| %&gt;</span>
</span><span class='line'><span class="sr">    &lt;%= f.error_messages %&gt;</span>
</span><span class='line'><span class="sr">    &lt;div&gt;&lt;label&gt;subject&lt;/</span><span class="n">label</span><span class="o">&gt;&lt;</span><span class="sx">%= f.text_field :subject %&gt;&lt;/div&gt;</span>
</span><span class='line'><span class="sx">    &lt;div&gt;&lt;label&gt;content&lt;/label&gt;&lt;%=</span> <span class="n">f</span><span class="o">.</span><span class="n">text_area</span> <span class="ss">:content</span> <span class="sx">%&gt; &lt;/div&gt;</span>
</span><span class='line'>    <span class="o">&lt;</span><span class="sx">%= f.submit &quot;Submit&quot;, :disable_with =</span><span class="o">&gt;</span> <span class="s1">&#39;Submiting...&#39;</span> <span class="sx">%&gt;</span>
</span><span class='line'><span class="sx">&lt;% end -%&gt;</span>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'><span class="o">&lt;%=</span> <span class="n">link_to</span> <span class="s1">&#39;Back&#39;</span><span class="p">,</span> <span class="n">posts_path</span> <span class="o">%&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>透過 form_for 傳送出來的表單，會被壓縮包裝成一個 parameter: params[:post]，</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">create</span>
</span><span class='line'>  <span class="vi">@post</span> <span class="o">=</span> <span class="no">Post</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:post</span><span class="o">]</span><span class="p">)</span>
</span><span class='line'>  <span class="k">if</span> <span class="vi">@post</span><span class="o">.</span><span class="n">save</span>
</span><span class='line'>    <span class="n">flash</span><span class="o">[</span><span class="ss">:notice</span><span class="o">]</span> <span class="o">=</span> <span class="s1">&#39;Post was successfully created.&#39;</span>
</span><span class='line'>    <span class="n">redirect_to</span> <span class="n">post_path</span><span class="p">(</span><span class="vi">@post</span><span class="p">)</span>
</span><span class='line'>  <span class="k">else</span>
</span><span class='line'>    <span class="n">render</span> <span class="ss">:action</span> <span class="o">=&gt;</span> <span class="s2">&quot;new&quot;</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>

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

<ul>
<li><a href="http://baike.baidu.com/view/1077487.htm">百度百科：REST</a></li>
<li><a href="http://zh.wikipedia.org/wiki/REST">Wikipedia: REST</a></li>
<li><a href="http://en.wikipedia.org/wiki/Resource-oriented_architecture">Wikipedia: ROA</a></li>
<li><a href="http://en.wikipedia.org/wiki/Resource-oriented_architecture">DHH: Discovering a world of Resource on Rails</a></li>
<li><a href="http://ihower.tw/blog/archives/1566">ihower: Rails RESTful 制約即解放</a></li>
<li><a href="http://ihower.tw/blog/archives/1542">ihower: 什麼是 REST 與 RESTful</a></li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[RESTful]]></title>
    <link href="http://erp-book.heroku.com/ch3-restful-intro-1/"/>
    <updated>2012-01-23T02:48:00+08:00</updated>
    <id>http://erp-book.heroku.com/ch3-restful-intro-1</id>
    <content type="html"><![CDATA[<p>Rails 在 1.2 版本的時候引進了 RESTful 這一套設計風格。而在 2.0 版強迫正式成為開發預設值。</p>

<p>說到 RESTful，幾乎每個剛踏進 Rails 框架中的開發者都皺起眉頭。幾乎沒有人能夠在初學階段搞的懂這是什麼玩意。偏偏初學者往往有一個「死扣細節」的壞習慣：沒「弄懂」就「不敢用」，結果在第一個關卡挫折感就堆的如山高，把玩 RESTful 後打退堂鼓，回頭大罵爛設計的人比比皆是。</p>

<h2>想學 Rails 就先把 RESTful 背起來</h2>

<p>RESTful 是 Rails 裡面使用的最基礎最頻繁的一個設計手法，偏偏它也是最難一次講解清楚讓剛入門者理解的一個主題。因為在傳統 web 應用程式開發流程中，根本沒有這樣的概念。更別提在市面上這麼多網頁框架，只有 Rails 將之視為預設值。</p>

<p>別說是初學者，就連筆者和一些 Rails 界的前輩，在 Rails 剛納入 RESTful 為預設風格時，也沒能通透其中原理。</p>

<p>但是不熟練 RESTful 的開發架構，在開發 Rails 時幾乎會寸步難行。在訓練新 Developer 時如何讓他短時間就熟練便上手呢？</p>

<p>我的方法很簡單：強迫他背起來，然後重複寫十遍。</p>

<p>聽起來很唬爛，但練過十遍以後。這時候再重新講解一次 RESTful 的概念，原本模糊的概念一下就變得豁然開朗了。</p>

<h3>初步熟悉使用 Rails RESTful</h3>

<h4>在 config/routes.rb 內加入 resources</h4>

<p>在 config/routes.rb 加入 <code>resources :posts</code>，就是具體在 rails 對一個 controller 實作 RESTful 的方式（對一個 controller 宣告成為 resources)。</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='ruby'><span class='line'><span class="no">Demo</span><span class="o">::</span><span class="no">Application</span><span class="o">.</span><span class="n">routes</span><span class="o">.</span><span class="n">draw</span> <span class="k">do</span>
</span><span class='line'>  <span class="n">resources</span> <span class="ss">:posts</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>這樣的宣告將在自動對應 URL路徑跟 Controller 的 action ，而有以下的結果：</p>

<ul>
<li>GET: /posts => [:action => &#8216;index&#8217;]</li>
<li>GET: /posts.xml => [:action => &#8216;index&#8217;, :format => &#8216;xml&#8217;]</li>
<li>GET: /posts/1 => [:action => &#8216;show&#8217;, :id => 1]</li>
<li>GET: /posts/1/edit => [:action => &#8216;edit&#8217;, :id => 1]</li>
<li>GET: /posts/1.xml => [:action => &#8216;show&#8217;, :id => 1, :format => &#8216;xml&#8217;]</li>
<li>POST: /posts => [:action => &#8216;create&#8217;]</li>
<li>PUT: /posts/1 => [:action => &#8216;update&#8217;, :id => 1]</li>
<li>DELETE: /posts/1 => [:action => &#8216;destroy&#8217;, :id => 1]</li>
</ul>


<p>並產生了一套 url helpers 給 View 使用。</p>

<table>
<thead>
<tr>
<th>helpers            </th>
<th> HTTP Verb </th>
<th> 路徑            </th>
<th> Action</th>
</tr>
</thead>
<tbody>
<tr>
<td>posts_path         </td>
<td>    GET    </td>
<td> <code>/posts</code>       </td>
<td> index</td>
</tr>
<tr>
<td>post_path(id)      </td>
<td>    GET    </td>
<td> <code>/posts/1</code>     </td>
<td> show</td>
</tr>
<tr>
<td>new_post_path      </td>
<td>    GET    </td>
<td> <code>/posts/new</code>   </td>
<td> new</td>
</tr>
<tr>
<td>posts_path         </td>
<td>    POST   </td>
<td> <code>/posts</code>       </td>
<td> create</td>
</tr>
<tr>
<td>edit_post_path(id) </td>
<td>    GET    </td>
<td> <code>/posts/1/edit</code></td>
<td> edit</td>
</tr>
<tr>
<td>bloh_path(id)      </td>
<td>    PUT    </td>
<td> <code>/posts/1</code>     </td>
<td> update </td>
</tr>
<tr>
<td>post_path(id)      </td>
<td>   DELETE  </td>
<td> <code>/posts/1</code>     </td>
<td> destroy</td>
</tr>
</tbody>
</table>


<br>


<br>


<h3>各 action 內部的動作以及對應 path</h3>

<p>以下透過專案中使用 RESTful 應該會出現的程式碼作講解。讀者可以透過 <code>rails generate scaffold Post subject:string content:text</code> 看到這些程式碼。</p>

<h4>index</h4>

<p>通常會放置列表。因此 Post.all 是拉出 Post 這個 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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'>  <span class="k">def</span> <span class="nf">index</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">all</span>
</span><span class='line'>  <span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<h4>show</h4>

<p>秀出單筆資料。 Post.find(123) 是指找 Post model 裡 id 為 123 的資料。http://demosite.com/posts/show/123 的 posts 是 controller、show 是 action；如果在 route 裡面沒有特別指定，則 123 通常就是 params[:id] 。</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='ruby'><span class='line'>  <span class="k">def</span> <span class="nf">show</span>
</span><span class='line'>    <span class="vi">@post</span> <span class="o">=</span> <span class="no">Post</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">:id</span><span class="o">]</span><span class="p">)</span>
</span><span class='line'>  <span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<h4>new</h4>

<p>initial 一個新的 Post object。</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='ruby'><span class='line'>  <span class="k">def</span> <span class="nf">new</span>
</span><span class='line'>    <span class="vi">@post</span> <span class="o">=</span> <span class="no">Post</span><span class="o">.</span><span class="n">new</span>
</span><span class='line'>  <span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<h4>edit</h4>

<p>query 出指定的 Post model object，然後進行編輯。</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='ruby'><span class='line'>  <span class="k">def</span> <span class="nf">edit</span>
</span><span class='line'>    <span class="vi">@post</span> <span class="o">=</span> <span class="no">Post</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">:id</span><span class="o">]</span><span class="p">)</span>
</span><span class='line'>  <span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<h4>create</h4>

<p>這邊要搭配 <code>app/views/posts/new.html.erb</code> 這個 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>
<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">create</span>
</span><span class='line'>  <span class="vi">@post</span> <span class="o">=</span> <span class="no">Post</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:post</span><span class="o">]</span><span class="p">)</span>
</span><span class='line'>  <span class="k">if</span> <span class="vi">@post</span><span class="o">.</span><span class="n">save</span>
</span><span class='line'>    <span class="n">flash</span><span class="o">[</span><span class="ss">:notice</span><span class="o">]</span> <span class="o">=</span> <span class="s1">&#39;Post was successfully created.&#39;</span>
</span><span class='line'>    <span class="n">redirect_to</span> <span class="n">post_path</span><span class="p">(</span><span class="vi">@post</span><span class="p">)</span>
</span><span class='line'>  <span class="k">else</span>
</span><span class='line'>    <span class="n">render</span> <span class="ss">:action</span> <span class="o">=&gt;</span> <span class="s2">&quot;new&quot;</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>




<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="o">&lt;</span><span class="n">h1</span><span class="o">&gt;</span><span class="no">New</span> <span class="n">post</span><span class="o">&lt;</span><span class="sr">/h1&gt;</span>
</span><span class='line'>
</span><span class='line'><span class="sr">&lt;%= form_for @post , :url =&gt; posts_path do |f| %&gt;</span>
</span><span class='line'><span class="sr">    &lt;%= f.error_messages %&gt;</span>
</span><span class='line'><span class="sr">    &lt;div&gt;&lt;label&gt;subject&lt;/</span><span class="n">label</span><span class="o">&gt;&lt;</span><span class="sx">%= f.text_field :subject %&gt;&lt;/div&gt;</span>
</span><span class='line'><span class="sx">    &lt;div&gt;&lt;label&gt;content&lt;/label&gt;&lt;%=</span> <span class="n">f</span><span class="o">.</span><span class="n">text_area</span> <span class="ss">:content</span> <span class="sx">%&gt; &lt;/div&gt;</span>
</span><span class='line'>    <span class="o">&lt;</span><span class="sx">%= f.submit &quot;Submit&quot;, :disable_with =</span><span class="o">&gt;</span> <span class="s1">&#39;Submiting...&#39;</span> <span class="sx">%&gt;</span>
</span><span class='line'><span class="sx">&lt;% end -%&gt;</span>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'><span class="o">&lt;%=</span> <span class="n">link_to</span> <span class="s1">&#39;Back&#39;</span><span class="p">,</span> <span class="n">posts_path</span> <span class="o">%&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>在 RESTful Rails 的寫法中，對 <code>posts_path</code> 丟 POST 就是對應到 create 的動作。而 Rails 在設計上，form 是綁 model 的，因此整個 form 的內容會被包成一個 hash，在這裡就是 params[:post]。create action 初始一個 object ，並把 params[:post] 整包塞進這個 object 裡。如果 @post 能夠成功的儲存，就「重導」到 index action ，失敗則「退回」到 new action 。</p>

<h4>update</h4>

<p>這邊也要翻回 app/views/posts/edit.html.erb 這個 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>
<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">update</span>
</span><span class='line'>    <span class="vi">@post</span> <span class="o">=</span> <span class="no">Post</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">:id</span><span class="o">]</span><span class="p">)</span>
</span><span class='line'>    <span class="k">if</span> <span class="vi">@post</span><span class="o">.</span><span class="n">update_attributes</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:post</span><span class="o">]</span><span class="p">)</span>
</span><span class='line'>     <span class="n">flash</span><span class="o">[</span><span class="ss">:notice</span><span class="o">]</span> <span class="o">=</span> <span class="s1">&#39;Post was successfully updated.&#39;</span>
</span><span class='line'>     <span class="n">redirect_to</span> <span class="n">post_path</span><span class="p">(</span><span class="vi">@post</span><span class="p">)</span>
</span><span class='line'>    <span class="k">else</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;edit&quot;</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>




<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="o">&lt;</span><span class="n">h1</span><span class="o">&gt;</span><span class="no">Editing</span> <span class="n">post</span><span class="o">&lt;</span><span class="sr">/h1&gt;</span>
</span><span class='line'><span class="sr">&lt;%= form_for @post , :url =&gt; post_path(@post) , :html =&gt; {:method =&gt; :put} do |f| %&gt;</span>
</span><span class='line'><span class="sr">    &lt;%= f.error_messages %&gt;</span>
</span><span class='line'><span class="sr">    &lt;div&gt;&lt;%= f.text_field :subject %&gt;</span>
</span><span class='line'><span class="sr">    &lt;div&gt;&lt;%= f.text_area :content %&gt;&lt;/</span><span class="n">div</span><span class="o">&gt;</span>
</span><span class='line'>    <span class="o">&lt;</span><span class="sx">%= f.submit &quot;Submit&quot;, :disable_with =</span><span class="o">&gt;</span> <span class="s1">&#39;Submiting...&#39;</span> <span class="sx">%&gt;</span>
</span><span class='line'><span class="sx">&lt;% end -%&gt;</span>
</span><span class='line'>
</span><span class='line'><span class="o">&lt;</span><span class="sx">%= link_to &#39;Show&#39;, post_path(post) %&gt; |</span>
</span><span class='line'><span class="sx">&lt;%=</span> <span class="n">link_to</span> <span class="s1">&#39;Back&#39;</span><span class="p">,</span> <span class="n">posts_path</span> <span class="o">%&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>在 RESTful Rails 的寫法中，對 <code>post_path</code> 丟 PUT 就是對應到 update 的動作。form 會背包成一個 hash，如果 @post 能夠吃進 params[:post] 進行更新且成功儲存，就會「重導」到 show action，失敗則「退回」到 edit action。</p>

<h4>delete</h4>

<p>找到該筆資料並刪除之。在 RESTful Rails 的寫法中，對 <code>post_path</code> 丟一個 DELETE，就會對應到 destroy 的動作。可看一下 <code>link_to "Destroy"</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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="o">&lt;</span><span class="n">h1</span><span class="o">&gt;</span><span class="no">My</span> <span class="n">post</span><span class="o">&lt;</span><span class="sr">/h1&gt;</span>
</span><span class='line'><span class="sr">&lt;h2&gt;&lt;%= link_to &#39;New post&#39;, new_post_path %&gt;&lt;/</span><span class="n">h2</span><span class="o">&gt;</span>
</span><span class='line'><span class="o">&lt;</span><span class="n">table</span><span class="o">&gt;</span>
</span><span class='line'>  <span class="o">&lt;</span><span class="n">tr</span><span class="o">&gt;</span>
</span><span class='line'>  <span class="o">&lt;</span><span class="sr">/tr&gt;</span>
</span><span class='line'>
</span><span class='line'><span class="sr">&lt;% @posts.each do |post| %&gt;</span>
</span><span class='line'><span class="sr">  &lt;tr&gt;</span>
</span><span class='line'><span class="sr">    &lt;td&gt;&lt;%= post.subject %&gt; &lt;/</span><span class="n">td</span><span class="o">&gt;</span>
</span><span class='line'>    <span class="o">&lt;</span><span class="n">td</span><span class="o">&gt;&lt;</span><span class="sx">%= link_to &#39;Show&#39;, post_path(post) %&gt;&lt;/td&gt;</span>
</span><span class='line'><span class="sx">    &lt;td&gt;&lt;%=</span> <span class="n">link_to</span> <span class="s1">&#39;Edit&#39;</span><span class="p">,</span> <span class="n">edit_post_path</span><span class="p">(</span><span class="n">post</span><span class="p">)</span> <span class="sx">%&gt;&lt;/td&gt;</span>
</span><span class='line'>    <span class="o">&lt;</span><span class="n">td</span><span class="o">&gt;&lt;</span><span class="sx">%= link_to &#39;Destroy&#39;,  post_path(post), :method =</span><span class="o">&gt;</span> <span class="ss">:delete</span> <span class="sx">%&gt;&lt;/td&gt;</span>
</span><span class='line'>  <span class="o">&lt;</span><span class="sr">/tr&gt;</span>
</span><span class='line'><span class="sr">&lt;% end %&gt;</span>
</span><span class='line'><span class="sr">&lt;/</span><span class="n">table</span><span class="o">&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>還是不理解為什麼要這樣寫？沒關係，先練習十遍。練到不需要思考就能夠手寫這些程式碼再說。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Counter Cache]]></title>
    <link href="http://erp-book.heroku.com/ch3-counter-cache/"/>
    <updated>2012-01-19T07:05:00+08:00</updated>
    <id>http://erp-book.heroku.com/ch3-counter-cache</id>
    <content type="html"><![CDATA[<p><code>#TODO</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='ruby'><span class='line'><span class="o">-&lt;</span><span class="n">h1</span><span class="o">&gt;</span><span class="no">Listing</span> <span class="n">posts</span><span class="o">&lt;</span><span class="sr">/h1&gt;</span>
</span><span class='line'><span class="sr">+&lt;h1&gt;Listing posts - Total &lt;%= @board.posts.size %&gt; posts &lt;/</span><span class="n">h1</span><span class="o">&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>使用 @board.posts.size 機制直接算出 boards 下有幾篇文章，產生的 SQL query 是使用 SELECT COUNT(*)</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="no">SQL</span> <span class="p">(</span><span class="mi">0</span><span class="o">.</span><span class="mi">1</span><span class="n">ms</span><span class="p">)</span>  <span class="no">SELECT</span> <span class="no">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span> <span class="no">FROM</span> <span class="s2">&quot;posts&quot;</span> <span class="no">WHERE</span> <span class="p">(</span><span class="s2">&quot;posts&quot;</span><span class="o">.</span><span class="n">board_id</span> <span class="o">=</span> <span class="mi">1</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>若在 posts 數量相當大時，存取 boards#index 將會造成沈重的負擔。我們應該找個方法把這個數值 cache 起來。</p>

<h4>Solution 1</h4>

<ul>
<li>應該在 board model 新增一個 integer 欄位紀錄有多少篇文章</li>
<li>每當文章被 新增 / 刪除之際，在 controller +1/-1 文章數量</li>
</ul>


<h3>Solution 2</h3>

<p>這樣要寫太多行 code 了，事實上 Rails 內建了一個 counter_cache 的機制。當文章建立或刪除時，會自動進行 +1/-1 功能。</p>

<p>修改 app/models/post.rb</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="o">-</span>  <span class="n">belongs_to</span> <span class="ss">:board</span>
</span><span class='line'><span class="o">+</span>  <span class="n">belongs_to</span> <span class="ss">:board</span><span class="p">,</span> <span class="ss">:counter_cache</span> <span class="o">=&gt;</span> <span class="kp">true</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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="err">$</span> <span class="n">rails</span> <span class="n">g</span> <span class="n">migration</span> <span class="n">add_posts_count_to_board</span>
</span></code></pre></td></tr></table></div></figure>


<p>修改 db/migrate/20110529014114_add_posts_count_to_board.rb</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">class</span> <span class="nc">AddPostsCountToBoard</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Migration</span>
</span><span class='line'>  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">up</span>
</span><span class='line'>    <span class="n">add_column</span> <span class="ss">:boards</span><span class="p">,</span> <span class="ss">:posts_count</span> <span class="p">,</span> <span class="ss">:integer</span><span class="p">,</span> <span class="ss">:default</span> <span class="o">=&gt;</span> <span class="mi">0</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="nc">self</span><span class="o">.</span><span class="nf">down</span>
</span><span class='line'>    <span class="n">remove_column</span> <span class="ss">:boards</span><span class="p">,</span> <span class="ss">:posts_count</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>

<p>之後 @board.posts.size 就是直接去撈 posts_count 欄位的數字了。</p>

<h4>注意事項</h4>

<p>若 boards 內早已有文章，直接加上 counter_cache => true 會導致抓到的 size 是初始值 0。建議先開設 posts_count 欄位，手動跑個計算，先將正確統計數字加總後回存 posts_count，再宣告 counter_cache => true</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Scope]]></title>
    <link href="http://erp-book.heroku.com/ch3-scope-intro/"/>
    <updated>2012-01-19T06:59:00+08:00</updated>
    <id>http://erp-book.heroku.com/ch3-scope-intro</id>
    <content type="html"><![CDATA[<p>Scope 的最前身，是一套 Rails Plugin：has_finder。在遙遠的 2007 年，那時候的 ActiveRecord 遠不如現今強大。很多 finder 都需要自己定義 method 手刻。</p>

<p>如果今天需要下一段 query 做出 recent 效果，我們會手寫一段 method</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='ruby'><span class='line'><span class="k">def</span> <span class="nf">recent</span>
</span><span class='line'>   <span class="n">find</span><span class="p">(</span><span class="ss">:all</span><span class="p">,</span> <span class="ss">:order</span> <span class="o">=&gt;</span> <span class="err">“</span><span class="n">created_at</span> <span class="no">DESC</span><span class="err">”</span><span class="p">)</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>需要 popular</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='ruby'><span class='line'><span class="k">def</span> <span class="nf">popular</span>
</span><span class='line'>   <span class="n">find</span><span class="p">(</span><span class="ss">:all</span><span class="p">,</span> <span class="ss">:order</span> <span class="o">=&gt;</span> <span class="err">â</span><span class="n">visitors_count</span> <span class="no">DESC</span><span class="err">â</span><span class="p">)</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>但如果，我們需要 Post.recent.popular 呢？</p>

<p>Rails 的 method 好像不能這樣串。
腦袋當機&#8230;.</p>

<p>也許我們可以寫個 recent_and_popular</p>

<p>但是，在別一段 code 我們需要的是 Post.popular.recent 呢？</p>

<p>兩個條件已經夠讓你頭大了，但真實世界會有無窮的條件等著排列組合。你不可能永遠定義 method 去暴力解決串接的問題。</p>

<p>has_finder 就是設計來滿足串接的需求，自動產生需要產生的 query。而 has_finder 在 Rails 2 時被納為內建 feature named_scope，在 Rails 3 時強化原先的 feature，再次更名為 scope。</p>

<h2>Default Scope</h2>

<p><code>#TODO</code></p>

<h2>Other Scope</h2>

<p><code>#TODO</code></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Sass/SCSS 與 Compass]]></title>
    <link href="http://erp-book.heroku.com/ch3-scss-and-compass/"/>
    <updated>2012-01-18T12:41:00+08:00</updated>
    <id>http://erp-book.heroku.com/ch3-scss-and-compass</id>
    <content type="html"><![CDATA[<p>自 Rails 3.1 起提供的 Asset Pipeline，是一套讓開發者很方便能夠削減 (minify) 以及 壓縮 (compress) Javascript / CSS 檔案的框架。它同時也提供你直接利用其他語言如 CoffeeScript / SASS / ERB ，直接撰寫這些 assets (stylesheets / javascripts / images ) 的可能性。</p>

<p>它的面世，推動了新的 Asset 整理概念，也向世界的開發者，介紹了 SCSS / Compass，引爆在 CSS 開發上無限的可能。</p>

<h2>使用 Sass / SCSS 撰寫 CSS</h2>

<p>Asset Pipeline 提供了內建直接使用 Sass 撰寫 CSS 的功能。</p>

<p>Sass 是 Haml 這個 project 中的一部分 feature。 Haml 能讓開發者能夠使用縮排的方式撰寫 HTML，輕鬆做到永不忘記關 Tag 的效果。</p>

<h3>Haml</h3>

<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='haml'><span class='line'><span class="nt">%h1</span><span class="p">=</span> <span class="s2">&quot;Hello World&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<p>產生出來的 HTML 就會長這樣</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;h1&gt;</span>Hello World<span class="nt">&lt;/h1&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>Haml 是需要使用縮排撰寫，再行 compile 的 markup language。它可以讓你：</p>

<ul>
<li>阻絕撰寫出錯誤結構的 HTML TAG 的機會</li>
</ul>


<p>只要 syntax 一錯誤，編譯就無法成功。利用這樣的特性，很容易阻絕寫出會在瀏覽器因為 TAG 結構錯誤而難以 debug 出的 HTML。</p>

<p>在網頁設計開發階段，若要大幅調整 HTML 結構，對網頁設計師也是很傷腦筋的一件事。因為大幅的搬動 HTML，通常有時候會造成：「少剪到一個 TAG」或 「改了開頭 TAG ，卻忘了改關閉 TAG 」的憾事。</p>

<p>在 Haml 中，這些狀況都不會發生。因為 Haml 本身就屬於「塊狀結構」、「自我 close」。因此不論怎樣搬動和調整，只要符合縮排，幾乎都不會爆炸。</p>

<ul>
<li>輕鬆調整排版</li>
</ul>


<p>然而 Haml 並沒有如預期中風行？反倒是原先屬於副功能的 Sass 大紅特紅。原因就在於 Haml 的特性：不只需要被機器 compile，它也需要被人腦 compile。HTML 本身就是一門相當直觀的 markup language。在撰寫 Haml 時，排版雖然相當輕鬆。但接手維護 Haml 版面的人，卻通常痛苦不堪。因為「非常不直觀」。</p>

<h3>Sass / SCSS</h3>

<p>回頭談 Sass，Sass 原先是附屬在 Haml 裡面的一個副功能。這也是 sass-convert 這個指令必須要安裝 haml 這個 gem 才能使用的原因。Sass 的原理，是讓開發者可以透過「縮排」的方式去撰寫維護 CSS，同樣可以避免忘記關 TAG 而大爆炸的糗事。而因為 CSS 的結構特性，造成了 Sass 與 Haml 截然不同的命運。多數人反對 Haml，是因為 Haml 反而造成了 HTML 在閱讀上的不直觀。</p>

<p>而 Sass 的語法</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='sass'><span class='line'>  <span class="nc">.content</span>
</span><span class='line'>    <span class="na">margin</span><span class="o">:</span> <span class="mi">2</span><span class="kt">em</span> <span class="mi">0</span>
</span><span class='line'>    <span class="nt">h1</span>
</span><span class='line'>      <span class="na">font-size</span><span class="o">:</span> <span class="mi">2</span><span class="kt">em</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>
</pre></td><td class='code'><pre><code class='scss'><span class='line'>   <span class="nc">.content</span><span class="p">{</span>
</span><span class='line'>    <span class="na">margin</span><span class="o">:</span> <span class="mi">2</span><span class="kt">em</span> <span class="mi">0</span><span class="p">;</span>
</span><span class='line'>   <span class="p">}</span>
</span><span class='line'>   <span class="nc">.content</span> <span class="nt">h1</span><span class="p">{</span>
</span><span class='line'>      <span class="na">font-size</span><span class="o">:</span> <span class="mi">2</span><span class="kt">em</span><span class="p">;</span>
</span><span class='line'>   <span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>反倒讓 CSS 的維護變得直觀了。</p>

<p>接觸 Sass / SCSS 後的不少開發者甚至認為：縮排 block 的撰寫方式才是 CSS 在被發明時應該被設計出來的樣子。現在撰寫 CSS 的方式，有一個絕大缺點：只要在結構上涉及巢狀或多個 class，維護者就必須複製貼上 style。不少人認為這真是愚蠢至極且煩人透頂的設計。</p>

<h3>內建 Killer features 讓維護 CSS 變得更簡單</h3>

<p>Sass 也提供了其他便利功能，如變數、函數、數學、繼承、mixin …等等功能。</p>

<p>在進行網頁 protyping 時，更改全站配色或者是直接提供兩個以上的設計，對設計師來說是家常便飯的事。</p>

<p>但更改全站配色卻是相當麻煩的一件事，因為「尋找 + 全數取代」，並不能保證最後會有正確的結果。很有可能：你更改了所有 CSS 中涉及連結的顏色，卻發現在全數取代的過程中，不小心也改到邊框的顏色。</p>

<p>若能使用變數去指定特定 style 的顏色，該有多好。</p>

<ul>
<li>變數 ( Variables )</li>
</ul>


<figure class='code'><figcaption><span>Sass</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='sass'><span class='line'>   <span class="na">$border-color</span><span class="o">:</span> <span class="mh">#3bbfce</span>
</span><span class='line'>   <span class="na">$link-color</span><span class="o">:</span> <span class="mh">#3bbfcf</span>
</span><span class='line'>   <span class="nc">.content</span>
</span><span class='line'>      <span class="na">border-color</span><span class="o">:</span> <span class="nv">$border-color</span>
</span><span class='line'>      <span class="nt">a</span>
</span><span class='line'>        <span class="na">color</span><span class="o">:</span> <span class="nv">$link-color</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>CSS</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='css'><span class='line'>   <span class="nc">.content</span><span class="p">{</span> <span class="k">border-color</span><span class="o">:</span> <span class="m">#3bbfce</span><span class="p">;</span> <span class="p">}</span>
</span><span class='line'>   <span class="nc">.content</span> <span class="nt">a</span><span class="p">{</span><span class="k">color</span><span class="o">:</span> <span class="m">#3bbfcf</span><span class="p">;</span> <span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<ul>
<li>數學</li>
</ul>


<p>在調整區塊寬度時，你也希望：每次調整寬度時，可不可以不要每次都按計算機，再全數手動修改…</p>

<figure class='code'><figcaption><span>Sass</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='sass'><span class='line'>   <span class="nc">.content</span>
</span><span class='line'>      <span class="na">width</span><span class="o">:</span> <span class="p">(</span><span class="mi">500</span><span class="kt">px</span><span class="o">/</span><span class="mi">2</span><span class="p">)</span><span class="err">;</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>CSS</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='css'><span class='line'>   <span class="nc">.content</span><span class="p">{</span> <span class="k">width</span><span class="o">:</span> <span class="m">250px</span><span class="p">;</span> <span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<ul>
<li>內建函式</li>
</ul>


<p>在調整顏色亮度時，你希望可否無需再開調色盤，直接改 CSS 就讓 style 暗一點？</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='sass'><span class='line'>   <span class="nv">$color</span><span class="o"> =</span> <span class="nf">darken</span><span class="p">(</span><span class="mh">#800</span><span class="o">,</span> <span class="mi">20</span><span class="kt">%</span><span class="p">)</span>
</span><span class='line'>   <span class="nc">.content</span>
</span><span class='line'>      <span class="na">background-color</span><span class="o">:</span> <span class="nv">$color</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>
</pre></td><td class='code'><pre><code class='css'><span class='line'>   <span class="nc">.content</span><span class="p">{</span> <span class="k">background-color</span><span class="o">:</span> <span class="m">#200</span><span class="p">;</span> <span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>這都還只是 Sass 所提供的當中一小部分基礎功能而已，卻足以讓網頁設計師驚艷十足了。加上撰寫維護時十分直觀，這也難怪為何 Sass 只是 Haml 中的副功能，後繼的聲勢浪頭卻遠高於 Haml 本身。</p>

<h3>SCSS</h3>

<p>那 SCSS 又與 Sass 有什麼差別，他們看起來好像是類似的東西？</p>

<p>是這樣的，Sass 原先使用的縮排，對於網頁設計師來說，還是相當不直觀。而且實務上也不能直接將舊有的 CSS 直接貼進 Sass 中。其實還是存在一定的不方便度。也因此 Sass 進行了進化，改良了 syntax，而 Sass 3 後來就被稱為 SCSS ( Sassy CSS)。</p>

<p>它的 syntax 與 CSS 原有的 syntax 完全 compatible，使用了 { } 去取代原先的縮排方式。</p>

<p>比如說原有的</p>

<figure class='code'><figcaption><span>Sass</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='sass'><span class='line'>  <span class="nc">.content</span>
</span><span class='line'>    <span class="na">margin</span><span class="o">:</span> <span class="mi">2</span><span class="kt">em</span> <span class="mi">0</span>
</span><span class='line'>    <span class="nt">h1</span>
</span><span class='line'>      <span class="na">font-size</span><span class="o">:</span> <span class="mi">2</span><span class="kt">em</span>
</span></code></pre></td></tr></table></div></figure>


<p>在 SCSS 中變成了</p>

<figure class='code'><figcaption><span>SCSS</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='scss'><span class='line'>  <span class="nc">.content</span><span class="p">{</span>
</span><span class='line'>    <span class="na">margin</span><span class="o">:</span> <span class="mi">2</span><span class="kt">em</span> <span class="mi">0</span><span class="p">;</span>
</span><span class='line'>    <span class="nt">h1</span> <span class="p">{</span><span class="na">font-size</span><span class="o">:</span> <span class="mi">2</span><span class="kt">em</span> <span class="p">}</span>
</span><span class='line'>  <span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>在撰寫上，更加無比的直觀，同時也能將舊有的 CSS 直接貼進去，完全沒問題！SCSS 更新增了不少關於 CSS3 的 feature 函式。</p>

<p>就拿我最愛的背景漸變色來說好了，原先要做漸變色，CSS 必須要這樣寫：</p>

<figure class='code'><figcaption><span>CSS</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>
</pre></td><td class='code'><pre><code class='css'><span class='line'> <span class="nf">#linear-gradient</span> <span class="p">{</span>
</span><span class='line'>   <span class="k">background-image</span><span class="o">:</span> <span class="o">-</span><span class="n">webkit</span><span class="o">-</span><span class="n">gradient</span><span class="p">(</span><span class="n">linear</span><span class="o">,</span> <span class="m">0</span><span class="o">%</span> <span class="m">0</span><span class="o">%,</span> <span class="m">100</span><span class="o">%</span> <span class="m">100</span><span class="o">%,</span> <span class="k">color</span><span class="o">-</span><span class="n">stop</span><span class="p">(</span><span class="m">0</span><span class="o">%,</span> <span class="m">#ffffff</span><span class="p">)</span><span class="o">,</span> <span class="k">color</span><span class="o">-</span><span class="n">stop</span><span class="p">(</span><span class="m">100</span><span class="o">%,</span> <span class="m">#dddddd</span><span class="p">));</span>
</span><span class='line'>   <span class="k">background-image</span><span class="o">:</span> <span class="o">-</span><span class="n">webkit</span><span class="o">-</span><span class="n">linear</span><span class="o">-</span><span class="n">gradient</span><span class="p">(</span><span class="k">left</span> <span class="k">top</span><span class="o">,</span> <span class="m">#ffffff</span><span class="o">,</span> <span class="m">#dddddd</span><span class="p">);</span>
</span><span class='line'>   <span class="k">background-image</span><span class="o">:</span> <span class="o">-</span><span class="n">moz</span><span class="o">-</span><span class="n">linear</span><span class="o">-</span><span class="n">gradient</span><span class="p">(</span><span class="k">left</span> <span class="k">top</span><span class="o">,</span> <span class="m">#ffffff</span><span class="o">,</span> <span class="m">#dddddd</span><span class="p">);</span>
</span><span class='line'>   <span class="k">background-image</span><span class="o">:</span> <span class="o">-</span><span class="err">o</span><span class="o">-</span><span class="n">linear</span><span class="o">-</span><span class="n">gradient</span><span class="p">(</span><span class="k">left</span> <span class="k">top</span><span class="o">,</span> <span class="m">#ffffff</span><span class="o">,</span> <span class="m">#dddddd</span><span class="p">);</span>
</span><span class='line'>   <span class="k">background-image</span><span class="o">:</span> <span class="o">-</span><span class="n">ms</span><span class="o">-</span><span class="n">linear</span><span class="o">-</span><span class="n">gradient</span><span class="p">(</span><span class="k">left</span> <span class="k">top</span><span class="o">,</span> <span class="m">#ffffff</span><span class="o">,</span> <span class="m">#dddddd</span><span class="p">);</span>
</span><span class='line'>   <span class="k">background-image</span><span class="o">:</span> <span class="n">linear</span><span class="o">-</span><span class="n">gradient</span><span class="p">(</span><span class="k">left</span> <span class="k">top</span><span class="o">,</span> <span class="m">#ffffff</span><span class="o">,</span> <span class="m">#dddddd</span><span class="p">);</span>
</span><span class='line'> <span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>因為你必須支援所有的 Browser。</p>

<p>但在 SCSS 中，一行就搞定了！</p>

<figure class='code'><figcaption><span>SCSS</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='scss'><span class='line'> <span class="nn">#linear-gradient</span> <span class="p">{</span> <span class="k">@include</span><span class="nd"> background-image</span><span class="p">(</span><span class="nf">linear-gradient</span><span class="p">(</span><span class="no">left</span> <span class="no">top</span><span class="o">,</span> <span class="nb">white</span><span class="o">,</span> <span class="mh">#dddddd</span><span class="p">));</span> <span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<h2>什麼是 Compass</h2>

<h3>Sass : extend / mixin / import</h3>

<p>在 Sass 一章，我們只提到了一些基礎的部分。更強大的其實是這些功能</p>

<ul>
<li>extend</li>
<li>mixin</li>
<li>include</li>
</ul>


<h4>extend</h4>

<p>extend 可以讓你將某些已經寫好的樣式，直接套入另外一組樣式中重複使用。</p>

<p>CSS 的原始內容：</p>

<figure class='code'><figcaption><span>CSS</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='css'><span class='line'>   <span class="nc">.warning</span><span class="p">{</span>
</span><span class='line'>      <span class="k">border</span><span class="o">:</span> <span class="m">1px</span><span class="p">;</span>
</span><span class='line'>      <span class="k">color</span><span class="o">:</span> <span class="nb">red</span><span class="p">;</span>
</span><span class='line'>   <span class="p">}</span>
</span><span class='line'>   <span class="nc">.serious-warning</span><span class="p">{</span>
</span><span class='line'>       <span class="k">border</span><span class="o">:</span> <span class="m">1px</span><span class="p">;</span>
</span><span class='line'>       <span class="k">color</span><span class="o">:</span> <span class="nb">red</span><span class="p">;</span>
</span><span class='line'>       <span class="k">font-weight</span><span class="o">:</span> <span class="k">bold</span><span class="p">;</span>
</span><span class='line'>   <span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>而使用 extend，只需這樣寫，就可生成上述 CSS 了：</p>

<figure class='code'><figcaption><span>SCSS</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>
</pre></td><td class='code'><pre><code class='scss'><span class='line'>   <span class="nc">.warning</span><span class="p">{</span>
</span><span class='line'>      <span class="na">border</span><span class="o">:</span> <span class="mi">1</span><span class="kt">px</span><span class="p">;</span>
</span><span class='line'>      <span class="na">color</span><span class="o">:</span> <span class="nb">red</span><span class="p">;</span>
</span><span class='line'>   <span class="p">}</span>
</span><span class='line'>   <span class="nc">.serious-warning</span><span class="p">{</span>
</span><span class='line'>       <span class="k">@extend</span> <span class="nc">.warning</span><span class="o">;</span>
</span><span class='line'>       <span class="nt">font-weight</span><span class="nd">:</span> <span class="nt">bold</span><span class="o">;</span>
</span><span class='line'>   <span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<h4>mixin</h4>

<p>可以預先寫好自定要被 mixin 的 function ，需要用時 include 進來</p>

<figure class='code'><figcaption><span>SCSS</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='scss'><span class='line'> <span class="k">@mixin</span><span class="nf"> rounded-top</span> <span class="p">{</span>
</span><span class='line'>   <span class="nv">$side</span><span class="o">:</span> <span class="no">top</span><span class="p">;</span>
</span><span class='line'>   <span class="nv">$radius</span><span class="o">:</span> <span class="mi">10</span><span class="kt">px</span><span class="p">;</span>
</span><span class='line'>   <span class="nt">border-</span><span class="nn">#</span><span class="p">{</span><span class="err">$</span><span class="nt">side</span><span class="p">}</span><span class="na">-radius</span><span class="o">:</span> <span class="nv">$radius</span><span class="p">;</span>
</span><span class='line'>   <span class="nt">-moz-border-radius-</span><span class="nn">#</span><span class="p">{</span><span class="err">$</span><span class="nt">side</span><span class="p">}</span><span class="nd">:</span> <span class="err">$</span><span class="nt">radius</span><span class="o">;</span>
</span><span class='line'>   <span class="nt">-webkit-border-</span><span class="nn">#</span><span class="p">{</span><span class="err">$</span><span class="nt">side</span><span class="p">}</span><span class="na">-radius</span><span class="o">:</span> <span class="nv">$radius</span><span class="p">;</span>
</span><span class='line'> <span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>要用時 include 進來</p>

<figure class='code'><figcaption><span>SCSS</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='scss'><span class='line'>   <span class="nn">#navbar</span> <span class="nt">li</span> <span class="p">{</span> <span class="k">@include</span><span class="nd"> rounded-top</span><span class="p">;</span> <span class="p">}</span>
</span><span class='line'>   <span class="nn">#footer</span> <span class="p">{</span> <span class="k">@include</span><span class="nd"> rounded-top</span><span class="p">;</span> <span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<h4>import</h4>

<p>可以把太長的 SCSS 拆開成一個一個的 partial，要用時 import 進來。</p>

<figure class='code'><figcaption><span>style.scss</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='scss'><span class='line'>   <span class="c1">//需要被 import 的檔案必須以 _開頭命名，如 _rounded.scss</span>
</span><span class='line'>   <span class="k">@import</span> <span class="s2">&quot;rounded&quot;</span><span class="p">;</span>
</span></code></pre></td></tr></table></div></figure>


<p>有了 extend / mixin / import，加上 variable，加上支援部分 Ruby syntax。</p>

<p>聰明的你，應該想到這些元素集合起來可以作什麼？</p>

<p>沒錯，就是造 CSS Framework！</p>

<h3>Compass = Sass framework powered by community</h3>

<p>Sass 提供了一堆基本武器可以讓許多開發者造出自己的最佳實踐，再讓其他人可以重複利用。而 <a href="http://compass-style.org/">Compass</a> 就是這些實踐產物的集合體。</p>

<p>例如 Compass 實際就提供以下這些熱門 CSS framework 的 mixin：</p>

<ul>
<li><a href="http://blueprintcss.org/">Blueprint</a></li>
<li><a href="http://960.gs/">960</a></li>
<li><a href="http://www.oddbird.net/susy/">Susy</a></li>
<li><a href="http://developer.yahoo.com/yui/grids/">YUI</a></li>
</ul>


<p>開發者也能夠利用 Sass + Compass 自己造出很多超棒的小工具，如： <a href="https://github.com/imathis/fancy-buttons">Fancy Buttons</a>。</p>

<p><img src="https://a248.e.akamai.net/assets.github.com/img/128e3bd4bd5924214f4c2b38d11d9cbc1fa3d7dc/687474703a2f2f73332e696d61746869732e636f6d2f6465762f636f6d706173732f66616e63792d627574746f6e732f64656d6f2e706e67"></p>

<p>而 Compass 自己的 core 更是充滿 killer features，比如說：</p>

<ul>
<li>CSS3 helpers <a href="http://compass-style.org/reference/compass/css3/">http://compass-style.org/reference/compass/css3/</a></li>
</ul>


<p>撰寫漸層色與圓角框不再是令人頭大的難事。</p>

<p>漸層色 Background Gradients : <a href="http://compass-style.org/examples/compass/css3/gradient/">http://compass-style.org/examples/compass/css3/gradient/</a></p>

<p><img src="http://erp-book.heroku.com/images/gradiants.png"></p>

<figure class='code'><figcaption><span>SCSS</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='scss'><span class='line'><span class="nn">#linear-gradient</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">@include</span><span class="nd"> background-image</span><span class="p">(</span><span class="nf">linear-gradient</span><span class="p">(</span><span class="no">left</span> <span class="no">top</span><span class="o">,</span> <span class="nb">white</span><span class="o">,</span> <span class="mh">#dddddd</span><span class="p">));</span> <span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>CSS</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>
</pre></td><td class='code'><pre><code class='css'><span class='line'><span class="nf">#linear-gradient</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">background-image</span><span class="o">:</span> <span class="o">-</span><span class="n">webkit</span><span class="o">-</span><span class="n">gradient</span><span class="p">(</span><span class="n">linear</span><span class="o">,</span> <span class="m">0</span><span class="o">%</span> <span class="m">0</span><span class="o">%,</span> <span class="m">100</span><span class="o">%</span> <span class="m">100</span><span class="o">%,</span> <span class="k">color</span><span class="o">-</span><span class="n">stop</span><span class="p">(</span><span class="m">0</span><span class="o">%,</span> <span class="m">#ffffff</span><span class="p">)</span><span class="o">,</span> <span class="k">color</span><span class="o">-</span><span class="n">stop</span><span class="p">(</span><span class="m">100</span><span class="o">%,</span> <span class="m">#dddddd</span><span class="p">));</span>
</span><span class='line'>  <span class="k">background-image</span><span class="o">:</span> <span class="o">-</span><span class="n">webkit</span><span class="o">-</span><span class="n">linear</span><span class="o">-</span><span class="n">gradient</span><span class="p">(</span><span class="k">left</span> <span class="k">top</span><span class="o">,</span> <span class="m">#ffffff</span><span class="o">,</span> <span class="m">#dddddd</span><span class="p">);</span>
</span><span class='line'>  <span class="k">background-image</span><span class="o">:</span> <span class="o">-</span><span class="n">moz</span><span class="o">-</span><span class="n">linear</span><span class="o">-</span><span class="n">gradient</span><span class="p">(</span><span class="k">left</span> <span class="k">top</span><span class="o">,</span> <span class="m">#ffffff</span><span class="o">,</span> <span class="m">#dddddd</span><span class="p">);</span>
</span><span class='line'>  <span class="k">background-image</span><span class="o">:</span> <span class="o">-</span><span class="err">o</span><span class="o">-</span><span class="n">linear</span><span class="o">-</span><span class="n">gradient</span><span class="p">(</span><span class="k">left</span> <span class="k">top</span><span class="o">,</span> <span class="m">#ffffff</span><span class="o">,</span> <span class="m">#dddddd</span><span class="p">);</span>
</span><span class='line'>  <span class="k">background-image</span><span class="o">:</span> <span class="o">-</span><span class="n">ms</span><span class="o">-</span><span class="n">linear</span><span class="o">-</span><span class="n">gradient</span><span class="p">(</span><span class="k">left</span> <span class="k">top</span><span class="o">,</span> <span class="m">#ffffff</span><span class="o">,</span> <span class="m">#dddddd</span><span class="p">);</span>
</span><span class='line'>  <span class="k">background-image</span><span class="o">:</span> <span class="n">linear</span><span class="o">-</span><span class="n">gradient</span><span class="p">(</span><span class="k">left</span> <span class="k">top</span><span class="o">,</span> <span class="m">#ffffff</span><span class="o">,</span> <span class="m">#dddddd</span><span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>圓角框 Border radius: <a href="http://compass-style.org/examples/compass/css3/border_radius/">http://compass-style.org/examples/compass/css3/border_radius/</a></p>

<p><img src="http://erp-book.heroku.com/images/radius.png"></p>

<figure class='code'><figcaption><span>SCSS</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='scss'><span class='line'><span class="nn">#border-radius</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">@include</span><span class="nd"> border-radius</span><span class="p">(</span><span class="mi">25</span><span class="kt">px</span><span class="p">);</span> <span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>CSS</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>
</pre></td><td class='code'><pre><code class='css'><span class='line'><span class="nf">#border-radius</span> <span class="p">{</span>
</span><span class='line'>  <span class="o">-</span><span class="n">moz</span><span class="o">-</span><span class="k">border</span><span class="o">-</span><span class="n">radius</span><span class="o">:</span> <span class="m">25px</span><span class="p">;</span>
</span><span class='line'>  <span class="o">-</span><span class="n">webkit</span><span class="o">-</span><span class="k">border</span><span class="o">-</span><span class="n">radius</span><span class="o">:</span> <span class="m">25px</span><span class="p">;</span>
</span><span class='line'>  <span class="o">-</span><span class="err">o</span><span class="o">-</span><span class="k">border</span><span class="o">-</span><span class="n">radius</span><span class="o">:</span> <span class="m">25px</span><span class="p">;</span>
</span><span class='line'>  <span class="o">-</span><span class="n">ms</span><span class="o">-</span><span class="k">border</span><span class="o">-</span><span class="n">radius</span><span class="o">:</span> <span class="m">25px</span><span class="p">;</span>
</span><span class='line'>  <span class="o">-</span><span class="n">khtml</span><span class="o">-</span><span class="k">border</span><span class="o">-</span><span class="n">radius</span><span class="o">:</span> <span class="m">25px</span><span class="p">;</span>
</span><span class='line'>  <span class="k">border</span><span class="o">-</span><span class="n">radius</span><span class="o">:</span> <span class="m">25px</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<ul>
<li>CSS3 Pie : <a href="http://compass-style.org/reference/compass/css3/pie/">http://compass-style.org/reference/compass/css3/pie/</a></li>
</ul>


<p>擔心使用 CSS3 ，IE 無法支援？Compass 內建支援 <a href="http://css3pie.com/">CSS3 Pie</a>。</p>

<ul>
<li>輕鬆造出多欄設計 <a href="http://compass-style.org/examples/blueprint/grid/two_cols/">http://compass-style.org/examples/blueprint/grid/two_cols/</a></li>
</ul>


<p>討厭在 HTML 上寫 CSS grid system require 的 class name 嗎？配合 Blueprint 做 mixin，輕鬆造出多欄版面！</p>

<p><img src="http://erp-book.heroku.com/images/columns.png"></p>

<figure class='code'><figcaption><span>SCSS</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='scss'><span class='line'><span class="nv">$blueprint_grid_columns</span><span class="o">:</span> <span class="mi">8</span><span class="p">;</span>
</span><span class='line'><span class="nv">$blueprint_grid_width</span><span class="o">:</span> <span class="mi">40</span><span class="kt">px</span><span class="p">;</span>
</span><span class='line'><span class="k">@import</span> <span class="s2">&quot;blueprint&quot;</span><span class="p">;</span>
</span><span class='line'><span class="nc">.two-col</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">@include</span><span class="nd"> container</span><span class="p">;</span>
</span><span class='line'>  <span class="na">background-color</span><span class="o">:</span> <span class="mh">#cccccc</span><span class="p">;</span>
</span><span class='line'>  <span class="nn">#header</span><span class="o">,</span> <span class="nn">#footer</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">@include</span><span class="nd"> column</span><span class="p">(</span><span class="mi">8</span><span class="p">);</span> <span class="p">}</span>
</span><span class='line'>  <span class="nn">#sidebar</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">@include</span><span class="nd"> column</span><span class="p">(</span><span class="mi">3</span><span class="p">);</span> <span class="p">}</span>
</span><span class='line'>  <span class="nn">#content</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">@include</span><span class="nd"> column</span><span class="p">(</span><span class="mi">5</span><span class="o">,</span> <span class="n-Pseudo">true</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>HTML</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>
</pre></td><td class='code'><pre><code class='html'><span class='line'><span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">&#39;two-col&#39;</span><span class="nt">&gt;</span>
</span><span class='line'>  <span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">&#39;header&#39;</span><span class="nt">&gt;</span>
</span><span class='line'>    <span class="nt">&lt;h1&gt;</span>This is the Header<span class="nt">&lt;/h1&gt;</span>
</span><span class='line'>  <span class="nt">&lt;/div&gt;</span>
</span><span class='line'>  <span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">&#39;sidebar&#39;</span><span class="nt">&gt;</span>
</span><span class='line'>    <span class="nt">&lt;ul&gt;</span>
</span><span class='line'>      <span class="nt">&lt;li&gt;</span>
</span><span class='line'>        <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">&#39;#&#39;</span><span class="nt">&gt;</span>Nav #1<span class="nt">&lt;/a&gt;</span>
</span><span class='line'>      <span class="nt">&lt;/li&gt;</span>
</span><span class='line'>      <span class="nt">&lt;li&gt;</span>
</span><span class='line'>        <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">&#39;#&#39;</span><span class="nt">&gt;</span>Nav #2<span class="nt">&lt;/a&gt;</span>
</span><span class='line'>      <span class="nt">&lt;/li&gt;</span>
</span><span class='line'>      <span class="nt">&lt;li&gt;</span>
</span><span class='line'>        <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">&#39;#&#39;</span><span class="nt">&gt;</span>Nav #3<span class="nt">&lt;/a&gt;</span>
</span><span class='line'>      <span class="nt">&lt;/li&gt;</span>
</span><span class='line'>    <span class="nt">&lt;/ul&gt;</span>
</span><span class='line'>  <span class="nt">&lt;/div&gt;</span>
</span><span class='line'>  <span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">&#39;content&#39;</span><span class="nt">&gt;</span>
</span><span class='line'>    <span class="nt">&lt;p&gt;</span>
</span><span class='line'>      Lorem ipsum dolor sit amet,
</span><span class='line'>      consectetur adipisicing elit, sed do
</span><span class='line'>      eiusmod tempor incididunt ut labore et
</span><span class='line'>      dolore magna aliqua.
</span><span class='line'>    <span class="nt">&lt;/p&gt;</span>
</span><span class='line'>  <span class="nt">&lt;/div&gt;</span>
</span><span class='line'>  <span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">&#39;footer&#39;</span><span class="nt">&gt;</span>
</span><span class='line'>    This is the footer.
</span><span class='line'>  <span class="nt">&lt;/div&gt;</span>
</span><span class='line'><span class="nt">&lt;/div&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<ul>
<li>不費吹灰之力實作 CSS Sprites</li>
</ul>


<p>知道什麼是 CSS Sprites 技巧嗎？</p>

<p>CSS Sprites 是 Minimize HTTP Requests 中的一招，實作方式是將 CSS 中的小圖合併成較大的一張，再透過 CSS 定位的方式呼叫。比如說這個 CSS 中原先需要用到十張小圖，合併到只剩一張，就變得非常節省 HTTP requests（ 因為光是發出 requests 也相當耗時）。</p>

<p>問題是，負責 Scaling 的 server guy 很喜歡這招，但網頁設計師卻對這件事相當感冒。因為太麻煩了！</p>

<p>試想，url(&#8216;xxx.gif&#8217;) 到處貼多方便呀，誰喜歡合併成一張，然後仔細精算定位？後續維護又怎麼辦呢？</p>

<p>Well，Compass 幫你「自動解決了問題」！<a href="http://compass-style.org/help/tutorials/spriting/">http://compass-style.org/help/tutorials/spriting/</a></p>

<p>你可以把原先所有要用的小圖，通通丟進同一個資料夾。讓 Compass 幫你自動合併並算出位置！</p>

<figure class='code'><figcaption><span>SCSS</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='scss'><span class='line'><span class="k">@import</span> <span class="s2">&quot;icon/*.png&quot;</span><span class="p">;</span>
</span><span class='line'><span class="k">@include</span><span class="nd"> all-icon-sprites</span><span class="p">;</span>
</span></code></pre></td></tr></table></div></figure>


<p>Compass 會幫你自動合併並算出位置：</p>

<figure class='code'><figcaption><span>CSS</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='css'><span class='line'><span class="nc">.icon-sprite</span><span class="o">,</span>
</span><span class='line'><span class="nc">.icon-delete</span><span class="o">,</span>
</span><span class='line'><span class="nc">.icon-edit</span><span class="o">,</span>
</span><span class='line'><span class="nc">.icon-new</span><span class="o">,</span>
</span><span class='line'><span class="nc">.icon-save</span>   <span class="p">{</span> <span class="k">background</span><span class="o">:</span> <span class="sx">url(&#39;/images/icon-s34fe0604ab.png&#39;)</span> <span class="k">no-repeat</span><span class="p">;</span> <span class="p">}</span>
</span><span class='line'><span class="nc">.icon-delete</span> <span class="p">{</span> <span class="k">background-position</span><span class="o">:</span> <span class="m">0</span> <span class="m">0</span><span class="p">;</span> <span class="p">}</span>
</span><span class='line'><span class="nc">.icon-edit</span>   <span class="p">{</span> <span class="k">background-position</span><span class="o">:</span> <span class="m">0</span> <span class="m">-32px</span><span class="p">;</span> <span class="p">}</span>
</span><span class='line'><span class="nc">.icon-new</span>    <span class="p">{</span> <span class="k">background-position</span><span class="o">:</span> <span class="m">0</span> <span class="m">-64px</span><span class="p">;</span> <span class="p">}</span>
</span><span class='line'><span class="nc">.icon-save</span>   <span class="p">{</span> <span class="k">background-position</span><span class="o">:</span> <span class="m">0</span> <span class="m">-96px</span><span class="p">;</span> <span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>毛骨悚然的強大呀！</p>

<h3>如何整理 SCSS</h3>

<p>在實務演變中，Sass 的通用規則大致上是朝往這幾個方向拆：</p>

<ul>
<li>by feature</li>
<li>by special block</li>
<li>by default block ( header / footer / sidebar )</li>
<li>by (Rails) controller</li>
<li>by page specific</li>
</ul>


<h4>Examples</h4>

<p><code>#TODO</code></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Rake]]></title>
    <link href="http://erp-book.heroku.com/ch3-rake-intro/"/>
    <updated>2012-01-16T18:35:00+08:00</updated>
    <id>http://erp-book.heroku.com/ch3-rake-intro</id>
    <content type="html"><![CDATA[<p>Rake 的意思就是字面上直觀的 Ruby Make，Ruby 版 Make：用 Ruby 開發的 Build Tool。</p>

<p>你也許會問，Ruby 是直譯語言，不需要 compile，為何還需要 Build Tool。</p>

<p>與其說 Rake 是一套 Build Tool，還不如說是一套 task 管理工具。我們通常會使用 Ruby based 的 Rake 在 Rails 專案裡編寫 task。</p>

<h2>Rails 內的 rake tasks</h2>

<p><code>rake -T</code> 會秀出一大串這個 Rails project 可用的 rake tasks (包含 plugin 內建的 task 也會秀出來)。</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>
</pre></td><td class='code'><pre><code class=''><span class='line'>rake about              # List versions of all Rails frameworks and the environment
</span><span class='line'>rake assets:clean       # Remove compiled assets
</span><span class='line'>rake assets:precompile  # Compile all the assets named in config.assets.precompile
</span><span class='line'>rake db:create          # Create the database from config/database.yml for the current Rails.env (use db:create:all to create all dbs in the config)
</span><span class='line'>rake db:drop            # Drops the database for the current Rails.env (use db:drop:all to drop all databases)
</span><span class='line'>rake db:fixtures:load   # Load fixtures into the current environment's database.
</span><span class='line'>rake db:migrate         # Migrate the database (options: VERSION=x, VERBOSE=false).
</span><span class='line'>rake db:migrate:status  # Display status of migrations
</span><span class='line'>rake db:rollback        # Rolls the schema back to the previous version (specify steps w/ STEP=n).
</span><span class='line'>rake db:schema:dump     # Create a db/schema.rb file that can be portably used against any DB supported by AR
</span><span class='line'>rake db:schema:load     # Load a schema.rb file into the database
</span><span class='line'>rake db:seed            # Load the seed data from db/seeds.rb
</span><span class='line'>rake db:setup           # Create the database, load the schema, and initialize with the seed data (use db:reset to also drop the db first)
</span><span class='line'>rake db:structure:dump  # Dump the database structure to an SQL file
</span><span class='line'>rake db:version         # Retrieves the current schema version number
</span><span class='line'>rake doc:app            # Generate docs for the app -- also available doc:rails, doc:guides, doc:plugins (options: TEMPLATE=/rdoc-template.rb, TITLE="Custom Title")
</span><span class='line'>rake log:clear          # Truncates all *.log files in log/ to zero bytes
</span><span class='line'>rake middleware         # Prints out your Rack middleware stack
</span><span class='line'>rake notes              # Enumerate all annotations (use notes:optimize, :fixme, :todo for focus)
</span><span class='line'>rake notes:custom       # Enumerate a custom annotation, specify with ANNOTATION=CUSTOM
</span><span class='line'>rake rails:template     # Applies the template supplied by LOCATION=(/path/to/template) or URL
</span><span class='line'>rake rails:update       # Update configs and some other initially generated files (or use just update:configs, update:scripts, or update:application_controller)
</span><span class='line'>rake routes             # Print out all defined routes in match order, with names.
</span><span class='line'>rake secret             # Generate a cryptographically secure secret key (this is typically used to generate a secret for cookie sessions).
</span><span class='line'>rake stats              # Report code statistics (KLOCs, etc) from the application
</span><span class='line'>rake test               # Runs test:units, test:functionals, test:integration together (also available: test:benchmark, test:profile, test:plugins)
</span><span class='line'>rake test:recent        # Run tests for recenttest:prepare / Test recent changes
</span><span class='line'>rake test:single        # Run tests for singletest:prepare
</span><span class='line'>rake test:uncommitted   # Run tests for uncommittedtest:prepare / Test changes since last checkin (only Subversion and Git)
</span><span class='line'>rake time:zones:all     # Displays all time zones, also available: time:zones:us, time:zones:local -- filter with OFFSET parameter, e.g., OFFSET=-6
</span><span class='line'>rake tmp:clear          # Clear session, cache, and socket files from tmp/ (narrow w/ tmp:sessions:clear, tmp:cache:clear, tmp:sockets:clear)
</span><span class='line'>rake tmp:create         # Creates tmp directories for sessions, cache, sockets, and pids</span></code></pre></td></tr></table></div></figure>


<h2>批次動作</h2>

<p>在專案開發中，我們不免需要寫一些 script 批次執行動作。開發者可以用 runner 或者 Rake。</p>

<h3>runner</h3>

<p><code>#TODO</code></p>

<h3>Rake</h3>

<p>但多半我們會使用 Rake 而非 runner，這是因為 Rake 可以載入整個環境( Rails environment)，對 ORM 或者是 Rails 專案的內部做存取。而使用 Rake 撰寫的程式也較有組織性。</p>

<p>自己撰寫的 rake 的程式位置應該放在 <code>lib/tasks</code> 下。</p>

<h4>lib/tasks/dev.rake</h4>

<p>筆者在專案開發中尚未上線之際，還蠻常需要清掉整個資料庫重新跑一次的。這時候一次一次重打 <code>rake db:drop</code>、<code>rake db:create</code>、 <code>rake db:migrate</code>。實在是很煩的事。這樣的連鎖動作我通常會包裝成一支 rake。</p>

<ul>
<li>dev:build</li>
</ul>


<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="n">namespace</span> <span class="ss">:dev</span> <span class="k">do</span>
</span><span class='line'>  <span class="n">desc</span> <span class="s2">&quot;Rebuild system&quot;</span>
</span><span class='line'>  <span class="n">task</span> <span class="ss">:build</span> <span class="o">=&gt;</span> <span class="o">[</span><span class="s2">&quot;tmp:clear&quot;</span><span class="p">,</span> <span class="s2">&quot;log:clear&quot;</span><span class="p">,</span> <span class="s2">&quot;db:drop&quot;</span><span class="p">,</span> <span class="s2">&quot;db:create&quot;</span><span class="p">,</span> <span class="s2">&quot;db:migrate&quot;</span><span class="o">]</span>
</span><span class='line'>  <span class="n">task</span> <span class="ss">:rebuild</span> <span class="o">=&gt;</span> <span class="o">[</span> <span class="s2">&quot;dev:build&quot;</span><span class="p">,</span> <span class="s2">&quot;db:seed&quot;</span> <span class="o">]</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<ul>
<li>dev:fake</li>
</ul>


<p><code>#TODO</code></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Bundler]]></title>
    <link href="http://erp-book.heroku.com/ch3-bundler-intro/"/>
    <updated>2012-01-16T18:06:00+08:00</updated>
    <id>http://erp-book.heroku.com/ch3-bundler-intro</id>
    <content type="html"><![CDATA[<p>Bundler 原先是 Rails3 架構師 Yehuda Katz 開發出來解決 Rails 中 package dependency 的工具（ 在開發 Merb 這個 framework 時，Katz 就開始嘗試實作了）。</p>

<p>package dependency 一直是相當麻煩的問題。解不開，就無法將程式 boot 起來。</p>

<p>原先大家也只有拿 Bundler 搭配 Rails 使用。</p>

<p>而後來 Bundler 也逐漸變成一般 Ruby 程式中預設的套件 dependency 管理程式。</p>

<p>Bundler 中的 Gemfile 設計，不只能讓開發者能夠輕易的解決套件相依問題，並且可以直接引入「開發中」套件，解決 3rd gem 版本更新過慢，卡住自己開發進度的問題。</p>

<h4>Gemfile 範例</h4>

<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">gem</span> <span class="s1">&#39;nokogiri&#39;</span><span class="p">,</span> <span class="ss">:git</span> <span class="o">=&gt;</span> <span class="s1">&#39;git://github.com/tenderlove/nokogiri.git&#39;</span>
</span><span class='line'><span class="n">gem</span> <span class="s1">&#39;nokogiri&#39;</span><span class="p">,</span> <span class="ss">:git</span> <span class="o">=&gt;</span> <span class="s1">&#39;git://github.com/tenderlove/nokogiri.git&#39;</span><span class="p">,</span><span class="ss">:branch</span> <span class="o">=&gt;</span> <span class="s1">&#39;stable-2&#39;</span>
</span><span class='line'><span class="n">gem</span> <span class="s1">&#39;nokogiri&#39;</span><span class="p">,</span> <span class="ss">:git</span> <span class="o">=&gt;</span> <span class="s1">&#39;git://github.com/tenderlove/nokogiri.git&#39;</span><span class="p">,</span><span class="ss">:tag</span> <span class="o">=&gt;</span> <span class="s1">&#39;tag-2&#39;</span>
</span><span class='line'><span class="n">gem</span> <span class="s1">&#39;nokogiri&#39;</span><span class="p">,</span> <span class="ss">:git</span> <span class="o">=&gt;</span> <span class="s1">&#39;git://github.com/tenderlove/nokogiri.git&#39;</span><span class="p">,</span><span class="ss">:ref</span> <span class="o">=&gt;</span> <span class="s1">&#39;23456&#39;</span>
</span></code></pre></td></tr></table></div></figure>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[View: Helper]]></title>
    <link href="http://erp-book.heroku.com/ch3-helper-intro/"/>
    <updated>2012-01-16T11:15:00+08:00</updated>
    <id>http://erp-book.heroku.com/ch3-helper-intro</id>
    <content type="html"><![CDATA[<p>Helper 與 Partial 的運用是幾個初學者使得不稱手的主題之一。原因有幾：</p>

<ol>
<li>不知道有 Rails 提供了許多好用的 Helper 可以用</li>
<li>不知道 Helper 與 Partial 他們各自的使用時機。</li>
<li>擔心使用 Helper 會造成效能下降。</li>
<li>以不好的方式使用 Helper 反而使維護性降低。</li>
</ol>


<h2>Helper 與 Partial 的不同</h2>

<h3>Partial</h3>

<p>Partial 指的是局部樣板。而 Helper 指的卻是在樣板中的一些輔助方法（Ruby Method）。這兩種都是整理又臭又長的 HTML 版面時的好工具。</p>

<p>一般而言，我們會使用 Partial 去處理大段且重複的程式碼。或者是經常使用到的局部程式碼。</p>

<ul>
<li>大段程式碼：（如 new / edit 會複用到的表單）</li>
</ul>


<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='ruby'><span class='line'><span class="o">&lt;</span><span class="sx">%= form_for @post do |f| %&gt;</span>
</span><span class='line'><span class="sx">    &lt;%=</span> <span class="n">render</span> <span class="ss">:partial</span> <span class="o">=&gt;</span> <span class="s2">&quot;form&quot;</span><span class="p">,</span> <span class="ss">:locals</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="ss">:f</span> <span class="o">=&gt;</span> <span class="n">f</span><span class="p">}</span> <span class="sx">%&gt;</span>
</span><span class='line'><span class="sx">&lt;% end %&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<ul>
<li>經常使用到的局部程式碼：（如 sidebar 內的區塊）</li>
</ul>


<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='html'><span class='line'><span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">&quot;sidebar&quot;</span><span class="nt">&gt;</span>
</span><span class='line'>  <span class="err">&lt;</span>%= render :partial =&gt; &quot;recent_posts&quot; %&gt;
</span><span class='line'>  <span class="err">&lt;</span>%= render :partial =&gt; &quot;recent_comments&quot; %&gt;
</span><span class='line'><span class="nt">&lt;/div&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>…etc.</p>

<h4>Partial 的優點</h4>

<ul>
<li>Don&#8217;t repeat yourself（DRY）程式碼不重複</li>
<li>程式修改會比較清楚</li>
<li>Partial 樣板比較容易被重複使用</li>
</ul>


<h3>Helper</h3>

<p>Partial 的定位多半是被用來處理「大片 HTML 」的工具，而 Helper 卻是比較屬於需要邏輯性輸出 HTML 時用的整理工具。</p>

<p>一般我們學 Rails 常見的</p>

<ul>
<li>stylsheet_link_tag</li>
<li>link_to</li>
<li>image_tag</li>
<li>form_for 中的 f.text_field…etc</li>
</ul>


<p>都屬於 Helper 的範疇。</p>

<h2>在專案中使用 Rails 內建 Helper 開發的原因</h2>

<h3>其一：為了省力</h3>

<p>Rails 最令其他 Ruby Web Framework 羨慕的，就是內建的很多方便 Helper。</p>

<p>舉幾個其實很方便，但大家其實不太知道它們存在的 Helper 好了。</p>

<ul>
<li><a href="http://apidock.com/rails/ActionView/Helpers/TextHelper/simple_format">simple_format</a> : 可以處理使用者的內容中 \r\n 自動轉 br 和 p 的工作</li>
<li><a href="http://apidock.com/rails/ActionView/Helpers/TextHelper/auto_link">auto_link</a> :可以處理使用者的內容中，若有連結，就自動 link 的工作。</li>
<li><a href="http://apidock.com/rails/ActionView/Helpers/TextHelper/truncate">truncate</a>: 使用者輸入的內容，若過長，可以指定多少字後就自動砍掉，並加入 &#8220;….&#8221;</li>
<li><a href="http://apidock.com/rails/ERB/Util/html_escape/class">html_escape</a>: 使用者輸入的內容，若有 html tag，為了怕使用者輸入惡意 tag 進行 hack。自動過濾。（以前要手動加 h 過濾，現在 Rails 預設 escape，不想被 escape 才手動指定 raw 閃掉 escape）</li>
</ul>


<p>這些東西若自己寫 parser 處理，不知道要花費多少精力，還不一定濾的徹底。卻都是 Rails 預設內建 Helper。</p>

<h3>其二：為了跟 Rails 內建的其他更棒的基礎建設整合</h3>

<ul>
<li><code>stylesheet_link_tag</code> 與 <code>image_tag</code></li>
</ul>


<p>有些人也覺得，這東西還要用 Helper 嗎？直接貼 HTML 不是也一樣會動嗎？有什麼差別。差別可大了。</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="o">&lt;</span><span class="sx">%= stylesheet_link_tag &quot;abc&quot;, &quot;def&quot;, :cache =</span><span class="o">&gt;</span> <span class="kp">true</span> <span class="o">%&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>這一個 Helper，可以在 production 環境時，自動幫你將兩支 CSS 自動壓縮成一支 all.css 。直接實現了 Yahoo <a href="http://developer.yahoo.com/performance/rules.html">Best Practices for Speeding Up Your Web Site</a>中，minify HTTP reqesut 的建議。而在 Rails 3.1 之後，甚至還會自動幫你 trim 與 gzip。</p>

<p>完全不需要去在 deploy process 中 hook 另外的 compressor 就可以達到。</p>

<p>至於 image_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='ruby'><span class='line'> <span class="o">&lt;%=</span> <span class="n">image_tag</span><span class="p">(</span><span class="s2">&quot;xxx.jpg&quot;</span><span class="p">)</span> <span class="o">%&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>Rails 可以幫你的 asset 自動在後面上 query string，如：xxx.jpg?12345</p>

<p>這樣在網站若有整合 CDN 架構時，可以自動處理 invalid cache 的問題。</p>

<p>而 Rails 也有選項可以實作 asset parallel download 的機制，一旦打開，站上的 asset 也會配合你的設定，亂數吐不同來源的 asset host 實做平行下載。</p>

<p>輕鬆就可以把網站 Scale 上去。</p>

<ul>
<li>form_for</li>
</ul>


<p>這也是 Rails 相當為人稱讚的一個利器。Rails 的表單欄位是綁 model (db 欄位的)，除了開發方便（ <code>Post.new(params[:post])</code> 直接收參數做 mapping）之外，也內建了防 CSRF (<code>protect_from_forgery</code>)的防禦措施。</p>

<p>開發者可以在一眨眼的工夫，借助框架的力量，實作出業界（performace /security）最佳實踐。而卻不需要先修煉成架構大師。</p>

<h2>為什麼在專案中我們要撰寫「自用」 Helper？</h2>

<h3>其一：在 View 裡面實作 LOGIC 是不好的。</h3>

<h4>造成效能低落</h4>

<p>在 View 裡面實作 LOGIC 的影響，最主要導致的後遺症就是：「效能低落」（此部份詳細來龍去賣在其他章節會進行解釋 <code>#TODO</code>）。簡單版本的原因是 ERB 是用 eval 實作 執行 Ruby code 的，在 View 裡面穿插大量的 LOGIC 會造成 render 的效率低落。</p>

<h4>造成程式碼混亂難讀</h4>

<p>View 在 MVC 的模式中，原本就是只為了做 UI 輸出的功用的。如果有程式邏輯，或者是資料查詢，應該挪到 Controller 或 Model 去做。</p>

<p>這通常在 PHP 轉檯轉過來的 developer 身上，比較能找到這樣的問題。原因是在 PHP 程式寫作開發中，這是很天經地義的作法。但眾所週知的，PHP 的 project 也特別容易藏汙納垢。</p>

<p>如果你手上的 Project，View 打開來都 7-8 頁以上，別懷疑，肯定都是 LOGIC in View 造成的。</p>

<p>而根據過往經驗：有冗長 View 的 project，往往比有冗長 controller 的 project 還要難纏。</p>

<p>一個 View 裡面若有著很多 if / else / elsif , query_some_data。或者有著 if / else / elsif , change some css class ？</p>

<p><strong><em>人的大腦不是 Ruby Interpreter，很難腦內想像這麼複雜的 code 會長什麼樣子，沒多久就會當機的&#8230;.</em></strong></p>

<h3>其二：讓 View 更加直觀好維護。</h3>

<p>在沒有注釋的情況下，你看得懂這段 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='html'><span class='line'>  <span class="err">&lt;</span>% if current_user <span class="err">&amp;&amp;</span> (post.user == current_user || current.user.is_forum_admin? || current.user_is_admin? ) %&gt;
</span><span class='line'>  <span class="err">&lt;</span>%= link_to(&quot;Edit&quot;, edit_post_path(post) ) %&gt;
</span><span class='line'>  <span class="err">&lt;</span>% end %&gt;
</span></code></pre></td></tr></table></div></figure>


<p>這行範例程式碼，我想邏輯還算簡單，應該不難讓人讀懂。不過若再經過兩三輪的維護，應該就會變得超難維護了。</p>

<h4>正確的整理法</h4>

<p>正確的整理法其實應該要把邏輯拆出來，放在 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>
</pre></td><td class='code'><pre><code class='html'><span class='line'>  <span class="err">&lt;</span>% if can_edit?(post) %&gt;
</span><span class='line'>     <span class="err">&lt;</span>%= link_to(&quot;Edit&quot;, edit_post_path(post) ) %&gt;
</span><span class='line'>  <span class="err">&lt;</span>% end %&gt;
</span></code></pre></td></tr></table></div></figure>


<p>這樣就能讓後續維護者知道，這一段程式碼就是在表示：<strong>如果當前使用者有權限編輯，就應該顯示編輯頁面的連結。</strong></p>

<h3>其三：給 Code 取名字，容易尋找並複用成果。</h3>

<p>前段程式碼寫得不算太優，因為到目前為止它只表明了：<strong>如果當前使用者有權限編輯，就應該顯示編輯頁面的連結。</strong>。並沒達到正確闡述自己存在的的意義。</p>

<p>而這一塊原始碼的意圖是：若有編輯權限，就應該顯示一塊 toolbar。</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="err">&lt;</span>%= render_tool_bar %&gt;
</span></code></pre></td></tr></table></div></figure>


<p>包裝成 toolbar 後，這一塊程式碼就變得有名字了。下次開發者在某個頁面要寫到類似的功能時，就只要呼叫 render_tool_bar 就可以了。</p>

<p>而最重要的，是以後再維護這一塊程式碼時，完全不必再猜測程式意圖，也很容易找當初亂丟在哪裡了。</p>

<h3>其四： 不會被以前寫的 Code 害死。</h3>

<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="err">&lt;</span>% if current_user <span class="err">&amp;&amp;</span> (post.user == current_user || current.user.is_forum_admin? || current.user_is_admin? ) %&gt;
</span><span class='line'>  <span class="err">&lt;</span>%= link_to(&quot;Edit&quot;, edit_post_path(post) ) %&gt;
</span><span class='line'>  <span class="err">&lt;</span>% end %&gt;
</span></code></pre></td></tr></table></div></figure>


<p>這種 code 在網站初期建設需要高速開發時沒有什麼不好。其實也還算讓人看得懂&#8230;</p>

<p>但我個人的建議是：一旦接近完工狀態，就要儘快抽空 refactor 掉它。</p>

<p>沒有組織結構的 code 一旦越來越多，專案就會越來越難維護。累積到一個程度，就會轉變成 unmaintainable 的狀態，到時候就算呼叫神仙也難回天。</p>

<p>像是我個人參與的專案，在開發中都有一個慣例：一旦專案開發快到尾聲，大家就會開始整理程式碼，把重複的部分重新翻修包成有名字的 Helper。</p>

<p>這樣作有什麼好處？</p>

<ol>
<li><p>網站以後要進行結構重整時，只要調整已定義好的 Helper 內部架構就好了。如果還是東一個西一個到處亂放，同樣的東西重複貼 10 個地方，將來想要調整就要改 10 遍。相信我，你不喜歡在「改版時期」改 code 改 10 遍。就算是可以使用 git grep 抓漏還是會失手…</p></li>
<li><p>無痛升級 Rails 版本。有很多人好奇筆者手上的每個專案，為何可以一路從 2.3.x 一路升升升到 3.1.x 去。卻還算輕鬆愉快？</p></li>
</ol>


<p>印象當中這中間的改版：不是有令人抓狂的 html_escape API 行為改變問題？asset_pipeline 架構調整？這些都是很令人抓狂的過程。不少開發者都在升級過程中放棄了。為什麼我們辦得到。</p>

<p>我想，當中最關鍵的原因，不在於「有沒有寫 test」，而是在於「有沒有擁有定期整理 code 的習慣」。</p>

<p>若定時整理 helper / partial / controller / model 的程式碼。只要是發現重複 code，馬上進行封裝整理。將來就算有東西爆炸，多半也只要調整一下 helper 或者是 model 的輸出，馬上就解決問題了。</p>

<p>所以就算是遇到 Rails 升級的問題，開發者也可以專注把精力集中在處理幾個 deprecated method 或 incompatible API 的調整。就算 view 爆掉了，修一個地方，十個地方隨之生效。自然寫意無比。</p>

<h3>其五：容易複用並開創專案打下來的 best practices</h3>

<p>在進行專案過程中，也會漸漸的養出自己的一套 HTML 架構 與 CSS (SCSS)。很多元件在不同專案中都是共通的，比如說自己用來 bootstrap （非 twitter）專案的 view 和 helper。</p>

<p>navgation_bar, user_bar, breadcrumb, menu list, table, button,gravator 這些都是專案必備。</p>

<p>這是台灣知名的 Rails 網站設計公司 <a href="http://handlino.com">Handlino</a> 設計的一套 helper
<a href="https://github.com/handlino/handicraft_helper">https://github.com/handlino/handicraft_helper</a></p>

<p>可以很方便就寫出 menu, table, body class with browser type, breadcrumb…etc.</p>

<p>而筆者在自己作網站還會多裝上幾套自己養出來的標準 Helper</p>

<ul>
<li>SEO 最佳化實踐</li>
<li>Social Media Share-Friendly</li>
<li>Content Site 常用功能最佳實踐</li>
</ul>


<p>專案越寫越多，後期越來越輕鬆，但不管之後再寫什麼新網站，依舊是幾乎都預設含有以前維護舊網站時打下的 Best Practices.</p>

<p>這就是越資深的 Rails Developer 開發新專案能夠越來越快速的原因之一。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[重複發明輪子]]></title>
    <link href="http://erp-book.heroku.com/ch4-reinvent-wheels/"/>
    <updated>2012-01-12T19:38:00+08:00</updated>
    <id>http://erp-book.heroku.com/ch4-reinvent-wheels</id>
    <content type="html"><![CDATA[<p><code>#TODO : ref from [Helper Antipatterns]</code>
* auto_link
* content_tag
* truncate
* simple_format
* cycle
* localized
* escaple_javascripts</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Query in Loop]]></title>
    <link href="http://erp-book.heroku.com/ch4-query-in-loop/"/>
    <updated>2012-01-12T19:38:00+08:00</updated>
    <id>http://erp-book.heroku.com/ch4-query-in-loop</id>
    <content type="html"><![CDATA[<p>ABUSE: SQL Query in LOOP</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="err">&lt;</span>% Post.limit(10).each do |post| %&gt;
</span><span class='line'><span class="err">&lt;</span>%= post.categories.map{ |c| &quot;<span class="nt">&lt;span&gt;</span>#{c.name}<span class="nt">&lt;/span&gt;</span>&quot;} =%&gt;
</span><span class='line'><span class="err">&lt;</span>% end %&gt;
</span></code></pre></td></tr></table></div></figure>


<ul>
<li>Some operation is very EXPENSIVE</li>
</ul>


<h3>Solution</h3>

<ul>
<li>cache expensive operation

<ul>
<li>counter_cache, memorize, cache</li>
</ul>
</li>
<li>includes</li>
<li>find_in_batch</li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[不使用 RESTful]]></title>
    <link href="http://erp-book.heroku.com/ch4-not-use-restful/"/>
    <updated>2012-01-12T19:38:00+08:00</updated>
    <id>http://erp-book.heroku.com/ch4-not-use-restful</id>
    <content type="html"><![CDATA[<h2>Abuse : 厭惡使用 RESTful</h2>

<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="c1"># match &#39;:controller(/:action(/:id(.:format)))&#39;</span>
</span></code></pre></td></tr></table></div></figure>


<ul>
<li>程式碼失去組織性</li>
<li>嚴重的 security issue</li>
</ul>


<h3>免跨站偽造請求 Cross-site request forgery</h3>

<ul>
<li>所有讀取、查詢性質操作，都應該用GET</li>
<li>修改或刪除到資料的，則要用POST、PUT或DELETE</li>
<li>所有的POST請求，都必須加上一個安全驗證碼</li>
</ul>


<h3>內建機制</h3>

<p><code>app/controllers/application_controller.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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">class</span> <span class="nc">ApplicationController</span> <span class="o">&lt;</span> <span class="no">ActionController</span><span class="o">::</span><span class="no">Base</span>
</span><span class='line'>  <span class="n">protect_from_forgery</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p><code>app/views/layout/application.html.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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="o">&lt;%=</span> <span class="n">csrf_meta_tag</span> <span class="o">%&gt;</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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'> <span class="c1"># match &#39;:controller(/:action(/:id(.:format)))&#39;</span>
</span></code></pre></td></tr></table></div></figure>


<ul>
<li>硬幹就失去天然屏障了，任何 action 都可以 get</li>
</ul>


<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;img</span> <span class="na">src=</span><span class="s">&quot;/posts/delete_all&quot;</span><span class="nt">&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p></p>

<h3>安全性：透過對資源操作的限制，可以阻擋不安全的操作。</h3>

<p>需要安全性的操作行為，需要透過表單的 POST，才能執行。</p>

<p>然而開發者可能只會記得在 .htaccess 加入對 /question/1/delete 的 URL mapping，卻忘記這個動作需要關閉 GET，否則 hacker 就可以透過製造惡意的連結，誘騙管理者誤開連結，砍除大量的資料。</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='html'><span class='line'><span class="nt">&lt;form</span> <span class="na">accept-charset=</span><span class="s">&quot;UTF-8&quot;</span> <span class="na">action=</span><span class="s">&quot;/post/delete_all&quot;</span> <span class="na">method=</span><span class="s">&quot;post&quot;</span> <span class="na">novalidate=</span><span class="s">&quot;novalidate&quot;</span><span class="nt">&gt;</span>
</span><span class='line'><span class="nt">&lt;/form&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>而在 Rails 中，因為採用 REST，對於一個資源送 GET / POST / PUT / DELETE 都會對應到不同的 action，所以不會有這樣的情形發生。</p>

<h4>修改文章的表單</h4>

<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="o">&lt;</span><span class="n">h1</span><span class="o">&gt;</span><span class="no">Editing</span> <span class="n">post</span><span class="o">&lt;</span><span class="sr">/h1&gt;</span>
</span><span class='line'><span class="sr">&lt;%= form_for @post , :url =&gt; post_path(@post) , :html =&gt; {:method =&gt; :put} do |f| %&gt;</span>
</span><span class='line'><span class="sr">    &lt;%= f.error_messages %&gt;</span>
</span><span class='line'><span class="sr">    &lt;div&gt;&lt;%= f.text_field :subject %&gt;</span>
</span><span class='line'><span class="sr">    &lt;div&gt;&lt;%= f.text_area :content %&gt;&lt;/</span><span class="n">div</span><span class="o">&gt;</span>
</span><span class='line'>    <span class="o">&lt;</span><span class="sx">%= f.submit &quot;Submit&quot;, :disable_with =</span><span class="o">&gt;</span> <span class="s1">&#39;Submiting...&#39;</span> <span class="sx">%&gt;</span>
</span><span class='line'><span class="sx">&lt;% end -%&gt;</span>
</span><span class='line'>
</span><span class='line'><span class="o">&lt;</span><span class="sx">%= link_to &#39;Show&#39;, post_path(post) %&gt; |</span>
</span><span class='line'><span class="sx">&lt;%=</span> <span class="n">link_to</span> <span class="s1">&#39;Back&#39;</span><span class="p">,</span> <span class="n">posts_path</span> <span class="o">%&gt;</span>
</span></code></pre></td></tr></table></div></figure>



]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[濫用 ORM 無限串接]]></title>
    <link href="http://erp-book.heroku.com/ch4-abusing-orm-scope/"/>
    <updated>2012-01-12T19:38:00+08:00</updated>
    <id>http://erp-book.heroku.com/ch4-abusing-orm-scope</id>
    <content type="html"><![CDATA[<h2>ABUSE: ORM 無限串接</h2>

<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">post</span><span class="o">.</span><span class="n">aa</span><span class="o">.</span><span class="n">bb</span><span class="o">.</span><span class="n">cc</span><span class="o">.</span><span class="n">dd</span><span class="o">.</span><span class="n">ee</span><span class="o">.</span><span class="n">ff</span><span class="o">.</span><span class="n">gg</span><span class="o">.</span><span class="n">hh</span><span class="o">.</span><span class="n">ii</span><span class="o">.</span><span class="n">jj</span><span class="o">.</span><span class="n">kk</span><span class="o">.</span><span class="n">ll</span><span class="o">.</span><span class="n">mm</span><span class="o">.</span><span class="n">nn</span><span class="o">.</span><span class="n">oo</span><span class="o">.</span><span class="n">pp</span>
</span></code></pre></td></tr></table></div></figure>


<h3>後果</h3>

<ul>
<li>Create A LOT OF objects</li>
<li>Complex JOIN &amp; SLOW query</li>
</ul>


<h3>Solution</h3>

<ul>
<li>使用 scope 整理 code</li>
<li>使用 method 整理 code</li>
<li>使用 query_reviewer 檢查 query</li>
<li>減少 join 的機會，使用多個 select 造出 query</li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Helper Antipatterns]]></title>
    <link href="http://erp-book.heroku.com/ch4-helper-antipatterns/"/>
    <updated>2012-01-12T19:37:00+08:00</updated>
    <id>http://erp-book.heroku.com/ch4-helper-antipatterns</id>
    <content type="html"><![CDATA[<h2>Helper AntiPatterns</h2>

<p>Helper （輔助方法）的存在目的是用來輔助整理 View 中內嵌的複雜 Ruby 程式碼。設計得當的 Helper 可以加速專案的開發，以及增進程式的可讀性。然而，設計不好的 Helper 卻可能造成嚴重的反效果。</p>

<p>以下列舉常見的幾種糟糕的 Helper 設計模式：</p>

<h3>1. 矯往過正：用 Helper 作 partial 該做的事</h3>

<p>開發者以為 partial 效率是低下的，刻意不使用 partial，而改用 Helper 完成所有的動作：將需要重複使用的 HTML 通通寫成了 Ruby code，串接成 HTML：</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="k">def</span> <span class="nf">show_index_block</span><span class="p">(</span><span class="n">block_name</span><span class="p">,</span> <span class="n">post</span><span class="p">,</span> <span class="n">is_show_game</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">block_title</span> <span class="o">=</span> <span class="n">content_tag</span><span class="p">(</span><span class="ss">:h3</span><span class="p">,</span> <span class="n">block_name</span><span class="p">)</span>
</span><span class='line'>  <span class="n">section_header</span> <span class="o">=</span> <span class="n">content_tag</span><span class="p">(</span><span class="ss">:div</span><span class="p">,</span> <span class="n">block_title</span><span class="p">,</span> <span class="ss">:class</span> <span class="o">=&gt;</span> <span class="s2">&quot;section-header&quot;</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">game_name</span> <span class="o">=</span> <span class="n">is_show_game</span> <span class="p">?</span> <span class="s2">&quot;【 </span><span class="si">#{</span><span class="n">post</span><span class="o">.</span><span class="n">games</span><span class="o">.</span><span class="n">first</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2"> 】&quot;</span> <span class="p">:</span> <span class="s2">&quot;&quot;</span>
</span><span class='line'>  <span class="n">title</span> <span class="o">=</span> <span class="n">content_tag</span><span class="p">(</span><span class="ss">:h4</span><span class="p">,</span> <span class="n">link_to</span><span class="p">(</span><span class="s2">&quot;</span><span class="si">#{</span><span class="n">game_name</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="n">post</span><span class="o">.</span><span class="n">title</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">,</span> <span class="n">post_path</span><span class="p">(</span><span class="n">post</span><span class="p">)))</span>
</span><span class='line'>  <span class="n">image</span> <span class="o">=</span> <span class="n">content_tag</span><span class="p">(</span><span class="ss">:div</span><span class="p">,</span> <span class="n">render_post_image</span><span class="p">(</span><span class="n">post</span><span class="p">),</span> <span class="ss">:class</span> <span class="o">=&gt;</span> <span class="s2">&quot;thumbnail&quot;</span><span class="p">)</span>
</span><span class='line'>  <span class="n">content</span> <span class="o">=</span> <span class="n">content_tag</span><span class="p">(</span><span class="ss">:p</span><span class="p">,</span> <span class="n">truncate</span><span class="p">(</span> <span class="n">post</span><span class="o">.</span><span class="n">content</span><span class="p">,</span> <span class="ss">:length</span> <span class="o">=&gt;</span> <span class="mi">100</span><span class="p">))</span>
</span><span class='line'>  <span class="n">section_content</span> <span class="o">=</span> <span class="n">content_tag</span><span class="p">(</span><span class="ss">:div</span><span class="p">,</span> <span class="s2">&quot;</span><span class="si">#{</span><span class="n">title</span><span class="si">}#{</span><span class="n">image</span><span class="si">}#{</span><span class="n">content</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">,</span> <span class="ss">:class</span> <span class="o">=&gt;</span> <span class="s2">&quot;section-content&quot;</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">section_footer</span> <span class="o">=</span> <span class="n">content_tag</span><span class="p">(</span><span class="ss">:div</span><span class="p">,</span> <span class="n">link_to</span><span class="p">(</span><span class="s2">&quot;閱讀全文&quot;</span><span class="p">,</span> <span class="n">post_path</span><span class="p">(</span><span class="n">post</span><span class="p">)),</span> <span class="ss">:class</span> <span class="o">=&gt;</span> <span class="s2">&quot;section-footer&quot;</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">return</span> <span class="n">content_tag</span><span class="p">(</span><span class="ss">:div</span><span class="p">,</span> <span class="s2">&quot;</span><span class="si">#{</span><span class="n">section_header</span><span class="si">}#{</span><span class="n">section_content</span><span class="si">}#{</span><span class="n">section_footer</span><span class="si">}</span><span class="s2">&quot;</span> <span class="p">,</span> <span class="ss">:class</span> <span class="o">=&gt;</span> <span class="s2">&quot;article-teaser&quot;</span><span class="p">)</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>Helper 的作用只是協助整理 HTML 中的邏輯程式碼。若有大片 HTML 需要重複使用，應當需要利用 partial 機制進行 HTML 的重複利用。這樣的寫法，非但效率低下（可以用 HTML 產生，卻使用 Ruby 呼叫 Tag Helper，且製造大量 Ruby Object），且嚴重降低程式的可讀性，其他維護者將難以對這樣的 DOM 進行後續的維護翻修。</p>

<h3>2. 容易混淆：在 Helper 裡面穿插 HTML tag</h3>

<p>這也是另外一個矯枉過正的例子，不過方向剛好相反：「因為覺得使用 Ruby code 產生 HTML tag 可能浪費效能，而直接插入 HTML 在 Helper 裡面與 Ruby Code 混雜。」也造成了專案維護上的困難：因為 Ruby 中的字串是使用雙引號<code>"</code>，而 HTML 也是使用雙引號<code>"</code>，，所以就必須特別加入 <code>\"</code> 跳脫，否則就可能造成 syntax error。</p>

<h4>錯誤</h4>

<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="k">def</span> <span class="nf">post_tags_tag</span><span class="p">(</span><span class="n">post</span><span class="p">,</span> <span class="n">opts</span> <span class="o">=</span> <span class="p">{})</span>
</span><span class='line'>  <span class="c1"># ....</span>
</span><span class='line'>   <span class="n">raw</span> <span class="n">tags</span><span class="o">.</span><span class="n">collect</span> <span class="p">{</span> <span class="o">|</span><span class="n">tag</span><span class="o">|</span>  <span class="s2">&quot;&lt;a href=</span><span class="se">\&quot;</span><span class="si">#{</span><span class="n">posts_path</span><span class="p">(</span><span class="ss">:tag</span> <span class="o">=&gt;</span> <span class="n">tag</span><span class="p">)</span><span class="si">}</span><span class="se">\&quot;</span><span class="s2"> class=</span><span class="se">\&quot;</span><span class="s2">tag</span><span class="se">\&quot;</span><span class="s2">&gt;</span><span class="si">#{</span><span class="n">tag</span><span class="si">}</span><span class="s2">&lt;/a&gt;&quot;</span> <span class="p">}</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">&quot;, &quot;</span><span class="p">)</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>大量的 <code>"</code> 混雜在程式碼裡面，嚴重造成程式的可閱讀性，而且發生 syntax error 時難以 debug。</p>

<h4>錯誤</h4>

<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="k">def</span> <span class="nf">post_tags_tag</span><span class="p">(</span><span class="n">post</span><span class="p">,</span> <span class="n">opts</span> <span class="o">=</span> <span class="p">{})</span>
</span><span class='line'>  <span class="c1"># ....</span>
</span><span class='line'>  <span class="n">raw</span> <span class="n">tags</span><span class="o">.</span><span class="n">collect</span> <span class="p">{</span> <span class="o">|</span><span class="n">tag</span><span class="o">|</span> <span class="s2">&quot;&lt;a href=&#39;</span><span class="si">#{</span><span class="n">posts_path</span><span class="p">(</span><span class="ss">:tag</span> <span class="o">=&gt;</span> <span class="n">tag</span><span class="p">)</span><span class="si">}</span><span class="s2">&#39; class=&#39;tag&#39;&gt;</span><span class="si">#{</span><span class="n">tag</span><span class="si">}</span><span class="s2">&lt;/a&gt;&quot;</span> <span class="p">}</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">&quot;, &quot;</span><span class="p">)</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>即便換成 <code>'</code> 單引號，狀況並沒有好上多少。</p>

<h4>正確</h4>

<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="k">def</span> <span class="nf">post_tags_tag</span><span class="p">(</span><span class="n">post</span><span class="p">,</span> <span class="n">opts</span> <span class="o">=</span> <span class="p">{})</span>
</span><span class='line'><span class="c1"># ...</span>
</span><span class='line'>  <span class="n">raw</span> <span class="n">tags</span><span class="o">.</span><span class="n">collect</span> <span class="p">{</span> <span class="o">|</span><span class="n">tag</span><span class="o">|</span> <span class="n">link_to</span><span class="p">(</span><span class="n">tag</span><span class="p">,</span><span class="n">posts_path</span><span class="p">(</span><span class="ss">:tag</span> <span class="o">=&gt;</span> <span class="n">tag</span><span class="p">))</span> <span class="p">}</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">&quot;, &quot;</span><span class="p">)</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>正確的作法應該是妥善使用 Rails 內建的 Helper，使 Helper 裡面維持著都是 Ruby code 的狀態，並且具有高可讀性。</p>

<h3>3. 強耦合：把 CSS 應該做的事綁在 Ruby Helper 上。</h3>

<h4>錯誤</h4>

<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">red_alert</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
</span><span class='line'>  <span class="k">return</span> <span class="n">content_tag</span><span class="p">(</span><span class="ss">:span</span><span class="p">,</span><span class="n">message</span><span class="p">,</span> <span class="ss">:style</span> <span class="o">=&gt;</span> <span class="s2">&quot;font-color: red;&quot;</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">green_notice</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
</span><span class='line'>  <span class="k">return</span> <span class="n">content_tag</span><span class="p">(</span><span class="ss">:span</span><span class="p">,</span><span class="n">message</span><span class="p">,</span> <span class="ss">:style</span> <span class="o">=&gt;</span> <span class="s2">&quot;font-color: green;&quot;</span><span class="p">)</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>開發者不熟悉 unobtrusive 的設計手法，直接就把 design 就綁上了 Ruby Helper。將來設計上若需要變更時，難以修改或擴充。</p>

<h4>正確</h4>

<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="k">def</span> <span class="nf">stickies</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="n">message_type</span><span class="p">)</span>
</span><span class='line'>  <span class="n">content_tag</span><span class="p">(</span><span class="ss">:span</span><span class="p">,</span><span class="n">message</span><span class="p">,</span> <span class="ss">:class</span> <span class="o">=&gt;</span> <span class="n">message_type</span><span class="o">.</span><span class="n">to_sym</span><span class="p">)</span>
</span><span class='line'><span class="k">end</span>
</span><span class='line'>
</span><span class='line'><span class="o">&lt;</span><span class="n">span</span> <span class="n">class</span><span class="o">=</span><span class="s2">&quot;alert&quot;</span><span class="o">&gt;</span> <span class="no">Please</span> <span class="no">Login</span><span class="o">!!</span> <span class="o">&lt;</span><span class="sr">/span&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>樣式應該由 CSS 決定，使用 CSS class 控制，而非強行綁在 Helper 上。</p>

<h3>4. 重複發明輪子</h3>

<p>Rails 已內建許多實用 Helper，開發者卻以較糟的方式重造輪子。在此舉幾個比較經典的案例：</p>

<ul>
<li><a href="http://apidock.com/rails/ActionView/Helpers/TextHelper/cycle">cycle</a></li>
</ul>


<p>如何設計 table 的雙色列效果？</p>

<h4>劣</h4>

<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='ruby'><span class='line'><span class="o">&lt;</span><span class="sx">% count </span><span class="o">=</span> <span class="mi">0</span> <span class="o">&gt;</span>
</span><span class='line'><span class="o">&lt;</span><span class="n">table</span><span class="o">&gt;</span>
</span><span class='line'><span class="o">&lt;</span><span class="sx">% @items.each </span><span class="k">do</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span> <span class="sx">%&gt;</span>
</span><span class='line'><span class="sx">  &lt;% if count % 2 == 0 %&gt;</span>
</span><span class='line'>    <span class="o">&lt;</span><span class="sx">% css_class </span><span class="o">=</span> <span class="s2">&quot;even &quot;</span><span class="sx">%&gt;</span>
</span><span class='line'><span class="sx">  &lt;% else %&gt;</span>
</span><span class='line'>    <span class="o">&lt;</span><span class="sx">% css_class </span><span class="o">=</span> <span class="s2">&quot;odd&quot;</span> <span class="sx">%&gt;</span>
</span><span class='line'><span class="sx">  &lt;% end %&gt;</span>
</span><span class='line'>  <span class="o">&lt;</span><span class="n">tr</span> <span class="n">class</span><span class="o">=</span><span class="s2">&quot;&lt;%= css_class %&gt;&quot;</span><span class="o">&gt;</span>
</span><span class='line'>    <span class="o">&lt;</span><span class="n">td</span><span class="o">&gt;</span><span class="n">item</span><span class="o">&lt;</span><span class="sr">/td&gt;</span>
</span><span class='line'><span class="sr">  &lt;/</span><span class="n">tr</span><span class="o">&gt;</span>
</span><span class='line'>  <span class="o">&lt;</span><span class="sx">% count </span><span class="o">+=</span> <span class="mi">1</span><span class="sx">%&gt;</span>
</span><span class='line'><span class="sx">&lt;% end %&gt;</span>
</span><span class='line'><span class="o">&lt;</span><span class="sr">/table&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>一般的想法會是使用兩種不同 CSS class : <code>even</code> 與 <code>odd</code>，著上不同的顏色。</p>

<h4>劣</h4>

<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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="o">&lt;</span><span class="n">table</span><span class="o">&gt;</span>
</span><span class='line'><span class="o">&lt;</span><span class="sx">% @items.each_with_index </span><span class="k">do</span> <span class="o">|</span><span class="n">item</span><span class="p">,</span> <span class="n">count</span><span class="o">|</span> <span class="sx">%&gt;</span>
</span><span class='line'><span class="sx">  &lt;% if count % 2 == 0 %&gt;</span>
</span><span class='line'>    <span class="o">&lt;</span><span class="sx">% css_class </span><span class="o">=</span> <span class="s2">&quot;even &quot;</span><span class="sx">%&gt;</span>
</span><span class='line'><span class="sx">  &lt;% else %&gt;</span>
</span><span class='line'>    <span class="o">&lt;</span><span class="sx">% css_class </span><span class="o">=</span> <span class="s2">&quot;odd&quot;</span> <span class="sx">%&gt;</span>
</span><span class='line'><span class="sx">  &lt;% end %&gt;</span>
</span><span class='line'>  <span class="o">&lt;</span><span class="n">tr</span> <span class="n">class</span><span class="o">=</span><span class="s2">&quot;&lt;%= css_class %&gt;&quot;</span><span class="o">&gt;</span>
</span><span class='line'>    <span class="o">&lt;</span><span class="n">td</span><span class="o">&gt;</span><span class="n">item</span><span class="o">&lt;</span><span class="sr">/td&gt;</span>
</span><span class='line'><span class="sr">  &lt;/</span><span class="n">tr</span><span class="o">&gt;</span>
</span><span class='line'>  <span class="o">&lt;</span><span class="sx">% count </span><span class="o">+=</span> <span class="mi">1</span><span class="sx">%&gt;</span>
</span><span class='line'><span class="sx">&lt;% end %&gt;</span>
</span><span class='line'><span class="o">&lt;</span><span class="sr">/table&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>這是一般初心者會犯的錯誤。實際上 Ruby 的 Array 內建 <code>each_with_index</code>，不需另外宣告一個 count。</p>

<h4>優</h4>

<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="o">&lt;</span><span class="n">table</span><span class="o">&gt;</span>
</span><span class='line'><span class="o">&lt;</span><span class="sx">% @items.each </span><span class="k">do</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span> <span class="sx">%&gt;</span>
</span><span class='line'><span class="sx">  &lt;tr class=&quot;&lt;%= cycle(&quot;odd&quot;, &quot;even&quot;) %&gt;</span><span class="s2">&quot;&gt;</span>
</span><span class='line'><span class="s2">    &lt;td&gt;item&lt;/td&gt;</span>
</span><span class='line'><span class="s2">  &lt;/tr&gt;</span>
</span><span class='line'><span class="s2">&lt;% end %&gt;</span>
</span><span class='line'><span class="s2">&lt;/table&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>但其實還有更簡便的方法：Rails 內建了 <code>cycle</code> 這個 Helper。所以只要這樣寫就好了&#8230;</p>

<h4>常用你可能不知道的 Helper</h4>

<p>限於篇幅，直接介紹幾個因為使用機率高，所以很容易被重造輪子的 Helper。開發者會寫出的相關 AntiPattern 部分就跳過了。</p>

<ul>
<li><a href="http://apidock.com/rails/ActionView/Helpers/TextHelper/truncate">truncate</a></li>
<li><a href="http://apidock.com/rails/ActionView/Helpers/TextHelper/auto_link">auto_link</a></li>
<li><a href="http://apidock.com/rails/ActionView/Helpers/RecordTagHelper/div_for">div_for</a> &amp; <a href="http://apidock.com/rails/ActionController/RecordIdentifier/dom_id">dom_id</a></li>
<li><a href="http://apidock.com/rails/ActionView/Helpers/TextHelper/simple_format">simple_format</a></li>
</ul>


<p><code>#TODO: examples</code></p>

<h2>5. Ask, Not Tell</h2>

<p>這也是在 View 中會常出現的問題，直接違反了 Law of Demeter 原則，而造成了效能問題。十之八九某個 View 緩慢無比，最後抓出來背後幾乎都是這樣的原因。</p>

<p>不少開發者會設計出這樣的 helper：</p>

<h4>劣</h4>

<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="k">def</span> <span class="nf">post_tags_tag</span><span class="p">(</span><span class="n">post</span><span class="p">,</span> <span class="n">opts</span> <span class="o">=</span> <span class="p">{})</span>
</span><span class='line'>  <span class="n">tags</span> <span class="o">=</span> <span class="n">post</span><span class="o">.</span><span class="n">tags</span>
</span><span class='line'>  <span class="n">tags</span><span class="o">.</span><span class="n">collect</span> <span class="p">{</span> <span class="o">|</span><span class="n">tag</span><span class="o">|</span> <span class="n">link_to</span><span class="p">(</span><span class="n">tag</span><span class="p">,</span><span class="n">posts_path</span><span class="p">(</span><span class="ss">:tag</span> <span class="o">=&gt;</span> <span class="n">tag</span><span class="p">))</span> <span class="p">}</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">&quot;, &quot;</span><span class="p">)</span>
</span><span class='line'><span class="k">end</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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="o">&lt;</span><span class="sx">% @posts.each </span><span class="k">do</span> <span class="o">|</span><span class="n">post</span><span class="o">|</span> <span class="sx">%&gt;</span>
</span><span class='line'><span class="sx">  &lt;%= post_tags_tag(post) %&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>這種寫法會造成在 View 中，執行迴圈時，造成不必要的大量 query (n+1)，以及在 View 中製造不確定數量的大量物件。View 不僅效率低落也無法被 optimized。</p>

<h4>優</h4>

<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='ruby'><span class='line'><span class="k">def</span> <span class="nf">post_tags_tag</span><span class="p">(</span><span class="n">post</span><span class="p">,</span> <span class="n">tags</span><span class="p">,</span> <span class="n">opts</span> <span class="o">=</span> <span class="p">{})</span>
</span><span class='line'>  <span class="n">tags</span><span class="o">.</span><span class="n">collect</span> <span class="p">{</span> <span class="o">|</span><span class="n">tag</span><span class="o">|</span> <span class="n">link_to</span><span class="p">(</span><span class="n">tag</span><span class="p">,</span><span class="n">posts_path</span><span class="p">(</span><span class="ss">:tag</span> <span class="o">=&gt;</span> <span class="n">tag</span><span class="p">))</span> <span class="p">}</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">&quot;, &quot;</span><span class="p">)</span>
</span><span class='line'><span class="k">end</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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="o">&lt;</span><span class="sx">% @posts.each </span><span class="k">do</span> <span class="o">|</span><span class="n">post</span><span class="o">|</span> <span class="sx">%&gt;</span>
</span><span class='line'><span class="sx">  &lt;%= post_tags_tag(post, post.tags) %&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>




<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='ruby'><span class='line'>  <span class="k">def</span> <span class="nf">index</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">recent</span><span class="o">.</span><span class="n">includes</span><span class="p">(</span><span class="ss">:tags</span><span class="p">)</span>
</span><span class='line'>  <span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>正確的方法是使用 <a href="http://pragprog.com/articles/tell-dont-ask">Tell, dont ask</a> 原則，主動告知會使用的物件，而非讓 Helper 去猜。並配合 ActiveRecord 的 includes 減少不必要的 query（ includes 可以製造 join query ，一次把需要的 posts 和 tags 撈出來）。</p>

<p>且在 controller query 有 object cache 效果，在 view 中則無。</p>

<h2>小結</h2>

<p>Helper 是 Rails Developer 時常在接觸的工具。但可惜的是，多數開發者卻無法將此利器使得稱手，反而造成了更多問題。在我所曾經參與的幾十個 Rails 專案中，很多設計和效能問題幾乎都是因為寫的不好的 View / Helper 中的 slow query 或伴隨產生的大量 object 所造成的 memory bloat 導致的。但參與專案的開發者並沒有那麼多的經驗，能夠抓出確切的病因，卻都將矛頭直接是 Rails 的效能問題，或者是沒打上 Cache 的關係。這樣的說法只是把問題掩蓋起來治標，而非治本。</p>

<p>下次若有遇到 performance issue，請先往 View 中瞧看看是不是裡面出現了問題。也許你很快就可以找到解答。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[在 Model 裡寫 view code]]></title>
    <link href="http://erp-book.heroku.com/ch4-write-view-code-in-model/"/>
    <updated>2012-01-12T19:34:00+08:00</updated>
    <id>http://erp-book.heroku.com/ch4-write-view-code-in-model</id>
    <content type="html"><![CDATA[<h2>場景</h2>

<p><code>#TODO</code></p>

<h4>Wrong</h4>

<p><code>app/models/post.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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">def</span> <span class="nf">tag_list_for_welcome_page</span>
</span><span class='line'>   <span class="n">tags</span><span class="o">.</span><span class="n">maps</span> <span class="p">{</span><span class="o">|</span><span class="n">tag</span><span class="o">|</span> <span class="s2">&quot;&lt;span&gt; tag.name &lt;/span&gt;&quot;</span> <span class="p">}</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">&quot; / &quot;</span><span class="p">)</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<h4>Correct</h4>

<p><code>app/helpers/post_helper.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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">def</span> <span class="nf">tag_list</span><span class="p">(</span><span class="n">tags</span><span class="p">)</span>
</span><span class='line'>  <span class="n">tags</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">tag</span><span class="o">|</span> <span class="n">content_tag</span><span class="p">(</span><span class="ss">:span</span><span class="p">,</span> <span class="n">tag</span><span class="o">.</span><span class="n">name</span> <span class="p">)</span> <span class="p">}</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">&quot; / &quot;</span><span class="p">)</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[在 Controller 裡處理資料]]></title>
    <link href="http://erp-book.heroku.com/ch4-process-data-in-controller/"/>
    <updated>2012-01-12T19:30:00+08:00</updated>
    <id>http://erp-book.heroku.com/ch4-process-data-in-controller</id>
    <content type="html"><![CDATA[<h2>場景</h2>

<p><code>#TODO</code></p>

<h4>Wrong</h4>

<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="k">def</span> <span class="nf">checkout</span>
</span><span class='line'>  <span class="n">book</span> <span class="o">=</span> <span class="no">Book</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">:id</span><span class="o">]</span><span class="p">)</span>
</span><span class='line'>  <span class="n">current_user</span><span class="o">.</span><span class="n">balance</span> <span class="o">-=</span> <span class="n">book</span><span class="o">.</span><span class="n">price</span>
</span><span class='line'>  <span class="n">curremt_user</span><span class="o">.</span><span class="n">save!</span>
</span><span class='line'>  <span class="n">redirect_to</span> <span class="n">account_path</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<h3>### Correct</h3>

<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="k">def</span> <span class="nf">checkout</span>
</span><span class='line'>  <span class="n">book</span> <span class="o">=</span> <span class="no">Book</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">:id</span><span class="o">]</span><span class="p">)</span>
</span><span class='line'>  <span class="n">current_user</span><span class="o">.</span><span class="n">purchase</span><span class="p">(</span><span class="n">book</span><span class="p">)</span>
</span><span class='line'>  <span class="n">redirect_to</span> <span class="n">account_path</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[LOGIC in View]]></title>
    <link href="http://erp-book.heroku.com/ch4-logic-in-view/"/>
    <updated>2012-01-12T19:27:00+08:00</updated>
    <id>http://erp-book.heroku.com/ch4-logic-in-view</id>
    <content type="html"><![CDATA[<h2>場景</h2>

<p><code>#TODO</code></p>

<h4>Wrong</h4>

<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='ruby'><span class='line'><span class="o">&lt;</span><span class="sx">% if </span><span class="n">current_user</span> <span class="o">&amp;&amp;</span> <span class="n">current_user</span> <span class="o">==</span> <span class="n">post</span><span class="o">.</span><span class="n">user</span> <span class="sx">%&gt;</span>
</span><span class='line'><span class="sx">  &lt;%= link_to(&quot;Edit&quot;, edit_post_path(post))%&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>


<h3>Correct</h3>

<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='ruby'><span class='line'><span class="o">&lt;</span><span class="sx">% if </span><span class="n">editable?</span><span class="p">(</span><span class="n">post</span><span class="p">)</span> <span class="sx">%&gt;</span>
</span><span class='line'><span class="sx">  &lt;%= link_to(&quot;Edit&quot;, edit_post_path(post))%&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>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Ch6-when-ever]]></title>
    <link href="http://erp-book.heroku.com/ch6-whenever/"/>
    <updated>2012-01-12T18:21:00+08:00</updated>
    <id>http://erp-book.heroku.com/ch6-whenever</id>
    <content type="html"><![CDATA[<ul>
<li>crontab should with projects, not with machines</li>
</ul>


<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>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">every</span> <span class="mi">3</span><span class="o">.</span><span class="n">hours</span> <span class="k">do</span>
</span><span class='line'>  <span class="n">runner</span> <span class="s2">&quot;MyModel.some_process&quot;</span>
</span><span class='line'>  <span class="n">rake</span> <span class="s2">&quot;my:rake:task&quot;</span>
</span><span class='line'>  <span class="n">command</span> <span class="s2">&quot;/usr/bin/my_great_command&quot;</span>
</span><span class='line'><span class="k">end</span>
</span><span class='line'>
</span><span class='line'><span class="n">every</span> <span class="mi">1</span><span class="o">.</span><span class="n">day</span><span class="p">,</span> <span class="ss">:at</span> <span class="o">=&gt;</span> <span class="s1">&#39;4:30 am&#39;</span> <span class="k">do</span>
</span><span class='line'>  <span class="n">runner</span> <span class="s2">&quot;DB.Backup&quot;</span>
</span><span class='line'><span class="k">end</span>
</span><span class='line'>
</span><span class='line'><span class="n">every</span> <span class="ss">:hour</span> <span class="k">do</span> <span class="c1"># Many shortcuts available: :hour, :day, :month, :year, :reboot</span>
</span><span class='line'>  <span class="n">runner</span> <span class="s2">&quot;SomeModel.ladeeda&quot;</span>
</span><span class='line'><span class="k">end</span>
</span><span class='line'>
</span><span class='line'><span class="n">every</span> <span class="ss">:sunday</span> <span class="k">do</span> <span class="c1"># Use any day of the week or :weekend, :weekday </span>
</span><span class='line'>  <span class="n">runner</span> <span class="s2">&quot;Task.do_something_great&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[OmniAuth]]></title>
    <link href="http://erp-book.heroku.com/ch6-omniauth/"/>
    <updated>2012-01-12T18:21:00+08:00</updated>
    <id>http://erp-book.heroku.com/ch6-omniauth</id>
    <content type="html"><![CDATA[
]]></content>
  </entry>
  
</feed>

