傑克達與瑞兒絲的大小事

快速筆記整理網站開發上碰到的各種技巧和問題,免得年紀大忘光,至少還有一個地方可以查找

Monthly Archives: 四月 2012

查詢遠端的 gem 檔

如果想要知道查詢目前有哪些 gem 檔可用,除了可以利用 The Ruby Toolbox 這個網站查找之外

可以透過 gem search -r 指令來找尋目前遠端上符合關鍵字的 gem 檔, 如:

$ gem search -r ckeditor
廣告

在 nginx 上執行 ruby on rails

最近想要在本地端設定一組 local 的 domain name 來測試一些劇情,所以需要 nginx 的配合,但又不想透過 unicorn 做為 web server ,因為不方便開發時 debug, 之前一直在思考如何在 nginx 上執行 ROR,以往的教學範例都是直接透過 rails -s 直接執行 web server,後來查到一篇文章教學,原來這麼簡單,趕快筆記一下

實作方式:

建立一組 nginx vhost 設定,內容如下:

upstream myapp {
server localhost:3000;
}
server {
listen 80;
server_name myapp.example.local;

access_log /var/www/myapp.example.local/log/access.log;
error_log /var/www/myapp.example.local/log/error.log;
root /var/www/myapp.example.local;
index index.html;

location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
try_files /system/maintenance.html $uri $uri/index.html $uri.html @ruby;
}

location @ruby {
proxy_pass http://myapp;
}
}

只要設定 uptream  指向到本地端的 rails server 就好了,最後在瀏覽器上輸入剛剛自訂好的 domain name (myapp.example.local) ,應該就能正確看到畫面結果,這樣開發時也比較容易 debug 了

p.s:

  1. 完成新增 vhost 設定後,記得要重啟 nginx
  2. 別忘了在本地端的 /etc/hosts 下新增一組 domain name 設定

參考文章:

http://kevin.vanzonneveld.net/techblog/article/ruby_with_nginx_on_ubuntu_lucid/

什麼是 A/B Testing

最近聽同事提到 A/B Testing 這個名詞,完全就是個菜鳥的我當然是什麼都不懂,為了讓自己加深印象,趕快將查到的相關資料整理重點筆記一下

什麼是 A/B Testing:

節錄自 阿舍的隨手記記、隨手寫寫… – A/B Testing 是什麼 ? 文章的定義:

A/B Testing 的用途是用來測試多種版本的網站編排,讓網站的設計者或經營者能透過對不同版本的網站排版方式來測試那一種是最能達到想要的目的,包括購買、註冊、點閱及下載等不同的目的。

節錄自 ihower – 使用 A/B Testing 工具引導你做網站設計 這篇文章的解釋

當你有多個設計選擇,不知道哪一種比較好的時候,A/B testing 可以幫助你測試哪一種比較有效果。例如: 你的網站註冊有兩種設計方式,哪一種讓比較多人註冊呢?

a/b testing 的作法是: 1.輪撥這些選項 2. 設定追蹤點(goal) 3. 一段時間後,觀察哪個選項達成的 goal 比較多

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

綜合以上解釋, A/B Testing 就是在幫你驗證多種不同的網站設計架構,何者最能達成你所設定的目標,利用實際的數據來佐證

相關工具:

目前 Google 提供了 Google Website Optimizer 這套工具來做 a/b testing。Google 的方式就單純用 JavaScript 來記錄:

  1. 你先告訴 Google 你有哪幾種頁面,例如 Original page、Variation page 1、Variation page 2 三種選項
  2. 接著提供 Conversion page 是指達成 goal 的頁面
  3. 然後將 Google 會給你 control script 貼到 Original page 去(讓使用者可以輪撥到其他頁面)
  4. 以及 tracking script 貼到各個頁面。

這邊有篇由 Ryan Carson 提供透過 Goolgle Website Optimizer 搭配 wordpress 實作 A/B Testing 的教學影片,可以參考一下,影片網址: How to do A/B Testing in WordPress

Rails 如何實作 Testing:

目前以 Rails 來說,有三套可以考慮使用: A/Bingo 、Split 和 Vanity。 A/Bingo 和 Split 都很容易安裝和使用,Rails Cast 有相關的教學影片可參考

#214 A/B Testing with A/Bingo : http://railscasts.com/episodes/214-a-b-testing-with-a-bingo

#331 A/B Testing with Split : http://railscasts.com/episodes/331-a-b-testing-with-split

參考文章:

http://ihower.tw/blog/archives/3974

http://www.arthurtoday.com/2010/09/ab-testing.html

http://thinkvitamin.com/design/how-to-do-ab-testing-in-wordpress/

如何在 Rails 實作 Multitenant Application 架構

最近在專案開發中必須使用到 Multitenant Application 的概念來實作

由於第一次接觸,還是快速筆記免得之後又忘記

什麼是 Multitenant Application

在使用 Multitenant Application 前,還是必須先搞清楚什麼是 Multitenant Application 才能正確理解各項操作的意義
在這邊引用了 阿舍的隨手記記、隨手寫寫… – 什麼是 Multitenant Application (Multitenancy) 這篇文章的解釋

「如果你有一個應用系統(程式)用來服務不同的客戶,而這些客戶用的都是同一台主機,同一個資料庫,同一個應用系統(程式),只有透過程式設計的方式來讓不同的客戶存取不同的資料,那麼就可以說這是一個 Multitenant Application 」

另外這篇 Multi-Tenant Data Architecture 分享 (多租戶的資料設計架構) part1 文章裡有更明確的架構說明

在這次專案裡,只實作最簡單的 “Shared Database, Shared Schemas" 架構,共用一個資料庫,同一個主機

Rails 如何實作

在 Gemfile 載入 multitenant,相關的 github 說明可看這裡

gem 'multitenant'

完成設定後,執行安裝

$ bundle install

安裝成功後,請在 /config/environments/development.rb|production.rb|test.rb 三個環境設定下加入 config.domain = “#{你的網域名稱}",如:

config.domain = 'localhost'

由於需要服務不同的客戶,所以會將每個客戶的帳號做為 subdomain(子網域名稱) 建立對應的 URL 路徑
最後應用程式便可透過 subdomain 取得客戶身份,要做到這件事,便需要從 routing 這邊下手
修改 routes.rb,並使用 :constraints (特殊限定) 設定檢查

定義 constraints 所需要使用的檢查條件類別,建立於 lib 目錄下,命名為 sub_domain.rb

# /lib/sub_domain.rb
class SubDomain
def self.matches?( requrest )
# 取得 environments 環境參數定義的 domain 設定值
# 用來比對目前執行的 host 的 domain 是否為可被允許執行的網站名單
domain = TestProject::Application.config.domain
case requrest.host
# 以下幾個網站都是預設被使用的 domain
when "www.#{domain}", "#{domain}", nil
false
else
true
end
end
end

註: 請確定 /config/application.rb 設定中有自動載入 lib 目錄檔案,設定方式可於 /config/application.rb 加入 config.autoload_paths += %W(#{config.root}/lib)


在 routes.rb 建立限定檢查,檢查目前的 request host 的 subdomain name,是否為不被允許的 subdomain name,這邊定義的是 www 或是 空白,則一律導回至 www 首頁

# routes.rb
# 檢查目前傳入的 request host 是否為不被允許的 subdomain name
# 允許的話則可繼續使用服務,否則統一導向 www 首頁
constraints( SubDomain ) do
root :to => 'index#index'
end
# 導回 www 首頁
root :to => 'index#destroy'

IndexController 請定義 index 和 destroy 二個 action 處理導向狀況

class IndexController < ApplicationController
def index
end

# 負責處理請求網址的客戶不在的情況
# 一律導向至產品網站首頁
#----------------------------------------------------
def destroy
redirect_to "http://www.testproject.com.tw"
end
end

當完成 Multitenant 路徑設定後,接下來是要取得 User 的資料,判斷目前傳入的路徑是否有對應的 User 資料存在

先建立 migration 針對 User model 加入一些必要的欄位用來保存註冊的網域資訊,

$ rails g migration add_domain_fields_to_users.rb

內容如下:

add_column :users, :subdomain_name, :string, :null => false, :default => ""
add_column :users, :cache_domain, :string, :null => false, :default => ""
add_index :users, :subdomain_name, :unique => true

執行資料庫欄位建立

$ rake db:migrate

完成 subdomain 資料欄位新增後,即可將會員註冊的 subdomain 資料保存至資料庫,留待接下來的驗證使用
接著修改 application_controller.rb,加入驗證邏輯,如下:

# application_controller.rb

class ApplicationController < ActionController::Base
protect_from_forgery

before_filter :check_user_domain_exists!

protected
# 透過 subdomain 取得對應的 user 資料
#----------------------------------------------------
def check_user_domain_exists!
# 將目前的 request host 拿來查詢是否有其會員註冊使用,取得該會員
user ||= User.select('id').find_by_cache_domain( request.host )

# 如果會員為空,則一律導向至 www 網站首頁
if( user.blank? )
redirect_to "http://www.testproject.com.tw"
end
end
end

到這為止,基本的 Multitenant 驗證檢查都已經完成,接下來就可以透過 multitenant gem 發功
舉個簡單的例子,一位會員可以擁有很多本書,1對多的關係,所以我們會有 User 和 Book 二個 model,在 db 的結構裡,book table 會記錄 user_id,以前的做法就會是利用 user_id 關聯找出所有的 book 資料,multitenant 也是做類似的事,只是它幫我們包裝的更好,更容易使用

Book Model 結構設定如下:

# models/book.rb
class Book < ActiveRecord::Base
# 設定 Book 和 User 之間的關聯
belongs_to :user
# 設定 multitenant 關聯
belongs_to_multitenant :user
end

之後我們要取得目前使用者擁有的所有書本,使用方式是將目前登入使用者 (current_user) 做為參數傳入 Multitenant.with_tenant 函式中,表示縮限在這個 User 範圍下處理相關操作,包含新增、修改、刪除

Multitenant.with_tenant current_user do
# queries within this block are automatically
# scoped to the current tenant
Book.all

# new objects created within this block are automatically
# assigned to the current tenant
Book.create :name => 'Harry Potter and the Deathly Hallows (Book 7) '
end

後記

以上是本次專案執行的 multitenant 設定流程,因應各種不同的狀況,可能會有不同的處理方式,要留意!!
如果有任何意見指教,也歡迎指正小弟

參考文章:

說明:
http://www.arthurtoday.com/2010/02/multi-tenant-application-multitenancy.html
http://kevingo75.blogspot.com/2011/05/multi-tenant-data-architecture-part1.html

技術:
https://github.com/wireframe/multitenant
http://ihower.tw/rails3/routing.html
http://intridea.com/posts/use-lambdas-for-rails3-route-constraints

將目前的 git 專案放到遠端主機上

先在 origin server 上建立一個新的 repo, 名稱為 foobar

$ git remote add origin git@git.example.com.tw:foobar
$ git push origin master

注意:
這項操作需要先在 git server 的 gitosis 裏正確設定存取權限,不然一定會出現 access denied 而無法正確將本地端資料放到遠端,可參考這篇說明

參考文章:

http://blog.longwin.com.tw/2009/05/git-learn-initial-command-2009/

用 Ruby 生成隨機字串/數字

當專案需要不同位數的字串,數字或英數混合的隨機組合,可利用以下方式生成

執行方式:

require 'securerandom'
s = SecureRandom.urlsafe_base64(16)

生成結果:

5b5cd0da3121fc53b4bc84d0c8af2e81

SecureRandom 還有以下幾種生成方法:

  • base_64
  • hex
  • random_bytes
  • random_number

參考文章:

http://stackoverflow.com/questions/88311/how-best-to-generate-a-random-string-in-ruby

解決 simple_form 整合 bootstrap 的 check box 文字無法正確對齊

在專案中使用了整合 bootstrap 樣式的 simple_form ,在處理 devise 會員登入  rember me 的樣式時,發現 label 文字無法正確向左對齊,我希望最後表現出來的結果是像這樣

但實際使用時,在登入頁面執行

<%= f.input :rember_me, :as => :boolean, :label => '記住我' %>

生成的頁面會變成

會發現文字並沒有正確地和 check box 對齊

解決方法:

開啟 config/initializers/simple_form.rb ,加上

config.wrappers :checkbox, :tag => 'div', :class => 'control-group', :error_class => 'error' do |b|

b.use :tag => 'div', :class => 'controls' do |ba|

ba.use :label_input

end

end

最後將登入頁面的 check_box 執行方式改為

<%= f.input, :remember_me, :as => :boolean, :wrapper => :checkbox, :label => '記住我' %>

再重新整理頁面之後,就會看到文字對齊於 check_box

註: 記得修改 initializers 目錄的檔案,要重新啟動 rails server

參考文章:

https://github.com/plataformatec/simple_form/issues/424#issuecomment-4001247

Undefined local variable or method `unconfirmed_email’ 錯誤解決

安裝 devise gem ,新增一筆 User 資料時,頁面出現

NameError in Devise::RegistrationsController#create

undefined local variable or method `unconfirmed_email’ for #<User:0x00000101a7dd28>

發生原因:

unconfirmed_email is required for reconfirmable. For some reason this was enabled by default in config/intializers/devise.rb:

config.reconfirmable = true

unconfirmed_email 欄位是針對 email 重新認證的必要欄位,預設 reconfirmable = true ,是表示當 email 欄位被修改後需要再重新認證,先將新的 email 記錄在 unconfirmed_email 欄位裡,直到完成認證,才會將 unconfirmed_email 記錄的 email 複製到 email 欄位

User 資料表中必須對應增加 unconfirmed_email 欄位,可參考透過 devise 生成的 migration

解決方法:

因為我只是需要 email 做為我的帳號,不會有變更發生,也就不會有再次認證的情況,所以將 config.reconfirmable 改為 false 就好,如下:

config.reconfirmable = false

參考文章:

http://stackoverflow.com/questions/9049151/undefined-local-variable-or-method-unconfirmed-email-when-registering-users

利用 RVM 管理多個不同的 Rails 版本專案

RVM 是什麼:

RVM( Ruby Version Manger),簡單來說就是 Ruby 的版本管理工具,可以讓你同時安裝多個不同版本的Ruby,RVM 裡的每個版本的 Ruby gem 也都可以分開安裝。

RVM 提供了一個 gemset 的功能,可以讓你在同一個 Ruby 環境下,建立二個不同的版本的 Rails 版本,例如想在 Ruby 1.9.3 的環境下建立 Rails 3.1.1 和 Rails 3.2.3 二個不同的 Rails 版本進行開發或測試

該怎麼建立 gemset :

1. 先建立 3.1.1 的 gemset

$ rvm gemset create 3.1.1

成功後會看到類似的訊息 ‘3.1.1’ gemset created (/Users/bjack/.rvm/gems/ruby-1.9.3-p0@3.1.1).

2. 切換至 3.1.1 的 gemset

$ rvm gemset use 3.1.1

成功後顯示 Using ruby-1.9.3-p0 with gemset 3.1.1

3. 安裝 Rails 3.1.1 版本

$ gem install rails -v 3.1.1

同理,安裝 Rails 3.2.3 的方式如下:

$ rvm gemset create 3.2.3
$ rem gemset use 3.2.3
$ gem insall rail -v 3.2.3

清除/刪除建立的 gemset :

清空建立的 gemset,但這個指令只會清空 gemset 資料,gemset 名字還會存在

$ rvm gemset empty 3.1.1

真正刪除 gemset 資料,資料被清空,gemset 名字被移除

$ rvm gemset delete 3.1.1

補充:

查詢目前已經建立的 gemset :

$ rvm gemset list

同時建立多個 gemset :

$ rvm gemset create 3.1.1 3.2.3

查詢已經安裝的 gem :

$ gem list

查詢目前安裝的 rails 版號 :

$ rails --version

參考文章:

RVM – Named Gem Sets :

http://beginrescueend.com/gemsets/basics/

高見龍 – RVM and Gemsets :

http://blog.eddie.com.tw/2011/04/08/rvm-and-gemsets/