原文:
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.rake
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.rake
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