Skip to content

Commit

Permalink
Add upgrader for active storage
Browse files Browse the repository at this point in the history
  • Loading branch information
tvdeyen committed Aug 4, 2024
1 parent 7b47c2c commit 09c3d21
Show file tree
Hide file tree
Showing 19 changed files with 279 additions and 28 deletions.
2 changes: 2 additions & 0 deletions alchemy_cms.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ Gem::Specification.new do |gem|
gem.add_runtime_dependency "awesome_nested_set", ["~> 3.1"]
gem.add_runtime_dependency "cancancan", [">= 2.1", "< 4.0"]
gem.add_runtime_dependency "coffee-rails", [">= 4.0", "< 6.0"]
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 "handlebars_assets", ["~> 0.23"]
gem.add_runtime_dependency "image_processing", [">= 1.2"]
Expand Down
13 changes: 13 additions & 0 deletions app/models/alchemy/attachment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,19 @@ 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: 16 additions & 0 deletions app/models/alchemy/picture.rb
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,22 @@ 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

Expand Down
13 changes: 13 additions & 0 deletions db/migrate/20240611080918_rename_alchemy_attachment_file.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class RenameAlchemyAttachmentFile < ActiveRecord::Migration[7.0]
COLUMNS = %i[
file_name
file_size
file_uid
]

def change
COLUMNS.each do |column|
rename_column :alchemy_attachments, column, :"legacy_#{column}"
end
end
end
16 changes: 16 additions & 0 deletions db/migrate/20240611080918_rename_alchemy_picture_image_file.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class RenameAlchemyPictureImageFile < ActiveRecord::Migration[7.0]
COLUMNS = %i[
image_file_format
image_file_height
image_file_name
image_file_size
image_file_uid
image_file_width
]

def change
COLUMNS.each do |column|
rename_column :alchemy_pictures, column, :"legacy_#{column}"
end
end
end
1 change: 0 additions & 1 deletion lib/alchemy/test_support/factories/attachment_factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,5 @@
end

name { "image" }
file_name { "image.png" }
end
end
128 changes: 128 additions & 0 deletions lib/alchemy/upgrader/seven_point_three.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# frozen_string_literal: true

require "alchemy/shell"
require "benchmark"
require "active_storage/service/disk_service"

module Alchemy
class Upgrader::SevenPointThree < 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
}

class << self
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
print "."
end
end
puts "\nDone in #{realtime.round(2)}s!"
else
log "No Dragonfly image files for migration found.", :skip
end
end

def migrate_attachments_to_active_storage
attachments_without_as_attachment = Alchemy::Attachment.where.missing(:file_attachment)
count = attachments_without_as_attachment.count
if count > 0
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
print "."
end
end
puts "\nDone in #{realtime.round(2)}s!"
else
log "No Dragonfly attachment files for migration found.", :skip
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
2 changes: 2 additions & 0 deletions lib/alchemy_cms.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
require "active_model_serializers"
require "awesome_nested_set"
require "cancan"
require "dragonfly"
require "dragonfly_svg"
require "gutentag"
require "handlebars_assets"
require "importmap-rails"
Expand Down
9 changes: 9 additions & 0 deletions lib/generators/alchemy/install/install_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,15 @@ def install_assets
copy_file "all.js", app_vendor_assets_path.join("javascripts", "alchemy", "admin", "all.js")
end

def set_active_storage_service
insert_into_file app_config_path.join("storage.yml"), <<-YAML.strip_heredoc
alchemy_cms:
service: Disk
root: <%= Rails.root.join("storage") %>
YAML
end

def copy_demo_views
return if options[:skip_demo_files]

Expand Down
28 changes: 27 additions & 1 deletion lib/tasks/alchemy/upgrade.rake
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ namespace :alchemy do
desc "Upgrades your app to AlchemyCMS v#{Alchemy::VERSION}."
task upgrade: [
"alchemy:upgrade:prepare",
"alchemy:upgrade:7.0:run"
"alchemy:upgrade:7.0:run",
"alchemy:upgrade:7.3:run"
] do
Alchemy::Upgrader.display_todos
end
Expand Down Expand Up @@ -49,5 +50,30 @@ namespace :alchemy do
Alchemy::Upgrader::SevenPointZero.remove_admin_entrypoint
end
end

desc "Upgrade Alchemy to v7.3"
task "7.3" => [
"alchemy:upgrade:prepare",
"alchemy:upgrade:7.3:run"
] do
Alchemy::Upgrader.display_todos
end

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

desc "Migrate pictures to active_storage"
task migrate_pictures_to_active_storage: [:environment] do
Alchemy::Upgrader::SevenPointThree.migrate_pictures_to_active_storage
end

desc "Migrate attachments to active_storage"
task migrate_attachments_to_active_storage: [:environment] do
Alchemy::Upgrader::SevenPointThree.migrate_attachments_to_active_storage
end
end
end
end
4 changes: 4 additions & 0 deletions spec/dummy/config/storage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@ local:
# service: Mirror
# primary: local
# mirrors: [ amazon, google, microsoft ]

alchemy_cms:
service: Disk
root: <%= Rails.root.join("storage") %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# This migration comes from alchemy (originally 20240611080918)
class RenameAlchemyAttachmentFile < ActiveRecord::Migration[7.0]
COLUMNS = %i[
file_name
file_size
file_uid
]

def change
COLUMNS.each do |column|
rename_column :alchemy_attachments, column, :"legacy_#{column}"
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# This migration comes from alchemy (originally 20240611080918)
class RenameAlchemyPictureImageFile < ActiveRecord::Migration[7.0]
COLUMNS = %i[
image_file_format
image_file_height
image_file_name
image_file_size
image_file_uid
image_file_width
]

def change
COLUMNS.each do |column|
rename_column :alchemy_pictures, column, :"legacy_#{column}"
end
end
end
24 changes: 12 additions & 12 deletions spec/dummy/db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[7.1].define(version: 2024_06_11_151253) do
ActiveRecord::Schema[7.1].define(version: 2024_06_11_152554) do
create_table "active_storage_attachments", force: :cascade do |t|
t.string "name", null: false
t.string "record_type", null: false
Expand Down Expand Up @@ -41,16 +41,16 @@

create_table "alchemy_attachments", force: :cascade do |t|
t.string "name"
t.string "file_name"
t.string "legacy_file_name"
t.string "file_mime_type"
t.integer "file_size"
t.integer "legacy_file_size"
t.integer "creator_id"
t.integer "updater_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "file_uid"
t.string "legacy_file_uid"
t.index ["creator_id"], name: "index_alchemy_attachments_on_creator_id"
t.index ["file_uid"], name: "index_alchemy_attachments_on_file_uid"
t.index ["legacy_file_uid"], name: "index_alchemy_attachments_on_legacy_file_uid"
t.index ["updater_id"], name: "index_alchemy_attachments_on_updater_id"
end

Expand Down Expand Up @@ -234,19 +234,19 @@

create_table "alchemy_pictures", force: :cascade do |t|
t.string "name"
t.string "image_file_name"
t.integer "image_file_width"
t.integer "image_file_height"
t.string "legacy_image_file_name"
t.integer "legacy_image_file_width"
t.integer "legacy_image_file_height"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "creator_id"
t.integer "updater_id"
t.string "upload_hash"
t.string "image_file_uid"
t.integer "image_file_size"
t.string "image_file_format"
t.string "legacy_image_file_uid"
t.integer "legacy_image_file_size"
t.string "legacy_image_file_format"
t.index ["creator_id"], name: "index_alchemy_pictures_on_creator_id"
t.index ["image_file_name"], name: "index_alchemy_pictures_on_image_file_name"
t.index ["legacy_image_file_name"], name: "index_alchemy_pictures_on_legacy_image_file_name"
t.index ["name"], name: "index_alchemy_pictures_on_name"
t.index ["updater_id"], name: "index_alchemy_pictures_on_updater_id"
end
Expand Down
Loading

0 comments on commit 09c3d21

Please sign in to comment.