Ruby on Rails中的Rake教程(Rake如何把我灌醉!)[轉]

2007-06-13 14:54
原文:Ruby on Rails Rake Tutorial (aka. How rake turned me into an alcoholic)
引言:作爲一個rails的開發者,你可能很熟悉使用rake進行你的測試,或者使用rake db:migrate運行你的migrations,但是你真的知道Rake的背後故事嗎?你意識到可以自己寫一個Rake任務或者一個有用的lib嗎

下面是我們使用Rake任務的例子:

1、給列表中的用戶發送郵件
2、每晚數據的計算和報告
3、過期或重新生成緩存
4、備份數據和svn版本(how's this : subversion repository)
5、運行數據處理腳本(sort of,how much is called this )
6、Pouring drinks to get a good buzz on(一句玩笑,是這兩位仁兄的風格)

這篇文章中,我們將討論爲什麼要創建Rake,和他怎麼樣幫助我們的rails應用。最好你可以寫自己的Rake。

一、歷史回顧:make

爲了瞭解Rake的來歷,我們先了解一下Rake的爺爺:Make。
讓我們回到那個代碼塊需要編譯,解釋性語言和iphone還沒出現在地球上的時代。
回到那時,我們下載的大型程序,還是一堆源代碼和一個shell腳本。這個shell腳本包含了所有需要用來compile/link/build的代碼。你需要運行“install_me.sh”這個腳本,每一行代碼將被運行(編譯每一行源文件),然後生成一個你能夠運行的文件。

對於大多數人這樣是不錯的,但是對於程序開發人員卻是一個不幸。每次你對源代碼進行一個小的改動,並進行測試的時候,你需要回到shell腳本,並重新編譯所有的源代碼,顯然對於大的程序“那是相當的”耗時的。

1977年(作者出生那年,我78年),貝爾實驗室的Stuart Feldman創造了“make”。解決了編譯時間過長的問題。Make用來編譯程序,取得兩方面的進步:

Stuart Feldman

(1)Make可以發現哪個文件在上一次編譯後改動過,根據這點,再次運行Make時,僅編譯改動過的文件。這個很大程序上減少了重新編譯大型程序的時間。

(2)Make可以進行從屬跟蹤。你可以告訴編譯器,源文件A的編譯需要源文件B,源文件B的編譯需要源文件C,所以Make在編譯A時發現B沒有編譯,將會先編譯B。

可以這樣定義:Make是一個可執行程序。像ls或dir一樣。讓Make理解如何讓編譯一個項目,需要創建一個makefile文件,描述所有的源文件和依賴關係。makefiles有自己的語法,你不用去了解。

這些年Make出現了其他的變體,並且被其他的語言使用。事實上,ruby用戶在rake出現前也在使用它。

“但是,ruby並不需要編譯,我們用它來幹嘛?”

是啊。ruby是一個解釋性語言,我們不需要編譯它的源代碼,所以ruby程序員爲什麼使用它呢?

兩個重要的原因:

(1)創建任務
在大型的應用中,你經常編寫腳本,在命令行下運行一些任務。比如清除緩存,維護任務,或者遷移數據庫。你可以寫一個MakeFile來組織你的任務,而不是寫十個不相干的腳本(或者一個複雜的)。這樣你可以簡單的運行:“make stupid”。

(2)從屬任務跟蹤
當你開始寫一些維護任務的時候,可能發現有些任務的使用可能有重複。比如,“migrate”任務和“schema:dump”都需要鏈接數據庫,這樣我可以創建一個任務"connect_to_database",使“migrate”和“schema:dump”都依賴於"connect_to_database",這樣下次運行“migrate”時,"connect_to_database"會先於“migrate”運行

二、如何得到Rake

幾年前,Jim Weirich在一個java項目上使用了Make,他發現如果在他的Makefile中寫一小段ruby代碼將會帶來非常大的方便。所以他創建了Rake。


左:Jim Weirich,中:Jason Seifer,右:Gregg Pollack
(後面兩位爲本文作者)


Jim 爲Rake創建了任務功能,附屬關係跟蹤,甚至創建了時間段判斷(timestamp recognition),(在上一次編譯的基礎上僅編譯改動的部分),當然,對於ruby,我們並不需要編譯。

我很想知道Jim在代碼裏做了什麼,你也想知道吧。Jim可能從來沒想給這個代碼寫個文檔,可能現在他也是被煩透了,寫了一個。呵呵


三、Rake如何工作

開始我想給這個部分起名爲"How to get wasted with Rake"。

那麼我想喝點酒,該怎麼做呢?

1、去買酒
2、喝酒
3、喝醉



如果我要使用Rake完成這個任務,我會創建一個“Rakefile”文件:

task :purchaseAlcohol do
puts "Purchased Vodka"
end

task :mixDrink do
puts "Mixed Fuzzy Navel"
end

task :getSmashed do
puts "Dood, everthing's blurry, can I halff noth'r drinnnk?"
end
這樣我可以在這個Rakefile的目錄,分別運行這些任務:

$ rake purchaseAlcohol
Purchased Vodka
$ rake mixDrink
Mixed Fuzzy Navel
$ rake getSmashed
Dood, everthing's blurry, can I halff noth'r drinnnk?
酷!但是從順序上看,我可以用任何的順序運行這個任務。比如喝醉在買酒或者喝酒之前。當然這不符合人的習慣。


四、Rake的順序

task :purchaseAlcohol do
puts "Purchased Vodka"
end

task :mixDrink => :purchaseAlcohol do
puts "Mixed Fuzzy Navel"
end

task :getSmashed => :mixDrink do
puts "Dood, everthing's blurry, can I halff noth'r drinnnk?"
end
這樣,如果想喝酒,就得先去買,如果想喝醉,就得先喝酒。

$ rake purchaseAlcohol
Purchased Vodka
$ rake mixDrink
Purchased Vodka
Mixed Fuzzy Navel
$ rake getSmashed
Purchased Vodka
Mixed Fuzzy Navel
Dood, everthing's blurry, can I halff noth'r drinnnk?
看到了吧,我喝醉和,因爲酒已經買了,也被我喝了。(譯者:我是喜歡百事的,所以倘若我寫,定然拿百事當例子。但是我讓我兒子和可口,爲什麼呢?下面告訴你。)

現在,你的慾望無法滿足了,你想讓你的朋友加入進來。就像一個團隊的開發,如果你想加入一個新人,你得有合適的規劃。你得有文檔。那麼問題來了。



五、如何給我的Rake添加文檔

Rake添加文檔非常的方便,使用“desc”就可以了:

desc "This task will purchase your Vodka"
task :purchaseAlcohol do
puts "Purchased Vodka"
end

desc "This task will mix a good cocktail"
task :mixDrink => :purchaseAlcohol do
puts "Mixed Fuzzy Navel"
end

desc "This task will drink one too many"
task :getSmashed => :mixDrink do
puts "Dood, everthing's blurry, can I halff noth'r drinnnk?"
end
看到了吧,我的每個任務都添加了desc,這樣我們可以輸入"rake -T"或者"rake --tasks":

$rake --tasks
rake getSmashed # This task will drink one too many
rake mixDrink # This task will mix a good cocktail
rake purchaseAlcohol # This task will purchase your Vodka
簡單乎?呵呵


六、Rake的命名空間

當你開始酗酒,並且開始使用大量的rake任務的時候,你需要一個好方法將他們分類,這時用到了命名空間,如果我在上面的例子使用了命名空間,那麼:

namespace :alcoholic do
desc "This task will purchase your Vodka"
task :purchaseAlcohol do
puts "Purchased Vodka"
end

desc "This task will mix a good cocktail"
task :mixDrink => :purchaseAlcohol do
puts "Mixed Fuzzy Navel"
end

desc "This task will drink one too many"
task :getSmashed => :mixDrink do
puts "Dood, everthing's blurry, can I halff noth'r drinnnk?"
end
end
命名空間允許你將一些任務放到特定的分類中,在一個Rakefile中,你可以加入幾個命名空間。運行rake --tasks

rake alcoholic:getSmashed        # This task will drink one too many
rake alcoholic:mixDrink # This task will mix a good cocktail
rake alcoholic:purchaseAlcohol # This task will purchase your Vodka
所以如果想運行這個任務,只要輸入 rake alcoholic:getSmashed:


七、如何寫一個有用的ruby任務

最近,我想用ruby創建幾個文件夾:

desc "Create blank directories if they don't already exist"
task(:create_directories) do

# The folders I need to create
shared_folders = ["icons","images","groups"]

for folder in shared_folders

# Check to see if it exists
if File.exists?(folder)
puts "#{folder} exists"
else
puts "#{folder} doesn't exist so we're creating"
Dir.mkdir "#{folder}"
end

end
end
當然,還可以在rake中使用更多的 文件工具File Utils,或者加入其他的部分。


八、如何爲我的rails應用寫一個Rake任務

一個rails應用中,已經有了一些rake任務,你可以在你的項目目錄裏運行:rake --tasks。

爲了給你的rails應用添加一個新的任務,你可以打開/lib/tasks目錄(已經存在的),添加一個叫
something.rake的文件,這個任務會被自動的檢索到,這些任務會被添加到rake tasks列表中,你可以在根目錄裏運行他們,現在把我們上面的例子放到這個rails應用中。

utils.tasks

namespace :utils do
desc "Create blank directories if they don't already exist"
task(:create_directories) do

# The folders I need to create
shared_folders = ["icons","images","groups"]

for folder in shared_folders

# Check to see if it exists
if File.exists?("#{RAILS_ROOT}/public/#{folder}")
puts "#{RAILS_ROOT}/public/#{folder} exists"
else
puts "#{RAILS_ROOT}/public/#{folder} doesn't exist so we're creating"
Dir.mkdir "#{RAILS_ROOT}/public/#{folder}"
end

end
end
end
注意上面的代碼,我使用了#{RAILS_ROOT} 來得到rails應用的當前位置,現在運行“rake --tasks”,你可以看到我們的任務已經添加到tasks列表中了。
...
rake tmp:pids:clear # Clears all files in tmp/pids
rake tmp:sessions:clear # Clears all files in tmp/sessions
rake tmp:sockets:clear # Clears all files in tmp/sockets
rake utils:create_directories # Create blank directories if they don't already exist
...

九、如何在任務中調用rails的model

呵呵,這個正是我最多使用rake的地方,寫一個rake任務,代替原來需要手工操作的地方,或者一些任務代替經常需要按照計劃自動執行(使用
cronjobs)的事情。就像我開頭說的那樣我用rake任務執行下面的擦作:

1、給列表中的用戶發送郵件
2、每晚數據的計算和報告
3、過期或重新生成緩存
4、備份數據和svn版本(how's this : subversion repository)
5、運行數據處理腳本(sort of,how much is called this )

這個補充了原來的功能,而且相當簡單。下面這個任務是檢查用戶的過期時間,對快過期的用戶發送郵件。

utils.tasks

namespace :utils do
desc "Finds soon to expire subscriptions and emails users"
task(:send_expire_soon_emails => :environment) do
# Find users to email
for user in User.members_soon_to_expire
puts "Emailing #{user.name}"
UserNotifier.deliver_expire_soon_notification(user)
end
end
end
使用你的model只用一步,"=> :environment"

task(:send_expire_soon_emails => :environment) do

如果在我的開發環境上運行這個任務,我只需要
"rake utils:send_expire_soon_emails",如果我想在產品環境下運行這個任務,我需要"rake RAILS_ENV=production utils:send_expire_soon_emails"

如果你想在每晚都運行這個任務,你需要寫一個
cronjob ,像這樣:

0 0 * * * cd /var/www/apps/rails_app/ && /usr/local/bin/rake RAILS_ENV=production utils:send_expire_soon_emails
相當的方便。

十、在哪找到一些例子

現在對一個有用的rake任務已經瞭解很多了,那麼我將給你幾個資源,我想最好的學習方法是看看別人的代碼。

brand new rake tasks 在edge rails 中,這個可以創建和重置你的數據庫。

Craig Ambrose寫的數據庫備份, database backups

Adam Greene寫了一組任務 set of Rake tasks,可以將所有的數據備份到amazon S3。他還給了我一個升級版本,你可以在這下載here

Jay Fields的任務測試,testing rake tasks

a new way of setting the RAILS_ENV and teaches how to use rake to boot you into a Mysql shell (看的時候留意一下他的註釋)

Rake Bookshelf Books,和Martin Fowler的Using the Rake Build Language 教程,這兩個都很有用,雖然有點過時。

如果你發現其他更好的文章,發貼子給我們。

譯者:恩,這段不用翻譯,懂的朋友自然會去看的了。

Still reading? If you are, I wanted to let you know that we're looking for more people to write for RailsEnvy. If you have an idea for a good rails tutorial we want to hear from you! Basically we would work with you to flesh out the tutorial and help polish (acting as an editor). It could definitely be a great way to get your name out there, and start getting some hits (for your blog or company). Email Gregg at RailsEnvy if you're interested.

另:我剛收到jim的郵件,如何更簡單的創建我的目錄。
# This is needed because the existing version of directory in Rake is slightly broken, but Jim says it'll be fixed in the next version.
alias :original_directory :directory
def directory(dir)
original_directory dir
Rake::Task[dir]
end

# Do the directory creation
namespace :utils do
task :create_directories => [
directory('public/icons'),
directory('public/images'),
directory('public/groups'),
]
end


注:圖片均來自英文原文(Pics come from the english page)

(譯者:目前這個內容不是研究的重點,所以會不忙的時候翻譯一點點)
發佈了7 篇原創文章 · 獲贊 0 · 訪問量 1753
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章