paper-trail-gem--paper_trail/lib/paper_trail/attributes_serialization.rb

90 lines
2.7 KiB
Ruby

module PaperTrail
module AttributesSerialization
class NoOpAttribute
def type_cast_for_database(value)
value
end
def type_cast_from_database(data)
data
end
end
NO_OP_ATTRIBUTE = NoOpAttribute.new
class SerializedAttribute
def initialize(coder)
@coder = coder.respond_to?(:dump) ? coder : PaperTrail.serializer
end
def type_cast_for_database(value)
@coder.dump(value)
end
def type_cast_from_database(data)
@coder.load(data)
end
end
SERIALIZE, DESERIALIZE =
if ::ActiveRecord::VERSION::MAJOR >= 5
[:serialize, :deserialize]
else
[:type_cast_for_database, :type_cast_from_database]
end
if ::ActiveRecord::VERSION::STRING < '4.2'
# Backport Rails 4.2 and later's `type_for_attribute` to build
# on a common interface.
def type_for_attribute(attr_name)
serialized_attribute_types[attr_name.to_s] || NO_OP_ATTRIBUTE
end
def serialized_attribute_types
@attribute_types ||= Hash[serialized_attributes.map do |attr_name, coder|
[attr_name, SerializedAttribute.new(coder)]
end]
end
private :serialized_attribute_types
end
# Used for `Version#object` attribute.
def serialize_attributes_for_paper_trail!(attributes)
alter_attributes_for_paper_trail!(SERIALIZE, attributes)
end
def unserialize_attributes_for_paper_trail!(attributes)
alter_attributes_for_paper_trail!(DESERIALIZE, attributes)
end
def alter_attributes_for_paper_trail!(serializer, attributes)
# Don't serialize before values before inserting into columns of type
# `JSON` on `PostgreSQL` databases.
return attributes if paper_trail_version_class.object_col_is_json?
attributes.each do |key, value|
attributes[key] = type_for_attribute(key).send(serializer, value)
end
end
# Used for Version#object_changes attribute.
def serialize_attribute_changes_for_paper_trail!(changes)
alter_attribute_changes_for_paper_trail!(SERIALIZE, changes)
end
def unserialize_attribute_changes_for_paper_trail!(changes)
alter_attribute_changes_for_paper_trail!(DESERIALIZE, changes)
end
def alter_attribute_changes_for_paper_trail!(serializer, changes)
# Don't serialize before values before inserting into columns of type
# `JSON` on `PostgreSQL` databases.
return changes if paper_trail_version_class.object_changes_col_is_json?
changes.clone.each do |key, change|
type = type_for_attribute(key)
changes[key] = Array(change).map { |value| type.send(serializer, value) }
end
end
end
end