Skip to content

Commit

Permalink
wip: append upgrader
Browse files Browse the repository at this point in the history
  • Loading branch information
tvdeyen committed Oct 4, 2024
1 parent 4b3c0ce commit 9587e59
Show file tree
Hide file tree
Showing 9 changed files with 170 additions and 114 deletions.
2 changes: 0 additions & 2 deletions alchemy_cms.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ Gem::Specification.new do |gem|
gem.add_runtime_dependency "cancancan", [">= 2.1", "< 4.0"]
gem.add_runtime_dependency "coffee-rails", [">= 4.0", "< 6.0"]
gem.add_runtime_dependency "csv", ["~> 3.3"]
gem.add_runtime_dependency "dragonfly", ["~> 1.4"]
gem.add_runtime_dependency "dragonfly_svg", ["~> 0.0.4"]
gem.add_runtime_dependency "gutentag", ["~> 2.2", ">= 2.2.1"]
gem.add_runtime_dependency "image_processing", [">= 1.2"]
gem.add_runtime_dependency "importmap-rails", ["~> 1.2", ">= 1.2.1"]
Expand Down
13 changes: 0 additions & 13 deletions app/models/alchemy/attachment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,6 @@ class Attachment < BaseRecord
include Alchemy::Taggable
include Alchemy::TouchElements

# Legacy Dragonfly file attachments
extend Dragonfly::Model
dragonfly_accessor :legacy_file, app: :alchemy_attachments
DEPRECATED_COLUMNS = %i[
legacy_file
legacy_file_name
legacy_file_size
legacy_file_uid
].each do |column|
deprecate column, deprecator: Alchemy::Deprecation
deprecate :"#{column}=", deprecator: Alchemy::Deprecation
end

# Use ActiveStorage file attachments
has_one_attached :file, service: :alchemy_cms

Expand Down
16 changes: 0 additions & 16 deletions app/models/alchemy/picture.rb
Original file line number Diff line number Diff line change
Expand Up @@ -80,22 +80,6 @@ def self.preprocessor_class=(klass)
@_preprocessor_class = klass
end

# Legacy Dragonfly image attachments
extend Dragonfly::Model
dragonfly_accessor :legacy_image_file, app: :alchemy_pictures
DEPRECATED_COLUMNS = %i[
legacy_image_file
legacy_image_file_format
legacy_image_file_height
legacy_image_file_name
legacy_image_file_size
legacy_image_file_uid
legacy_image_file_width
].each do |column|
deprecate column, deprecator: Alchemy::Deprecation
deprecate :"#{column}=", deprecator: Alchemy::Deprecation
end

# Use ActiveStorage image processing
has_one_attached :image_file, service: :alchemy_cms do |attachable|
# Only works in Rails 7.1
Expand Down
109 changes: 28 additions & 81 deletions lib/alchemy/upgrader/eight_zero.rb
Original file line number Diff line number Diff line change
@@ -1,53 +1,42 @@
require "alchemy/shell"
require "alchemy/upgrader/tasks/active_storage_migration"
require "benchmark"
require "active_storage/service/disk_service"
require "fileutils"
require "thor"

module Alchemy
class Upgrader::EightZero < Upgrader
extend Alchemy::Shell
DEFAULT_CONTENT_TYPE = "application/octet-stream"
DISK_SERVICE = ActiveStorage::Service::DiskService
SERVICE_NAME = :alchemy_cms

# Prevents (down)loading the original file
METADATA = {
identified: true, # Skip identifying file type
analyzed: true, # Skip analyze job
composed: true # Skip checksum check
}
include Thor::Base
include Thor::Actions

class << self
def install_active_storage
Rake::Task["active_storage:install"].invoke
Rake::Task["db:migrate"].invoke

text = <<-YAML.strip_heredoc
alchemy_cms:
service: Disk
root: <%= Rails.root.join("storage") %>
YAML

storage_yml = Rails.application.root.join("config/storage.yml")
if File.exist?(storage_yml)
task.insert_into_file(storage_yml, text)
else
task.create_file(storage_yml, text)
end
end

def migrate_pictures_to_active_storage
pictures_without_as_attachment = Alchemy::Picture.where.missing(:image_file_attachment)
count = pictures_without_as_attachment.count
if count > 0
log "Migrating #{count} Dragonfly image file(s) to ActiveStorage."
realtime = Benchmark.realtime do
pictures_without_as_attachment.find_each do |picture|
Alchemy::Deprecation.silence do
uid = picture.legacy_image_file_uid
key = key_for_uid(uid)
content_type = Mime::Type.lookup_by_extension(picture.legacy_image_file_format) || DEFAULT_CONTENT_TYPE
Alchemy::Picture.transaction do
blob = ActiveStorage::Blob.create!(
key: key,
filename: picture.legacy_image_file_name,
byte_size: picture.legacy_image_file_size,
content_type: content_type,
metadata: METADATA.merge(
width: picture.legacy_image_file_width,
height: picture.legacy_image_file_height
),
service_name: SERVICE_NAME
)
picture.create_image_file_attachment!(
name: :image_file,
record: picture,
blob: blob
)
end
move_file(Rails.root.join("uploads/pictures", uid), key)
end
Alchemy::Upgrader::Tasks::ActiveStorageMigration.migrate_picture(picture)
print "."
end
end
Expand All @@ -64,26 +53,7 @@ def migrate_attachments_to_active_storage
log "Migrating #{count} Dragonfly attachment file(s) to ActiveStorage."
realtime = Benchmark.realtime do
attachments_without_as_attachment.find_each do |attachment|
Alchemy::Deprecation.silence do
uid = attachment.legacy_file_uid
key = key_for_uid(uid)
Alchemy::Attachment.transaction do
blob = ActiveStorage::Blob.create!(
key: key,
filename: attachment.legacy_file_name,
byte_size: attachment.legacy_file_size,
content_type: attachment.file_mime_type.presence || DEFAULT_CONTENT_TYPE,
metadata: METADATA,
service_name: SERVICE_NAME
)
attachment.create_file_attachment!(
record: attachment,
name: :file,
blob: blob
)
end
move_file(Rails.root.join("uploads/attachments", uid), key)
end
Alchemy::Upgrader::Tasks::ActiveStorageMigration.migrate_attachment(attachment)
print "."
end
end
Expand All @@ -95,31 +65,8 @@ def migrate_attachments_to_active_storage

private

# ActiveStorage::Service::DiskService stores files in a folder structure
# based on the first two characters of the file uid.
def key_for_uid(uid)
case service
when DISK_SERVICE
uid.split("/").last
else
uid
end
end

# ActiveStorage::Service::DiskService stores files in a folder structure
# based on the first two characters of the file uid.
def move_file(uid, key)
case service
when DISK_SERVICE
if File.exist?(uid)
service.send(:make_path_for, key)
FileUtils.mv uid, service.send(:path_for, key)
end
end
end

def service
ActiveStorage::Blob.services.fetch(SERVICE_NAME)
def task
@_task || new
end
end
end
Expand Down
Empty file removed lib/alchemy/upgrader/tasks/.keep
Empty file.
108 changes: 108 additions & 0 deletions lib/alchemy/upgrader/tasks/active_storage_migration.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
require "active_storage/service"
require "active_storage/service/disk_service"

module Alchemy
class Upgrader
module Tasks
class ActiveStorageMigration
DEFAULT_CONTENT_TYPE = "application/octet-stream"
DISK_SERVICE = ActiveStorage::Service::DiskService
SERVICE_NAME = :alchemy_cms

METADATA = {
identified: true, # Skip identifying file type
analyzed: true, # Skip analyze job
composed: true # Skip checksum check
}

class << self
def migrate_picture(picture)
Alchemy::Picture.extend Dragonfly::Model
Alchemy::Picture.dragonfly_accessor :legacy_image_file, app: :alchemy_pictures

Alchemy::Deprecation.silence do
uid = picture.legacy_image_file_uid
key = key_for_uid(uid)
content_type = Mime::Type.lookup_by_extension(picture.legacy_image_file_format) || DEFAULT_CONTENT_TYPE
Alchemy::Picture.transaction do
blob = ActiveStorage::Blob.create!(
key: key,
filename: picture.legacy_image_file_name,
byte_size: picture.legacy_image_file_size,
content_type: content_type,
# Prevents (down)loading the original file
metadata: METADATA.merge(
width: picture.legacy_image_file_width,
height: picture.legacy_image_file_height
),
service_name: SERVICE_NAME
)
picture.create_image_file_attachment!(
name: :image_file,
record: picture,
blob: blob
)
end
move_file(Rails.root.join("uploads/pictures", uid), key)
end
end

def migrate_attachment(attachment)
Alchemy::Attachment.extend Dragonfly::Model
Alchemy::Attachment.dragonfly_accessor :legacy_file, app: :alchemy_attachments

Alchemy::Deprecation.silence do
uid = attachment.legacy_file_uid
key = key_for_uid(uid)
Alchemy::Attachment.transaction do
blob = ActiveStorage::Blob.create!(
key: key,
filename: attachment.legacy_file_name,
byte_size: attachment.legacy_file_size,
content_type: attachment.file_mime_type.presence || DEFAULT_CONTENT_TYPE,
metadata: METADATA,
service_name: SERVICE_NAME
)
attachment.create_file_attachment!(
record: attachment,
name: :file,
blob: blob
)
end
move_file(Rails.root.join("uploads/attachments", uid), key)
end
end

private

# ActiveStorage::Service::DiskService stores files in a folder structure
# based on the first two characters of the file uid.
def key_for_uid(uid)
case service
when DISK_SERVICE
uid.split("/").last
else
uid
end
end

# ActiveStorage::Service::DiskService stores files in a folder structure
# based on the first two characters of the file uid.
def move_file(uid, key)
case service
when DISK_SERVICE
if File.exist?(uid)
service.send(:make_path_for, key)
FileUtils.mv uid, service.send(:path_for, key)
end
end
end

def service
ActiveStorage::Blob.services.fetch(SERVICE_NAME)
end
end
end
end
end
end
3 changes: 1 addition & 2 deletions lib/alchemy_cms.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@
require "acts_as_list"
require "action_view/dependency_tracker"
require "active_model_serializers"
require "active_storage/engine"
require "awesome_nested_set"
require "cancan"
require "dragonfly"
require "dragonfly_svg"
require "gutentag"
require "importmap-rails"
require "kaminari"
Expand Down
4 changes: 4 additions & 0 deletions lib/generators/alchemy/install/install_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ def install_assets
append_to_file Rails.root.join("app/assets/config/manifest.js"), "//= link alchemy/admin/custom.css\n"
end

def install_active_storage
rake "active_storage:install:migrations"
end

def set_active_storage_service
insert_into_file app_config_path.join("storage.yml"), <<-YAML.strip_heredoc
Expand Down
29 changes: 29 additions & 0 deletions lib/tasks/alchemy/upgrade.rake
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,33 @@ namespace :alchemy do
namespace :upgrade do
desc "Alchemy Upgrader: Prepares the database and updates Alchemys configuration file."
task prepare: [
"alchemy:upgrade:ensure_dragonfly_gems",
"alchemy:upgrade:database",
"alchemy:upgrade:config"
]

task :ensure_dragonfly_gems do
require "dragonfly"
require "dragonfly_svg"
rescue LoadError
abort <<~WARN
== Alchemy Upgrader ==
Please make sure you have `dragonfly` and `dragonfly_svg` gems installed in order to migrate Alchemy.
Add these to your Gemfile:
gem "dragonfly", "~> 1.4", require: false
gem "dragonfly_svg", "~> 0.0.4", require: false
and run `bundle install`.
Then try again, please!
WARN
end

desc "Alchemy Upgrader: Prepares the database."
task database: [
"alchemy:install:migrations",
Expand All @@ -32,10 +55,16 @@ namespace :alchemy do

namespace "8.0" do
task "run" => [
"alchemy:upgrade:8.0:install_active_storage",
"alchemy:upgrade:8.0:migrate_pictures_to_active_storage",
"alchemy:upgrade:8.0:migrate_attachments_to_active_storage"
]

desc "Install active_storage"
task install_active_storage: [:environment] do
Alchemy::Upgrader::EightZero.install_active_storage
end

desc "Migrate pictures to active_storage"
task migrate_pictures_to_active_storage: [:environment] do
Alchemy::Upgrader::EightZero.migrate_pictures_to_active_storage
Expand Down

0 comments on commit 9587e59

Please sign in to comment.