Вложенные классы против компактных в 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

В случае модели модуль совпадает с путем, а имя класса совпадает с именем файла. В случае контроллеров вторая часть пути, "tenant", является частью имени класса.

Рубокоп говорит мне, что я должен "использовать вложенные определения классов вместо компактного стиля" в Tenant::MembersController линия, так что если я правильно понимаю...

module AppCore  
  class Tenant
    class MembersController < ApplicationController
    end
  end
end

... это не должно иметь значения.

Теперь мой вопрос заключается в том, что у меня в качестве модели AppCore::Tenant, но затем AppCore::Tenant выглядит открытым, и класс MembersController добавляется в него как вложенный класс. Означает ли это, что у моего класса Арендатора всегда будет этот вложенный класс? Нужно ли называть мои модели и маршруты контроллеров по-другому? Это совершенно нормально и не о чем беспокоиться? Не совсем уверен, что это значит.

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, вам придется отказаться от некоторых соглашений, но конфигурация, требуемая для этого, не так уж плоха.

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

Есть ли конкретная причина, в первую очередь, почему вы хотите...

  1. ... ввести "путь", как иерархия?
  2. ... разместить контроллер внутри модельного класса?

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

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

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

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

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

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
Другие вопросы по тегам