Rails 自动加载:常量在测试之间丢失或引入循环依赖
Rails autoloading: constants are lost between tests or a circular dependency is introduced
我 运行 遇到了 Rails 自动加载在 development/test 环境中的工作方式的问题。我想要实现的是一种父级 class 的可插入实现,可以在应用程序启动时修改。更详细地说,我用以下想法实现了 class Measure
:
# app/models/measure.rb
class Measure
attr_reader :implementations
def self.register(klass)
@implementations << klass
end
def self.from(raw_value)
# ... find a suitable implementation and instantiate it
end
# ...
end
然后我有不同 Measures
的实现,例如:
# app/models/measures/grams.rb
module Measures
class Grams < Measure
# ... code to tell Measures if this class can be
# instantiated from a given raw value
end
end
然后应使用 Measure.register(Measures::Grams)
等调用来设置这些。
问题是,如果我在初始化程序中执行此操作,则度量实现会在测试 运行 之间丢失,因为常量缓存显然已被清除。这意味着,像 Measure.from("2 grams")
这样应该有效的调用不再有效,因为 Measure.implementations
不包含 Measures::Grams
。另一方面,如果我在 app/models/measure.rb
中添加一个 require_dependency
,引用一些执行注册的代码,那么在 Rails 加载之前引用其中一个实现时,我有一个循环依赖Measure
class。经过相当多的摆弄和阅读 Rails 如何自动加载之后,我一直无法想出一个令人满意的解决方案。
所以我的问题是:应该如何设置此层次结构才能与 Rails 自动加载代码的方式相得益彰?
看来我找到了解决这个问题的方法,将跟踪已注册实现的责任从 Measure
基础 class 转移到 Measures
模块,并且还在定义模块的同一文件中进行注册。在我看来,这也是一个更合理的架构选择。唯一剩下要做的就是用 's' 词缀来区分名称。
我 运行 遇到了 Rails 自动加载在 development/test 环境中的工作方式的问题。我想要实现的是一种父级 class 的可插入实现,可以在应用程序启动时修改。更详细地说,我用以下想法实现了 class Measure
:
# app/models/measure.rb
class Measure
attr_reader :implementations
def self.register(klass)
@implementations << klass
end
def self.from(raw_value)
# ... find a suitable implementation and instantiate it
end
# ...
end
然后我有不同 Measures
的实现,例如:
# app/models/measures/grams.rb
module Measures
class Grams < Measure
# ... code to tell Measures if this class can be
# instantiated from a given raw value
end
end
然后应使用 Measure.register(Measures::Grams)
等调用来设置这些。
问题是,如果我在初始化程序中执行此操作,则度量实现会在测试 运行 之间丢失,因为常量缓存显然已被清除。这意味着,像 Measure.from("2 grams")
这样应该有效的调用不再有效,因为 Measure.implementations
不包含 Measures::Grams
。另一方面,如果我在 app/models/measure.rb
中添加一个 require_dependency
,引用一些执行注册的代码,那么在 Rails 加载之前引用其中一个实现时,我有一个循环依赖Measure
class。经过相当多的摆弄和阅读 Rails 如何自动加载之后,我一直无法想出一个令人满意的解决方案。
所以我的问题是:应该如何设置此层次结构才能与 Rails 自动加载代码的方式相得益彰?
看来我找到了解决这个问题的方法,将跟踪已注册实现的责任从 Measure
基础 class 转移到 Measures
模块,并且还在定义模块的同一文件中进行注册。在我看来,这也是一个更合理的架构选择。唯一剩下要做的就是用 's' 词缀来区分名称。