2010-04-23 69 views
3

我繼承了一個項目,其中包含許多寫得很糟糕的Rake任務,我需要清理一下。因爲Rakefiles是巨大的,並且往往容易出現奇怪的無意義的依賴關係,所以我通過將所有東西重構爲類來簡化和隔離事情。如何使用Ruby元編程來重構此通用代碼?

具體而言,這種模式是這樣的:

namespace :foobar do 
    desc "Frozz the foobar." 
    task :frozzify do 
    unless Rake.application.lookup('_frozzify') 
     require 'tasks/foobar' 
     Foobar.new.frozzify 
    end 
    Rake.application['_frozzify'].invoke 
    end 

    # Above pattern repeats many times. 
end 

# Several namespaces, each with tasks that follow this pattern. 

tasks/foobar.rb,我有一些看起來像這樣:

class Foobar 
    def frozzify() 
    # The real work happens here. 
    end 

    # ... Other tasks also in the :foobar namespace. 
end 

對於我來說,這是偉大的,因爲它可以讓我分開任務依賴關係以及將它們完全移動到另一個位置,並且我已經能夠徹底地簡化事物並隔離依賴關係。在您真正嘗試運行任務之前,Rakefile未達到require。以前這是造成嚴重問題的原因,因爲如果沒有炸燬任務,你甚至無法列出任務。

我的問題是我非常頻繁地重複這個習語。請注意以下模式:

  • 對於每一個命名空間:xyz_abc,有文件tasks/[namespace].rbtasks/...相應的類,它有一個類名,看起來像XyzAbc

  • 對於特定名稱空間中的每個任務,在關聯的名稱空間類中都有一個名稱相同的方法。例如,如果名稱空間:foo_bar有一個任務:apples,則預計在FooBar類中看到def apples() ...,該類本身在tasks/foo_bar.rb中。

  • 每個任務:t定義了一個用於完成實際工作的「元任務」_t(即任務名稱以下劃線爲前綴)。

我還是希望能夠指定一個desc -description爲我定義的任務,這將是爲每個任務不同。而且,當然,我有一小部分任務完全不遵循上述模式,所以我將在我的Rakefile中手動指定這些任務。

我相信這可以通過某種方式進行重構,所以我不必一直重複相同的習語,但是我缺乏經驗來看它如何完成。有人可以給我一個協助嗎?

回答

4

這樣的事情應該適合你。

# Declaration of all namespaces with associated tasks. 
task_lists = { 
    :foobar => { 
    :task_one => "Description one", 
    :task_two => "Description two", 
    :frozzify => "Frozz the foobar", 
    :task_three => "Description three" }, 
    :namespace_two => { 
    :task_four => "Description four", 
    :etc => "..."} } 

# For every namespace with list of tasks... 
task_lists.each do |ns, tasks| 
    # In the given the namespace... 
    namespace ns do 
    # For every task in the task list... 
    tasks.each do |task_name, description| 
     # Set the task description. 
     desc description 
     # Define the task. 
     task task_name do 
     unless Rake.application.lookup("_#{task_name}") 
      # Require the external file identified by the namespace. 
      require "tasks/#{ns}" 
      # Convert the namespace to a class name and retrieve its associated 
      # constant (:foo_bar will be converted to FooBar). 
      klass = Object.const_get(ns.to_s.gsub(/(^|_)(.)/) { $2.upcase }) 
      # Create a new instance of the class and invoke the method 
      # identified by the current task. 
      klass.new.send(task_name) 
     end 
     Rake.application["_#{task_name}"].invoke 
     end 
    end 
    end 
end 

更新:補充說明。

(請注意,我沒有測試過,所以有可能在那裏小錯誤。)

+0

這肯定看起來像它的工作,但他會如何,如果他去這條路線指定任務描述? – 2010-04-23 11:52:28

+0

@John,好點。我用散列替換了任務數組。這樣也有一些方法來設置描述。然而,這可能會變得越來越模糊,所以在某種程度上可能會創建某種API。 – molf 2010-04-23 14:33:27