Допустим, у нас есть эта простая модель:
class Project < ApplicationRecord
enum stage: {Idea: "idea", Done: "done", "On hold": "on hold", Cancelled: "cancelled"}
enum status: [:draft, :published, :archived]
end
Когда мы обращаемся к перечислениям из модели (Project.stages, Project.statuses), мы получаем в результате обработанный (ActiveRecord::Enum) ответ, оба в виде хеша.
irb(main):001:0> Project.stages
=> {"Idea"=>"idea", "Done"=>"done", "On hold"=>"on hold", "Cancelled"=>"cancelled"}
irb(main):002:0> Project.statuses
=> {"draft"=>0, "published"=>1, "archived"=>2}
Я изо всех сил пытаюсь понять, когда перечисление было объявлено как хэш или как массив, имеющий только модель и имя перечисления.
Любая идея о том, как получить начальный хеш или массив из перечисления?
@muistooshort да, я работаю над функцией для Avo (avohq.io), где мне нужно относиться к ним немного по-другому
Если мы не можем понять это, нам нужно попросить разработчика указать тип перечисления, и мы пытаемся избежать этого.
Я не думаю, что вы можете, источник просто проверяет, как он может перебирать значения (each_pair, если это хеш, each_with_index в противном случае).
Интересно, сможем ли мы каким-то образом получить этот values аргумент, который получает источник
Может быть, вам на самом деле нужно просто использовать Project.type_for_attribute('stages') для получения типа базового столбца базы данных?
С другой стороны, enum stage: {Idea: "idea", Done: "done", "On hold": "on hold", Cancelled: "cancelled"} также очень однозначен, так как использование клавиш в верхнем регистре приведет к методам с именами в верхнем регистре, такими как Idea, Idea? и Idea!. Если вы вызовете Idea с неявным получателем (без себя), Ruby будет думать, что это константа. Не делай этого. Ваши ключи перечисления должны быть строчными и змеиными.
Спасибо за ответы и за советы @max, я полностью с вами согласен. Как я упоминал в комментариях выше, этот вопрос возник, когда я разрабатывал для Avo. Мы стремимся к тому, чтобы Avo работал для каждого разработчика, даже если хостинг-приложение хорошо или плохо использует перечисления. В любом случае, Project.type_for_attribute('stage') дает ответ, который мы искали!!
Вы не должны относиться к ним по-разному из-за принципа наименьшего удивления.
Enum status: [:draft, :published, :archived] — это просто неявное сокращение для определения перечисления, где отображение совпадает с индексами массива. Вот как это задокументировано, и когда ваш код делает что-то еще, открывается настоящий момент WTF.
Неявное определение перечислений также считается плохой практикой, поскольку добавление новых статусов в середину массива очень незаметно нарушит работу вашего приложения. Существует даже полицейский Rubocop, который обнаруживает использование массивов с помощью регулярного выражения в исходном коде.
На самом деле это просто небрежный способ написания enum status: { draft: 0, published: 1, archived: 2}. Вы не должны поощрять его использование.
Аргументы, которые вы передаете enum, используются только для метапрограммирования сеттеров, геттеров и создания метаданных, в которых хранится отображение перечисления. Он не сохраняет исходные аргументы, поскольку в этом нет смысла.
Если вы ДЕЙСТВИТЕЛЬНО хотели это сделать, вы могли бы обезвредить метод перечисления:
module WonkyEnum
# this is silly. Don't do this
def enum(definitions)
@_enum_definitions ||= []
@_enum_definitions.push(definitions)
super(definitions)
end
end
Но какой бы ни была реальная проблема, скорее всего, есть лучшее решение.
Спасибо за подробное объяснение @max! Мы создаем поле выбора, в котором разработчик может использовать перечисление в качестве параметров. Мы не хотим использовать их по-другому, но после некоторой отладки мы обнаружили, что если мы хотим, чтобы выбор работал идеально, нам нужно предварительно обработать параметры по-разному, когда перечисление имеет простой массив или объявление хэша. PS: После некоторого расследования я заметил, что мы не использовали options_for_select() в нашем теге выбора, что исправило некоторые из наших проблем!
Зачем вам это знать? Какую проблему вы пытаетесь решить, определяя, как было определено перечисление? Хотели бы вы относиться к enum status: %i[draft published archived] и enum status: { draft: 0, published: 1, archived: 2 } по-разному?