Last active
February 10, 2016 23:20
-
-
Save rusterholz/a454dae50e2c50ab0127 to your computer and use it in GitHub Desktop.
Migration to fix serialized attributes on Versions made with paper_trail 4.0.2 or earlier
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class PatchVersions < ActiveRecord::Migration | |
# This modifies PaperTrail versions made with 4.0.2 or before to be compatible with 4.1.0. | |
# | |
# The difference is that 4.0.2 stored serialized attributes in their native (reified) form, | |
# while 4.1.0 and above store serialized attributes in their serialized form; e.g.: | |
# {'foo'=>52, 'bar'=>'baz'} <----- 4.0.2 yamlizes this Hash | |
# '{"foo":52,"bar":"baz"}' <----- 4.1.0 yamlizes this String | |
# This causes 4.1.0 to raise errors when attempting to reify versions created with 4.0.2. | |
# | |
def up | |
# Get the list of all extant model classes currently referenced by PaperTrail versions: | |
models = PaperTrail::Version.pluck(:item_type).uniq.sort.map do |k| | |
begin | |
k.constantize | |
rescue NameError | |
nil | |
end | |
end.compact | |
# Determine which ones have serialized attributes: | |
serialized = Hash[ models.zip( models.map{ |m| m.columns.select{ |c| ActiveRecord::Type::Serialized === c.cast_type } } ) ] | |
serialized.delete_if{ |_,c| c.blank? } | |
# Perform all fixes in a single transaction so no data is lost if the migration fails: | |
PaperTrail::Version.transaction do | |
serialized.each do |klass,cols| | |
# For each affected model, correct each affected Version record: | |
print "Patching #{klass.name} versions (#{cols.map(&:name).join(', ')})" | |
PaperTrail::Version.where( item_type: klass.name ).each do |v| | |
obj = v.object && YAML.load( v.object ) | |
chgs = v.object_changes && YAML.load( v.object_changes ) | |
cols.each do |c| | |
# For each serialized attribute, if this version contains that attribute, replace it with its serialized form: | |
obj[ c.name ] = c.cast_type.coder.dump( obj[ c.name ] ) if obj && obj[ c.name ] | |
chgs[ c.name ].map!{ |x| x && c.cast_type.coder.dump(x) } if chgs && chgs[ c.name ] | |
end | |
# Replace the YAML strings on the Version record with the corrected strings: | |
updates = {} | |
updates[:object] = YAML.dump( obj ) if obj | |
updates[:object_changes] = YAML.dump( chgs ) if chgs | |
v.update! updates if updates.present? | |
print '.' | |
end | |
puts | |
end | |
end | |
end | |
def down | |
raise ActiveRecord::IrreversibleMigration # In principle this could be done, but has not been implemented. | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment