Rails: странно вызывать модуль в модуле как Enum - PullRequest
2 голосов
/ 31 января 2020

Извините за мой бедный Engli sh.

Я использую сервисный уровень в своем проекте Rails.
Классы обслуживания упакованы модулем для пространства имен.
И модуль имеет модуль как Enum.
Я пытаюсь вызвать этот модуль enum, но рельсы обычно говорят StandardError exception: uninitialized constant.

app/helpers/application_helper.rb

def no_problem_method
  logger.debug LeaderBoard::FetchBoardMembersService.new.call
end

def problem_method
  logger.debug LeaderBoard::BoardType::TOTAL  # uninitialized constant LeaderBoard::BoardType
end

app/services/leader_board/fetch_board_members_service.rb

module LeaderBoard
  module BoardType
    TOTAL = 'total'
    IN_SESSION = 'in_session'
    GROUP = 'group'
  end.freeze

  class FetchBoardMembersService < BaseService

Но, изменяя порядок звонков, это не вызывает ошибку.

app/helpers/application_helper.rb

def problem_method
  logger.debug LeaderBoard::BoardType::FetchBoardMembersService
  logger.debug LeaderBoard::BoardType::TOTAL  # SUCCESS!
end

Почему это происходит?
Понятия не имею. Пожалуйста, помогите ... Более того, я поместил код и журналы для вашего понимания этих странных вещей.

def problem_method
  logger.info LeaderBoard
  logger.info LeaderBoard.constants
  logger.info LeaderBoard::FetchBoardMembersService  
  logger.info LeaderBoard.constants
  logger.info LeaderBoard::BoardType
  logger.info LeaderBoard::BoardType::TOTAL
end

и журналы

rails_1  | LeaderBoard
rails_1  | []
rails_1  | LeaderBoard::FetchBoardMembersService
rails_1  | [:BoardType, :FetchBoardMembersService]
rails_1  | LeaderBoard::BoardType
rails_1  | total

1 Ответ

2 голосов
/ 31 января 2020

Это довольно просто на самом деле. Автозагрузчик rails не знает, что app/services/leader_board/fetch_board_members_service.rb объявляет константу LeaderBoard::BoardType и как это может быть?

Это не имеет ничего общего с тем фактом, что это "Enum", который на самом деле не вещь в Ruby. Это просто модуль, который объявляет несколько констант и, таким образом, ничем не отличается от любого другого модуля, это просто шаблон, который отдаленно напоминает типы enum в других языках, таких как C ++ и Java.

При поиске константы автозагрузчик делает предположения на основе местоположения файла и ожидает, что файл будет находиться в одном из путей автозагрузки. Пути загрузки в вашем среднем приложении rails - это каждый подкаталог /app. Поэтому, когда вы ссылаетесь на LeaderBoard::BoardType Rails ожидает, что он будет определен в /app/**/leader_board/board_type.rb.

Как вы уже поняли, это работает, если вы уже ссылались на LeaderBoard::BoardType::FetchBoardMembersService, поскольку app/services/leader_board/fetch_board_members_service.rb уже требовалось.

Решение также простое: выложите свой код правильно. «root файл модуля» должен объявлять любые константы, которые не находятся в отдельных файлах.

# app/services/leader_board.rb
module LeaderBoard
  module BoardType
    TOTAL = 'total'.freeze
    IN_SESSION = 'in_session'.freeze
    GROUP = 'group'.freeze
  end.freeze
end
# app/services/leader_board/fetch_board_members_service.rb
module LeaderBoard
  class FetchBoardMembersService < BaseService
  end
end

Это работает, так как Rails сначала "поднимет" вложенность модуля и автоматически загрузит "внешние модули". Это не только делает его автозагрузкой должным образом - это также помогает другим разработчикам найти код, так как это логичное место для поиска, если оно не определено в его собственном файле.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...