aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG.md18
-rw-r--r--activerecord/lib/active_record/enum.rb19
-rw-r--r--activerecord/test/cases/enum_test.rb40
3 files changed, 76 insertions, 1 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 170818cd1c..9de076961b 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,21 @@
+* Make enum fields work as expected with the `ActiveModel::Dirty` API.
+
+ Before this change, using the dirty API would have surprising results:
+
+ conversation = Conversation.new
+ conversation.status = :active
+ conversation.status = :archived
+ conversation.status_was # => 0
+
+ After this change, the same code would result in:
+
+ conversation = Conversation.new
+ conversation.status = :active
+ conversation.status = :archived
+ conversation.status_was # => "active"
+
+ *Rafael Mendonça França*
+
* Ensure `second` through `fifth` methods act like the `first` finder.
The famous ordinal Array instance methods defined in ActiveSupport
diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb
index 3deb2d65f8..06e87cf854 100644
--- a/activerecord/lib/active_record/enum.rb
+++ b/activerecord/lib/active_record/enum.rb
@@ -63,6 +63,12 @@ module ActiveRecord
#
# Conversation.where("status <> ?", Conversation.statuses[:archived])
module Enum
+ DEFINED_ENUMS = [] # :nodoc:
+
+ def enum_attribute?(attr_name) # :nodoc:
+ DEFINED_ENUMS.include?(attr_name.to_sym)
+ end
+
def enum(definitions)
klass = self
definitions.each do |name, values|
@@ -70,6 +76,8 @@ module ActiveRecord
enum_values = ActiveSupport::HashWithIndifferentAccess.new
name = name.to_sym
+ DEFINED_ENUMS.unshift name
+
# def self.statuses statuses end
klass.singleton_class.send(:define_method, name.to_s.pluralize) { enum_values }
@@ -114,7 +122,16 @@ module ActiveRecord
private
def _enum_methods_module
@_enum_methods_module ||= begin
- mod = Module.new
+ mod = Module.new do
+ def save_changed_attribute(attr_name, value)
+ if self.class.enum_attribute?(attr_name)
+ old = clone_attribute_value(:read_attribute, attr_name)
+ changed_attributes[attr_name] = self.class.public_send(attr_name.pluralize).key old
+ else
+ super
+ end
+ end
+ end
include mod
mod
end
diff --git a/activerecord/test/cases/enum_test.rb b/activerecord/test/cases/enum_test.rb
index 1f98801e93..0fe7dfe4ea 100644
--- a/activerecord/test/cases/enum_test.rb
+++ b/activerecord/test/cases/enum_test.rb
@@ -51,6 +51,46 @@ class EnumTest < ActiveRecord::TestCase
assert @book.written?
end
+ test "enum changed attributes" do
+ old_status = @book.status
+ @book.status = :published
+ assert_equal old_status, @book.changed_attributes[:status]
+ end
+
+ test "enum changes" do
+ old_status = @book.status
+ @book.status = :published
+ assert_equal [old_status, 'published'], @book.changes[:status]
+ end
+
+ test "enum attribute was" do
+ old_status = @book.status
+ @book.status = :published
+ assert_equal old_status, @book.attribute_was(:status)
+ end
+
+ test "enum attribute changed" do
+ @book.status = :published
+ assert @book.attribute_changed?(:status)
+ end
+
+ test "enum attribute changed to" do
+ @book.status = :published
+ assert @book.attribute_changed?(:status, to: 'published')
+ end
+
+ test "enum attribute changed from" do
+ old_status = @book.status
+ @book.status = :published
+ assert @book.attribute_changed?(:status, from: old_status)
+ end
+
+ test "enum attribute changed from old status to new status" do
+ old_status = @book.status
+ @book.status = :published
+ assert @book.attribute_changed?(:status, from: old_status, to: 'published')
+ end
+
test "assign non existing value raises an error" do
e = assert_raises(ArgumentError) do
@book.status = :unknown