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:
parent
deaf285824
commit
db41eb8a6e
7 changed files with 128 additions and 2 deletions
|
@ -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*
|
||||
|
|
|
@ -37,6 +37,7 @@ module ActiveRecord
|
|||
autoload :ConnectionHandling
|
||||
autoload :CounterCache
|
||||
autoload :DynamicMatchers
|
||||
autoload :Enum
|
||||
autoload :Explain
|
||||
autoload :Inheritance
|
||||
autoload :Integration
|
||||
|
|
|
@ -291,6 +291,7 @@ module ActiveRecord #:nodoc:
|
|||
extend Translation
|
||||
extend DynamicMatchers
|
||||
extend Explain
|
||||
extend Enum
|
||||
extend Delegation::DelegateCache
|
||||
|
||||
include Persistence
|
||||
|
|
60
activerecord/lib/active_record/enum.rb
Normal file
60
activerecord/lib/active_record/enum.rb
Normal 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
|
36
activerecord/test/cases/enum_test.rb
Normal file
36
activerecord/test/cases/enum_test.rb
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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|
|
||||
|
|
Loading…
Reference in a new issue