Skip to content

Commit

Permalink
Merge pull request #857 from airblade/read_enums_from_pt4
Browse files Browse the repository at this point in the history
Fix deserialization of enums written by PT 4
  • Loading branch information
jaredbeck authored Sep 2, 2016
2 parents 7d60a41 + 7884c14 commit 5df7ef3
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 22 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

### 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) -
Fix a rare bug with serialization of enums in rails 4.2 only when
using `touch_with_version`.
Expand Down
57 changes: 36 additions & 21 deletions lib/paper_trail/attribute_serializers/cast_attribute_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,41 @@ module AttributeSerializers
# 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
# 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
# This implementation uses AR 5's `serialize` and `deserialize`.
class CastAttributeSerializer
def initialize(klass)
@klass = klass
end

def serialize(attr, val)
@klass.type_for_attribute(attr).serialize(val)
end

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
else
Expand All @@ -29,10 +51,6 @@ def deserialize(attr, val)
# `type_cast_for_database` in our shim attribute type classes,
# `NoOpAttribute` and `SerializedAttribute`.
class CastAttributeSerializer
def initialize(klass)
@klass = klass
end

def serialize(attr, val)
castable_val = val
if defined_enums[attr]
Expand All @@ -44,21 +62,18 @@ def serialize(attr, val)
end

def deserialize(attr, val)
val = @klass.type_for_attribute(attr).type_cast_from_database(val)
if defined_enums[attr]
defined_enums[attr].key(val)
else
if defined_enums[attr] && val.is_a?(::String)
# Because PT 4 used to save the string version of enums to `object_changes`
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

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
Expand Down
12 changes: 11 additions & 1 deletion spec/models/post_with_status_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,24 @@
describe PostWithStatus, type: :model do
if defined?(ActiveRecord::Enum)
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
post.published!
post.archived!
expect(post.paper_trail.previous_version.published?).to be true
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
subject { post.versions.last }

Expand Down

0 comments on commit 5df7ef3

Please sign in to comment.