Возможность сопоставления значений перечисления с строковым типом вместо целого

Атрибуты Enum великолепны, и я хочу их использовать. Но преобразование значений перечисления в integer затрудняло бы поддержание как кода, так и базы данных. Кроме того, моя база данных будет тесно связана с моим кодом, который, я думаю, должен считать, что это плохо.

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

Есть ли способ по умолчанию перечислить строки в строки?

Ответ 1

Насколько я знаю, это невозможно при использовании встроенных функций enum Active Record. Тем не менее, есть несколько популярных сторонних драгоценных камней, которые делают это. Самое близкое совпадение с тем, что происходит с Active Record, вероятно, enumerize и SimpleEnum.

Однако, если вы ищете что-то немного другое, я бы рекомендовал ClassyEnum (полное раскрытие: я его написал). Вот некоторые из моих заметок о различии между перечислением и SimpleEnum vs ClassyEnum:

Перечисления класса (enumerize, SimpleEnum) отлично подходят для простого использования случаи, когда вам просто нужно поле для представления одного из фиксированного набора значения. Основной вопрос, который у меня есть с этим решением, заключается в том, что он поощряет условные обозначения, разбросанные по всем вашим моделям, контроллерам и представлениям. Заманчиво использовать эти драгоценные камни, потому что они самые простые но долгосрочный выигрыш просто не существует ИМО для чего-либо но простейшие случаи.

ClassyEnum был разработан для решения проблемы рассеяния условная логика, связанная с различными перечислениями. Вы все еще можете использовать его для простых коллекций, у которых нет логики, но когда вам нужно добавьте логику (и вы почти наверняка будете), вы можете отдельные классы enum и использовать полиморфизм.

Ответ 2

Посмотрев код для перечисления, вы можете сделать это (по крайней мере, в 4.1 +): https://github.com/rails/rails/blob/master/activerecord/lib/active_record/enum.rb#L96-98, передав хеш для пример:

class Foo
  enum name: {
    foo: 'myfoo',
    bar: 'mybar'
  }

Alltough с неожиданными результатами при доступе к нему см. https://github.com/rails/rails/issues/16459

foo_instance.foo!
foo_instance.name
=> "foo"
foo_instance[:name]
=> "myfoo"

Ответ 3

Короткий ответ - нет. Вам нужно будет использовать драгоценный камень (например, magic-enum), если вы хотите делать что-либо, кроме целых чисел.

В своих собственных словах DHH из комментариев на запрос на перенос, который добавил эту функцию:

Это довольно неэффективно для хранения перечислений в виде текста. Вы повторяете один и тот же текст снова и снова. Я считаю, что это анти-шаблон. Людям лучше делать переход к ints, если они хотят использовать это.

Ответ 4

Похоже, что только с Rails 5 API атрибут enum будет сохраняться в базе данных как целое, но будет опубликован в общедоступном API в виде строки (с помощью ActiveModel:: Сериализатор).

Например,

Модель статьи:

class Article < ApplicationRecord
  enum status: [ :visible, :hidden ]
end

Сериализатор статьи:

class ArticleSerializer < ActiveModel::Serializer
  attributes :id, :status, :title, :body
end

опубликует следующий json:

{
  "id": "1",
  "type": "articles",
  "attributes": {
    "status": "visible",
    "title": "Enums are passed as string in a json API render",
    "body": "Great!",
}

Ответ 5

Как насчет:

class Foo < ApplicationRecord
  NAMES = [
    :foo,
    :bar
  ]

  enum names: NAMES.zip(NAMES).to_h
end