Ruby:require vs include / extend:在另一个模块中使用模块,但不向用户公开

时间:2018-10-13 22:10:32

标签: ruby

在对模块进行混合测试时,我无法理解错误。

假设要测试的mixin是模块A:

require 'path/b'    
module A
    def methodA()
        puts methodB.attr1 
    end
end

这取决于在path / b.rb文件中定义的另一个mixin B

module B
    def methodB
       return someObject #that has property 'attr1'
    end       
end

现在,我们有一个可以对模块A进行单元测试的类

require 'path/moduleA'
class TestA
    include Path::moduleA
end

describe 'test moduleA.methodA'
  it 'does something'
     testObject = TestA.new
     testObject.methodA()
     expect(....)
  end
end  

在运行rspec测试时出现以下错误

NameError:
   undefined local variable or method `methodB' for #<TestA:0x00007f77a03db9a8>

我可以通过以下方式之一解决它:

  1. 在模块A中包括模块B
  2. 在TestA类中包括模块B

问题

  1. 我不清楚为什么需要include才能访问中的methodB 模块A和TestA类(当在模块A中已经添加了'require'时)。

  2. 我的意图是在模块A中使用模块B的方法,但不要让模块A的用户自动访问模块B的方法。

      上面的
    • 分辨率1使A的用户可以访问B的方法

    • 分辨率2强制A的用户(在此示例中为user->单元测试类)直接包含A的依赖项B,即使用户仅对访问A的方法感兴趣。

因此,两种分辨率都达不到我想要的效果。有办法实现吗?

我是Ruby的新手,所以可能不支持此功能。我来自Java背景,我将A和B建模为两个类,将B的实例作为A的字段,然后向A的用户公开A自己的公共方法。但是由于它们是mixins,因此我需要在红宝石。

3 个答案:

答案 0 :(得分:1)

非常明确:require / require_relative / loadinclude / extend / prepend绝对没有任何事情可做彼此。

前三个仅运行Ruby文件。而已。那就是他们所做的一切。他们在搜索文件的方式和位置上有所不同,但是在找到文件后,除了执行文件之外,他们不执行任何其他操作。

后三个将模块添加到祖先链。 include本质上使模块成为超类,extend实际上与singleton_class.include相同(即使模块成为单例类的超类),并且prepend将模块插入到祖先链的开始,即实际上是在之前的类别。

答案 1 :(得分:0)

require只是告诉ruby读取/加载ruby文件中的代码。在这种情况下,它将仅定义模块。但是,为了使模块内的代码包含在另一个模块或类内,您必须在模块或类内include进行编码。所以您应该像您提到的那样:

require 'path/b'    
module A
  include B
  def methodA()
    puts methodB.attr1 
  end
end

由于模块A已经包括模块B,因此您不需要更改测试。但是,在这里这不是一个很好的OOP设计模式。但是希望您现在明白为什么。

答案 2 :(得分:0)

经过更多的搜索后,我使用来自以下方面的建议找到了第二个问题的答案: https://makandracards.com/makandra/977-calling-selected-methods-of-a-module-from-another-module

所以我基本上做到了:

require 'path/b'
module A
   module B_RequiredMethods
       include Path::B
       module_function :methodB
   end

    def methodA
        puts B_RequiredMethods.methodB.attr1
    end
end

在我的情况下,可以正确命名B_RequiredMethods以表示B公开的B方法。理想情况下,我将这些方法设为B的类级别方法,但由其他团队来管理。