1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Attached one and many

This commit is contained in:
David Heinemeier Hansson 2017-07-05 16:09:41 +02:00
parent aaf8415188
commit b7cc003aa0
9 changed files with 139 additions and 41 deletions

View file

@ -0,0 +1,34 @@
require "active_vault/blob"
require "active_vault/attachment"
require "action_dispatch/http/upload"
require "active_support/core_ext/module/delegation"
class ActiveVault::Attached
attr_reader :name, :record
def initialize(name, record)
@name, @record = name, record
end
private
def create_blob_from(attachable)
case attachable
when ActiveVault::Blob
attachable
when ActionDispatch::Http::UploadedFile
ActiveVault::Blob.create_after_upload! \
io: attachable.open,
filename: attachable.original_filename,
content_type: attachable.content_type
when Hash
ActiveVault::Blob.create_after_upload!(attachable)
else
nil
end
end
end
require "active_vault/attached/one"
require "active_vault/attached/many"
require "active_vault/attached/macros"

View file

@ -0,0 +1,15 @@
module ActiveVault::Attached::Macros
def has_one_attached(name)
define_method(name) do
instance_variable_get("@active_vault_attached_#{name}") ||
instance_variable_set("@active_vault_attached_#{name}", ActiveVault::Attached::One.new(name, self))
end
end
def has_many_attached(name)
define_method(name) do
instance_variable_get("@active_vault_attached_#{name}") ||
instance_variable_set("@active_vault_attached_#{name}", ActiveVault::Attached::Many.new(name, self))
end
end
end

View file

@ -0,0 +1,22 @@
class ActiveVault::Attached::Many < ActiveVault::Attached
delegate_missing_to :attachments
def attachments
@attachments ||= ActiveVault::Attachment.where(record_gid: record.to_gid.to_s, name: name)
end
def attach(*attachables)
@attachments = attachments + Array(attachables).collect do |attachable|
ActiveVault::Attachment.create!(record_gid: record.to_gid.to_s, name: name, blob: create_blob_from(attachable))
end
end
def attached?
attachments.any?
end
def purge
attachments.each(&:purge)
@attachments = nil
end
end

View file

@ -0,0 +1,24 @@
class ActiveVault::Attached::One < ActiveVault::Attached
delegate_missing_to :attachment
def attachment
@attachment ||= ActiveVault::Attachment.find_by(record_gid: record.to_gid.to_s, name: name)
end
def attach(attachable)
if @attachment
# FIXME: Have options to declare dependent: :purge to clean up
end
@attachment = ActiveVault::Attachment.create!(record_gid: record.to_gid.to_s, name: name, blob: create_blob_from(attachable))
end
def attached?
attachment.present?
end
def purge
attachment.purge
@attachment = nil
end
end

View file

@ -22,6 +22,5 @@ class ActiveVault::Attachment < ActiveRecord::Base
def purge
blob.purge
destroy
record.public_send "#{name}=", nil
end
end

View file

@ -1,30 +0,0 @@
require "active_vault/attachment"
require "action_dispatch/http/upload"
module ActiveVault::Attachments
def has_file(name)
define_method(name) do
(@active_vault_attachments ||= {})[name] ||=
ActiveVault::Attachment.find_by(record_gid: to_gid.to_s, name: name)&.tap { |a| a.record = self }
end
define_method(:"#{name}=") do |attachable|
case attachable
when ActiveVault::Blob
blob = attachable
when ActionDispatch::Http::UploadedFile
blob = ActiveVault::Blob.create_after_upload! \
io: attachable.open,
filename: attachable.original_filename,
content_type: attachable.content_type
when Hash
blob = ActiveVault::Blob.create_after_upload!(attachable)
when NilClass
blob = nil
end
(@active_vault_attachments ||= {})[name] = blob ?
ActiveVault::Attachment.create!(record_gid: to_gid.to_s, name: name, blob: blob)&.tap { |a| a.record = self } : nil
end
end
end

View file

@ -16,11 +16,11 @@ module ActiveVault
end
end
initializer "action_file.attachments" do
require "active_vault/attachments"
initializer "action_file.attached" do
require "active_vault/attached"
ActiveSupport.on_load(:active_record) do
extend ActiveVault::Attachments
extend ActiveVault::Attached::Macros
end
end
end

View file

@ -5,23 +5,57 @@ require "active_vault/blob"
# ActiveRecord::Base.logger = Logger.new(STDOUT)
class User < ActiveRecord::Base
has_file :avatar
has_one_attached :avatar
has_many_attached :highlights
end
class ActiveVault::AttachmentsTest < ActiveSupport::TestCase
setup { @user = User.create!(name: "DHH") }
test "create attachment from existing blob" do
@user.avatar = create_blob filename: "funky.jpg"
teardown { ActiveVault::Blob.all.each(&:purge) }
test "attach existing blob" do
@user.avatar.attach create_blob(filename: "funky.jpg")
assert_equal "funky.jpg", @user.avatar.filename.to_s
end
test "attach new blob" do
@user.avatar.attach io: StringIO.new("STUFF"), filename: "town.jpg", content_type: "image/jpg"
assert_equal "town.jpg", @user.avatar.filename.to_s
end
test "purge attached blob" do
@user.avatar = create_blob filename: "funky.jpg"
@user.avatar.attach create_blob(filename: "funky.jpg")
avatar_key = @user.avatar.key
@user.avatar.purge
assert_nil @user.avatar
assert_not @user.avatar.attached?
assert_not ActiveVault::Blob.site.exist?(avatar_key)
end
test "attach existing blobs" do
@user.highlights.attach create_blob(filename: "funky.jpg"), create_blob(filename: "wonky.jpg")
assert_equal "funky.jpg", @user.highlights.first.filename.to_s
assert_equal "wonky.jpg", @user.highlights.second.filename.to_s
end
test "attach new blobs" do
@user.highlights.attach(
{ io: StringIO.new("STUFF"), filename: "town.jpg", content_type: "image/jpg" },
{ io: StringIO.new("IT"), filename: "country.jpg", content_type: "image/jpg" })
assert_equal "town.jpg", @user.highlights.first.filename.to_s
assert_equal "country.jpg", @user.highlights.second.filename.to_s
end
test "purge attached blobs" do
@user.highlights.attach create_blob(filename: "funky.jpg"), create_blob(filename: "wonky.jpg")
highlight_keys = @user.highlights.collect(&:key)
@user.highlights.purge
assert_not @user.highlights.attached?
assert_not ActiveVault::Blob.site.exist?(highlight_keys.first)
assert_not ActiveVault::Blob.site.exist?(highlight_keys.second)
end
end

View file

@ -20,8 +20,8 @@ class ActiveSupport::TestCase
end
require "active_vault/attachments"
ActiveRecord::Base.send :extend, ActiveVault::Attachments
require "active_vault/attached"
ActiveRecord::Base.send :extend, ActiveVault::Attached::Macros
require "global_id"
GlobalID.app = "ActiveVaultExampleApp"