當你有多個設計選擇,不知道哪一種比較好的時候,A/B testing 可以幫助你測試哪一種比較有效果。例如: 你的網站註冊有兩種設計方式,哪一種讓比較多人註冊呢? 你的購物車按鈕有兩種設計,哪一種比較讓人順利結帳呢? 你的廣告版本有三種,哪一種比較吸引人呢?

別猜了!! 公說公有理,婆說婆有理。讓使用者的實際體驗跟統計結果來告訴你吧。a/b testing 的作法是: 1.輪撥這些選項 2. 設定追蹤點(goal) 3. 一段時間後,觀察哪個選項達成的 goal 比較多。

而 A/B testing 工具可以幫助你很容易做好這些事情: 1. 針對不同人提供不同選項 2. 但讓同一個人看到的都是同一個選項(這是一個重點,不然測試就不準了),例如透過 cookie 或使用者ID 3. 提供後台報表,並提供告訴你這些數據是否有統計學上的顯著差異 4. 方便安裝及使用 5. 夠快,不會對 production site 造成效能負擔

對 Rails 來說,目前有兩套可以考慮使用: A/BingoVanity。A/Bingo 很容易安裝使用,功能比較陽春,使用 ActiveRecord 搭配快取(memcached) 來記錄資料。Vanity 功能比較豐富,願景也比較大,提倡了一整套的 Experiment Driven Development 開發方式。它使用了 Redis 來記錄資料。不過他的使用文件似乎沒有跟上程式的更新速度,所以不太好安裝,得去翻 source code。如果你現在就想試試,我會先推薦 A/Bingo 比較容易上手。

如果你不想改 server side 的程式(或是你是不會寫程式的行銷人員),也可以透過 Google Website Optimizer 這套工具來做 a/b testing。Google 的方式就單純用 JavaScript 來記錄: 你先告訴 Google 你有哪幾種頁面,例如 Original page、Variation page 1、Variation page 2 三種選項,接著提供 Conversion page 是指達成 goal 的頁面,然後將 Google 會給你 control script 貼到 Original page 去(讓使用者可以輪撥到其他頁面),以及 tracking script 貼到各個頁面。

最後,A/B Testing 當然也不是萬能的: Why A/B testing of web design fails

ActiveSupport::Concern 是 Rails3 做 Modularity 的一個重要的小工具。他的任務是讓管理 modules 之間的 dependencies 變得容易。

假設我們有兩個 Modules 有依存關係,module Bar 依存於 module Foo,然後有一個宿主 Host 類別希望 include Bar 的功能,我們可以這樣寫:


module Foo
   # self.included 這個函式會在 Foo 被 include 時執行
    def self.included(base)
        base.send(:do_host_something) # 對宿主做某些操作,例如增強功能等等
    end
end

module Bar
    def self.included(base)
        base.send(:do_host_something)
    end
end

class Host
  include Foo, Bar
end

這有個討厭的缺點就是,我們必須在宿主中同時 include Foo 跟 Bar,也就是要把所有依存的 modules 都 include 進來。這很糟糕啊,為什麼我們需要在 Host 裡面知道這些 modules 的依存關係呢 :/

我們希望能夠將 modules 的依存關係寫在 module 中,而宿主 Host 就只要使用就好了。所以我們試著改寫成:


module Bar
  include Foo # 因為 Bar 依存於 Foo,所以我們在這裡 include 它

  def self.included(base)
    base.send(:do_host_something)
  end

end

class Host
  include Bar # 只要 include Bar 就好,不需要知道 Bar 還依存哪些 modules
end

這樣乍看之下好像沒問題,但是卻有個嚴重的問題導致無法執行,因為 Foo 變成是由 Bar 所 include,所以對 Foo 的 self.included 來說,他的參數 base 變成了 Bar 了,所以他就沒辦法存取到宿主 Host 的任何函式及變數,do_host_something 時就會失敗。

Okay,ActiveSupport::Concern 就是來幫助解決這個難題,我們希望宿主可以不需要知道 modules 之間的 dependencies 關係。dependencies 關係寫在 module 裡面就好了。


require 'active_support/concern'

module Foo
    extend ActiveSupport::Concern
    included do
        self.send(:do_host_something)
    end
end

module Bar
    extend ActiveSupport::Concern
    include Foo # 因為 Bar 依存於 Foo,所以我們在這裡 include 它

    included do
        self.send(:do_host_something)
    end
end

class Host
  include Bar # 只要 include Bar 就好,不需要知道 Bar 還依存哪些 modules
end


如此就搞定了。One more thing,如果你有定義 module ClassMethods 和 module InstanceMethods 在裡面的話,它也會自動幫你載入到宿主裡面去,就不用自己寫 send(:include, InstanceMethods) 跟 send(:extend, ClassMethods) 了。用法舉例:



module Foo
    extend ActiveSupport::Concern
    included do
        self.send(:do_host_something)
    end

   module ClassMethods
      def bite
        # do something
      end
   end

   module InstanceMethods
      def poke
         # do something
      end
   end
end


想知道 ActiveSupport::Concern 到底怎麼實作的話,請看 /activesupport/lib/active_support/concern.rb,只有 29 行,而且 ActiveSupport::Concern 也沒有再依存其他東西了,嘿。

在 Rails3 中,處理 JavaScript 改採用 UJS 的方式,也就是以往 inline JS 的情況將不復見,link_to_remote 跟 remote_form_for 都沒有了:

例如:

link_to 'Logout', session_path, :method => :delete

產生出來的是:

<a href="/session" data-method="delete" rel="nofollow">Logoout</a>


例如

link_to 'Ajax delete', person_path(person),
      :remote => true, :confirm => "Are you sure?"


產生出來的是

<a href="/people/1"  data-confirm="Are you sure?" data-method="delete"
              data-remote="true" rel="nofollow">Ajax delete</a>
# 點下去會向 Server 要求 JavaScript 內容執行

Rails3 預設還是使用 Prototype.js,要換成 jQuery 非常簡單。首先在產生 rails 專案時,可以輸入 rails your_project -J 就不會產生 Prototype.js 的檔案。

修改 /public/javascripts/rails.js 這個檔案,內容換成 http://github.com/rails/jquery-ujs/blob/master/src/rails.js 這個官方提供的 jQuery js driver。

在 Rails layout 的 HTML head 中加入:

<%= csrf_meta_tag %>
<%= javascript_include_tag "http://ajax.googleapis.com/ajax/libs/jquery/1.4.1/jquery.min.js" %>
<%= javascript_include_tag 'rails' %>

其中 csrf_mate_tag 產生的是做 CSRF 防禦的 authenticity_token,以往這也是 inline 在 HTML 中,現在則是先宣告在 HTML head 中。

另外要提醒你,如果你有使用 RJS 的話,Rails3 的 RJS 還是只會產生 prototype.js 的版本。所以如果非得用 RJS 的話,目前恐怕還是得裝以往 Rails2 用的 jRails,這好像有點蠢。也許是時候把 RJS 徹底揚棄,直接寫 jQuery 好了(?)

例如上面的 Ajax delete 例子,它的 controller 跟 js 可以這樣:

# people_controller.rb
  def destroy
    @person = Person.find(params[:id])
    @person.destroy

    respond_to do |format|
      format.html { redirect_to(people_url) }
      format.js # destroy.js.erb
    end
  end

# destroy.js.erb
jQuery("#<%= dom_id(@person)%>").remove();

這次很難得臨時邀請到即將前往日本慶應大學 W3C 擔任研究員的 Kenny 來跟我們分享語意網(Semantic Web)的現況、概論與鍵連資料(Linked Data)雲,所以臨時將 #9 我的 Rails Performance & Security 講題移到 Ruby Tuesday #10。

這裡的雲指的是這張圖,其中一個圈大概是一個RDF資料庫,連結就是跨domain的RDF的連結,這個架構可以把全部的資料庫們視為一個資料庫查詢,所以我們把這整個RDF Web稱為一個雲。

語意網的技術包括有RDF+SPARQL(RDF版的SQL)+RDFa+OWL+RIF,因為時間有限,講者會跟我們分享其中的概念總論,參考資料是講者老闆(Tim Berners-Lee,WWW 發明人) 在TED 2009的演講。當然,也會分享 RDFa on Rails 實作。

報名 #9 請前往: http://registrano.com/events/ruby-tuesday-9,時間是 2010/3/9 (二)

報名 #10 請前往: http://registrano.com/events/ruby-tuesday-10,時間是 2010/3/23 (二)

又要開課啦,由 中央研究院 資訊創新研究中心自由軟體鑄造場主辦的自由軟體技術分享工作坊—Ruby On Rails由淺入深課程。這是兩整天共 12hr 的上機實作課程,名額有限、完全免費。

活動名稱:自由軟體技術分享工作坊—Ruby On Rails由淺入深
活動時間:2010.02.27(六)/ 2010.02.28(日)
活動講者:ihower
報名首頁:http://whoswho.openfoundry.org/workshop.html
活動地點:台北市復興北路99號2樓 (恆逸教育訓練中心)

裝了 Rails3 beta 之後啊,那個 rails 新建專案的指令就變成產生 Rails3 了,那要怎麼產生本來的 Rails2 版本呢? 今天 Ruby Tuesday 聚會有人問了我這個問題。

整理 Rails3 資料的時候就有看到解法,回答如下:

首先,如果還沒裝 Rails3,請先將本來 Rails2 版本的 rails 指令複製一份成 rails2 (可以打 which rails 可以找到位置,這個檔案跟 gems/rails-2.3.5/bin/rails 不一樣哩),然後將裡面的 version = “>= 0″ 改成 version = “~> 2.0″ 即可。之後執行 rails2 project_name 就可以產生 Rails2 版本的專案了。這個 rails2 的檔案長得如下(第一行的 ruby 位置你的可能跟我不一樣):


#!/usr/local/bin/ruby
#
# This file was generated by RubyGems.
#
# The application 'rails' is installed as part of a gem, and
# this file is here to facilitate running it.
#

require 'rubygems'

version = "~> 2.0"

if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
  version = $1
  ARGV.shift
end

gem 'rails', version
load Gem.bin_path('rails', 'rails', version)

如果你已經安裝了 Rails3,那 Rails3 的這個檔案跟 Rails2 的差異只在最後兩行而已。

Next Page »