module ActiveRecord::StringEnum
Declare an enum attribute where the values map to integers in the database, but can be queried by name. Example:
class Conversation < ActiveRecord::Base enum status: [ :active, :archived ] end # conversation.update! status: :active conversation.active! conversation.active? # => true conversation.status # => "active" # conversation.update! status: :archived conversation.archived! conversation.archived? # => true conversation.status # => "archived" # conversation.update! status: :archived conversation.status = "archived" # conversation.update! status: nil conversation.status = nil conversation.status.nil? # => true conversation.status # => nil
Scopes based on the allowed values of the enum field will be provided as well. With the above example:
Conversation.active Conversation.archived
Of course, you can also query them directly if the scopes doesn’t fit your needs:
Conversation.where(status: [:active, :archived]) Conversation.where.not(status: :active)
You can set the default value from the database declaration, like:
create_table :conversations do |t| t.column :status, :string, default: :active end
In rare circumstances you might need to access the mapping directly. The mappings are exposed through a class method with the pluralized attribute name.
Conversation.statuses[0] # => :active Conversation.statuses[1] # => :archived
Constants
- ENUM_CONFLICT_MESSAGE
- VERSION
Public Instance Methods
str_enum(definitions)
click to toggle source
# File lib/active_record/string_enum.rb, line 98 def str_enum(definitions) klass = self definitions.each do |name, values| # statuses = [ ] enum_values = values.map(&:to_s) 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) { values } detect_enum_conflict!(name, name) detect_enum_conflict!(name, "#{name}=") # TODO: in Rails 4.2.1 this will be legal: # attribute name, EnumType.new(name, enum_values) # instead of the next lines: type = EnumType.new(name, enum_values) define_method("#{name}=") do |value| write_attribute(name, type.cast(value)) end define_method(name) { type.deserialize(self[name]) } _enum_methods_module.module_eval do enum_values.each do |value| # def active?() status == :active end klass.send(:detect_enum_conflict!, name, "#{value}?") define_method("#{value}?") { self[name] == value } # def active!() update! status: :active end klass.send(:detect_enum_conflict!, name, "#{value}!") define_method("#{value}!") { update! name => value } # scope :active, -> { where status: :active } klass.send(:detect_enum_conflict!, name, value, true) klass.scope value, -> { klass.where name => value } end end defined_str_enums[name.to_s] = enum_values end end
Private Instance Methods
_enum_methods_module()
click to toggle source
# File lib/active_record/string_enum.rb, line 142 def _enum_methods_module @_enum_methods_module ||= begin mod = Module.new include mod mod end end
detect_enum_conflict!(enum_name, method_name, klass_method = false)
click to toggle source
# File lib/active_record/string_enum.rb, line 155 def detect_enum_conflict!(enum_name, method_name, klass_method = false) if klass_method && dangerous_class_method?(method_name) raise ArgumentError, ENUM_CONFLICT_MESSAGE % { enum: enum_name, klass: self.name, type: 'class', method: method_name, source: 'Active Record' } elsif !klass_method && dangerous_attribute_method?(method_name) raise ArgumentError, ENUM_CONFLICT_MESSAGE % { enum: enum_name, klass: self.name, type: 'instance', method: method_name, source: 'Active Record' } elsif !klass_method && method_defined_within?(method_name, _enum_methods_module, Module) raise ArgumentError, ENUM_CONFLICT_MESSAGE % { enum: enum_name, klass: self.name, type: 'instance', method: method_name, source: 'another enum' } end end