2010-07-29 35 views
9

我正在寫使用fileutils標準庫中對文件操作的小Ruby的命令行應用程序。根據用戶如何調用應用程序,我將要爲包括FileUtilsFileUtils::DryRunFileUtils::Verbose如何選擇在Ruby中動態包含哪個版本的模塊?

由於include是私人的,但我不能把邏輯選擇到對象的initialize方法。 (這是我的第一個想法,從那以後,我可以只通過對用戶的選擇作爲參數傳遞給new的信息。)我想出來的,似乎工作的兩個選擇,但我不開心或者:

  1. 設置根據用戶的選擇在應用程序的命名空間的全局變量,然後做一個條件包括在類:

    class Worker 
        case App::OPTION 
        when "dry-run" 
        include FileUtils::DryRun 
        etc. 
    
  2. 創建的子類,其中唯一的區別是哪個版本他們包括FileUtils。根據用戶的選擇選擇合適的一個。

    class Worker 
        include FileUtils 
        # shared Worker methods go here 
    end 
    class Worker::DryRun < Worker 
        include FileUtils::DryRun 
    end 
    class Worker::Verbose < Worker 
        include FileUtils::Verbose 
    end 
    

第一種方法似乎乾兒,但我希望有更多的東西簡單,我沒有想到的。

回答

7

所以,如果它就是私有?

class Worker 
    def initialize(verbose=false) 
    if verbose 
     (class <<self; include FileUtils::Verbose; end) 
    else 
     (class <<self; include FileUtils; end) 
    end 
    touch "test" 
    end 
end 

這包括在特定的Worker的元類FileUtils::something - 不在主Worker類。不同的工作人員可以通過這種方式使用不同的FileUtils

+0

「那又怎樣」正確的聲音給我。 (這正是*我看不到的更直接的事情。)謝謝。 – Telemachus 2010-07-29 00:52:50

+0

糟糕,我的錯誤。我之前給出的代碼將修改'Worker'類,所有'Worker's將使用相同的設置。現在它實際上使用元類並允許工作前設置。 – taw 2010-07-29 01:52:10

+1

,而不是'(類<<自我;包括fileutils中端)'你也可以使用'擴展FileUtils'。 – 2010-07-29 04:23:11

0

如果你想避免的「開關」,注入的模塊中,

def initialize(injected_module) 
    class << self 
     include injected_module 
    end 
end 

語法將無法正常工作(該injected_module變量超出範圍)。您可以使用self.class.send伎倆,但每個對象實例擴展似乎更合理的對我來說,不僅是因爲它是短寫:

def initialize(injected_module = MyDefaultModule) 
    extend injected_module 
end 

而且它最大限度地減少了副作用 - 共享和容易可變的狀態,這可能導致更大的項目出現意想不到的行爲。在Ruby中,並不是真正的「隱私」,但有些方法被標記爲私有的,而不是沒有理由的。

0

有條件地包括通過發送方法的模塊工作,我作爲在下面的測試,例如:

class Artefact 
    include HPALMGenericApi 
    # the initializer just sets the server name we will be using ans also the 'transport' method : Rest or OTA (set in the opt parameter) 
    def initialize server, opt = {} 
    # conditionally include the Rest or OTA module 
    self.class.send(:include, HPALMApiRest) if (opt.empty? || (opt && opt[:using] opt[:using] == :Rest)) 
    self.class.send(:include, HPALMApiOTA) if (opt && opt[:using] opt[:using] == :OTA)  
    # ... rest of initialization code 
    end 
end