Fix enum serialization in rails 4.2

Fixes #798

Our test suite has one model that tests enums, PostWithStatus. Its
spec did not cover `touch_with_version`. Somehow, probably during
our upgrade to rails 5, we broke `touch_with_version` for enums.
Unlike other methods, when an enum arrives at
`CastAttributeSerializer#serialize` during `touch_with_version`, it
is already a number. We assumed it was always a string.

This is sort of a hack, just handling both strings and numbers in
`#serialize`. I'd rather figure out how a number got to `#serialize`
in the first place. However, a hack is acceptable, as it's only
for rails 4.2, and the hack will eventually be deleted when we
drop support for 4.2.
This commit is contained in:
Jared Beck 2016-08-14 00:24:13 -04:00
parent d8892b3ccd
commit bc1308ea80
3 changed files with 27 additions and 5 deletions

View File

@ -14,7 +14,9 @@
### Fixed
- None
- [#798](https://github.com/airblade/paper_trail/issues/798) -
Fix a rare bug with serialization of enums in rails 4.2 only when
using `touch_with_version`.
## 5.2.0 (2016-06-27)

View File

@ -34,8 +34,13 @@ module PaperTrail
end
def serialize(attr, val)
val = defined_enums[attr][val] if defined_enums[attr]
@klass.type_for_attribute(attr).type_cast_for_database(val)
castable_val = val
if defined_enums[attr]
# `attr` is an enum. Find the number that corresponds to `val`. If `val` is
# a number already, there won't be a corresponding entry, just use `val`.
castable_val = defined_enums[attr][val] || val
end
@klass.type_for_attribute(attr).type_cast_for_database(castable_val)
end
def deserialize(attr, val)
@ -49,6 +54,8 @@ module PaperTrail
private
# ActiveRecord::Enum was added in AR 4.1
# http://edgeguides.rubyonrails.org/4_1_release_notes.html#active-record-enums
def defined_enums
@defined_enums ||= (@klass.respond_to?(:defined_enums) ? @klass.defined_enums : {})
end

View File

@ -1,7 +1,7 @@
require "rails_helper"
# This model is in the test suite soley for the purpose of testing ActiveRecord::Enum,
# which is available in ActiveRecord4+ only
# This model tests ActiveRecord::Enum, which was added in AR 4.1
# http://edgeguides.rubyonrails.org/4_1_release_notes.html#active-record-enums
describe PostWithStatus, type: :model do
if defined?(ActiveRecord::Enum)
with_versioning do
@ -22,6 +22,19 @@ describe PostWithStatus, type: :model do
expect(subject.changeset["status"]).to eql %w(published archived)
end
end
describe "#touch_with_version" do
it "preserves the enum value (and all other attributes)" do
post = described_class.create(status: :draft)
expect(post.versions.count).to eq(1)
expect(post.status).to eq("draft")
Timecop.travel 1.second.since # because MySQL lacks fractional seconds precision
post.paper_trail.touch_with_version
expect(post.versions.count).to eq(2)
expect(post.versions.last[:object]).to include("status: 0")
expect(post.paper_trail.previous_version.status).to eq("draft")
end
end
end
end
end