对于ruby gems / modules的命名空间扩展的约定是什么?

时间:2013-08-10 16:46:39

标签: ruby module namespaces directory organization

我不能为我的生活找到任何简单的解释。

我正在开发一个拥有自己的自定义代码的项目,但也有一些扩展/覆盖可用的类虽然包含了宝石。

实现此目的的正确文件夹结构和模块命名模式是什么?

例如,以下是我如何为第三方“parallel_tests”gem的扩展组织代码,我正在处理的库名为“ChemistyKit”:

lib/
  chemistrykit/
     parallel_tests/ # the gem name space i'm extending
       rspec/ 
         runner.rb

所以我基本上遵循与parallel_tests文件夹下面的gem相同的命名结构。

然后在那个runner.rb文件中我有类似的东西:

module ChemistryKit
  module ParallelTests
    module RSpec
      class Runner < ParallelTests::Test::Runner

但是使用它会导致错误。我认为这是因为它实际上从来没有超过所讨论的类,因为它在ChemistryKit下被命名空间这令人困惑,因为在this extension of the RSpec HTML formatter之类的东西中,格式化程序在gem的顶级命名空间下使用。那是因为在这种情况下,它创建了一个类似命名但不同的类吗?意图不是覆盖,而是扩展?

1 个答案:

答案 0 :(得分:1)

要完全替换RSpec中的Runner类,您需要执行以下操作

module RSpec
  module Core
    remove_const(:Runner)
    class Runner < ParallelTests::Test::Runner
      ...
    end
  end
end

模块内部的代码使用基于其名称的路径查找常量。因此,您在示例中定义的Runner类引用常量ChemistryKit::ParallelTests::RSpec::Runner

您可以通过指定RSpec模块位于全局命名空间中来引用其中一个模块中的RSpec Runner,如下例所示。

module ChemistryKit
  module ParallelTests
    module ::RSpec
      module Core
        class Runner < ParallelTests::Test::Runner

现在即使你这样做,还有一个问题就是用这种方式修改类。因为你想要替换你必须首先删除它,否则你将只修改现有的类(如果你指定一个不同的超类,你不能重新打开一个类,rspec运行器已经是Object的子类,你可以不要改变。这就是我的第一个例子中remove_const调用的内容。

然而,额外的模块实际上并没有在上面的例子中做任何事情,除非让事情变得不那么清楚。在修改现有gem的代码时,只需将所有代码放在rspec子文件夹中(就像你一样),但使用与原始代码完全相同的模块名称。

请记住,以这种方式修改类很容易失败。例如,如果在需要时使用该类,则直到事后才能进行更换,这可能会导致无法预料的问题。这就是为什么你链接的格式化程序示例采用不同的方法,代码不是monkeypatch RSpec,而是在运行时附加它的新类。