【问题标题】:Nested classes versus compact in RubyRuby 中的嵌套类与紧凑类
【发布时间】:2014-08-15 01:33:43
【问题描述】:

从事初始 Rails 项目,并使用Rubocop 分析代码风格。这让我开始质疑 Ruby 的嵌套类在 Rails 上下文中是如何工作的。例如,在我的引擎中,我有一个模型:

# app/models/app_core/tenant.rb
module AppCore
  class Tenant < ActiveRecord::Base
  end
end

和一个控制器:

# app/controllers/app_core/tenant/members_controller.rb
module AppCore
  class Tenant::MembersController < ApplicationController
  end
end

在模型的情况下,模块与路径相同,类名与文件名相同。在控制器的情况下,路径的第二部分“租户”是类名的一部分。

Rubocop 告诉我应该在 Tenant::MembersController 行中“使用嵌套类定义而不是紧凑样式”,所以如果我理解正确的话......

module AppCore  
  class Tenant
    class MembersController < ApplicationController
    end
  end
end

...这应该没什么区别。

现在,我的问题是我将 AppCore::Tenant 作为模型,但随后 AppCore::Tenant 看起来已重新打开,并且 MembersController 类作为嵌套类添加到其中。这是否意味着我的租户类将始终包含该嵌套类?我是否需要以不同的方式命名我的模型和控制器路由?这完全没问题,没有什么可担心的吗?不完全确定这意味着什么。

【问题讨论】:

    标签: ruby-on-rails ruby rubocop


    【解决方案1】:

    如果您使用的是嵌套并想返回顶级命名空间,您可以使用::

    def class user < ActiveRecord::Base 
       NAME = "Real User"
    end
    
    module SomeModule
        def class User 
           Name = "Fake User"
        end 
        module InnerModule
            class MyClass
                puts User.NAME # "Fake User"
                puts ::User.Name # "Real User" 
            end
        end
    end
    

    【讨论】:

      【解决方案2】:

      我知道您询问的是技术细节,Sami 已经回答了这个问题。但我忍不住要问:

      首先,您是否有特殊原因想要...

      1. ...引入类似层次结构的“路径”?
      2. ...将控制器放在模型类中?

      如果我觉得需要 1),我可能会有简单的“容器”模块来呼应真实路径。也就是说,app/model/tenant.rb => Model::Tenantapp/controller/members_controller.rb => Controller::MembersController

      但坦率地说,我真的不明白它背后的原因。 XyzController 约定已经很容易发现控制器。模型(大多数时候,我猜)很容易被它们的领域性质所识别。由于 ruby​​ 不需要甚至不建议将路径名与类名匹配(例如,与 Java 不同),所以明确的 1 级命名约定对我来说会更有用。

      子模块/子类层次结构对于 gem 来说非常有用,或者说是必需的,它们的功能类似于命名空间以避免冲突。

      2) (模型中的控制器)根本上是错误的。控制器与模型非常不同,当然也不存在于其中。

      【讨论】:

        【解决方案3】:

        一个细微的区别是您的范围不同,这可能会导致错误。在第一种情况下,常量将在AppCore 中查找,而在第二种情况下,常量将在AppCore::Tenant 中查找。如果您完全限定常量名称,那么它没有任何区别。

        Foo = :problem
        
        module A
          Foo = 42
        
          # looks up A::Foo because of lexical scope
          module B
            def self.foo
              Foo
            end
          end
        end
        
        # looks up ::Foo because of lexical scope
        module A::C
          def self.foo
            Foo
          end
        end
        
        # Looks up A::Foo, fully qualified ... ok technically ::A::Foo is fully qualified, but meh.
        module A::D
          def self.foo
            A::Foo
          end
        end
        
        A::B.foo # => 42
        A::C.foo # => :problem
        A::D.foo # => 42
        

        如果您从MembersController 中引用AppCore::Tenant 中定义的常量,那么它可能会对您产生影响。微妙但可能很重要,需要注意。当我有一个带有String 子模块的Util 模块时,我在现实生活中遇到了这个问题。我将一个方法移到Util 中,但由于该方法中的String 现在引用Util::String,所以它坏了。之后我更改了一些命名约定。

        您的Tenant 模块将始终将MembersController 作为嵌套类。在您的代码库中的任何其他地方,您都可以参考AppCore::Tenant::MembersController。如果您想要更好的分离,那么您应该以不同的方式命名您的模型类,或者将它们放在诸如AppCore::Model 或类似的模块中。如果您使用的是 Rails,您将不得不打破一些约定,但所需的配置还不错。

        【讨论】:

        • 我喜欢将我的模型放在模型模块中的想法(这很拗口)。很好的提示。
        • OTOH 紧凑形式不会创建父模块/类,而嵌套形式会创建,这也可能导致难以发现的错误。
        猜你喜欢
        • 2020-05-03
        • 2010-12-19
        • 1970-01-01
        • 2020-10-22
        • 1970-01-01
        • 2012-01-26
        • 1970-01-01
        • 1970-01-01
        • 2021-03-10
        相关资源
        最近更新 更多