Rake簡介
Rake的意思是Ruby Make,一個用ruby開發的代碼構建工具.
但是,為什麼Ruby需要Rake?
按理說Ruby代碼無需編譯,應該不需要Rake才對呀?原來,Rake另有妙用,即把Rake當做一個任務管理工具來使用...這樣做有兩個好處:
1.以任務的方式建立和運行指令碼
當然,你可以用指令碼來建立每一個你希望自動啟動並執行任務.但是,對於大型的應用來說,你幾乎總是需要為資料庫遷移(比如Rails中db:migrate任務)、清空緩衝、或者代碼維護等等編寫指令碼.對於每一項任務,你可能都需要寫若干指令碼,這會讓你的管理變得複雜.那麼,把它們用任務的方式整理到一起,會讓管理變得輕鬆很多.
2.追蹤和管理工作之間的依賴
Rake還提供了輕鬆管理工作之間依賴的方式.比如,"migrate"任務和"schema:dump"任務都依賴於 "connect_to_database"任務,那麼在"migrate"任務調用之前,"connect_to_database"任務都會被執行.
下面入正題吧,即如何用Rake編寫一個任務指令碼..
順序執行
在Rake中定義任務後,可以指定任務的執行順序,例如,每天早晨起床後的例行公事:
1. 關閉鬧鐘
2. 梳洗打扮
3. 泡杯咖啡
4. 遛狗
上面的幾項事物,在Rakefile中這樣描述
task :turn_off_alarm do puts "Turned off alarm. Would have liked 5 more minutes, though." end task :groom_myself do puts "Brushed teeth." puts "Showered." puts "Shaved." end task :make_coffee do cups = ENV["COFFEE_CUPS"] || 2 puts "Made #{cups} cups of coffee. Shakes are gone." end task :walk_dog do puts "Dog walked." end task :ready_for_the_day => [:turn_off_alarm, :groom_myself, :make_coffee, :walk_dog] do puts "Ready for the day!" end
通過rake ready_for_the_day來執行任務,然後你就可以看到,所有的task都在按照你預定的順序在執行。
Turned off alarm. Would have liked 5 more minutes, though. Brushed teeth. Showered. Shaved. Made 5 cups of coffee. Shakes are gone. Dog walked. Ready for the day!
此外還可以用過rake make_coffee COFFEE_CUPS=5這樣在命令中給變數賦值。
命名空間
上面那樣定義任務沒有問題,但如果你需要另外定義些事物,比如工作相關的,交通相關的,這時候所有的任務混雜到一起顯然就不合適了,畢竟上面那些任務只是我們起床的例行事物,與其它無關。
通過namespace可以協助我們定義出Rails中類似rake db:migrate的任務,描畫出事物間清晰的邊界,將上面的任務包括在namespace的一個代碼塊中,如下
namespace :morning do task :turn_of_alarm .... end
這一次我們的調用命令就需要稍微做出些許變化,rake COFFEE_CUPS=3 morning:ready_for_the_day。 是不是跟rails中的rake任務調用方式很像呢?
預設Task
有了上面那些設定,如果我們忘記或者不想寫詳細的任務名稱,直接執行rake會有什麼效果呢,結果是rake aborted!,任務被中斷的報錯。既然有這樣的可能性,我們就需要消除這些隱患,通過設定default預設任務就可以做到,如下:
task :default => 'morning:turn_off_alarm'
當你直接執行rake命令時,會執行預設操作,幫我們關閉鬧鐘。
描述你的Task
當任務漸漸層多以後,管理問題就暴露出來了,除了命名空間以外,我們還需要文檔類的支援,協助我們梳理任務並顯示的告訴我們每個任務的目的和功能,這時你可以試一試用desc描述任務。
... desc "Make coffee" task :make_coffee do cups = ENV["COFFEE_CUPS"] || 2 puts "Made #{cups} cups of coffee. Shakes are gone." end ...
上面的描述不僅可以在文檔中查看,同時使用rake -T也能清洗的瞭解到每個任務是做什麼的。rake -T的輸出結果是按照字母順序排的序。
rake morning:make_coffee # Make coffee
調取Task
Rake還允許在不同任務之間互相調用,例如下面的代碼,你想在下午也來杯咖啡,不用重複定義,直接使用上午的泡法,來一杯即可。
namespace :afternoon do task :make_coffee do Rake::Task['morning:make_coffee'].invoke puts "Ready for the rest of the day!" end end
Rake指令碼編寫
先來個簡單的例子,如下:
假設你是一個火星成員,老版本那些,即如燕兒虎跑之類的,在周末你打算去車車那邊打火鍋,然後集體PC去.對應這個情況,你需要為自己制定三個任務:搭車找車車、烤魚和網吧PC.用vim建立建立一個名叫rakefile的檔案(備忘:Rake會在當前路徑下尋找名叫Rakefile、rakefile、RakeFile.rb和rakefile.rb的rake檔案),並鍵入如下代碼:
desc "任務1 -- 搭車去車車那裡"#這據說是個苦差,因為太遠了 task :busboy do puts "發現挫男" end desc "任務2 -- 烤魚" task :bitchfish do puts "老闆,先烤九斤魚" end desc "任務3 -- 網吧PC" task :pc do puts "我選中路" end
開啟命令列工具,進入這個檔案所在目錄,然後運行下面的命令,大致應該類似如下結果:
D:\work>rake busboy (in D:/work) 發現挫男 D:\work>rake bitchfish (in D:/work) 老闆,先烤九斤魚 D:\work\ruby_works\ruby_book>rake laundry (in D:/work) 我選中路
(備忘:文字部分是沒啥邏輯的,純粹娛樂下...)
分析:
相信看完上面那段東東,你已經知道怎麼搞了...現在介紹些基礎知識,方便加深理解.從上面的代碼可以知道,此檔案一共定義了3個任務,desc是Rake定義的方法,表示對下面定義任務的描述.這個描述會在使用Rake --tasks(或者Rake -T)命令時輸出在螢幕上.
D:\work>rake --tasks (in D:/work) rake bitchfish #任務2 -- 烤魚 rake busboy #任務1 -- 搭車去車車那裡(這據說是個苦差,因為太遠了) rake pc 任務3 -- 網吧PC
task是Rake最重要的方法.它的方法定義是:task(args, &block).任務體是一個block,本例中只是簡單輸出你所要做的工作.需要注意的是代碼
完全是一個普通的Ruby語句,puts是Ruby中進行輸出的一般性方法,可以看出,Rake任務可以完全使用Ruby的能力,這使得它非常強大.
go..go..go..go..
接下來加入依賴關係:
很顯然,在我們定義的任務中,"烤魚"是依賴於"搭車去車車那裡"的(其它地方有沒烤魚吃不知道,反正地點就定在那了).那麼,我們需要在我們的任務定義中加入這個依賴關係,修改後的檔案如下:
desc "任務1 -- 搭車去車車那裡" task :busboy do puts "發現挫男" end desc "任務2 -- 烤魚" task :bitchfish => :busboy do puts "老闆,先烤九斤魚" end desc "任務3 -- 網吧PC" task :pc do puts "我選中路" end
再次運行烤魚任務,你會得到如下結果:
D:\work>rake bitchfish (in D:/work) 發現挫男 老闆,先烤九斤魚
加入命名空間:
跟任何程式設計語言類似,當你的rake檔案很多時,當你有很多任務的時候,你需要關注它們的命名衝突問題,命名空間(namespace)就是一個自然的解決方案.你可以為上面的三個任務定義一個叫做dan的命名空間.
namespace :dan do desc "任務1 -- 搭車去車車那裡" task :busboy do puts "發現挫男" end …… end
再次運行rake --tasks,你會得到如下的結果:
D:\work >rake --tasks (in D:/work) rake dan:bitchfish # 任務2 -- 烤魚 rake dan:pc # 任務3 -- 網吧PC rake dan:busboy # 任務1 -- 搭車去車車那裡
你現在需要使用rake dan:bitchfish才能啟動烤魚這個任務了.
(BTW,你可以在你的rakefile中使用多個命名空間,對任務進行分類.)
瞭解了上面兩個知識點後,我們來瞭解下兩個具體執行個體:
1.在一個任務中調用另外一個任務
當任務眾多的時候,你很可能需要在一個任務中調用另外一個任務,假設我們把今天所有要做的工作定義為一個任務:today.在這個任務中,有兩個任務需要被調用,一個是烤魚,一個是網吧PC.當然,由於烤魚依賴於搭車去車車那裡,我們還是需要搭車去車車那裡的.在檔案的頂部定義一個today的任務:
desc "今天的任務" task :today do Rake::Task["dan:bitchfish"].invoke Rake::Task["dan:pc"].invoke end namespace :dan do …… end
可以看出,調用其它任務的方式很簡單,只需要調用
Rake::Task["task_name"].invoke
方法就可以了.在命令列中啟動rake today,可以得到:
D:\work >rake today (in D:/work) 發現挫男 老闆,先烤九斤魚 我選中路
2.預設任務:
可以為Rake增加一個預設任務,這樣可以簡單地用Rake命令來觸發這個預設任務,在上面的rakefile中,我們可以用如下方式把"today"任務作為預設任務.
task :default => [:today]
然後調用直接在命令列中調用rake,可以得到跟調用rake today同樣的輸出結果.
這就是我們簡單的一個Rake任務定義,下面是完整的修改後的rakefile:
task :default => [:today] desc "今天的任務" task :today do Rake::Task["dan:bitchfish"].invoke Rake::Task["dan:pc"].invoke end namesoace :dan do desc "任務1 -- 搭車去車車那裡(這據說是個苦差,因為太遠了)" task :busboy do puts "發現挫男" end desc "任務2 -- 烤魚" task :bitchfish do puts "老闆,先烤九斤魚" end desc "任務3 -- 網吧PC" task :pc do puts "我選中路" end end
看完上面兩個例子,估計rake任務就算全部瞭解了...其它都是些代碼槍花罷了..多耍便是..