原文:
    
      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 為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
    
     教程,這兩個都很有用,雖然有點過時。
  
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061
 
					微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
 
					

 
    