scinote-web/db/migrate/20190613134100_convert_to_active_storage.rb

118 lines
3.7 KiB
Ruby
Raw Normal View History

2019-06-24 21:17:42 +08:00
# 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
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
Rails.application.eager_load!
models = ApplicationRecord.descendants.reject(&:abstract_class?)
transaction do
models.each do |model|
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,
2019-06-28 14:17:09 +08:00
checksum(attachment),
2019-06-24 21:17:42 +08:00
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)
# SecureRandom.uuid
# Alternatively:
2019-06-28 14:17:09 +08:00
if ENV['PAPERCLIP_STORAGE'] == 's3'
interpolate(':class/:attachment/:id_partition/:hash/original/:filename', instance, attachment)
else
instance.class.generate_unique_secure_token
# File.join('storage', key.first(2), key.first(4).last(2))
2019-06-28 14:17:09 +08:00
end
2019-06-24 21:17:42 +08:00
end
def checksum(_attachment)
'dummy'
# local files stored on disk:
# url = attachment.path
# Digest::MD5.base64digest(File.read(url))
# remote files stored on another person's computer:
# url = attachment.url
# Digest::MD5.base64digest(Net::HTTP.get(URI(url)))
end
end