【问题标题】:How to access a class defined in module B from module A如何从模块 A 访问模块 B 中定义的类
【发布时间】:2020-05-08 01:57:46
【问题描述】:

错误信息:

NameError: uninitialized constant FinancialFunctions::ComputationSteps::TwoBranchesPerPeriod::Strategy1

我希望 Rails 自动查看包含的 Strategies 模块并查看其中定义的类,而不仅仅是查看 TwoBranchesPerPeriod。

这不是一次性问题,而是更广泛的架构重组的一部分。解决这个问题将有助于整个代码库的许多地方。

在寻找两种具体的解决方案:

  • 在文件顶部有一个 requireload 语句,因为我希望 Rails 自动加载成为此问题的核心解决方案,而不必需要多个文件。
  • 在初始化类时明确说明范围。我知道只需调用Strategies::Strategy1.new 就可以了,但是由于几个类的长度,这个解决方案对我不起作用。我想在文件顶部使用include 语句简单地定义依赖关系,并可以访问包含的模块中定义的方法/类。
  • 我对诸如 strategy.rb 等文件中的 autoload 语句持开放态度,但根据我的测试,这也不起作用。

app/lib/financial_functions/computation_steps/two_branches_per_period.rb:

module FinancialFunctions
  module ComputationSteps
   module TwoBranchesPerPeriod
      include Strategies
      def a_method
        #The below line is resulting in the error
        # NameError: uninitialized constant FinancialFunctions::ComputationSteps::TwoBranchesPerPeriod::Strategy1
        a=Strategy1.new
      end
   end
  end
end 

/app/lib/financial_functions/computation_steps/strategies/strategy1.rb:

module FinancialFunctions
  module ComputationSteps
   module Strategies
    class Strategy1
      def initialize
        puts "this should work"
      end
    end
  end
end

【问题讨论】:

  • 包含或扩展一个模块只复制方法,它对类没有任何作用。改用Strategies::Strategy1 是不是打字太多了?
  • classmodule 应为小写。
  • 是的,它们都是小写的,只是输入错误。 @maxpleaner 许多这些类名都很长,而且它们通常有超过 1 个“分开”的模块,这意味着几个链式作用域/命名空间在一起。出于可读性的目的,这就是为什么我想在文件顶部的某个地方声明这些长作用域,并且只使用方法名称就可以在下面访问
  • 如果您明确建议包括名称,我可以。我能问你另一个问题的根源吗?在让 Rails 正确加载/重新加载对文件的更改方面,我遇到了很多麻烦。因此,如果我包含一个模块,那么如果我更改方法并再次发出请求,有时对方法的更改将看不到或不可见。因此,我养成了一个坏习惯,即在类的顶部显式地创建大量显式加载语句。关于如何解决这个问题的任何建议?一切都在 app/lib 下,所以应该自动加载
  • 是的,明确表达肯定更容易,即使它类似于Strategy1 = Strategies::Strategy1。关于重新加载文件,很抱歉我不记得了,不过肯定有办法做到这一点。

标签: ruby-on-rails ruby


【解决方案1】:

我想简单地在文件顶部定义依赖项 include 语句,并可以访问定义的方法/类 包含的模块。

您的期望在 Ruby 中是不现实的。 Ruby 没有包系统,includes 不等同于 Python 或 ES6 等语言中的imports

正如@maxpleaner 在 cmets 中所述,仅包含模块的方法的副本。

因此,您必须在消费类/模块中定义新常量:

module FinancialFunctions
  module ComputationSteps
    module TwoBranchesPerPeriod
      include Strategies
      Strategy1 = ComputationSteps::Strategies::Strategy1

      def a_method
        Strategy1.new
      end
    end
  end
end

您可以通过Module#included 挂钩动态执行此操作:

module Foo
  module Bar
  end

  module Baz
  end

  def self.included(base)
    self.constants.each do |c|
      base.constant_set(c, self.constant_get(c))
    end
  end
end
module X
  includes Foo
end
irb(main):002:0> X::Bar
=> Foo::Bar

但除了作为元编程的一个有趣示例之外,我并不认为它有那么明智,因为没有办法处理命名空间冲突。您还可以构建一个临时等效的导入构造:

module Importer
  def import(*constants, from:)
    constants.each do |c|
      const_set(c, from.const_get(c))
    end
  end
end
module Foo
  module Bar
    module A
    end
    module B
    end
  end
end
class Thing
  extend Importer
  import :A, :B, from: Foo::Bar
end
irb(main):001:0> Thing::A
=> Foo::Bar::A
irb(main):002:0> Thing::B
=> Foo::Bar::B

但这听起来也像是与语言对抗的例子,并且可能会引入比它解决的问题更多的问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-03
    相关资源
    最近更新 更多