scinote-web/db/migrate/20190613134100_convert_to_active_storage.rb
2019-10-09 16:47:32 +02:00

104 lines
3.3 KiB
Ruby

# frozen_string_literal: true
class ConvertToActiveStorage < ActiveRecord::Migration[5.2]
require 'open-uri'
ID_PARTITION_LIMIT = 1_000_000_000
DIGEST = OpenSSL::Digest.const_get('SHA1').new
MODELS = [Asset, TempFile, TinyMceAsset, User, ZipExport].freeze
def up
ActiveRecord::Base.connection.raw_connection.prepare('active_storage_blob_statement', <<-SQL)
INSERT INTO active_storage_blobs (
key, filename, content_type, metadata, byte_size, checksum, created_at
) VALUES ($1, $2, $3, '{}', $4, $5, $6)
RETURNING id;
SQL
ActiveRecord::Base.connection.raw_connection.prepare('active_storage_attachment_statement', <<-SQL)
INSERT INTO active_storage_attachments (
name, record_type, record_id, blob_id, created_at
) VALUES ($1, $2, $3, $4, $5)
SQL
transaction do
MODELS.each do |model|
next unless ActiveRecord::Base.connection.table_exists?(model.table_name)
attachments = model.column_names.map do |c|
$1 if c =~ /(.+)_file_name$/
end.compact
next if attachments.blank?
model.find_each.each do |instance|
attachments.each do |attachment|
next if instance.__send__("#{attachment}_file_name").blank?
res = ActiveRecord::Base.connection.raw_connection.exec_prepared(
'active_storage_blob_statement', [
key(instance, attachment),
instance.__send__("#{attachment}_file_name"),
instance.__send__("#{attachment}_content_type"),
instance.__send__("#{attachment}_file_size") || 0,
checksum(attachment),
instance.updated_at.iso8601
]
)
ActiveRecord::Base.connection.raw_connection.exec_prepared(
'active_storage_attachment_statement', [
attachment,
model.name,
instance.id,
res[0]['id'],
instance.updated_at.iso8601
]
)
end
end
end
end
end
def down
raise ActiveRecord::IrreversibleMigration
end
private
ID_PARTITION_LIMIT = 1_000_000_000
DIGEST = OpenSSL::Digest.const_get('SHA1').new
def id_partition(id)
if id < ID_PARTITION_LIMIT
format('%09d', id).scan(/\d{3}/).join('/')
else
format('%012d', id).scan(/\d{3}/).join('/')
end
end
def hash_data(instance, attachment)
"#{instance.class.to_s.underscore.pluralize}/#{attachment.pluralize}/#{instance.id}/original"
end
def interpolate(pattern, instance, attachment)
path = pattern
path = path.gsub(':class', instance.class.to_s.underscore.pluralize)
path = path.gsub(':attachment', attachment.pluralize)
path = path.gsub(':id_partition', id_partition(instance.id))
path = path.gsub(':hash', OpenSSL::HMAC.hexdigest(DIGEST,
ENV['PAPERCLIP_HASH_SECRET'],
hash_data(instance, attachment)))
path.gsub(':filename', instance.__send__("#{attachment}_file_name"))
end
def key(instance, attachment)
interpolate(':class/:attachment/:id_partition/:hash/original/:filename', instance, attachment)
end
def checksum(_attachment)
'dummy'
end
end