1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Added ActiveRecord::Base#enum for declaring enum attributes where the values map to integers in the database, but can be queried by name

This commit is contained in:
David Heinemeier Hansson 2013-11-02 12:01:31 -07:00
parent deaf285824
commit db41eb8a6e
7 changed files with 128 additions and 2 deletions

View file

@ -1,3 +1,28 @@
* Added ActiveRecord::Base#enum for declaring enum attributes where the values map to integers in the database, but can be queried by name.
Example:
class Conversation < ActiveRecord::Base
enum status: %i( active archived )
end
Conversation::STATUS # => { active: 0, archived: 1 }
# 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.update! status: 1
conversation.status = :archived
*DHH*
* ActiveRecord::Base#attribute_for_inspect now truncates long arrays (more than 10 elements)
*Jan Bernacki*

View file

@ -37,6 +37,7 @@ module ActiveRecord
autoload :ConnectionHandling
autoload :CounterCache
autoload :DynamicMatchers
autoload :Enum
autoload :Explain
autoload :Inheritance
autoload :Integration

View file

@ -291,6 +291,7 @@ module ActiveRecord #:nodoc:
extend Translation
extend DynamicMatchers
extend Explain
extend Enum
extend Delegation::DelegateCache
include Persistence

View file

@ -0,0 +1,60 @@
module ActiveRecord
# 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: %i( active archived )
# end
#
# Conversation::STATUS # => { active: 0, archived: 1 }
#
# # 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.update! status: 1
# conversation.status = :archived
#
# You can set the default value from the database declaration, like:
#
# create_table :conversation do
# t.column :status, :integer, default: 0
# end
#
# Good practice is to let the first declared status be the default.
module Enum
def enum(definitions)
definitions.each do |name, values|
const_name = name.to_s.upcase
# DIRECTION = { }
const_set const_name, {}
# def direction=(value) self[:direction] = DIRECTION[value] end
class_eval "def #{name}=(value) self[:#{name}] = #{const_name}[value] end"
# def direction() DIRECTION.key self[:direction] end
class_eval "def #{name}() #{const_name}.key self[:#{name}] end"
values.each_with_index do |value, i|
# DIRECTION[:incoming] = 0
const_get(const_name)[value] = i
# scope :incoming, -> { where direction: 0 }
scope value, -> { where name => i }
# def incoming?() direction == 0 end
class_eval "def #{value}?() self[:#{name}] == #{i} end"
# def incoming! update! direction: :incoming end
class_eval "def #{value}!() update! #{name}: :#{value} end"
end
end
end
end
end

View file

@ -0,0 +1,36 @@
require 'cases/helper'
require 'models/book'
class StoreTest < ActiveRecord::TestCase
fixtures :books
setup do
@book = Book.create! name: 'REMOTE'
end
test "query state by predicate" do
assert @book.proposed?
assert_not @book.written?
assert_not @book.published?
end
test "query state with symbol" do
assert_equal :proposed, @book.status
end
test "update by declaration" do
@book.written!
assert @book.written?
end
test "update by setter" do
@book.update! status: :written
assert @book.written?
end
test "constant" do
assert_equal 0, Book::STATUS[:proposed]
assert_equal 1, Book::STATUS[:written]
assert_equal 2, Book::STATUS[:published]
end
end

View file

@ -2,8 +2,10 @@ class Book < ActiveRecord::Base
has_many :authors
has_many :citations, :foreign_key => 'book1_id'
has_many :references, -> { distinct }, :through => :citations, :source => :reference_of
has_many :references, -> { distinct }, through: :citations, source: :reference_of
has_many :subscriptions
has_many :subscribers, :through => :subscriptions
has_many :subscribers, through: :subscriptions
enum status: %i( proposed written published )
end

View file

@ -94,6 +94,7 @@ ActiveRecord::Schema.define do
create_table :books, :force => true do |t|
t.integer :author_id
t.column :name, :string
t.column :status, :integer, default: 0
end
create_table :booleans, :force => true do |t|