Skip to content

Instantly share code, notes, and snippets.

@mihaic195
Created September 10, 2019 09:25
Show Gist options
  • Save mihaic195/693bd6a50fffb7ff5db556be878a467a to your computer and use it in GitHub Desktop.
Save mihaic195/693bd6a50fffb7ff5db556be878a467a to your computer and use it in GitHub Desktop.
Paperclip to ActiveStorage migration rake task
namespace :paperclip do
task migrate: :environment do
arr = Array.new
ApplicationRecord.descendants.reject(&:abstract_class?).each { |klass| arr.push(klass) }
while model = arr.pop
begin
attachments = model.column_names.filter { |column| column.match?(/(.+)_file_name$/) }.map { |column| column[/(?<name>\w*)_file_name/, :name] }
next if attachments.blank?
attachments.each do |attachment|
query = <<~SQL
NOT EXISTS(
SELECT * FROM active_storage_blobs AS blobs
INNER JOIN active_storage_attachments AS attachments ON attachments.blob_id = blobs.id
WHERE record_type = ? AND record_id = #{model.table_name}.id
)
SQL
model.where.not("#{attachment}_file_name" => nil).where(query, model.name).find_each do |instance|
instance.instance_variable_set(:@is_migration, true)
instance.duplicate_active_storage if instance.respond_to?(:duplicate_active_storage)
end
end
rescue OpenURI::HTTPError, ActiveRecord::StatementInvalid => e
Rails.logger.error("Error during ActiveStorage migration: #{e} \n for #{model}")
Raven.capture_exception("Error during ActiveStorage migration: #{e} \n for #{model}")
next
end
end
end
end
@JohnSmall
Copy link

A useful task to have. I'm doing a paperclip to active storage migration now so it's very useful

But one question. Why use this code

  arr = Array.new	
  ApplicationRecord. descendants.reject(&:abstract_class?).each { |klass| arr.push(klass) }	
  
  while model = arr.pop 
  etc 

and not this code

  arr = ApplicationRecord.descendants.reject(&:abstract_class?)
  
  arr.each do | model |
    etc

or even just this code

ApplicationRecord.descendants.reject(&:abstract_class?).each do | model |
etc

I'm puzzled

@mihaic195
Copy link
Author

mihaic195 commented Mar 6, 2020

@JohnSmall Either way works. It's even better the way you do it. When I created this gist, I refactored it "on the go" to not use Ruby Threads (as I was using it in my project), because I thought it would be too complex to also write about that in the medium article, so I wanted to keep it simple and I overlooked these simple variations :) .

@JohnSmall
Copy link

Ok, I thought there might be some special thing that made you do it that way. I'll change my code as appropriate.

@JohnSmall
Copy link

Whoops one other thing you need to have

 Rails.application.eager_load! 

before you do

 ApplicationRecord.descendants

or the result will be an empty array.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment