Nexus Weblogging
ChinaonRails
You are here ChinaonRails > Ruby on Rails > Rails > Midnight Rubyist
Midnight Rubyist
本频道中共有 15 条消息,返回讨论版 Rails,10 个其他相关频道

  Midnight Rubyist

Vlad the Deployer #3 
Published on 2008-4-5 16:29:00 by

當您在程式中提供小人頭圖像上傳功能時,搭配Vlad the Deployer八成會出問題,因為Vlad the Deployer會從SVN下載新的版本,並把舊版本整個換掉,亦即使用者的個人圖像統統會消失。

為解決這問題,我們可以善用Vlad the Deployer的語法,首先要做的就是把原先上傳到Rails public目錄的圖檔整個搬移到Web server的目錄下:

mv /your_rails_app/public/user /var/www/user
接著修改該目錄的權限:
sudo chmod -R mongrel:mongrel /var/www/user

於是,我們可以在Vlad the Deployer的設定檔中,增加一個新的Remote task,負責幾件事情:
  • 砍掉空的圖像目錄
  • 做symbolic link連到/var/www/user

請在您的deploy.rb檔案中,加入以下語法:

namespace :vlad do
  def sudo(command)
    run [sudo_cmd, sudo_flags, command].join(' ')
  end

  desc "Setup user photos." 
  remote_task :setup_photo do
    user_path = "#{current_path}/public/user" 
    sudo "rm -r #{user_path}" 
    sudo "ln -s /var/www/user #{user_path}" 
  end
end
這個setup_photo要做的就是,每當遠端程式更新完畢之後,處理一些圖檔目錄相關的動作,於是往後更新版本,都可以保留使用者上傳的所有個人圖像。


Publish Subversion Messages to Twitter 
Published on 2008-3-28 1:22:00 by

把開發團隊的程式碼庫與Twitter訊息中心結合在一起該有多好?新推出的幾個Subversion hosting服務幾乎都有這種整合功能,例如Versionshelf,如此一來可以讓開發團隊成員直接訂閱Twitter即得知最近程式庫有哪些變更。這種做法相當有趣而且簡單:

1. 要擁有一個Subversion repository。 2. 要申請一個Twitter帳號,供團隊成員訂閱。 3. Subversion所在的伺服器必須可以跑curl,我們要利用Twitter的API來送出commit訊息。

把開發團隊的程式碼庫與Twitter訊息中心結合在一起該有多好?新推出的幾個Subversion hosting服務幾乎都有這種整合功能,例如Versionshelf,如此一來可以讓開發團隊成員直接訂閱Twitter即得知最近程式庫有哪些變更。這種做法相當有趣而且簡單:

1. 要擁有一個Subversion repository。 2. 要申請一個Twitter帳號,供團隊成員訂閱。 3. Subversion所在的伺服器必須可以跑curl,我們要利用Twitter的API來送出commit訊息。

請切換到伺服器上的Subversion repository目錄下:

cd /my_svn_repository_path

找到底下的hooks目錄:

cd hooks

先把post-commit.tmpl複製一份:

cp post-commit.tmpl post-commit

將其變更為可執行權限:

chmod 755 post-commit

post-commit.tmpl這個檔案是svn commit指令下達後所要執行的動作樣板,透過這個樣板我們可以附加其他動作進去,例如email通知,甚至是Twitter訊息發佈。

把樣板複製成一份可執行檔案,於是當Subversion伺服器執行完程式員送來的commit動作之後,便會尋找hooks目錄下是否有執行檔,如果有的話,便依照執行檔內容跑接下來的動作,所以他叫做post-commit。這個檔案內容如下:

REPOS="$1" 
REV="$2" 
COMMENT=`svnlook log -r${REV} ${REPOS}`

curl -u twitter_user:twitter_pass -d status="[Subversion] Revision ${REV}: ${COMMENT}" \ http://twitter.com/statuses/update.xml

請將您所申請的Twitter帳號密碼,填入twitter_user:twitter_pass之中。

最後,在Twitter上出現的訊息,會像底下這樣,或者您也可以根據需要修改curl後面的status內容:

<center></center>

Piston 
Published on 2008-3-22 12:52:00 by
在Rails底下使用外掛是很方便沒錯,問題是這些第三方的外掛常常在更新。我們當然可以很簡單地使用以下指令安裝外掛:
cd /myrails
plugin install http://some_where_to_my_favorite_plugin
這種直接安裝外掛的問題在於第三方的外掛更新時,我們並不知道。 或者也可以用以下指令安裝外掛:
plugin install -x http://some_where_to_my_favorite_plugin
這種安裝外掛的方式,是將外掛以svn co的方式安裝,每次我們svn ci更新遠端程式碼時,系統就會先去問第三方外掛的svn看看有沒有更新,有更新會先更新外掛之後,才把修改過的程式以及更新的外掛送到遠端的svn repository。 在Rails底下使用外掛是很方便沒錯,問題是這些第三方的外掛常常在更新。我們當然可以很簡單地使用以下指令安裝外掛:
cd /myrails
plugin install http://some_where_to_my_favorite_plugin
這種直接安裝外掛的問題在於第三方的外掛更新時,我們並不知道。 或者也可以用以下指令安裝外掛:
plugin install -x http://some_where_to_my_favorite_plugin
這種安裝外掛的方式,是將外掛以svn co的方式安裝,每次我們svn ci更新遠端程式碼時,系統就會先去問第三方外掛的svn看看有沒有更新,有更新會先更新外掛之後,才把修改過的程式以及更新的外掛送到遠端的svn repository。

但這又有一個麻煩,他會自動去更新我並沒有要更新的外掛。例如,如果我把Rails整個lock在plugin裡頭,只想在這個版本底下的Rails開發時,這一更新整個就毀了。好吧,你可以說需要自動更新的才用plugin install -x,不要自動更新的就用plugin install。

那如果未來我要更新Rails版本的話,怎辦?整個砍掉重裝嗎?

Piston的出現正好可以解決這個問題,他可以讓你想更新便更新,不更新就不更新,隨心所欲而不踰矩。

請按照以下指令安裝Piston:
sudo gem install piston
要是你已經用plugin install -x安裝了一堆外掛,那很簡單,直接轉換成piston即可,如下:
cd /myrails/vender/plugins
piston convert
為什麼要轉換?因為Piston必須要知道外掛的原始svn repository才能更新。如果你是用plugin install安裝的話,很抱歉,請砍掉後用piston安裝:
cd /myrails/vender/plugins
rm -r my_plugin_1
rm -r my_plugin_2
用Piston安裝外掛相當簡單,如下:
piston import http://some_where_to_my_favorite_plugin
將整個外掛系統透過Piston轉換或安裝完畢之後,好戲上場,平常不更新外掛時,不用去理會他,一但你想要更新外掛版本時,只要輸入以下指令即可:
cd /myrails/vender/plugins
piston up my_plugin_need_to_be_updated
或者你也可以鎖住外掛,以避免不小心更新了:
piston lock vendor/rails
這樣一來,無論你怎麼piston up都不會動到這個被鎖住的外掛,解鎖方式如下:
piston unlock vendor/rails

Piston顧名思義是引擎中的活塞,除了增加引擎動力之外,也是很好的中間協調模組,這讓我們可以很方便地處理外掛版本更新。


Using Mongrel Cluster 
Published on 2008-3-22 0:36:00 by

要是你採用了Vlad Deployer,基本上可以簡化相當多Mongrel cluster的設定,但有關啟動Mongrel cluster的帳號或開機時一併啟動Mongrel cluster等等,還需要花點工夫。

首先,請在之前提過的deploy.rb設定檔做如下設定,安排好Mongrel的帳號、幾部Mongrel伺服器以及啟動的port、啟動的環境等等:
set :mongrel_user,        "mongrel" 
set :mongrel_group,       "www-data" 
set :mongrel_servers,     18
set :mongrel_port,        9000
set :mongrel_environment, "development"
設定好之後,按以下指令就可以產生Mongrel設定檔:

要是你採用了Vlad Deployer,基本上可以簡化相當多Mongrel cluster的設定,但有關啟動Mongrel cluster的帳號或開機時一併啟動Mongrel cluster等等,還需要花點工夫。

首先,請在之前提過的deploy.rb設定檔做如下設定,安排好Mongrel的帳號、幾部Mongrel伺服器以及啟動的port、啟動的環境等等:
set :mongrel_user,        "mongrel" 
set :mongrel_group,       "www-data" 
set :mongrel_servers,     18
set :mongrel_port,        9000
set :mongrel_environment, "development"
設定好之後,按以下指令就可以產生Mongrel設定檔:
rake vlad:target:macmini vlad:setup
接著,請建立一個mongrel帳號,專門用來啟動Mongrel,不這麼做的話,八成都是以root或您自己的管理帳號啟動,這會造成安全疑慮:
adduser --system --group --no-create-home mongrel
再來請修改log/ pids目錄權限,這樣以mongrel帳號啟動的Mongrel才有辦法將log檔、pid檔寫入這兩個目錄,絕大多數無法順利啟動Mongrel的原因,幾乎都是這兩個目錄權限沒修改過所造成的:
chown mongrel:www-data /myrails/shared/log
chown mongrel:www-data /myrails/shared/pids
以下是隨Debian linux開機時一併啟動Mongrel的方式,如果你的機器上有好幾種不同的Rails app,請做symbolic link到該目錄下:
mkdir /etc/mongrel_cluster
chown mongrel:www-data /etc/mongrel_cluster
ln -s /myrails/shared/mongrel_cluster.conf /etc/mongrel_cluster/railsapp1.yml
再來是做一個隨開機啟動的script,原先安裝在/etc/init.d底下的mongrel-cluster是爛掉的,我們要拿mongrel_cluster gem裡頭的原裝貨來用:
rm /etc/init.d/mongrel-cluster
cp /usr/lib/ruby/gems/1.8/gems/mongrel_cluster-1.0.1.1/resources/mongrel_cluster /etc/init.d/
接著修改成可執行權限,然後下指令放到相關的執行階段即可:
chmod +x /etc/init.d/mongrel_cluster
update-rc.d mongrel_cluster defaults

以上設定完成之後,下次您的Linux啟動時,您所指定的Mongrel cluster就會依照您所指定的帳號、數量、埠以及執行環境來啟動了。


Vlad the Deployer #2 
Published on 2008-3-7 23:36:00 by

Vlad the Deployer這玩意兒簡單又好用,就算有一百台主機分散在全世界,我想透過它來部署、更新系統也是輕而易舉。

但是在實務操作面,還是有幾個問題:

第一、它預設使用Apache,那麼頗受好評的輕量級網頁伺服器lighttpd怎麼辦呢?

第二、它預設執行環境為Production,那麼我要如何在Development環境下跑呢?

第三、每一台主機位址都不同,要在哪裡設定呢?

最簡單的方式是用namespace,如下:

namespace :macmini do
  task :settings do
    set :domain,      "tw.myrails.com" 
    set :rails_env,   "development" 
    set :web_command, "/etc/init.d/lighttpd" 
  end

  setup_tasks
end

namespace :macpro do
  task :settings do
    set :domain,    "us.myrails.com" 
    set :rails_env, "production" 
  end

  setup_tasks
end
看到沒?上述三個疑問一下解決掉,包括不同主機位置、不同執行環境與不同的網頁伺服器!!

Vlad the Deployer這玩意兒簡單又好用,就算有一百台主機分散在全世界,我想透過它來部署、更新系統也是輕而易舉。

但是在實務操作面,還是有幾個問題:

第一、它預設使用Apache,那麼頗受好評的輕量級網頁伺服器lighttpd怎麼辦呢?

第二、它預設執行環境為Production,那麼我要如何在Development環境下跑呢?

第三、每一台主機位址都不同,要在哪裡設定呢?

最簡單的方式是用namespace,如下:

namespace :macmini do
  task :settings do
    set :domain,      "tw.myrails.com" 
    set :rails_env,   "development" 
    set :web_command, "/etc/init.d/lighttpd" 
  end

  setup_tasks
end

namespace :macpro do
  task :settings do
    set :domain,    "us.myrails.com" 
    set :rails_env, "production" 
  end

  setup_tasks
end
看到沒?上述三個疑問一下解決掉,包括不同主機位置、不同執行環境與不同的網頁伺服器!!

設定很簡單,麻煩就麻煩在最後一行的setup_tasks,這還得另外生一個module去對付他,而且內容都是毫無意義的程式碼:

module VladHelper
  def setup_tasks
    desc "Setup Vlad the Deployer on Mac mini." 
    remote_task :setup => :settings do
      Rake::Task["vlad:setup"].invoke
      Rake::Task["vlad:migrate"].invoke
    end

    desc "Deploy everything to Mac mini." 
    remote_task :deploy => [:update, :start]

    desc "Update source code to Mac mini." 
    remote_task :update => :settings do
        Rake::Task["vlad:update"].invoke
    end

    desc "Retart web servers on Mac mini." 
    remote_task :start => :settings do
      Rake::Task["vlad:start_app"].invoke
      Rake::Task["vlad:start_web"].invoke
    end

    desc "Migrate dev database on Mac mini." 
    remote_task :migrate => :settings do
      Rake::Task["vlad:migrate"].invoke
    end
  end
end
除非要讓update, migrate, start等指令執行跟原本vlad不同的動作,否則特地去撰寫一個module來處理,是相當無意義的。

使用namespace的好處是,可以讓rake指令更有意義,原先的指令是這樣:

rake vlad:setup vlad:update vlad:migrate vlad:start
用了namespace之後,變成這樣:
rake macmini:setup macmini:update macmini:migrate macmini:start
以及
rake macpro:setup macpro:update macpro:migrate macpro:start

我比較偏好另一種方式,請修改以下檔案及設定:

Rakefile

namespace "vlad" do
  desc "Setup vlad deploy target." 
  task :target

  rule "" do |task|
    if task.to_s.match(/vlad:target:([a-z_-]+)/i)
      override_path = File.join(RAILS_ROOT, 'config', "deploy.#{$1}.rb")
      Kernel.load override_path if File.exists? override_path
    end
  end
end
deploy.rb
set :deploy_to,   "/home/winson/projects" 
set :repository,  "http://svn.myrails.com/projects/"
deploy.macmini.rb
set :domain, "tw.myrails.com" 
set :rails_env, "development" 
set :web_command, "/etc/init.d/lighttpd"
deploy.macpro.rb
set :domain, "us.myrails.com" 
set :rails_env, "production"
這種方式把共通的設定保留在deploy.rb,而將個別的設定獨立出來成為deploy.macmini.rb以及deploy.macpro.rb兩個檔案,實際操作也很簡單,如下:
rake vlad:target:macmini vlad:setup vlad:update vlad:migrate vlad:start
以及
rake vlad:target:macpro vlad:setup vlad:update vlad:migrate vlad:start
指令完全一樣,差就差在vlad:target。

往後當我程式改好,送上Subversion之後,只要敲下rake指令,各地的主機就開始自動拉下最新的程式碼、備份、重開mongrel以及lighttpd,真是輕鬆愉快。

該怎麼說呢?這就是人生吧。


Rails ReCAPTCHA Plugin 
Published on 2008-2-23 23:04:00 by

什麼是CAPTCHA呢?

CAPTCHA是”Completely Automated Public Turing test to tell Computers and Humans Apart”的縮寫,那是一種用來區別使用者是人還是電腦的方式,一般都是產生圖形,讓使用者輸入圖形所代表的文字並加以驗證,主要用途是防止電腦程式灌票,或防止電腦程式登錄大量帳號或灌入大量廣告留言等。

那麼,如何在Rails底下做到CAPTCHA的機制呢?其實解決方式還滿多的,但我個人選擇外掛,通常有幾項要求:

  • 不會有系統安全方面的問題
  • 程式碼必須漂亮簡單
  • 對人類有益

我看過有人推薦Simple Captcha,但我覺得這是最爛的選擇,而且在Ruby On Rails Security Guide一文中也不推薦使用。

什麼是CAPTCHA呢?

CAPTCHA是”Completely Automated Public Turing test to tell Computers and Humans Apart”的縮寫,那是一種用來區別使用者是人還是電腦的方式,一般都是產生圖形,讓使用者輸入圖形所代表的文字並加以驗證,主要用途是防止電腦程式灌票,或防止電腦程式登錄大量帳號或灌入大量廣告留言等。

那麼,如何在Rails底下做到CAPTCHA的機制呢?其實解決方式還滿多的,但我個人選擇外掛,通常有幾項要求:

  • 不會有系統安全方面的問題
  • 程式碼必須漂亮簡單
  • 對人類有益

我看過有人推薦Simple Captcha,但我覺得這是最爛的選擇,而且在Ruby On Rails Security Guide一文中也不推薦使用。

理由是產生出來的圖形辨識碼過於簡單,不合乎CAPTCHA必須防止以OCR方式解讀的實做規範,既然有安全上的漏洞,就不是一個好的解決方式。

什麼是ReCAPTCHA呢?

ReCAPTCHA是卡內基美隆大學的一項計畫, 本質上除了是一種圖形驗證碼服務之外,更是集全球網友之力協助將書籍典藏數位化的浩大工程,這在他們家有非常詳盡的說明,這裡不再贅述。

難道ReCAPTCHA就一定比較安全嗎?當然比較安全,因為ReCAPTCHA提示的圖片就是OCR掃描實際書本內容,但解讀錯誤的圖片,最後再加上一層防解讀條碼。當使用者在做圖形驗證時,正好可以協助人工解讀,而將書籍數位化!!

在Rails上,強力推薦使用ReCAPTCHA外掛,因為他完全符合我對外掛的要求:安全、簡單以及對人類有益。

ReCAPTCHA這個外掛將recaptcha.net所提供的服務包裝起來,成為短短幾行的程式呼叫。我們先把外掛安裝起來:

rapt install -x http://svn.ambethia.com/pub/rails/plugins/recaptcha

由於這個外掛必須搭配recaptcha.net使用,因此必須到recaptcha.net申請帳號,並輸入自己要使用這項圖形辨識服務的網域。

如果您是在本機測試使用的話,直接輸入localhost就行。

輸入好之後,記下他給你的公開金鑰與私密金鑰。接著,在Rails的config/environment.rb填入這兩個金鑰:

# ReCAPTCHA key variables.
ENV['RECAPTCHA_PUBLIC_KEY'] = "your_recaptcha_public_key_goes_here" 
ENV['RECAPTCHA_PRIVATE_KEY']= "your_recaptcha_private_key_goes_here"

接著,在您需要提供圖形辨識的那一個view裡頭多加一行:

<%= recaptcha_tags %>
通常這會是一個含有form_tag的view,例如投票區或留言板 之類的。

請記住一定要將這一段扣放在form_tag之中,這樣才會隨著表單送出而將辨識碼送到recaptcha.net驗證。

再來就是controller,這也許是一個投票動作存檔或留言存檔之處,我們只要把原先判斷存檔成功的程式碼多加入圖形驗證是否成功的判斷即可,例如:

if verify_recaptcha(@post) && @post.save
  # vote or comment process here
else
  # fail process
end

以上。

回顧一下,使用ReCAPTCHA的整個步驟是

  • 去recaptcha.net申請帳號,註冊網域,取得金鑰
  • 將公開金鑰與私密金鑰填入environment.rb
  • 在view加入recaptcha_tags
  • 在controller加入verify_recaptcha

整個過程簡單、安全,又可以協助將書籍典藏數位化,如此一來就覺得這個外掛真的是非用不可。

<center></center>

A fast and very simple Ruby web server 
Published on 2008-1-5 14:09:00 by

就在Mongrel主力開發者Zed Shaw宣佈從此以後退出Ruby界,決定不問世事、金盆洗手之後,一套號稱比Mongrel、甚至Event-drive Mongrel更快的Ruby web server出現了,那就是Thin

Thin的安裝相當簡單:

sudo gem install thin
也可以搭配Rails使用,只要切換到Rails專案目錄下,然後輸入:
thin start
如果要在背景執行,他的指令跟Mongrel差不多:
thin start -d -p 80 -c /your_rails_dir -P /your_rails_dir/log/thin.pid
跟Mongrel比較起來速度快是不用說,但Thin並未內建cluster,需搭配Merbgod才能夠做到cluster。

最後,祝Zed一路好走。


Find Randomly Redux 
Published on 2008-1-2 17:50:00 by

去年寫過隨機挑一筆資料的作法,現在看來其實還有很多可以改進之處。

一、直接寫成Something.random並不是很漂亮的作法,寫成Something.find :random才是符合Rails慣例的作法。

二、部份語法當時看起來簡單,現在則感覺累贅,牽扯到例外處理實在有點麻煩。但是,在實務上,我們也必須處理因為資料被刪除而跳號的情形。

去年寫過隨機挑一筆資料的作法,現在看來其實還有很多可以改進之處。

一、直接寫成Something.random並不是很漂亮的作法,寫成Something.find :random才是符合Rails慣例的作法。

二、部份語法當時看起來簡單,現在則感覺累贅,牽扯到例外處理實在有點麻煩。但是,在實務上,我們也必須處理因為資料被刪除而跳號的情形。

個人覺得最佳解是這樣子的:

def self.find(*args)
  scope = args.first
  if scope.to_s == "random" 
    super :first, :offset => (rand count).to_i
  elsif scope.to_s == "public" 
    # blah, blah...
  else
    super
  end
end
為什麼呢?直接抓取資料總筆數作為隨機變數的種子,並運用offset跳到那一筆,既簡單也不用處理例外。也有人把關鍵的那一行寫成這樣:
super :first, :order => "rand()"
這無疑是去呼叫資料庫本身的函數,語法也很簡單沒錯,但總令人覺得不舒服。

我個人的習慣是盡量避免在程式裡頭赤裸裸地寫SQL語法或運用資料庫函數,一來程式沒人看得懂,難以維護;二來會綁死在某個資料庫平台上造成將來系統移植的困難。

無論如何,深入思考以及隨時進行Refactoring,有一天總會想出最好的作法。


Upgrade to Rails 2.0.2 
Published on 2007-12-20 11:32:00 by

Rails向來以無痛開發、無痛升級著稱,透過gem來安裝Rails無論升級或移除都很簡便;萬一還是不放心,可以先裝到vender目錄下試試。雖然沒什麼大問題,不過還是有些小地方須注意:

有些報告指出,可能因為gem版本的問題導致無法安裝新版Rails。

新版gem最大的優點是,以往安裝gem需要加上-include-dependencies選項,以便同時安裝相關的gem,現在已經變成預設指令了,也就是說只需要輸入

sudo gem install xxx
就已經含有—include-dependencies選項,真是方便的很。

Rails向來以無痛開發、無痛升級著稱,透過gem來安裝Rails無論升級或移除都很簡便;萬一還是不放心,可以先裝到vender目錄下試試。雖然沒什麼大問題,不過還是有些小地方須注意:

有些報告指出,可能因為gem版本的問題導致無法安裝新版Rails。

新版gem最大的優點是,以往安裝gem需要加上-include-dependencies選項,以便同時安裝相關的gem,現在已經變成預設指令了,也就是說只需要輸入

sudo gem install xxx
就已經含有—include-dependencies選項,真是方便的很。

請輸入以下指令將gem更新到0.9.5版:

sudo gem update --system

請輸入以下指令升級到Rails 2.0.2版:

sudo gem install rails
花點時間等候他安裝完畢,接著啟動程式:
sudo mongrel_rails start -c ~/my_rails_project_dir
再來跟著錯誤訊息一步一步改:

config.breakpoint_server has been deprecated and has no effect.
這沒什麼,就是到config/environments/development.rb把config.breakpoint_server註解掉。
# Enable the breakpoint server that script/breakpointer connects to
# CHANGED: For Rails 2.0.2.
# config.breakpoint_server = true
undefined method 'call_filter' for class 'ActionController::Base' (NameError).
這是因為使用Stickies外掛造成的,把外掛升級一下就好。
undefined method 'nil_class_path' for #<ActionView::Base>.
如果有用到form_tag multipart,稍微修改一下即可:

<%# CHANGED: For Rails 2.0.2. %>
<%# form_tag [], :multipart => true do %>
<% form_tag "", :multipart => true do %>
Controller裡的動態scaffold已經不是預設,而是外掛了:
undefined method 'scaffold' for ActionController:Class.
所以,註解掉就好。 如果您看過我的Rails起手式,也跟我一樣喜歡用動態scaffold來寫程式的話,可輸入以下指令安裝動態scaffold:
script/plugin install http://dev.rubyonrails.com/svn/rails/plugins/scaffolding/
又因為動態scaffold會用到內建分頁,而內建分頁功能也被拿掉成為外掛,這裡請一併安裝服用:
script/plugin install svn://errtheblog.com/svn/plugins/classic_pagination

升級後感覺速度明顯加快<s>,不知道是不是心理作用</s>。這不是心理作用,因為Rails 2.0.2內建SQL query cache的緣故。


TextMate with Mega Zoomer 
Published on 2007-11-29 21:35:00 by

運用TextMate開發Ruby On Rails程式的效率真是快上加快,唯一的缺憾是偶爾需要切換成全螢幕編輯,但是TextMate並沒有提供快速鍵可以直接開啟全螢幕編輯模式。

當然,你要慢慢去拉視窗調整成全螢幕編輯模式也是可以,不過這就遜掉了。

如果可以按個鈕切換成全螢幕編輯模式,讓我能夠不受干擾,好好地、專心地寫程式,那該有多好?

運用TextMate開發Ruby On Rails程式的效率真是快上加快,唯一的缺憾是偶爾需要切換成全螢幕編輯,但是TextMate並沒有提供快速鍵可以直接開啟全螢幕編輯模式。

當然,你要慢慢去拉視窗調整成全螢幕編輯模式也是可以,不過這就遜掉了。

如果可以按個鈕切換成全螢幕編輯模式,讓我能夠不受干擾,好好地、專心地寫程式,那該有多好?

很好,我們可以用Mega Zoomer來開啟全螢幕編輯模式。

Mega Zoomer與Visor一樣都透過SIMBL的方式進行安裝,這是一種透過外掛套件修改Mac軟體行為的方式,例如許多Safari瀏覽器的增強功能都是透過SIMBL外掛方式達成的。

我最愛用的SafariStand也是屬於SIMBL外掛。

安裝成果如下,而且任何應用程式都可直接按command-enter開啟全螢幕,當你需要看右邊的目錄時,再按一下command-enter就可以立刻切換回來:

<center></center>

TextMate with Visor 
Published on 2007-11-28 16:24:00 by

運用TextMate開發Ruby On Rails程式的效率真是快上加快,唯一的缺憾是偶爾需要切換到終端機,並沒辦法直接在TextMate底下操作終端機。

當然你要按command-tab好幾次切換到終端機或用滑鼠點選終端機也是可以,但這就遜掉了。

如果可以像飛行員的抬頭顯示器一樣,按個快速鍵就從螢幕上方垂下一個漂亮的、半透明的終端機,那該有多好?

運用TextMate開發Ruby On Rails程式的效率真是快上加快,唯一的缺憾是偶爾需要切換到終端機,並沒辦法直接在TextMate底下操作終端機。

當然你要按command-tab好幾次切換到終端機或用滑鼠點選終端機也是可以,但這就遜掉了。

如果可以像飛行員的抬頭顯示器一樣,按個快速鍵就從螢幕上方垂下一個漂亮的、半透明的終端機,那該有多好?

很好,我們可以用Visor來擴充終端機的功能。

Visor與QuickSilver都屬於同一家公司的產品。QuickSilver則是著名的快速鍵軟體,我敢打包票TextMate作者一定很愛QuickSilver,所以才把相同的概念發揮到極致而設計出TextMate。

新版的Visor已經是一套Opensource軟體,原始碼放置在GoogleCode,請按網頁上的步驟安裝即可,保證完全與Leopard相容。

安裝成果如下,而且可分頁操作:

<center></center>

Homebrew Immutable Attribute 
Published on 2007-11-24 11:38:00 by

最近寫點小程式,客戶拜託我其中某個table不要用Rails內建的key,這不是新增一個key就好的問題,而是要用指定的格式當key。

好,從設計的角度來看,格式如何先不管,這個key的值除了生成的那一刻之外,往後都不可隨意變更,不只從view無法變更,甚至要防止程式不小心修改到,這才有資格作為一個key。

先看Rails是否內建防止某個欄位遭到修改的保護機制。這時候,第一個想到的自然是attr_protected:

最近寫點小程式,客戶拜託我其中某個table不要用Rails內建的key,這不是新增一個key就好的問題,而是要用指定的格式當key。

好,從設計的角度來看,格式如何先不管,這個key的值除了生成的那一刻之外,往後都不可隨意變更,不只從view無法變更,甚至要防止程式不小心修改到,這才有資格作為一個key。

先看Rails是否內建防止某個欄位遭到修改的保護機制。這時候,第一個想到的自然是attr_protected:

class User < ActiveRecord::Base
  attr_protected :my_key
end
查一下Rail的文獻,會發現這個attr_protected跟他親戚attr_accessible是用來防止批次指定的(mass-assignment),也就是說以下幾種指定方式都可防止掉:
user = User.new :my_key => 12345
user = User.create :my_key => 12345
user.attributes = { :my_key => 12345 }
user.update_attributes :my_key => 12345
而attr_protected跟attr_accessible的差別在於:
  • attr_protected防止批次指定
  • attr_accessible容許批次指定
講白一點,attr_protected是負面表列,attr_accessible是正面表列;attr_protected後面跟著的屬性是不可批次指定;而attr_accessible則是列出可以批次指定的屬性,其餘沒列出的都不可以被批次指定

對於作為一個key而言,這樣的保護夠嗎?要偷改他的值,至少還有以下幾種方式:

user.my_key = 12345
user.update_attribute :my_key, 12345
因為這兩種方式都不是批次指定,所以不在這兩個保鏢attr_protected、attr_accessible的保護範圍之內。

回過頭來看一下需求:

這個key的值除了生成的那一刻之外,往後都不可隨意變更。
所以,我只要去限制可以塞值給my_key的時機就好,其他一律不准!!

因為最近愛上了module,所以嘗試寫些自己的module:

module UntouchableModel
  def attr_immutable(attr_name)
    define_method "#{attr_name}=" do |new_value|
      if new_record?
        write_attribute attr_name, new_value
      else
        raise ActiveRecord::RecordNotSaved
      end
    end
  end
end
至於怎麼使用直接看扣比較快:
class User < ActiveRecord::Base
  include UntouchableModel
  attr_immutable :my_key
end


RSpec On Rails 
Published on 2007-11-19 11:19:00 by

每一位程式員都討厭測試,相較於寫程式時那種解題的快感,測試簡直百般無聊。那麼,為什麼要測試?目的是為了程式碼的品質,品質不單單是測試人員的事,那是程式員基本的責任。

測試等於是一種背書,證明程式的任何環節毫無問題;測試可以帶給程式員信心,所以只要是我寫的程式,我就願意負責,而且我一定會對我寫的程式感到驕傲!!

先看看為何要用RSpec來進行測試,難道在Rails裡頭用他基本的unit test來作不行嗎?當然可以,只是測試的風格不同而已,但我認為風格很重要,風格可以改造想法,而想法可以改造程式!!

每一位程式員都討厭測試,相較於寫程式時那種解題的快感,測試簡直百般無聊。那麼,為什麼要測試?目的是為了程式碼的品質,品質不單單是測試人員的事,那是程式員基本的責任。

測試等於是一種背書,證明程式的任何環節毫無問題;測試可以帶給程式員信心,所以只要是我寫的程式,我就願意負責,而且我一定會對我寫的程式感到驕傲!!

先看看為何要用RSpec來進行測試,難道在Rails裡頭用他基本的unit test來作不行嗎?當然可以,只是測試的風格不同而已,但我認為風格很重要,風格可以改造想法,而想法可以改造程式!!

這是一段用Rails unit test寫出來的測試程式:

class MessageTest < Test::Unit::TestCase
  def test_create
    msg = Message.create(:text => "test text")
    assert msg.save
  end
end
這種測試基本上還是在寫程式,太貼近程式的想法,無法把自己置身事外,當作一名測試員。

這是一段用RSpec寫出來檢查是否自動產生訊息主鍵代碼的測試程式,我需要說明他在做什麼嗎?完全不需要!!

describe Message do
  before do
    @message = Message.new
  end

  it "should auto generate uuid" do
    @message.text = "test message" 
    @message.save
    @message.uuid.should_not be_empty
  end
end

相較於普通unit test裡頭常見的語法assert_equal(e1, e2),RSpec最大的優點就是相當白話,一目了然。

assert_equal的壞處不用我講,那個e1, e2常常擺錯邊,老是忘記誰要檢查誰。

當測試這件事情變得平易近人之後,程式員自然會投資時間下去做,因為未來一定會得到自信與驕傲。

要使用RSpec得先安裝幾個gem:

sudo gem install rspec
然後切換到你的Rails project安裝幾個plugin:
script/plugin install -x svn://rubyforge.org/var/svn/rspec/tags/CURRENT/rspec
script/plugin install -x svn://rubyforge.org/var/svn/rspec/tags/CURRENT/rspec_on_rails
我通常會將RSpec與AutoTest併用:
sudo gem install ZenTest
script/plugin install -x http://svn.caldersphere.net/svn/main/plugins/rspec_autotest
一切就緒之後,再來就是產生model, controller的測試框架:
script/generate rspec
script/generate rspec_model message
script/generate rspec_controller message
這裡的message是測試的對象,例如要寫測試程式的對象是一個model,這個model的名字叫做message,就是去generate一個rspec_model;要測一支controller則是去generate一支rspec_controller。

產生完畢之後,你會發現Rails project底下多了個spec目錄,而spec目錄裡頭則有許多諸如controller, model, view等子目錄,進去model一看,裡頭躺著一支剛產生好的message_spec.rb,如下:

describe Message do
  before do
    @message = Message.new
  end

  it "should description" do

  end
end
這就是測試程式初始的框架,你可以先簡單描述他,然後慢慢補上測試細節,如下:
describe Message do
  before do
    @message = Message.new
  end

  it "should validate presence of text" do

  end

  it "should auto generate uuid" do

  end

  it "should update Twitter status" do
    pending "not yet" 
  end
end
也就是說我的這個訊息類別,必須要檢查是否有內文、必須要自動產生uuid,而最後必須要去更新我的Twitter狀態。因為更新Twitter的API目前還不知道該如何使用,所以我先pending。

實際進行測試之前,請記得先建立測試資料庫,並把development資料庫架構複製一份到test資料庫來。

rake db:test:clone_structure

我通常會在背景跑一支autotest程式,只要我一修改程式存檔,他就自動幫我跑測試程式,方便得很。要執行autotest請在Rails project目錄下,執行:

autotest -rails
接著autotest就會默默的在背景跑,並且告訴您總共有幾支example(每個it描述算一個example)、幾支通過、幾支失敗、幾支pending(例如update Twitter status就是一支暫不處理的example)。所以這裡的輸出就是:
3 examples, 0 failures, 1 pending
然後他又繼續在背景等,等你下次修改、存檔,又繼續自動幫您進行測試。

接著,補上一些細節:

describe Message do
  before do
    @message = Message.new
  end

  it "should validate presence of text" do
    lambda {
      @message.save.should_not be_true
    }.should_not change(Message, :count)

    @message.text = "test message" 
    @message.save.should be_true
  end

  it "should auto generate unique and immutable uuid" do
    lambda {
      @comment.save
    }.should change(@comment, :uuid).from(nil)

    lambda {
      @comment.uuid = "8fa82ce9-8f61-11dc-a4f7-000a95c72214" 
    }.should raise_error(ActiveRecord::RecordNotSaved)
  end

  it "should update Twitter status" do
    pending "not yet" 
  end
end
每個it後面寫的是白話的功能描述,也就是這項測試必須必須達成什麼要求,日後可以運用rdoc直接把這些描述轉成文件;describe則可以當作一種分類,如果有其他不同種類的message需要測試,也可以這樣寫:
describe Message, "with comments" do
  before do
    @message = Message.new
  end

  it "should be replied using comment" do

  end
end
should或should_not其實是類似assert,只不過unit test採用:
assert(e1, e2) # 動詞(主詞1, 主詞2)
的模式,常常搞不清楚兩個主詞之間的關係與意義;而RSpec採用:
message.should be_true # 主詞.should_be 結果
如此一來將更為直覺及口語化。

RSpec的優點當然不只直覺、簡單及口語化(難道這還不夠?),還可以運用mock將model從controller中抽離,另外,搭配TextMate甚至可以提供更多快速而且簡單的玩法,千萬不可錯過啊。

另外,新版ZenTest已經將許多好用的外掛直接整合進來,例如: menu, datetime, redgreen, growl等,現在都變成豪華的內裝配備。所以,舉凡測試開始、測試成功與測試失敗都會透過Growl來作提示。

要是您嫌預設的Growl提示圖案不好看的話,請輸入以下指令進行修改:

sudo mate /Library/Ruby/Gems/1.8/gems/ZenTest-3.6.1/lib/autotest/growl.rb
請修改此處:
def self.growl title, msg, pri=0
    title += " in #{Dir.pwd}" 
    msg += " at #{Time.now}" 
    # system "growlnotify -n autotest --image /Applications/Mail.app/Contents/Resources/Caution.tiff -p #{pri} -m #{msg.inspect} #{title}" 
    system "growlnotify -n autotest --image /Users/winson/Pictures/rails.png -p #{pri} -m #{msg.inspect} #{title}" 
  end


ExceptionGrowler 
Published on 2007-11-11 16:41:00 by

這是我用過最棒的Ruby On Rails外掛: http://code.google.com/p/rubyphunk/wiki/ExceptionGrowler

萬一不會自動通知,請進Growl把Network頁籤下的這兩個選項打勾:

  • Listen for incoming notifications
  • Allow remote application registration

這是我用過最棒的Ruby On Rails外掛: http://code.google.com/p/rubyphunk/wiki/ExceptionGrowler

萬一不會自動通知,請進Growl把Network頁籤下的這兩個選項打勾:

  • Listen for incoming notifications
  • Allow remote application registration


Vlad the Deployer 
Published on 2007-11-6 13:28:00 by

Ruby on Rails對全世界最大的貢獻不在於產生多少容易維護的程式碼,而在於把簡潔易用的觀念深植人心。

因為程式員透過簡單易用的Ruby on Rails開發各種程式,於是創造出Twitter、Radiant、Mephisto等傑出的應用,而Vlad the Deployer則是另一個強調簡單易用的傑出工具。

相較於威力強大的Capistrano,Vlad the Deployer是一套極為簡單的自動化部署工具,特別是當您使用Mongrel, Apache, Subversion環境時,更是輕鬆愉快。

那麼,Vlad the Deployer可以幫助我們做什麼?

Ruby on Rails對全世界最大的貢獻不在於產生多少容易維護的程式碼,而在於把簡潔易用的觀念深植人心。

因為程式員透過簡單易用的Ruby on Rails開發各種程式,於是創造出Twitter、Radiant、Mephisto等傑出的應用,而Vlad the Deployer則是另一個強調簡單易用的傑出工具。

相較於威力強大的Capistrano,Vlad the Deployer是一套極為簡單的自動化部署工具,特別是當您使用Mongrel, Apache, Subversion環境時,更是輕鬆愉快。

那麼,Vlad the Deployer可以幫助我們做什麼?

透過簡單的設定,所有Ruby on Rails的安裝部署工作只需打一行字,Vlad the Deployer就可以幫您把遠端機器上的production程式備份、從SVN拉下最新的程式、調整資料庫、重新啟動web server、重新啟動Mongrel cluster。Capistrano當然也可以達到相同的目的,但是他的設定實在太過麻煩。

Vlad the Deployer的安裝步驟如下:

第一、請在本地與遠端機器都裝上Vlad the Deployer:
sudo gem install vlad --include-dependencies
再來,請切換到本地的rails project目錄,修改Rails project的Rakefile,並加上這兩行字:
require 'vlad'
Vlad.load
接著,在config目錄下新增一個deploy.rb,做些環境設定:
set :user,        "my_admin" # your loggin username to production server.
set :domain,      "#{user}@my_production" # your production server.
set :deploy_to,   "/home_to_my_rails" # your home to rails production source
set :repository,  "http://svn.my_repository/svn/stable/" # your svn repository server
輸入以下指令幫您完成全部工作:
rake vlad:setup vlad:update vlad:migrate vlad:start
這樣就結束了嗎?理論上,遠端機器的環境跟本地開發環境的作業系統相同、PATH設定相同,而且資料庫帳號密碼都完全相同,這樣就結束了,很可惜地,往往是不相同的。

萬一您下rake vlad:setup指令之後,遇到command not found: mongrel_rails的錯誤訊息,那表示遠端機器跟您本地機器的作業系統或環境變數不同。那麼,請在遠端機器設幾個symbolic link,因為/usr/bin是大多數unix-base主機都有的執行路徑:
sudo ln -s /var/lib/gems/1.8/bin/mongrel_rails /usr/bin/mongrel_rails
sudo ln -s /var/lib/gems/1.8/bin/rake /usr/bin/rake
然後重新執行:
rake vlad:setup
如果資料庫帳號密碼不同的話,請修改遠端機器的設定檔config/database.yml。

第二、執行程式更新與資料庫更新,並啟動伺服器:
rake vlad:update vlad:migrate vlad:start

第三、沒有第三步了。


15 items

欢迎使用 RSS 阅读器订阅本页种子 http://midnightrubyist.blogspot.com/feeds/posts/default
© 2007 A Jesse Cai Production   -   About   -   京ICP备07020911号
a site powered by Project Babel