如何选择在Ruby中动态包含哪个版本的模块?

时间:2010-07-29 00:24:29

标签: ruby include module

我正在编写一个小型Ruby命令行应用程序,它使用标准库中的fileutils进行文件操作。根据用户调用应用程序的方式,我希望包含FileUtilsFileUtils::DryRunFileUtils::Verbose

由于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
    
  3. 第一种方法似乎是DRY-er,但我希望有一些我没有想过的更简单的方法。

3 个答案:

答案 0 :(得分:8)

那么如果它是私人的呢?

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

这包括FileUtils::something特别是Worker的元类 - 不在主Worker类中。不同的工作人员可以这种方式使用不同的FileUtils

答案 1 :(得分:0)

如果您想避免“切换”并注入模块,

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

语法不起作用(inject_module变量超出范围)。您可以使用self.class.send技巧,但每个对象实例扩展对我来说似乎更合理,不仅因为它写得更短:

def initialize(injected_module = MyDefaultModule)
    extend injected_module
end

但它也最小化了副作用 - 类的共享和易于改变的状态,这可能导致更大的项目中的意外行为。在Ruby中,这并不是真正的“隐私”,但有些方法被标记为私有,并非没有理由。

答案 2 :(得分:0)

通过send方法有条件地包含模块对我有用,如下面测试的例子所示:

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