Лучший способ сохранить значение Enum в ActiveRecord и преобразовать в строку для отображения

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

т.е.

Review Enum:

UNREVIEWED = {:title => "Unreviewed", :name => "UNREVIEWED"}
REVIEWED = {:title => "Reviewed", :name => "REVIEWED"}
FLAGGED = {:title => "Flagged as inappropriate", :name => "FLAGGED"} 

Итак, в java-стране я использовал для хранения имени ENUM, т.е. (REVIEWED) в базе данных, а затем преобразовал это имя в это фактическое перечисление на сервере, чтобы я мог называть его вспомогательные методы, то есть:

review = Review.valueOf(review)
review.title()

Есть ли что-то подобное в rails для выполнения этого?

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

Любой "стандартный" способ сделать это, поскольку я полагаю, что я не первый, кто борется с этой проблемой?

Спасибо!

Ответ 1

Перечисления ActiveRecord - лучший способ, поскольку он является частью фреймворка (начиная с версии 4.1).

Его использование довольно просто:

Миграция

class AddEnumToMyModel < ActiveRecord::Migration
  def change
    add_column :my_model, :status, :integer, default: 0
  end
end

Модель:

class MyModel < ActiveRecord::Base
  enum status: [:draft, :beta, :public]
end

Затем используйте его волю:

MyModel.draft # gets all drafts
MyModel.last.draft? # checks if the last model is draft
MyModel.last.status # gets the string description of the status of my model

Информацию о режиме см. в документации.

Ответ 2

есть много сообщений об этой проблеме, я думаю, это указывает на большинство из них: http://thinkinginrails.com/2010/04/using-enums-for-constants/

Я думаю, что это слишком сложная вещь, которая вам не нужна в динамически типизированном языке, таком как ruby.

просто используйте строки!

вы могли бы использовать это как:

class Review < ActiveRecord::Base
  validates_inclusion_of :status, :in => ["UNREVIEWED", "REVIEWED", "FLAGGED"]
  def status
    read_attribute(:status)
  end
  def status= (value)
    write_attribute(:status, value)
  end
end

Ответ 3

Использование примера @Godsaur.

class MyModel < ActiveRecord::Base
  enum status: [:draft, :beta, :public]
end 

Вы можете получить строковое значение как:

MyModel.last.status
=> "beta"

Но если вы хотите "заголовок", вы можете:

MyModel.last.status.titleize
=> "Beta"

Ответ 4

Хотя я согласен с тем, что ответ @Godsaur верен - мне лично не нравится подход к хранению целочисленных значений для представления значимых эквивалентов строк (при условии, что выполняется достаточная индексация базы данных и/или стоимость запросов строк в модуле БД аналогичен запросу целых чисел).

Мой подход обычно хранит текстовые значения в базе данных (для более простого понимания записей БД).

my_model.rb

class MyModel < ActiveRecord::Base

  STATUSES = HashWithIndifferentAccess.new({unverified: 'unverified', reviewed: 'reviewed', flagged: 'flagged'})
  #the text values are similar to their symbols key
  #you could also define additional attributes for each key if needed. Ex:
  #STATUSES = {unverified: {title: 'Unverified Text Title', txt: 'unverified'}, ...}

  # assuming a 'status' field
  scope :unverified, -> { where(status: STATUSES[:unverified]) }

  def unverified?
    status == STATUSES[:unverified]
  end

  # Or
  STATUSES.each do |sym, val|
    define_method("#{sym}?") {status == val}
  end
end

my_view.erb

<%= MyModel::STATUSES[@my_model.status] %> or 
<%= MyModel::STATUSES[@my_model.status].title %>

Ответ 5

Я согласен с @tamersalama. Здесь простой способ сделать это, используя строки в БД с пользовательским классом Enum. Обратите внимание, что это также поддерживает читаемые пользователем имена для использования в выпадающих меню.

https://gist.github.com/alexch/a7be54e1b085718473ff

SQL:

Table name: snacks
 id                         :integer
 ice_cream                  :string

Рельсы:

class Snack < ActiveRecord::Base
  FLAVORS = Enum.new [
      [:vanilla, "Vanilla"],
      [:chocolate, "Chocolate"],
      [:rocky_road, "Rocky Road"]
      ])
  ]
end

HTML:

<%= simple_form_for @snack do |f| %>
<%= f.collection_select :ice_cream, Snack::FLAVORS, :value, :label %>
<% end %>