<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>{&#124;ihower.tw&#124; blog } &#187; Programming</title>
	<atom:link href="http://ihower.tw/blog/archives/category/programming/feed" rel="self" type="application/rss+xml" />
	<link>http://ihower.tw/blog</link>
	<description>Ruby, Ruby on Rails, Mac and Agile development</description>
	<lastBuildDate>Tue, 27 Jul 2010 06:48:33 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>物件導向程式的九個體操練習</title>
		<link>http://ihower.tw/blog/archives/1960</link>
		<comments>http://ihower.tw/blog/archives/1960#comments</comments>
		<pubDate>Sun, 12 Oct 2008 15:01:39 +0000</pubDate>
		<dc:creator>ihower</dc:creator>
				<category><![CDATA[Agile]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://ihower.idv.tw/blog/?p=1960</guid>
		<description><![CDATA[最近在翻 The ThoughtWorks Anthology(知名軟體顧問公司 Thoughtworks 出的文集)，裡面有篇 Object Calisthenics 蠻有意思的。
好的物件導向設計很難，我們都很同意何謂好的設計原則：高內聚力(cohesion)、低耦合(loose coupling)、不重複程式(Don&#8217;t Repeat Yourselp)、封裝(encapsulation)、可測試性、易閱讀性等等，但是實際寫的時候卻不容易化身為一行行的程式碼。這篇作者列了九條規則，並建議你練習寫個千行程式嚴格遵守看看，用以改善你的OO實作能力。
初次看到這九條絕得有點誇張，但其實濃縮了不少OO想法在裡面，如果有閱讀過重構或物件導向設計原則等概念，應該能夠聯想到很多東西，挺有趣的。
1. 每個函式裡面只能有一層縮排，如果需要多一層，請多寫一個 method 去呼叫。
這個規則其實就是要求嚴格遵守 Compose Method：將邏輯操作轉換為細目等級相同的步驟，避免過深的邏輯而無法迅速了解，相信大家應該都有看(寫)過M型程式吧 :p
2. 不要使用到 else 這個關鍵字。
避免寫出複雜的 nested conditional 程式。不論是&#8221;重構&#8220;或是&#8221;重構－向範式前進&#8220;這兩本書，都有很多篇幅花在討論如何簡化條件邏輯，作法包括 
a. 重構一書提到的 Replace Nested Conditional with Guard Clauses 方式，直接使用 return 返回，不要再 else 了。
b. 請愛用 Ternary Operator：也就是 boolean-expression ? expr1 : expr2。很多簡單的 if else 都可以用 Ternary Operator 簡化到一行一目了然。舉個 Ruby code 例子：

  [...]]]></description>
			<content:encoded><![CDATA[<p>最近在翻 <a href="http://www.pragprog.com/titles/twa/thoughtworks-anthology">The ThoughtWorks Anthology</a>(知名軟體顧問公司 <a href="http://www.thoughtworks.com/">Thoughtworks</a> 出的文集)，裡面有篇 Object Calisthenics 蠻有意思的。</p>
<p>好的物件導向設計很難，我們都很同意何謂好的設計原則：高內聚力(cohesion)、低耦合(loose coupling)、不重複程式(Don&#8217;t Repeat Yourselp)、封裝(encapsulation)、可測試性、易閱讀性等等，但是實際寫的時候卻不容易化身為一行行的程式碼。這篇作者列了九條規則，並建議你練習寫個千行程式嚴格遵守看看，用以改善你的OO實作能力。</p>
<p>初次看到這九條絕得有點誇張，但其實濃縮了不少OO想法在裡面，如果有閱讀過重構或物件導向設計原則等概念，應該能夠聯想到很多東西，挺有趣的。</p>
<blockquote><p>1. 每個函式裡面只能有一層縮排，如果需要多一層，請多寫一個 method 去呼叫。</p></blockquote>
<p>這個規則其實就是要求嚴格遵守 <a href="http://www.industriallogic.com/xp/refactoring/composeMethod.html">Compose Method</a>：將邏輯操作轉換為細目等級相同的步驟，避免過深的邏輯而無法迅速了解，相信大家應該都有看(寫)過<a href="http://forth.wordpress.com/2007/11/08/m型程式/">M型程式</a>吧 :p</p>
<blockquote><p>2. 不要使用到 else 這個關鍵字。</p></blockquote>
<p>避免寫出複雜的 nested conditional 程式。不論是&#8221;<a href="http://tlsj.tenlong.com.tw/WebModule/BookSearch/bookSearchViewAction.do?isbn=9789861547534&#038;sid=46024">重構</a>&#8220;或是&#8221;<a href="http://tlsj.tenlong.com.tw/WebModule/BookSearch/bookSearchViewAction.do?isbn=9789861814025&#038;sid=43993">重構－向範式前進</a>&#8220;這兩本書，都有很多篇幅花在討論如何簡化條件邏輯，作法包括 </p>
<p>a. 重構一書提到的 <a href="http://www.refactoring.com/catalog/replaceNestedConditionalWithGuardClauses.html">Replace Nested Conditional with Guard Clauses</a> 方式，直接使用 return 返回，不要再 else 了。</p>
<p>b. 請愛用 Ternary Operator：也就是 boolean-expression ? expr1 : expr2。很多簡單的 if else 都可以用 Ternary Operator 簡化到一行一目了然。舉個 Ruby code 例子：</p>
<p><code><br />
  if ( is_something )<br />
    "foo"<br />
  else<br />
     "bar"<br />
  end<br />
</code><br />
如果改成三重操作子就俐落多了：<br />
<code><br />
 ( is_something )? "foo" : "bar"<br />
</code></p>
<p>另外初心者也常寫出根本不需要 if else 的情況：<br />
<code><br />
def is_foobar<br />
  if ( a > 0 )<br />
    return true<br />
  else<br />
    return false<br />
  end<br />
end<br />
</code><br />
其實只需要這樣就可以了：<br />
<code><br />
def is_foobar<br />
  ( a > 0 )<br />
end<br />
</code></p>
<p>c. 第三招要先念點書，請善用物件導向的多型(polymorphism)能力，請參考設計模式的 <a href="http://en.wikipedia.org/wiki/Strategy_pattern">Strategy pattern</a> 或重構的 <a href="http://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.html">Replace Conditional with Polymorphism</a></p>
<p><span id="more-1960"></span></p>
<blockquote><p>3. 所有基本型別都包裝成物件 </p></blockquote>
<p>如果你要用到 integer 或 string 等基本型別，請多用一層帶有意義 Class 包裝起來。例如在傳遞 &#8220;年&#8221; 跟 &#8220;月&#8221; 的參數時，不要只用 integer 傳遞哪一年哪一個月，而是用個 Date 物件包裝起來。</p>
<p>為什麼呢? 透過這種小型物件，我們可以寫出較容易維護的程式，一看就懂那個 integer 代表的意義是什麼。而且只用基本型別，編譯器也無法幫助(偵測)你寫出你真正想要的正確&#8221;語意&#8221;。另外一個小型物件好處是提供了很好的放 method 的地方，例如在這方面 Ruby 就做的很好，因為所有東西都是物件，所以甚至是 integer 或 string 都可以直接有自己的基本操作，例如：<br />
<code><br />
-123456.abs # 123456<br />
"FOObar".downcase # "foobar"。<br />
Time.now.year # 2008<br />
</code></p>
<blockquote><p>4. 每一行只能出現 dot 一次(即每行只能呼叫一次 method)</p></blockquote>
<p>這規則對 Ruby 可能太嚴格了&#8230;XD 規則的目的是要減少耦合，避免破壞封裝原則：你只能玩你的玩具，而不是玩具的玩具。細節請找 The Law of Demeter (&#8220;只對你的朋友說話&#8221;)，引用 Qing 長輩的<a href="http://www.javaworld.com.tw/roller/qing/entry/漫談程式碼的相依性_4">漫談程式碼的相依性</a>：&#8221;在遵守所謂的Law of Demeter時，在某一物件的某一函式中所能呼叫函式的對象是受限制的，它只能是：該物件本身、傳入該函式的引數、在該函式中所產生的物件、以及該物件的資料成員。很明顯的，你不能在此函式中呼叫其他函式所回傳物件的函式，因為其他函式所回傳的物件，並不在上述的對象之中。</p>
<blockquote><p>5. 不要縮寫名稱 </p></blockquote>
<p>我超愛這個規則，每次看到有人用縮寫亂取 method name 或 variable name，就覺得很抓狂。即使用 full name 會長了點，但是我寧願一看就懂而不是玩猜謎(例 what&#8217;s sm? short_message? smart_phone? single_machine?)。即使用暫時變數我也覺得應該好好命名，而不是只用一個 &#8220;t&#8221;。我自己在 Textmate 的經驗是只要按 Esc 就可以自動比對出你要的 full name，從來都不覺得打完整名稱會很辛苦。</p>
<p>如果你真的覺得太長，就應該好好重新思考：是不是這個 method 放錯 class 了? (放到別的 class 之下也許不需要這麼長就可以知道作用)。另一個取名常犯的錯誤是不需要重複 context (語境)：例如一個叫 Order Class，它的 method name 就不需要命名為 ship_order (即@order.ship_order)，只需要取名 ship (即@order.ship)就好了。</p>
<blockquote><p>6. 保持東西輕薄</p></blockquote>
<p>class 不超過50行，每個 package 不超過10個檔案。50行的程式不會超過一個螢幕，這樣螢幕不用捲就可以一掃而過。這個挑戰在於你要努力去 grouping 有共通 logical sense 的東西。</p>
<blockquote><p>7. 不要用任何一個含超過兩個 instance 變數的 class </p></blockquote>
<p>這大概是最難的練習了，作者認為當一個 Class 處理超過一個 state variable 時，就應該另建一個新的 class 分解及包裝起來。實際的作法我想可以參考重構的 <a href="http://www.refactoring.com/catalog/extractClass.html">Extract Class</a> 方法。</p>
<blockquote><p>8. 使用第一級 collections</p></blockquote>
<p>任何包含 collection 的類別就不應該再含有別的變數，也就是你應該把 collection 當做一個很基本的類別來使用。</p>
<blockquote><p>9. 不要使用任何 getters/setters/properties</p></blockquote>
<p>目標是要練習 &#8220;Tell, don&#8217;t ask&#8221; 原則，你應該盡量告訴物件你想要什麼(tell)，而不是去問(ask)物件的狀態，然後做決定，再告訴物件做什麼。更詳細可以看看 The Pragmatic Bookshelf 的 <a href="http://www.pragprog.com/articles/tell-dont-ask">Tell, Don&#8217;t Ask</a> 一文 (這篇也有提到Law of Demeter)。</p>
]]></content:encoded>
			<wfw:commentRss>http://ihower.tw/blog/archives/1960/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>最佳化 ActiveRecord SQL 查詢</title>
		<link>http://ihower.tw/blog/archives/1766</link>
		<comments>http://ihower.tw/blog/archives/1766#comments</comments>
		<pubDate>Thu, 04 Sep 2008 13:29:28 +0000</pubDate>
		<dc:creator>ihower</dc:creator>
				<category><![CDATA[Performance]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Rails]]></category>

		<guid isPermaLink="false">http://ihower.idv.tw/blog/?p=1766</guid>
		<description><![CDATA[Update(2008/9/5) 補充 named_scope 也可以用在 :select，感謝 tsechingho++
要看 AciveRecord 產生的 SQL 是什麼，除了可以直接 tail -f log/development.log 之外，也可以在 script/console 的情況下透過以下指令直接把 log 直接顯示出來：

ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Base.clear_active_connections!

如此便可以好好在 console 的環境下實驗 ActiveRecord 囉。
我看到關於 ActiveRecord SQL 查詢最佳化方式有這幾種：
1. 使用 :select 
只撈需要用到的欄位，特別是如果不需要用到的 text 或  binary 欄位請排除。

Event.find(:all, :select => "id, title, description")

搭配 named_scope 我們可以把常用的 :select 預先設定好，例如：

class User < ActiveRecord::Base
  named_scope :short, :select => "id, name, email"
end
User.short.find(:all)

2. [...]]]></description>
			<content:encoded><![CDATA[<p>Update(2008/9/5) 補充 named_scope 也可以用在 :select，感謝 tsechingho++</p>
<p>要看 AciveRecord 產生的 SQL 是什麼，除了可以直接 tail -f log/development.log 之外，也可以在 script/console 的情況下透過以下指令直接把 log 直接顯示出來：<br />
<code><br />
ActiveRecord::Base.logger = Logger.new(STDOUT)<br />
ActiveRecord::Base.clear_active_connections!<br />
</code><br />
如此便可以好好在 console 的環境下實驗 ActiveRecord 囉。</p>
<p>我看到關於 ActiveRecord SQL 查詢最佳化方式有這幾種：</p>
<h3>1. 使用 :select </h3>
<p>只撈需要用到的欄位，特別是如果不需要用到的 text 或  binary 欄位請排除。<br />
<code><br />
Event.find(:all, :select => "id, title, description")<br />
</code></p>
<p>搭配 named_scope 我們可以把常用的 :select 預先設定好，例如：<br />
<code><br />
class User < ActiveRecord::Base<br />
  named_scope :short, :select => "id, name, email"<br />
end</p>
<p>User.short.find(:all)<br />
</code></p>
<h3>2. 使用 :include </h3>
<p>使用 :include 避免 N+1 次 queries 的問題。</p>
<p><code><br />
  @events = Event.find(:all, :include => [:group] )<br />
</code></p>
<p><code><br />
  @events.each do |e|<br />
      e.group.title<br />
   end<br />
</code></p>
<p>如果沒有加 :include 把相關的 groups 一起載入，在迴圈中就會產生 @event.size 次對 group 的個別 SQL 查詢，會非常傷。加了 :include 之後總共只需查詢兩次。</p>
<p>另外一個比較少人知道的是，在設定 Model associations 時，如果有很明顯的情境一定會順道載入二階 association model，可以設定 :include 在 has_many, belongs_to, has_one 上面，例如：</p>
<p><code><br />
class User < ActiveRecord::Base<br />
 has_one :foo, :include => [:bar]<br />
end<br />
</code></p>
<p>如此便會在載入 @user.foo 的同時，也會提早載入 @user.foo.bar。</p>
<h3>3. 資料庫索引</h3>
<p>針對 foreign key 要加上資料庫索引 index。在 migration 上透過 add_index 就可以加上去了。</p>
<h3>4. 特定情況下可用 :joins 取代 :include</h3>
<p>在只需要用到 :conditions 而不需要載入該 model 的情況下，可以用 :joins 取代 :include。<br />
<code><br />
  Group.find(:all, :include => [ :group_memberships ], :conditions => [ "group_memberships.created_at > ?", Time.now - 30.days ] )<br />
</code><br />
因為其中會載入 group_memberships model 只是為了加條件式，而沒有要撈出裡面的資料，所以可以改用 :joins，這其中的差異你看產生出的SQL就知道了 :)</p>
<p><code><br />
  Group.find(:all, :joins => [ :group_memberships ], :conditions => [ "group_memberships.created_at > ?", Time.now - 30.days ] )<br />
</code></p>
<h3>5. 自己寫 SQL </h3>
<p>ActiveRecord 可以直接寫 find_by_sql。</p>
<h3>6. denormalization 逆正規化</h3>
<p>當資料非常多，又要常常查詢其中計算的結果，這時可以考慮使用逆正規化的手法，將計算的結果也當做資料存起來。</p>
<p>一個最常見最基本的用法就是計算總數了，例如以下的例子可以解決需要常常查詢 @topic.posts.size 該篇主題有多少文章的情境：</p>
<p><code><br />
class Topic < ActiveRecord::Base<br />
 has_many :posts<br />
end</p>
<p>class Posts < ActiveRecord::Base<br />
   belongs_to :topic, :counter_cache => true<br />
end<br />
</code></p>
<p>我們會在 Topic model 新增一個欄位 posts_count，然後 :counter_cache 就會幫我們在新增刪除 Post 時，自動更新所屬 Topic 的 posts_count 欄位資料。這時只要打 @topic.posts.size 就會直接回傳 posts_count，而無需每次都再發一個 SQL query 去 count 有多少筆 posts。</p>
<p>其他逆正規化的方式也包括了統計結算報表等，我們把需要複雜計算的的結果(可以透過非同步的機制如 cron 定期去觸發) 存到 Report model，這樣使用者直接撈 Report 的結果即可。</p>
]]></content:encoded>
			<wfw:commentRss>http://ihower.tw/blog/archives/1766/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>小探 Rails ActiveSupport</title>
		<link>http://ihower.tw/blog/archives/1719</link>
		<comments>http://ihower.tw/blog/archives/1719#comments</comments>
		<pubDate>Sun, 06 Apr 2008 19:56:04 +0000</pubDate>
		<dc:creator>ihower</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Rails]]></category>

		<guid isPermaLink="false">http://ihower.idv.tw/blog/?p=1719</guid>
		<description><![CDATA[Update(2008/4/9):  這篇 RAILS RUBYISMS ADVENT也可以一看。
ActiveSupport 是 Rails 的工具箱。最近在看 Advanced Rails, O&#8217;Reilly 一書，有幾樣東西值得記上一筆： 
JSON
我們有 Object#to_json，物件如 array,hash 等都可以呼叫 to_json 轉 JSON 格式，非常方便與 JavaScript 做銜接。
Blank
所有的物件都加上了 blank? 這個函式，回傳 true 如果是 1. 空字串 2. 只含空白的字串 3. false 4. nil 5. empty array [] 6. empty hash {}。所以別再寫 ( s.nil? &#124;&#124; s.empty? ) 啦。
Class Attribute Accessors
可用宣告的方式定義 Class Attribute，如

  class [...]]]></description>
			<content:encoded><![CDATA[<p>Update(2008/4/9):  這篇 <a href="http://errtheblog.com/posts/42-rails-rubyisms-advent">RAILS RUBYISMS ADVENT</a>也可以一看。</p>
<p>ActiveSupport 是 Rails 的工具箱。最近在看 Advanced Rails, O&#8217;Reilly 一書，有幾樣東西值得記上一筆： </p>
<h3>JSON</h3>
<p>我們有 Object#to_json，物件如 array,hash 等都可以呼叫 to_json 轉 JSON 格式，非常方便與 JavaScript 做銜接。</p>
<h3>Blank</h3>
<p>所有的物件都加上了 blank? 這個函式，回傳 true 如果是 1. 空字串 2. 只含空白的字串 3. false 4. nil 5. empty array [] 6. empty hash {}。所以別再寫 ( s.nil? || s.empty? ) 啦。</p>
<h3>Class Attribute Accessors</h3>
<p>可用宣告的方式定義 Class Attribute，如</p>
<pre>
  class Foo
     cattr_accessor :bar
     self.bar = ""
  end
</pre>
<p>這樣會定義出來的 C.bar 即 @@bar</p>
<h3>Class Inheritable Attributes</h3>
<p>Class Attribute 是整個類別繼承體系共用，這在我們寫 ActiveRecord 相關 plugin 時非常不適用，因為所有的 model 都繼承自 ActiveRecord，但是各自又要有不同的 Class Attributes 值。最常見的使用狀況就是 plugin 了，model A 和 model B 都 include 某個 plugin，但是這個 plugin 的設定值要不一樣。拿大家都在用的 attactment_fu 舉例：</p>
<pre>
class UploadImage < ActiveRecord::Base
  has_attachment :content_type => :image, :storage => :file_system
end
</pre>
<p>翻出 has_attachment 的 source code 你就看到這招了：</p>
<pre>
def has_attachment(options = {})
      ......
     class_inheritable_accessor :attachment_options
     self.attachment_options = options    # 這裡的 self 指的是 UploadImage
      ......
end
</pre>
<p>除了 class_inheritable_accessor(*syms) ，還有 class_inheritable_array(*syms) 和class_inheritable_hash(*syms) 等。</p>
<p>Class Attribute Accessors 的原理可以請參考 <a href="http://ihower.idv.tw/blog/archives/1696">class instance variables</a> 這篇。</p>
<h3>Date and Time conversions</h3>
<p>不需要每次寫 Helper 用 strftime，我們可在 environment.rb 新增自訂的 format，例如</p>
<pre>
 ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS.merge!( :foo => '%m/%d %l:%M %p')
</pre>
<p>這樣就可以對 Time 物件呼叫 to_s(:foo)，內建還有 :default, :short, :long, :db 等等。</p>
<h3>alias_method_chain</h3>
<p>在 Rails source code 十分常見：</p>
<pre>
  alias_method_chain :target, :feature
</pre>
<p>等同於</p>
<pre>
  alias_method :target_without_feature, :target
  alias_method :target, :target_with_feature
</pre>
<h3>Delegation</h3>
<p>將 methods 傳給另一個 object</p>
<pre>
  class Account < ActiveRecord::Base
      has_one :foo
      delegate :free?, :to => :foo
  end
</pre>
<p>這樣 account.free? 就會呼叫 account.foo.free? 考慮 foo 可能 nil，我們可以多一個檢查：</p>
<pre>
  delegate :free?, :to => "something.nil? ? false : something"
</pre>
<p>甚至兩層，假設 foo 有 bar：</p>
<pre>
  delegate :free?, :to => "foo.bar"
</pre>
<p>這樣 account.free? 就會呼叫 account.foo.bar.free?</p>
<h3>#Object#returning</h3>
<p>讓你執行一些操作然後傳回：</p>
<pre>
 returning(User.new) do |u|
   u.name = "Foo"
 end
</pre>
<h3>#Object#with_options</h3>
<p>最常用在 routes.rb，不過其實任意物件都可以用，他會將參數自動 merge 到 method call 的 hash 參數：</p>
<pre>
 map.with_options( :controller => "people" ) do |p|
   p.root :action => "index"
   p.about :action => "about"
 end
</pre>
]]></content:encoded>
			<wfw:commentRss>http://ihower.tw/blog/archives/1719/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>FaceboxRender: Rails 無縫使用 lightbox</title>
		<link>http://ihower.tw/blog/archives/1708</link>
		<comments>http://ihower.tw/blog/archives/1708#comments</comments>
		<pubDate>Tue, 26 Feb 2008 03:33:24 +0000</pubDate>
		<dc:creator>ihower</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Rails]]></category>

		<guid isPermaLink="false">http://ihower.idv.tw/blog/archives/1708</guid>
		<description><![CDATA[FaceboxRender  是我最近寫的第二個 Ajax UI plugin，它讓你在 Rails 中可以無縫使用 lightbox 效果，搭配使用的 Javascript library 正如其名是 Facebox。首先你得先裝好 jQuery 和 Facebox:

 下載 jQuery (建議你可以考慮用 jRails 完全換掉 Prototype library)
下載 facebox
把 facebox js file 放到 /public/javascripts/
把 facebox css file 放到 /public/styleshees/
把 facebox all image files 放到 /public/facebox/
設定好 layout 的 stylesheet_link_tag 和 javascript_include_tag 加入這些 js,css 檔案

facebox-render 目前放在 github，請下載後放到 vender/plugins/facebox_render
然後在你要使用的 Controller 加入以下程式碼

 include [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://github.com/ihower/facebox_render/">FaceboxRender </a> 是我最近寫的第二個 Ajax UI plugin，它讓你在 Rails 中可以無縫使用 lightbox 效果，搭配使用的 Javascript library 正如其名是 <a href="http://famspam.com/facebox/">Facebox</a>。首先你得先裝好 jQuery 和 Facebox:</p>
<ol>
<li> 下載 jQuery (建議你可以考慮用 <a href="http://ennerchi.com/projects/jrails">jRails</a> 完全換掉 Prototype library)</li>
<li>下載 facebox</li>
<li>把 facebox js file 放到 /public/javascripts/</li>
<li>把 facebox css file 放到 /public/styleshees/</li>
<li>把 facebox all image files 放到 /public/facebox/</li>
<li>設定好 layout 的 stylesheet_link_tag 和 javascript_include_tag 加入這些 js,css 檔案</li>
</ol>
<p>facebox-render 目前放在 <a href="http://github.com/ihower/facebox_render/">github</a>，請下載後放到 vender/plugins/facebox_render</p>
<p>然後在你要使用的 Controller 加入以下程式碼</p>
<pre>
 include FaceboxRender
</pre>
<p>或是你直接放到 /app/controllers/application.rb 裡面也是可以。</p>
<p>回到 view code，當你要叫出一個 lightbox :</p>
<pre>
 facebox_link_to "Login", :url => login_url
</pre>
<p>或是你也可以用 remote_link_to, remote_form_tag 等Rails內建的 Ajax Helper，差別在於 facebox_link_to 會先秀出一個 Loading lightbox，然後再送 Ajax request。</p>
<p>重頭戲是 action :</p>
<pre>

def new
 # do some thing you want
 respond_to do |format|
  format.html
  format.js { render_to_facebox }
 end
end
</pre>
<p>這裡巧妙的運用了 respond_to 來處理 ajax request，預設的 render_facebox 會 render 不帶 layout 的HTML(即 new.html.erb)。當然我們也支援傳入 :action, :template, :partial 等常見的Rails參數，或是乾脆傳入 :html 也可以。</p>
<p>傳入 :msg 的話，會插入一段 &lt;div class=&#8221;message&#8221;>your msg&lt;/div> 到裡面去，方便你做一些提示或警告訊息。</p>
<p>除了 render_facebox，也有 close_facebox 可以關掉 lightbox。也可以用 redirect_from_lightbox 重新讀取另一頁。</p>
<p>實際的應用非常方便，你可以在網頁中用 remote link 或是 remote form submit 叫出 facebox。在 facebox 中也還可以再放 remote link 或 remote form submit，接收處理的 action 可用 render_facebox 或關掉 close_facebox 抑或重新讀取頁面 redirect_from_facebox。</p>
<p>FaceboxRender 的隨附文件請見 <a href="http://faceboxrender.rubyforge.org/svn/trunk/README">README </a>。</p>
<p>P.S. 這篇文的英文版在 <a href="http://handlino.com/blog/2008/02/26/57/">Handlino&#8217;s blog</a></p>
]]></content:encoded>
			<wfw:commentRss>http://ihower.tw/blog/archives/1708/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Rails Email 同時提供 plain 及 html 版本</title>
		<link>http://ihower.tw/blog/archives/1700</link>
		<comments>http://ihower.tw/blog/archives/1700#comments</comments>
		<pubDate>Thu, 21 Feb 2008 12:03:10 +0000</pubDate>
		<dc:creator>ihower</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Rails]]></category>

		<guid isPermaLink="false">http://ihower.idv.tw/blog/archives/1700</guid>
		<description><![CDATA[讓 Email 同時提供 text/plain 跟 text/html 版本，讓不支援 HTML 的 Email client 也可以顯示 plain 是一種是具有親和力的作法。根據 multipart-alternative 的定義，正確的格式應該長這樣，用 boundary 分隔成兩個部份:

   From:  Nathaniel Borenstein &#60;nsb@bellcore.com>
   To: Ned Freed &#60;ned@innosoft.com>
   Subject: Formatted text mail
   MIME-Version: 1.0
   Content-Type: multipart/alternative; boundary=boundary42

   --boundary42

   Content-Type: text/plain; charset=utf-8

  [...]]]></description>
			<content:encoded><![CDATA[<p>讓 Email 同時提供 text/plain 跟 text/html 版本，讓不支援 HTML 的 Email client 也可以顯示 plain 是一種是具有親和力的作法。根據 multipart-alternative 的定義，正確的格式應該長這樣，用 boundary 分隔成兩個部份:</p>
<pre>
   From:  Nathaniel Borenstein &lt;nsb@bellcore.com>
   To: Ned Freed &lt;ned@innosoft.com>
   Subject: Formatted text mail
   MIME-Version: 1.0
   Content-Type: multipart/alternative; boundary=boundary42

   --boundary42

   Content-Type: text/plain; charset=utf-8

   ...plain text version of message goes here....

   --boundary42
   Content-Type: text/html; charset=utf-8

   .... html version of same message goes here ...

   --boundary42--
</pre>
<p><span id="more-1700"></span><br />
一個方法來自 <a href="http://www.pragprog.com/titles/fr_rr">Rails Recipes</a>(for Rails 1.1) 書上的作法:</p>
<pre>

  def signup_notification(user)
    @from        = EXTERNAL_MAIL_SENDER
    @subject     = "Please activate your new account"
    @sent_on     = Time.now
    @recipients  = user.email
    @body[:user] = user

    content_type "multipart/alternative"

    part :content_type => "text/plain",
       :body => render_message("signup_notification_plain", @body )
     part :content_type => "text/html",
       :body => render_message("signup_notification", @body )
  end
</pre>
<p>檔案則是 signup_notification_plain.rhtml 跟 signup_notification.rhtml</p>
<p>不過經過 IRC #elixus 的朋友反應，在 mutt 跟 thunderbird 上看不到正確的 encoding。這才仔細看了 raw email data，竟然出來了三段 part 且第一段少了 charset utf-8。試著幾種不同寫法仍得不到看起來正確的格式，Rails Wiki 上的<a href="http://wiki.rubyonrails.org/rails/pages/HowToSendMimeMultipartEmailsWithActionMailer">作法</a>出來的格式也很奇怪。</p>
<p>本來想棄守 console-based email client 了，只提供 HTML 就好，但是布丁大人堅持要有 Gracefully Degrading 啊。於是再去看 <a href="http://api.rubyonrails.org/classes/ActionMailer/Base.html">ActionMailer API</a> 發現其實有更簡單厲害的作法:</p>
<pre>
  def signup_notification(user)
    @from        = EXTERNAL_MAIL_SENDER
    @subject     = "Please activate your new account"
    @sent_on     = Time.now
    @recipients  = user.email
    @body[:user] = user
  end
</pre>
<p>檔案則要命名成 <strong>signup_notification.text.plain.erb</strong> 跟 <strong>signup_notification.text.html.erb</strong>。嗯，Rails 依據副檔名慣例會自動合併成一封信，而無須寫明 content_type 跟使用 part。</p>
<p>我自己還有用<a href="http://www.globalize-rails.org/globalize/">Globalize plugin</a>提供中英文不同版本，所以又還有 <strong>signup_notification.zh-TW.text.plain.erb</strong> 及 <strong>signup_notification.zh-TW.text.html.erb</strong>，真是長的副檔名啊 :></p>
]]></content:encoded>
			<wfw:commentRss>http://ihower.tw/blog/archives/1700/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Rails L10n 多國語言方案</title>
		<link>http://ihower.tw/blog/archives/1699</link>
		<comments>http://ihower.tw/blog/archives/1699#comments</comments>
		<pubDate>Sun, 17 Feb 2008 19:53:18 +0000</pubDate>
		<dc:creator>ihower</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Rails]]></category>

		<guid isPermaLink="false">http://ihower.idv.tw/blog/archives/1699</guid>
		<description><![CDATA[如果是需要多國語言，我在 registrano.com 是使用Globalize plugin，它是把翻譯資料放資料庫，因此還可以做個網站後台來修改，非常方便。語法則是

"Hello, World!".t

我個人是蠻喜歡這種 method call 寫法。不過它也提供與 gettext 一樣的語法:

_("Hello, World!")

下載請前往 Github。
另一個更簡單不需要資料庫的 plugin 是 Gibberish，它使用 key-value 方式跟 yml 檔案儲存，語法是

"Hello, World!"[:welcome]

如果不是要多國，而只是想要一國(如中文)，則可以考慮裝 Localization Simplified 跟 Localization Plugin，前者可以處理 Rails 預設的錯誤訊息跟日期格式等。後者則是因為我自己有個需求是 RHTML view code 可以用中文沒關係，但是我在 controller 或 model 裡蠻不想打中文的(一來是我不知道客戶要的確切文字，也不想讓人家直接去改 controller。二來是 Textmate 不支援中文)，所以需要一個最簡單的 L10n 機制來幫忙。
採用 Localization Simplified 後，發現還是碰到 Model attribute name 無法順利中文化，例如出現 &#8220;Password 不能是空白&#8221; 這樣的錯誤訊息。目前想到這樣 hacking:

#put this in /config/environment.rb
class String
 [...]]]></description>
			<content:encoded><![CDATA[<p>如果是需要多國語言，我在 <a href="registrano.com">registrano.com</a> 是使用<a href="http://www.globalize-rails.org/globalize/">Globalize</a> plugin，它是把翻譯資料放資料庫，因此還可以做個網站後台來修改，非常方便。語法則是</p>
<pre>
"Hello, World!".t
</pre>
<p>我個人是蠻喜歡這種 method call 寫法。不過它也提供與 gettext 一樣的語法:</p>
<pre>
_("Hello, World!")
</pre>
<p>下載請前往 <a href="http://github.com/yannlugrin/globalize/tree/master">Github</a>。</p>
<p>另一個更簡單不需要資料庫的 plugin 是<a href="http://errtheblog.com/posts/55-ya-talkin-gibberish"> Gibberish</a>，它使用 key-value 方式跟 yml 檔案儲存，語法是</p>
<pre>
"Hello, World!"[:welcome]
</pre>
<p>如果不是要多國，而只是想要一國(如中文)，則可以考慮裝 <a href="http://rubyforge.org/projects/l10n-simplified/">Localization Simplified</a> 跟 <a href="http://wiki.rubyonrails.org/rails/pages/Localization+Plugin">Localization Plugin</a>，前者可以處理 Rails 預設的錯誤訊息跟日期格式等。後者則是因為我自己有個需求是 RHTML view code 可以用中文沒關係，但是我在 controller 或 model 裡蠻不想打中文的(一來是我不知道客戶要的確切文字，也不想讓人家直接去改 controller。二來是 Textmate 不支援中文)，所以需要一個最簡單的 L10n 機制來幫忙。</p>
<p>採用 Localization Simplified 後，發現還是碰到 Model attribute name 無法順利中文化，例如出現 &#8220;Password 不能是空白&#8221; 這樣的錯誤訊息。目前想到這樣 hacking:</p>
<pre>
#put this in /config/environment.rb
class String
  def humanize
      _( self.to_s.gsub(/_id$/, "").gsub(/_/, " ").capitalize )
  end
end
</pre>
<p>於是就可以跟 Localization Plugin 接軌了 :></p>
]]></content:encoded>
			<wfw:commentRss>http://ihower.tw/blog/archives/1699/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>一些 Ruby Dynamic Features 記事 (3) define_method</title>
		<link>http://ihower.tw/blog/archives/1698</link>
		<comments>http://ihower.tw/blog/archives/1698#comments</comments>
		<pubDate>Fri, 08 Feb 2008 22:56:07 +0000</pubDate>
		<dc:creator>ihower</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://ihower.idv.tw/blog/archives/1698</guid>
		<description><![CDATA[Update(2010/4/15): self, current class 與 class_eval, instance_eval 的關係
define_method 讓函式名稱也可以參數化。

動態定義物件方法 drag.foo

#法一
Dragon.send(:define_method, :foo){ puts "bar" }

#法二
class Dragon
    define_method(:foo) { puts "bar" }

    ['a','b','c','d','e','f'].each do &#124;x&#124;
        define_method(x) { puts x }
     end
end

#法三
Dragon.class_eval do
    define_method(:foo) { puts "bar" }

 [...]]]></description>
			<content:encoded><![CDATA[<p>Update(2010/4/15): <a href="http://gist.github.com/366372">self, current class 與 class_eval, instance_eval 的關係</a></p>
<p>define_method 讓函式名稱也可以參數化。</p>
<p><span id="more-1698"></span></p>
<h2>動態定義物件方法 drag.foo</h2>
<pre>
#法一
Dragon.send(:define_method, :foo){ puts "bar" }

#法二
class Dragon
    define_method(:foo) { puts "bar" }

    ['a','b','c','d','e','f'].each do |x|
        define_method(x) { puts x }
     end
end

#法三
Dragon.class_eval do
    define_method(:foo) { puts "bar" }

    def foo2
        puts "bar2"
    end
end

#法四
class Dragon
    def new_method(name, &#038;block)
        self.class.send(:define_method,name, &#038;block)
        #或
        self.class.class_eval do
            define_method(name, &#038;block)
        end
    end
end

drag.new_method(:foo) { puts "bar" }
</pre>
<h2>動態定義類別方法 Dragon.foo </h2>
<pre>
# 法一
class Dragon
    class &lt;&lt; self
        define_method(:foo) { puts "bar" }
    end
end

# 法二
class Dragon; end

metaclass = (class << Dragon; self; end)
metaclass.instance_eval do # 用 class_eval 也可以
    define_method("foo") { puts "bar" }
end
</pre>
]]></content:encoded>
			<wfw:commentRss>http://ihower.tw/blog/archives/1698/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>一些 Ruby Dynamic Features 記事 (2) singleton class</title>
		<link>http://ihower.tw/blog/archives/1697</link>
		<comments>http://ihower.tw/blog/archives/1697#comments</comments>
		<pubDate>Fri, 08 Feb 2008 22:49:26 +0000</pubDate>
		<dc:creator>ihower</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://ihower.idv.tw/blog/archives/1697</guid>
		<description><![CDATA[在Ruby中，singleton method指的是只有該物件才有的方法，而加在 Class object 上的 singleton method 也就變成了 class method。

假設目前有以下的程式碼:

 class Dragon
 end
 dran = Dragon.new

我想定義物件方法 dran.foo

#法一
class Dragon
    def n
        puts "nnn"
    end
end
#法二
Dragon.class_eval do
    def foo
        puts "bar"
    end
end

我想定義只有這個物件才有的方法 [...]]]></description>
			<content:encoded><![CDATA[<p>在Ruby中，singleton method指的是只有該物件才有的方法，而加在 Class object 上的 singleton method 也就變成了 class method。</p>
<p><span id="more-1697"></span></p>
<p>假設目前有以下的程式碼:</p>
<pre>
 class Dragon
 end
 dran = Dragon.new
</pre>
<h2>我想定義物件方法 dran.foo</h2>
<pre>
#法一
class Dragon
    def n
        puts "nnn"
    end
end
#法二
Dragon.class_eval do
    def foo
        puts "bar"
    end
end
</pre>
<h2>我想定義只有這個物件才有的方法 dran.foo (叫做 singleton method)</h2>
<pre>
#法一
class &lt;&lt; dran
    def foo
        puts "bar"
    end
end
drag.foo

#法二
drgn.instance_eval do
    def foo
        puts "bar"
    end
end

#法三
def dran.foo
    puts "bar"
end
</pre>
<h2>我想定義類別方法 Dragon.foo (class methods) </h2>
<pre>
#法一
class &lt;&lt; Dragon
    def foo
        puts "bar"
    end
end

#法二
class Dragon
    def self.foo
        puts "bar"
    end
end

#法三
class Dragon
    class &lt;&lt; self
        def foo
            puts "bar"
        end
     end
end

#法四
Dragon.instance_eval
    def foo
     puts "bar"
    end
end

#法五

Dragon.class_eval do # 這裡改用 instance_eval 也可以耶(why?)
  def self.foo
    puts "bar"
  end
end
</pre>
]]></content:encoded>
			<wfw:commentRss>http://ihower.tw/blog/archives/1697/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>一些 Ruby Dynamic Features 記事  (1) OOP</title>
		<link>http://ihower.tw/blog/archives/1696</link>
		<comments>http://ihower.tw/blog/archives/1696#comments</comments>
		<pubDate>Fri, 08 Feb 2008 22:29:10 +0000</pubDate>
		<dc:creator>ihower</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://ihower.idv.tw/blog/archives/1696</guid>
		<description><![CDATA[趁過年把 The Ruby Way 拿起來讀一讀，大部頭一本沒辦法從頭念到尾，就挑了最重要的第11章( OOP and Dynamic Features in Ruby) 來念。這個主題值得閱讀的還有 Ruby for Rails 第13章(Ruby dynamics) 跟最近出的 Advanced Rails 一書第1章(Foundational Techniques)，因此這幾篇文我還會修修改改就是了。

A &#8220;Fancy&#8221; Constructor

class Foo
 attr_accessor :a,:b
 def initialize(&#38;block)
  instance_eval &#38;block
 end
end
於是可以傳入 block 來使用建構式

bar = Foo.new do
 self.a = 1
 self.b = 2
end
class instance variables
因為 Ruby 的 class 也是物件(即 Object 的 instance )，所以除了 class [...]]]></description>
			<content:encoded><![CDATA[<p>趁過年把 The Ruby Way 拿起來讀一讀，大部頭一本沒辦法從頭念到尾，就挑了最重要的第11章( OOP and Dynamic Features in Ruby) 來念。這個主題值得閱讀的還有 Ruby for Rails 第13章(Ruby dynamics) 跟最近出的 Advanced Rails 一書第1章(Foundational Techniques)，因此這幾篇文我還會修修改改就是了。</p>
<p><span id="more-1696"></span></p>
<h2>A &#8220;Fancy&#8221; Constructor</h2>
<pre>
class Foo
 attr_accessor :a,:b
 def initialize(&amp;block)
  instance_eval &amp;block
 end
end</pre>
<p>於是可以傳入 block 來使用建構式</p>
<pre>
bar = Foo.new do
 self.a = 1
 self.b = 2
end</pre>
<h2>class instance variables</h2>
<p>因為 Ruby 的 class 也是物件(即 Object 的 instance )，所以除了 class variable 跟 instance variable 之外，還有種變數叫做 class instance variables (即 class Class 的 instance variable 叫做 the class instance variable)。</p>
<pre>
class Foo
@@var =1 # 這是 class variable
@var = 2 # 這是 class instance variable
attr_accessor :var #這將定義出 instance variable

class &lt;&lt; self
 attr_accessor :var2 #這將定義出 class instance variable
end

 def initialize
  @var,@var2 = 4,5 # 這是 instance variable
 end

 def mymethod1
  puts @@var # 1 這是 class variable
  puts @var # 4  這是 instance variable
  puts @var2 # 5  這是 instance variable
 end

 def Foo.classmethod
  puts @@var # 1 這是 class variable
  puts @var # 2 這是 class instance variable
  Foo.var2 = 6 # 這也是 class instance variable
end
end

class BarOne &lt; Foo
  self.var2 = 7
end

class BarTwo &lt; Foo
  self.var2 = 8
end

Foo.classmethod
puts Foo.var2 # 6
puts BarOne.var2 #7
puts BarTwo.var2 #8
</pre>
<p> class instance variable 跟 class variable 最大的不同在於，class variable 在整個 class 繼承體系都是指同一個，而 class instance variable 則是各個 class 皆不同，因為如此 class instance variable 很多時候比 class variable 好用，像是參數化的 classes。</p>
<h2>const_get</h2>
<p> const_get 可以從名稱拿到常數:</p>
<pre>
 str = "PI"
 Math.const_get(str) # 得到 Math::PI
</pre>
<p>而在 Ruby 裡面所有的 class 也都是 Object 裡的常數，所以要由名稱拿到 class 是這樣寫 : </p>
<pre>
 classname = "Array"
 klass = Object.const_get(classname) # 拿到 Array
 arr = klass.new(4,1) # [1,1,1,1]
</pre>
<h2>binding</h2>
<p>利用 eval 跟內建的 Binding 物件，我們可以把當下的 context 傳到函數裡面，建立出與本來的 scope 一樣的執行環境(因此這裡可以存取到 local variables )。</p>
<pre>
def print_var( var_name, binding )
 eval "puts #{var_name}", binding
end

foobar = 10
print_var( 'foobar' , binding )
</pre>
]]></content:encoded>
			<wfw:commentRss>http://ihower.tw/blog/archives/1696/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Spakit: A SPA Rails plugin</title>
		<link>http://ihower.tw/blog/archives/1695</link>
		<comments>http://ihower.tw/blog/archives/1695#comments</comments>
		<pubDate>Sat, 19 Jan 2008 02:52:50 +0000</pubDate>
		<dc:creator>ihower</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Rails]]></category>

		<guid isPermaLink="false">http://ihower.idv.tw/blog/archives/1695</guid>
		<description><![CDATA[Spakit 源自 registrano.com 和 Massage，是一個可以讓你將現成的網站輕鬆轉換成 SPA (single page application) 的 Rails Plugin。只需在 view 中換用 Spakit helper，不需要更動 controller code，就可將超連結和表單送出變成 Ajax 送出並更新頁面。
spakit 目前放在 github，另外也包成 gem，安裝方式如下:

 gem install spakit
 cd /your_rails_app/vendor/plugin
 gem unpack spakit
 mv spakit-0.9.0 spakit

並且 rename 成 spakit。裝好之後，首先建立一個 Spakit 專用的 Layout，一個簡單的範例如下:

# /view/layouts/spakit.rhtml
&#60;p&#62;&#60;%= flash[:notice] %&#62;&#60;/p&#62;
&#60;%= yield %&#62;
接著就可以使用 Spakit 專用的 helpers，目前有 spakit_link_to, spakit_form_for 和 spakit_form_tag 三種可以使用，用法如下:

spakit_link_to 'new person', [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://rubyforge.org/projects/spakit/">Spakit </a>源自 <a href="http://registrano.com">registrano.com</a> 和 <a href="http://blog.handlino.com/2007/07/massage-spa-plugin-for-rails.html">Massage</a>，是一個可以讓你將現成的網站輕鬆轉換成 <a href="http://en.wikipedia.org/wiki/Single_page_application">SPA</a> (single page application) 的 Rails Plugin。只需在 view 中換用 Spakit helper，不需要更動 controller code，就可將超連結和表單送出變成 Ajax 送出並更新頁面。</p>
<p>spakit 目前放在 <a href="http://github.com/ihower/spakit/tree">github</a>，另外也包成 <a href="https://rubyforge.org/projects/spakit/">gem</a>，安裝方式如下:</p>
<pre>
 gem install spakit
 cd /your_rails_app/vendor/plugin
 gem unpack spakit
 mv spakit-0.9.0 spakit
</pre>
<p>並且 rename 成 spakit。裝好之後，首先建立一個 Spakit 專用的 Layout，一個簡單的範例如下:</p>
<pre>
# /view/layouts/spakit.rhtml
&lt;p&gt;&lt;%= flash[:notice] %&gt;&lt;/p&gt;
&lt;%= yield %&gt;</pre>
<p>接著就可以使用 Spakit 專用的 helpers，目前有 spakit_link_to, spakit_form_for 和 spakit_form_tag 三種可以使用，用法如下:</p>
<pre>
spakit_link_to 'new person', :url =&gt; new_person_path
spakit_form_for @person, :url =&gt; people_path</pre>
<p>如此按下連結或送出表單後，Spakit 就會送出 Ajax request(i.e. prototype&#8217;s Ajax.Updater )，然後將回傳的 HTML 結果更新到#content 區塊。</p>
<p>如果你想要支援 Ajax 的 Histroy Bookmarks，建議可以採用 <a href="http://code.google.com/p/reallysimplehistory/">Really Simple History</a>(RSH) library 來處理瀏覽器的上一頁下一頁問題，這部份的範例程式附在 plugin 的 <a href="http://spakit.rubyforge.org/svn/trunk/README">README</a> (使用到 jquery library，好心人有空可以幫忙寫 prototype version)。</p>
<p>P.S. 這篇文的英文版在 <a href="http://handlino.com/blog/2008/02/17/52/">Handlino&#8217;s blog</a></p>
]]></content:encoded>
			<wfw:commentRss>http://ihower.tw/blog/archives/1695/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>如何寫出有效率的 Ruby Code</title>
		<link>http://ihower.tw/blog/archives/1691</link>
		<comments>http://ihower.tw/blog/archives/1691#comments</comments>
		<pubDate>Wed, 02 Jan 2008 20:48:31 +0000</pubDate>
		<dc:creator>ihower</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://ihower.idv.tw/blog/archives/1691</guid>
		<description><![CDATA[又來推薦 PDF 武功秘笈， Writing Efficient Ruby Code。
Ruby 是個很慢的語言，但有些作法應用得當，還是會有不錯的改善。不過要知道程式碼的可讀性跟執行效率有時候是衝突的，這點還需拿捏，尤其 software life cycle 一開始可讀性比較重要。有句最佳化的經典名言一定要引一下:
 未成年就這麼優，是一切邪惡的根源
Premature optimization is the root of all evil
這裡紀錄一些看到有趣的事情，PDF 裡有更詳盡的 example code。

Instance Variables versus Accessors
@attrubute 比 self.attrbute 快(method call 比較貴)，如果你不需要 public method 或有 sub-class 的需求，請考慮不要用 attr_accessor 等方式來建立 read/write method。
Local Variables Are Cheap
method 中傳進來的參數若常用，可以先存成 Local Variables 再來多次使用。
Interpolated Strnges
方法一 s = &#8220;:#{a}.#{b}&#8221; 比方法二 s = &#8220;:&#8221; [...]]]></description>
			<content:encoded><![CDATA[<p>又來推薦 PDF 武功秘笈， <a href="http://www.informit.com/store/product.aspx?isbn=0321540034&amp;rl=1">Writing Efficient Ruby Code</a>。</p>
<p>Ruby 是個很慢的語言，但有些作法應用得當，還是會有不錯的改善。不過要知道程式碼的可讀性跟執行效率有時候是衝突的，這點還需拿捏，尤其 software life cycle 一開始可讀性比較重要。有句最佳化的<a href="http://lukhnos.org/blog/zh/archives/575">經典名言</a>一定要引一下:</p>
<p style="margin: 1em"> <span style="font-size: 150%">未成年就這麼優，是一切邪惡的根源</span><br />
<span style="font-size: 120%">Premature optimization is the root of all evil</span></p>
<p>這裡紀錄一些看到有趣的事情，PDF 裡有更詳盡的 example code。</p>
<p><span id="more-1691"></span></p>
<h3>Instance Variables versus Accessors</h3>
<p>@attrubute 比 self.attrbute 快(method call 比較貴)，如果你不需要 public method 或有 sub-class 的需求，請考慮不要用 attr_accessor 等方式來建立 read/write method。</p>
<h3>Local Variables Are Cheap</h3>
<p>method 中傳進來的參數若常用，可以先存成 Local Variables 再來多次使用。</p>
<h3>Interpolated Strnges</h3>
<p>方法一 s = &#8220;:#{a}.#{b}&#8221; 比方法二 s = &#8220;:&#8221; &lt;&lt; a.to_s &lt;&lt; &#8220;.&#8221; &lt;&lt; b.to_s 快多囉!! 不需要多用 &lt;&lt; method call。另外單引號跟雙引號沒有效率上的差別。</p>
<h3>In-Place Updates</h3>
<p>直接修改比複製一份快: gsub! 比 gsub 快，merge! 比 merge 快。例如這個範例 s.gsub().gsub!().gsub! 而不是 s.gsub.gsub.gsub</p>
<h3>Set Versus Array</h3>
<p>如果你只用到 Array 的 uniq, |, %, &#8211; 等群集操作，請考慮改用 Set 會比較快，像是 include? 就可以在 O(1) 做完。而 Hash 又比 Array 貴。</p>
<h3>Make Decisions at Load Time</h3>
<p>注意到在 module 或 class 的 definition scope 也是可以執行程式的，而且只有第一次 require source code 時會執行編譯一次。</p>
<blockquote><p>class Foo<br />
if C=&#8217;somesetting&#8217;<br />
def A<span class="Apple-tab-span" style="white-space: pre"></span><br />
&#8230;version 1<br />
end<br />
else<br />
def A<br />
&#8230;version 2<br />
end<br />
end<br />
end</p></blockquote>
<h3>Self-Modifying Code</h3>
<p>避免寫出自己修改自己定義的程式(也不好讀code吧)，改用 singleton 方式來做 alias_method 跟 remove_method。</p>
<h3>Test Most Frequent Case First</h3>
<p>用 case 時把較常發生的放前面，若都差不多，把貴的操作的放後面。</p>
<h3>Optimize Access to Global Constants</h3>
<p>在 Constant 前面加上 namespace operator :: 會比較快，減少查詢時間</p>
<h3>Caching Data in Instance Variables</h3>
<p>例如在 controller 裡面 def captial_letters { (&#8220;A&#8221;..&#8221;Z&#8221;).to_a }  end 請改用 def captial_letters { @captial_letters ||= (&#8220;A&#8221;..&#8221;Z&#8221;).to_a } end</p>
<h3>Caching Data in Class Variables</h3>
<p>承上，如果 data 較大又持續存在，可以改用 @@capital_letter = (&#8220;A&#8221;..&#8221;Z&#8221;).to_a 然後定 def captial_letters { @@captial_letters }  end。在 ActiveRecord 中也可以先把 DB 的資料讀出來當作 CONSTANTS，就不需要每次都查資料庫了:</p>
<blockquote><p>class  State &lt; ActiveRecord::Base<br />
NAMES_ABBR = self.find(:all).map do { |s| [s.name,s.abbr] }<br />
end</p></blockquote>
<h3>Coding Variable Caching Efficiently</h3>
<p>愛用 @var ||= begin &#8230;expr&#8230; end 而不是 @var = begin &#8230;expr&#8230; end unless @var，這樣只需查 @var 一次。但如果 @var要能吃 nil 或 false 就只能多一個 boolean 變數來幫忙了。</p>
<h3>Initializing Variables with nil</h3>
<p>若沒有給值，就不需要初始成nil</p>
<h3>Using .nil?</h3>
<p>如果要測試的值已知不是 Boolean，那就不需要用 .nil 多一個 method call，直接 if x 就好了而不是 if x.nil?</p>
<h3>nil? or empty? versus blank?</h3>
<p>ActionPack 有提供 x.blank?，不需要用 x.nil? || x.empty?</p>
<h3>Using return</h3>
<p>雖然 Ruby 很聰明會自動回傳最後運算的值，但明確寫 return 會比較快 (Ruby 1.9有改善)</p>
<h3>Using any?</h3>
<p>用 empty? 來測試 string, array or hash 而不要用 any?，速度差個兩倍 (而且 Ruby 1.9 也拿掉 any?這個method了)</p>
<h3>Block Local Variables</h3>
<p>在 Ruby 1.8 裡面，在 Block 裡面存取 Block 外面的 local variable 竟然比裡面的 local variable 還快!! 不過 Ruby 1.9 修正這個奇怪的事實就是了。</p>
<h3>Parallel Assignments</h3>
<p>很方便沒錯，但 a,b=1,2 運算還丟出來 Array [1,2] 是垃圾。(Ruby 1.9 修正了，改丟 true)</p>
<h3>Date Formatting</h3>
<p>用 parse_date 把 String 變成 Date 是個非常昂貴的操作，建議你直接用字串操作(正規表示法)把 String(例如從DB撈出來的db_date) 變成你想要的格式，或是從中拆出你想要的年月日。(PDF 有 example code)</p>
<h3>Temporary Data Structure Constants</h3>
<p>如果用到一個暫時的資料結構(如Array)但接下來不會更改，可以改用 Constant 宣告，並避免複製。</p>
<h3>ObjectSpace.each_object</h3>
<p>DO NOT USE IT，你不會想在 per-request 下去執行的，很貴。</p>
<h3>Unnecessary Block Parameters</h3>
<p>def foo(bar, &amp;block) 的 &amp;block 若在函式內不需要用到(可以直接用 yield 使用的)，請不要加。轉成 Proc 比單純 yield 的慢5倍!!</p>
<h3>Symbol.to_proc</h3>
<p>ActiveSupport 提供的 @var.map(&amp;:name) 語法，雖然方便，但是用 inline block 的方法 @var.map{ |a| a.name| } 執行效率比較快。</p>
<h3>Requiring Files Dynamically</h3>
<p>在 Rails 中若碰到一個未定義的常數，它會自動去 load source files 來載入定義。為了避免混淆 file-loading 機制，不需寫 require &#8216;client&#8217;  (這又是另一套 auto-loaded 機制)，只要單單一行寫 Client 就好了。</p>
<h3>Including Modules versus  Opening Classes</h3>
<p>使用 module mix-in 會比打開 class 加入 method 還慢一些，如果你的 module 只針對一個 class 做 mix-in，那不如直接打開來加入 methods 就好了。</p>
]]></content:encoded>
			<wfw:commentRss>http://ihower.tw/blog/archives/1691/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>7/31 (二) Ruby Tuesday: RSpec</title>
		<link>http://ihower.tw/blog/archives/1674</link>
		<comments>http://ihower.tw/blog/archives/1674#comments</comments>
		<pubDate>Sun, 29 Jul 2007 18:01:27 +0000</pubDate>
		<dc:creator>ihower</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Testing]]></category>

		<guid isPermaLink="false">http://ihower.idv.tw/blog/archives/1674</guid>
		<description><![CDATA[Update(2007/7/31): 今日的投影片在這裡(xul)，請用 Firefox 開啟瀏覽。
這一次依然由我來跟大家分享 Ruby/Rails testing，題目是 RSpec, a Behavior-driven development framework。
時間是晚上 8:00-10:00，地點在 opcafe 三代。
]]></description>
			<content:encoded><![CDATA[<p>Update(2007/7/31): 今日的投影片<a href="http://ihower.idv.tw/presentation/rspec.xul">在這裡(xul)</a>，請用 Firefox 開啟瀏覽。</p>
<p>這一次依然由我來跟大家分享 Ruby/Rails testing，題目是 <a href="http://rspec.rubyforge.org/index.html" onclick="javascript:urchinTracker ('/outbound/article/rspec.rubyforge.org');">RSpec</a>, a Behavior-driven development framework。</p>
<p>時間是晚上 8:00-10:00，地點在 <a href="http://opcafe.net/" onclick="javascript:urchinTracker ('/outbound/article/opcafe.net');">opcafe</a> 三代。</p>
]]></content:encoded>
			<wfw:commentRss>http://ihower.tw/blog/archives/1674/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
