parent
7d60a41e13
commit
7884c143b6
|
@ -14,6 +14,8 @@
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
- [#857](https://github.com/airblade/paper_trail/pull/857) -
|
||||||
|
Fix deserialization of enums written by PT 4.
|
||||||
- [#798](https://github.com/airblade/paper_trail/issues/798) -
|
- [#798](https://github.com/airblade/paper_trail/issues/798) -
|
||||||
Fix a rare bug with serialization of enums in rails 4.2 only when
|
Fix a rare bug with serialization of enums in rails 4.2 only when
|
||||||
using `touch_with_version`.
|
using `touch_with_version`.
|
||||||
|
|
|
@ -8,19 +8,41 @@ module PaperTrail
|
||||||
# This implementation depends on the `type_for_attribute` method, which was
|
# This implementation depends on the `type_for_attribute` method, which was
|
||||||
# introduced in rails 4.2. In older versions of rails, we shim this method
|
# introduced in rails 4.2. In older versions of rails, we shim this method
|
||||||
# with `LegacyActiveRecordShim`.
|
# with `LegacyActiveRecordShim`.
|
||||||
|
class CastAttributeSerializer
|
||||||
|
def initialize(klass)
|
||||||
|
@klass = klass
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Returns a hash mapping attributes to hashes that map strings to
|
||||||
|
# integers. Example:
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# { "status" => { "draft"=>0, "published"=>1, "archived"=>2 } }
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
end
|
||||||
|
|
||||||
if ::ActiveRecord::VERSION::MAJOR >= 5
|
if ::ActiveRecord::VERSION::MAJOR >= 5
|
||||||
# This implementation uses AR 5's `serialize` and `deserialize`.
|
# This implementation uses AR 5's `serialize` and `deserialize`.
|
||||||
class CastAttributeSerializer
|
class CastAttributeSerializer
|
||||||
def initialize(klass)
|
|
||||||
@klass = klass
|
|
||||||
end
|
|
||||||
|
|
||||||
def serialize(attr, val)
|
def serialize(attr, val)
|
||||||
@klass.type_for_attribute(attr).serialize(val)
|
@klass.type_for_attribute(attr).serialize(val)
|
||||||
end
|
end
|
||||||
|
|
||||||
def deserialize(attr, val)
|
def deserialize(attr, val)
|
||||||
@klass.type_for_attribute(attr).deserialize(val)
|
if defined_enums[attr] && val.is_a?(::String)
|
||||||
|
# Because PT 4 used to save the string version of enums to `object_changes`
|
||||||
|
val
|
||||||
|
else
|
||||||
|
@klass.type_for_attribute(attr).deserialize(val)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
@ -29,10 +51,6 @@ module PaperTrail
|
||||||
# `type_cast_for_database` in our shim attribute type classes,
|
# `type_cast_for_database` in our shim attribute type classes,
|
||||||
# `NoOpAttribute` and `SerializedAttribute`.
|
# `NoOpAttribute` and `SerializedAttribute`.
|
||||||
class CastAttributeSerializer
|
class CastAttributeSerializer
|
||||||
def initialize(klass)
|
|
||||||
@klass = klass
|
|
||||||
end
|
|
||||||
|
|
||||||
def serialize(attr, val)
|
def serialize(attr, val)
|
||||||
castable_val = val
|
castable_val = val
|
||||||
if defined_enums[attr]
|
if defined_enums[attr]
|
||||||
|
@ -44,21 +62,18 @@ module PaperTrail
|
||||||
end
|
end
|
||||||
|
|
||||||
def deserialize(attr, val)
|
def deserialize(attr, val)
|
||||||
val = @klass.type_for_attribute(attr).type_cast_from_database(val)
|
if defined_enums[attr] && val.is_a?(::String)
|
||||||
if defined_enums[attr]
|
# Because PT 4 used to save the string version of enums to `object_changes`
|
||||||
defined_enums[attr].key(val)
|
|
||||||
else
|
|
||||||
val
|
val
|
||||||
|
else
|
||||||
|
val = @klass.type_for_attribute(attr).type_cast_from_database(val)
|
||||||
|
if defined_enums[attr]
|
||||||
|
defined_enums[attr].key(val)
|
||||||
|
else
|
||||||
|
val
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
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
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,7 +5,7 @@ require "rails_helper"
|
||||||
describe PostWithStatus, type: :model do
|
describe PostWithStatus, type: :model do
|
||||||
if defined?(ActiveRecord::Enum)
|
if defined?(ActiveRecord::Enum)
|
||||||
with_versioning do
|
with_versioning do
|
||||||
let(:post) { PostWithStatus.create!(status: "draft") }
|
let(:post) { described_class.create!(status: "draft") }
|
||||||
|
|
||||||
it "should stash the enum value properly in versions" do
|
it "should stash the enum value properly in versions" do
|
||||||
post.published!
|
post.published!
|
||||||
|
@ -13,6 +13,16 @@ describe PostWithStatus, type: :model do
|
||||||
expect(post.paper_trail.previous_version.published?).to be true
|
expect(post.paper_trail.previous_version.published?).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "can read enums in version records written by PT 4" do
|
||||||
|
post = described_class.create(status: "draft")
|
||||||
|
post.published!
|
||||||
|
version = post.versions.last
|
||||||
|
# Simulate behavior PT 4, which used to save the string version of
|
||||||
|
# enums to `object_changes`
|
||||||
|
version.update(object_changes: "---\nid:\n- \n- 1\nstatus:\n- draft\n- published\n")
|
||||||
|
assert_equal %w(draft published), version.changeset["status"]
|
||||||
|
end
|
||||||
|
|
||||||
context "storing enum object_changes" do
|
context "storing enum object_changes" do
|
||||||
subject { post.versions.last }
|
subject { post.versions.last }
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue