Rails 模块作为严格的命名空间

Posted

技术标签:

【中文标题】Rails 模块作为严格的命名空间【英文标题】:Rails modules as strict namespaces 【发布时间】:2018-12-10 10:34:42 【问题描述】:

我对 Rails 很陌生,对模块在这里的工作方式有点困惑。我有一个这样的项目结构:

# app/models/foo.rb
class Foo < ActiveRecord

# lib/external_service/foo.rb
module ExternalService
  class Foo

# lib/external_service/bar.rb
module ExternalService
  class Bar
    attribute :foo, Foo # not the model

我之前使用过许多编码语言,我希望可以很容易地在 Bar 和 ExternalService 中使用“Foo”,但是

LoadError: 无法自动加载常量 Foo,需要 lib/external_service/foo.rb 来定义它

ExternalService::Foo 通常不应该在 ExternalService 之外可见,但是整个项目都死在这个东西上

我是否只是遗漏了一种“严格模式”符号或任何东西以确保我显然是指服务内部的 ExternalService::Foo 并防止服务杀死我的模型?

我知道我可以只添加模块,但我想让代码保持可读性。

【问题讨论】:

旁注:这个问题与 ruby​​ 本身无关,它是一个奇怪而易碎的 rails autoloading 会破坏一切。 哦,真的吗?我认为模块可能是 ruby​​ 本身的一部分,感谢您的评论@mudasobwa,我要删除 ruby​​ 标签 模块是 Ruby 的一部分。文件的自动加载由 Rails 完成。 模块确实是红宝石。但是在 ruby​​ 中根本没有没有自动加载。必须明确地require 必要的文件,就像c 中的#include 一样。 @mudasobwa 还有autoload 【参考方案1】:

所以你使用的是 rails 4

如果你想创建一个模块,首先你需要导入或自动加载你的 lib 文件夹

例如在 application.rb 中,您可以将 lib 文件夹添加到自动加载:

config.autoload_paths << Rails.root.join('lib')

之后,因为您使用的是 rails,您应该创建一个文件夹层次结构,其中包含模块层次结构的蛇形名称 例如,如果您有:

module ExternalService
  class Foo
    ...
  end
end

您的 foo.rb 文件应位于名为“external_service”的文件夹中

project_root/lib/external_service/foo.rb

文件夹层次结构是rails的约定。

【讨论】:

哦,我在 application.rb 中找到了那行并删除了它。现在它不再在 lib 中寻找模型了,不知道问题是否解决了【参考方案2】:

Ruby 的行为就是这样,完全没问题。

在这种情况下,Foo-Model 已经加载,所以 ruby​​ 更喜欢这个而不是本地的。同样按字母顺序 app/ 在 lib/ 之前

一个不那么漂亮但快速的解决方法就是这样称呼它:

attribute :foo, ExternalService::Foo

【讨论】:

以上是关于Rails 模块作为严格的命名空间的主要内容,如果未能解决你的问题,请参考以下文章

Rails 使用同一命名空间中的模型作为 belongs_to 引用,如何从外部引用模型

Rails 和 RSpec:在不同的命名空间(模块)中测试具有相同名称的控制器

Rails url_for 和命名空间模型

删除存在于另一个命名空间 rails 中的模型

Ruby 嵌套模块作为命名空间

映射到命名空间模块时将 prop 作为模块名称传递