модуль ActiveRecord::Enum
Объявляет атрибут перечисления, значения которого отображаются в базе данных как целые числа, но могут быть запрошены по имени. Пример:
class Conversation < ActiveRecord::Base enum status: [ :active, :archived ] end # conversation.update! status: 0 conversation.active! conversation.active? # => true conversation.status # => "active" # conversation.update! status: 1 conversation.archived! conversation.archived? # => true conversation.status # => "archived" # conversation.status = 1 conversation.status = "archived" conversation.status = nil conversation.status.nil? # => true conversation.status # => nil
Также будут предоставлены области поиска, основанные на разрешенных значениях поля перечисления. На примере выше:
Conversation.active Conversation.archived
Можно установить значение по умолчанию из объявления базы данных, например:
create_table :conversations do |t| t.column :status, :integer, default: 0 end
Хорошей практикой является установка первого объявленного статуса в качестве значения по умолчанию.
Наконец, также возможно явно сопоставить связь между атрибутом и целым числом в базе данных с помощью Hash.
class Conversation < ActiveRecord::Base
enum status: { active: 0, archived: 1 }
end
Обратите внимание, что при использовании Array, неявное отображение значений в целые числа базы данных происходит из порядка появления значений в массиве. В примере :active отображается как 0, так как это первый элемент, а :archived отображается как 1. В общем случае, i-й элемент отображается как i-1 в базе данных.
Поэтому, после добавления значения в массив перечисления, необходимо сохранить его позицию в массиве, а новые значения следует добавлять только в конец массива. Для удаления неиспользуемых значений следует использовать синтаксис явного Hash.
В редких случаях может потребоваться прямой доступ к отображению. Отображения доступны через метод класса с множественным именем атрибута:
Conversation.statuses # => { "active" => 0, "archived" => 1 }
Используйте этот метод класса, когда вам необходимо узнать порядковое значение перечисления:
Conversation.where("status <> ?", Conversation.statuses[:archived])
Условие на атрибуте перечисления должно использовать порядковое значение перечисления.
Константы
- ENUM_CONFLICT_MESSAGE
Открытые методы экземпляров
# File activerecord/lib/active_record/enum.rb, line 81
def enum(definitions)
klass = self
definitions.each do |name, values|
# statuses = { }
enum_values = ActiveSupport::HashWithIndifferentAccess.new
name = name.to_sym
# def self.statuses statuses end
detect_enum_conflict!(name, name.to_s.pluralize, true)
klass.singleton_class.send(:define_method, name.to_s.pluralize) { enum_values }
_enum_methods_module.module_eval do
# def status=(value) self[:status] = statuses[value] end
klass.send(:detect_enum_conflict!, name, "#{name}=")
define_method("#{name}=") { |value|
if enum_values.has_key?(value) || value.blank?
self[name] = enum_values[value]
elsif enum_values.has_value?(value)
# Assigning a value directly is not a end-user feature, hence it's not documented.
# This is used internally to make building objects from the generated scopes work
# as expected, i.e. +Conversation.archived.build.archived?+ should be true.
self[name] = value
else
raise ArgumentError, "'#{value}' is not a valid #{name}"
end
}
# def status() statuses.key self[:status] end
klass.send(:detect_enum_conflict!, name, name)
define_method(name) { enum_values.key self[name] }
# def status_before_type_cast() statuses.key self[:status] end
klass.send(:detect_enum_conflict!, name, "#{name}_before_type_cast")
define_method("#{name}_before_type_cast") { enum_values.key self[name] }
pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
pairs.each do |value, i|
enum_values[value] = i
# def active?() status == 0 end
klass.send(:detect_enum_conflict!, name, "#{value}?")
define_method("#{value}?") { self[name] == i }
# def active!() update! status: :active end
klass.send(:detect_enum_conflict!, name, "#{value}!")
define_method("#{value}!") { update! name => value }
# scope :active, -> { where status: 0 }
klass.send(:detect_enum_conflict!, name, value, true)
klass.scope value, -> { klass.where name => i }
end
end
defined_enums[name.to_s] = enum_values
end
end
© 2004–2018 David Heinemeier Hansson
Licensed under the MIT License.