如何选择在Ruby中动态包含哪个版本的模块?
我正在编写一个小型的Ruby命令行应用程序,它使用标准库中的fileutils
进行文件操作。 根据用户调用应用程序的方式,我将要包括FileUtils
, FileUtils::DryRun
或FileUtils::Verbose
。
既然include
是私有的,我不能把逻辑选择到对象的initialize
方法中。 (这是我的第一个想法,从那时起我就可以将有关用户选择的信息作为参数传递给new
。)我已经提出了两个似乎有用的选项,但我对以下两种方法都不满意:
-
根据用户的选择在应用程序的命名空间中设置一个全局变量,然后在类中执行条件包含:
class Worker case App::OPTION when "dry-run" include FileUtils::DryRun etc.
-
创建子类,唯一的区别是它们包含哪个版本的
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
第一种方法似乎是DRY-er,但我希望有一些我没有想到的更简单的方法。
那么如果它是私人的呢?
class Worker def initialize(verbose=false) if verbose (class <
这包括FileUtils::something
特别是Worker
的元类 - 而不是主要的Worker
类。 不同的工作者可以通过这种方式使用不同的FileUtils
。
如果你想避免“切换”并注入模块,那么
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中,这并不是真正的“隐私”,但有些方法被标记为私有,并非没有理由。
通过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