在模块中嵌套类与使用模块作为类名的一部分

Posted

技术标签:

【中文标题】在模块中嵌套类与使用模块作为类名的一部分【英文标题】:Nesting class in module vs using module as part of class name 【发布时间】:2016-10-14 05:25:43 【问题描述】:

类似于module and class with the same name in Rails project,但我试图理解为什么将它们全部放在一行上但使用单独的行却不行。

使用邀请新用户加入帐户。我的user 模型具有最少的验证,并且我使用具有附加行为的子类。这类似于服务对象,但大多数时候子类比服务更干净,因为它们都保存在/app/models 文件夹中并且更易于维护。

我有app/models/user/as_invitation.rb。当它看起来像以下时,它工作正常:

class ::User::AsInvitation < ::User
  # ...
end

但是当我有这个时,它不起作用:“用户不是模块”。

module User
  class AsInvitation < ::User
    # ...
  end
end

我怀疑 Ruby 对模块/类使用相同的命名空间,并且由于我已经有一个 User 类(模型),它与 User 模块(模块与模型)发生冲突。

我可以使用::User::AsInvitation 格式并且它可以工作,但是当魔法发生时它会困扰我。作为另一种解决方案,我可以使用模块名称 Users 复数,但对我来说,使用单数 User 作为命名空间更有意义。

我试图了解 Ruby/Rails 是如何实现这一点的,并确定我是否继续在一行上使用 ::User::AsInvitation,我是否会发现自己陷入困境没有桨。

【问题讨论】:

模块或类没有命名空间。类或模块只是一个对象,就像任何其他对象一样。 @JörgWMittag:我想我想知道为什么 ::User::AsInivation 可以正常工作,但是当我将它们嵌套在不同的行上时它就不起作用了? module User 打开常量User 引用的模块(在当前范围内查找)如果定义了User,则首先创建带有空模块的常量User它没有定义。在您的情况下,定义了常量 User,但是 Ruby 在尝试打开模块时失败,因为 User 没有引用模块,它引用了一个类。 ::User::AsInvitation 取消引用常量User(在Object 的范围内),然后取消引用嵌套在User 引用的模块或类中的常量AsInvitation 重点是:类或模块没有什么特别之处。如果您将String 分配给User 并尝试对其进行Fixnum 操作,您也会收到错误消息。在这种情况下,您将 Class 分配给 User,但您正在对其使用 Module 操作。第一种情况,你不关心是类还是模块,你只解引用一个嵌套的常量,模块和类都可以包含常量。 @JörgWMittag 确实,模块或类没有命名空间,但有个命名空间用于constants,按惯例用于引用它们。对吗? 【参考方案1】:

@JörgWMittag 在 cmets 中回答了我的问题(如果您想将其放入答案中,我会接受):

module User
end

class User < ActiveRecord::Base
end

两者都解析为 Ruby 中的相同常量 (User),因此发生冲突,而:

module User
  class AsActive < ActiveRecord::Base
  end
end

class ::User::AsActive < ActiveRecord::Base
end

解析为不同的 Ruby 常量(User::AsActiveUser)。模块和类使用相同的常量命名空间(可能不是最好的形容词,但对我来说很有意义)。

【讨论】:

你得到的错误来自告诉Ruby User 是一个模块,说module User,它不是。在第二种情况下,您只对它应用 :: 运算符,它适用于所有对象,因此 User 是什么以及它是一个类而不是一个模块是无关紧要的。【参考方案2】:

至少在 Rails 6 中,在单独的文件中执行此操作的正确方法是:

# models/user.rb
class User < ApplicationRecord
end

# models/user/as_invitation.rb
class User
   class AsInvitation < User
   # ...
   end
end

# config/routes.rb
resources :users
namespace :user do
  resources :as_invitations
end

# controllers/users_controller.rb
class UsersController < ApplictionController
end

# controllers/user/as_invitations_controller.rb
class User::AsInvitationsController < UsersController
end

【讨论】:

以上是关于在模块中嵌套类与使用模块作为类名的一部分的主要内容,如果未能解决你的问题,请参考以下文章

何时使用嵌套类和嵌套在模块中的类?

Ruby 嵌套模块作为命名空间

在装饰器中使用全局嵌套模块

JAVA学习脚印9:外部类与嵌套类

JAVA学习脚印9:外部类与嵌套类

java类与嵌套嵌套后,怎么使用最外层的类建立对象后使用内部类的方法?