Skip to content

Instantly share code, notes, and snippets.

@joshmn
Created May 22, 2023 00:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joshmn/daefdfd7c5143bc820f9129eeac51e16 to your computer and use it in GitHub Desktop.
Save joshmn/daefdfd7c5143bc820f9129eeac51e16 to your computer and use it in GitHub Desktop.
ActiveStorage with ordered attachments using acts_as_list
module MyApp
class Application < Rails::Application
config.to_prepare do
ActiveStorage::Attachment.include OrderedAttachments
end
end
end
class ActiveStorage::AttachmentPosition < ApplicationRecord
belongs_to :attachment, class_name: "ActiveStorage::Attachment", foreign_key: :active_storage_attachment_id
belongs_to :record, polymorphic: true
acts_as_list scope: :record
end
module HasOrderedAttachments
extend ActiveSupport::Concern
class_methods do
def has_ordered_attachments
@has_ordered_attachments
end
def has_ordered_attachments_on?(name)
has_ordered_attachments.include?(name)
end
def has_many_ordered_attachments(name)
has_many_attached name
attachment_scope = proc { left_outer_joins(:active_storage_attachment_position).order(Arel.sql("COALESCE(active_storage_attachment_positions.position, 0)")) }
blob_scope = proc { left_outer_joins(attachments: :active_storage_attachment_position).order(Arel.sql("COALESCE(active_storage_attachment_positions.position, 0)")) }
self._reflections["#{name}_attachments"].instance_variable_set(:@scope, attachment_scope)
self._reflections["#{name}_blobs"].instance_variable_set(:@scope, blob_scope)
@has_ordered_attachments ||= Set.new
@has_ordered_attachments << name
generated_association_methods.class_eval <<-CODE, __FILE__, __LINE__ + 1
# frozen_string_literal: true
def #{name}
@active_storage_attached ||= {}
@active_storage_attached[:#{name}] ||= ActiveStorage::Attached::OrderedMany.new("#{name}", self)
end
CODE
end
end
end
# rails g model ActiveStorage::AttachmentPosition
class CreateActiveStorageAttachmentPositions < ActiveRecord::Migration[7.0]
def change
create_table :active_storage_attachment_positions do |t|
t.references :active_storage_attachment, null: false, foreign_key: true, index: { name: :index_attachment_positions_on_active_storage_attachment_id }
t.references :record, null: false, polymorphic: true
t.integer :position, null: false
t.timestamps
end
end
end
module OrderedAttachments
extend ActiveSupport::Concern
included do
has_one :active_storage_attachment_position, class_name: "ActiveStorage::AttachmentPosition", foreign_key: :active_storage_attachment_id, dependent: :delete
after_create_commit do
if ordered?(record_type, name)
ActiveStorage::AttachmentPosition.create!(record_type: record_type, record_id: record_id, attachment: self)
end
end
def ordered?(type, name)
klass = type.constantize
klass.respond_to?(:has_ordered_attachments) && klass.has_ordered_attachments_on?(name.to_sym)
end
end
delegate :insert_at, :position=, :position, :move_lower, :move_higher, :move_to_bottom, :move_to_top,
:remove_from_list, :increment_position, :decrement_position, :set_list_position, :first?, :last?, :in_list?,
:not_in_list?, :default_position?, :higher_item, :lower_item, :lower_items, to: :active_storage_attachment_position
end
class Post < ApplicationRecord
include HasOrderedAttachments
has_many_attached :images
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment