代码之家  ›  专栏  ›  技术社区  ›  Telemachus MrJames

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

  •  10
  • Telemachus MrJames  · 技术社区  · 14 年前

    我正在编写一个小的Ruby命令行应用程序,它使用 fileutils 文件操作的标准库。根据用户调用应用程序的方式,我想包括 FileUtils , FileUtils::DryRun FileUtils::Verbose .

    include 是私有的,但是,我不能将选择的逻辑放入对象的 initialize 方法(这是我的第一个想法,从那时起我就可以把用户选择的信息作为参数传递给 new

    1. 根据用户的选择在应用程序的命名空间中设置一个全局变量,然后在类中执行条件include:

      class Worker
        case App::OPTION
        when "dry-run"
          include FileUtils::DryRun
          etc.
      
    2. 创建子类,其中唯一的区别是 文件单元 它们包括。根据用户的选择,选择合适的。

      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 回复  |  直到 14 年前
        1
  •  8
  •   taw    14 年前

    如果是私人的呢?

    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 的元类-不是主要的 班级。不同的工人可以使用不同的 FileUtils 这种方式。

        2
  •  1
  •   kirkytullins    10 年前

    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
    
        3
  •  0
  •   kuonirat    12 年前

    如果要避免使用“开关”并注入模块,则

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

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

    def initialize(injected_module = MyDefaultModule)
        extend injected_module
    end
    

    但是它也将副作用降到了最低——类的共享和容易更改的状态,这可能导致在更大的项目中出现意外的行为。可以说,在Ruby中没有真正的“隐私”,但是有些方法被标记为private并不是没有原因的。