Вложенные классы против компактных в Ruby

Работа над первоначальным проектом 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 как вложенный класс. Означает ли это, что в моем классе Tenant всегда будет этот вложенный класс? Нужно ли называть мои модели и маршруты контроллеров как-то по-другому? Это нормально и не о чем беспокоиться? Не совсем уверен, что это значит.


person typeoneerror    schedule 24.06.2014    source источник


Ответы (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

Если вы ссылаетесь на константы, определенные в AppCore::Tenant, изнутри MembersController, это может иметь значение для вас. Тонкий, но, возможно, важный, и хорошо бы знать об этом. Я столкнулся с этим в реальной жизни, когда у меня был модуль Util с подмодулем String. Я переместил метод в Util, и он сломался, потому что String внутри этого метода теперь ссылается на Util::String. После этого я изменил некоторые соглашения об именах.

Ваш модуль Tenant всегда будет иметь MembersController в качестве вложенного класса. В любом другом месте вашей кодовой базы вы можете ссылаться на AppCore::Tenant::MembersController. Если вы хотите лучшего разделения, вы должны назвать свои классы моделей по-другому или поместить их в модуль, такой как AppCore::Model или аналогичный. Если вы используете Rails, вам придется отказаться от некоторых соглашений, но конфигурация, необходимая для этого, не так уж плоха.

person Sami Samhuri    schedule 24.06.2014
comment
Мне нравится идея помещать мои модели в модуль модели (это полный рот). Хороший совет. - person typeoneerror; 24.06.2014
comment
OTOH компактная форма не создает родительский модуль/класс, в то время как вложенная форма создает, что также может привести к трудно обнаруживаемым ошибкам. - person radiospiel; 18.11.2015

Я знаю, что вы спрашиваете о технических особенностях, и Сами ответил на это. Но я не могу удержаться и вынужден спросить:

Есть ли какая-то конкретная причина, по которой вы хотите...

  1. ... ввести «путь», например иерархию?
  2. ... поместить контроллер в класс модели?

Если бы я чувствовал потребность в 1), у меня, вероятно, были бы простые «контейнерные» модули, повторяющие реальные пути. То есть app/model/tenant.rb => Model::Tenant и app/controller/members_controller.rb => Controller::MembersController.

Но, честно говоря, я не вижу причин для этого. Контроллеры уже легко обнаруживаются по соглашению XyzController. Модели (я полагаю, в большинстве случаев) довольно легко распознаются по их предметной природе. Поскольку ruby ​​не требует и даже не предлагает сопоставлять имена путей с именами классов (в отличие, например, от Java), мне было бы полезнее четкое одноуровневое соглашение об именовании.

Иерархии подмодулей/подклассов очень полезны для драгоценных камней, где они функционируют как пространства имен, чтобы избежать конфликтов.

2) (Контроллер внутри модели) в корне неверен. Контроллеры очень сильно отличаются от Моделей и уж точно не живут внутри них.

person AnoE    schedule 14.01.2016

Если вы используете вложенные и хотите вернуться к пространству имен верхнего уровня, вы можете использовать ::.

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
person ignacio chiazzo    schedule 02.08.2017