mirror of
https://github.com/paper-trail-gem/paper_trail.git
synced 2022-11-09 11:33:19 -05:00
4cce9b0179
This PR addresses #1015 Starting from Rails version 5.0.2 the default serializer of PostgreSQL columns returns an ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Array object instead of a string. This new object is not suitable for JSON encoding and breaks versioning of any array fields backed by Postgres. Whenever a PostgreSQL array is used, instead of asking Active Record for a serializer we introduce our own, which simply returns the underlying array without any modifications.
81 lines
2.8 KiB
Ruby
81 lines
2.8 KiB
Ruby
require "paper_trail/attribute_serializers/attribute_serializer_factory"
|
|
|
|
module PaperTrail
|
|
# :nodoc:
|
|
module AttributeSerializers
|
|
# The `CastAttributeSerializer` (de)serializes model attribute values. For
|
|
# example, the string "1.99" serializes into the integer `1` when assigned
|
|
# to an attribute of type `ActiveRecord::Type::Integer`.
|
|
#
|
|
# This implementation depends on the `type_for_attribute` method, which was
|
|
# introduced in rails 4.2. As of PT 8, we no longer support rails < 4.2.
|
|
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 serialize(attr, val)
|
|
AttributeSerializerFactory.for(@klass, attr).serialize(val)
|
|
end
|
|
|
|
def deserialize(attr, 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
|
|
AttributeSerializerFactory.for(@klass, attr).deserialize(val)
|
|
end
|
|
end
|
|
end
|
|
else
|
|
# This implementation uses AR 4.2's `type_cast_for_database`. For
|
|
# versions of AR < 4.2 we provide an implementation of
|
|
# `type_cast_for_database` in our shim attribute type classes,
|
|
# `NoOpAttribute` and `SerializedAttribute`.
|
|
class CastAttributeSerializer
|
|
def serialize(attr, 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)
|
|
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
|
|
end
|
|
end
|
|
end
|
|
end
|