Skip to content
Permalink
Browse files

Implement ActiveStorage::Attached::{One,Many}#attach in terms of changes

  • Loading branch information...
georgeclaghorn committed Jul 13, 2018
1 parent d20d6c7 commit 28db8ba60e726d695cf35710cc43ea45966464e9
@@ -1,7 +1,5 @@
# frozen_string_literal: true

require "action_dispatch"
require "action_dispatch/http/upload"
require "active_support/core_ext/module/delegation"

module ActiveStorage
@@ -18,24 +16,6 @@ def initialize(name, record, dependent:)
def change
record.attachment_changes[name]
end

def create_blob_from(attachable)
case attachable
when ActiveStorage::Blob
attachable
when ActionDispatch::Http::UploadedFile, Rack::Test::UploadedFile
ActiveStorage::Blob.create_after_upload! \
io: attachable.open,
filename: attachable.original_filename,
content_type: attachable.content_type
when Hash
ActiveStorage::Blob.create_after_upload!(attachable)
when String
ActiveStorage::Blob.find_signed(attachable)
else
nil
end
end
end
end

@@ -12,6 +12,10 @@ def attachments
@attachments ||= subchanges.collect(&:attachment)
end

def blobs
@blobs ||= subchanges.collect(&:blob)
end

def upload
subchanges.each(&:upload)
end
@@ -1,5 +1,8 @@
# frozen_string_literal: true

require "action_dispatch"
require "action_dispatch/http/upload"

module ActiveStorage
class Attached::Changes::CreateOne #:nodoc:
attr_reader :name, :record, :attachable
@@ -12,19 +12,26 @@ def attachments
change.present? ? change.attachments : record.public_send("#{name}_attachments")
end

# Associates one or several attachments with the current record, saving them to the database.
# Returns all attached blobs.
def blobs
change.present? ? change.blobs : record.public_send("#{name}_blobs")
end

# Attaches one or more +attachables+ to the record.
#
# If the record is persisted and unchanged, the attachments are saved to
# the database immediately. Otherwise, they'll be saved to the DB when the
# record is next saved.
#
# document.images.attach(params[:images]) # Array of ActionDispatch::Http::UploadedFile objects
# document.images.attach(params[:signed_blob_id]) # Signed reference to blob from direct upload
# document.images.attach(io: File.open("/path/to/racecar.jpg"), filename: "racecar.jpg", content_type: "image/jpg")
# document.images.attach([ first_blob, second_blob ])
def attach(*attachables)
attachables.flatten.collect do |attachable|
if record.new_record?
attachments.build(record: record, blob: create_blob_from(attachable))
else
attachments.create!(record: record, blob: create_blob_from(attachable))
end
if record.persisted? && !record.changed?
record.update(name => blobs + attachables.flatten)
else
record.public_send("#{name}=", blobs + attachables.flatten)
end
end

@@ -17,17 +17,21 @@ def blank?
!attached?
end

# Associates a given attachment with the current record, saving it to the database.
# Attaches an +attachable+ to the record.
#
# If the record is persisted and unchanged, the attachment is saved to
# the database immediately. Otherwise, it'll be saved to the DB when the
# record is next saved.
#
# person.avatar.attach(params[:avatar]) # ActionDispatch::Http::UploadedFile object
# person.avatar.attach(params[:signed_blob_id]) # Signed reference to blob from direct upload
# person.avatar.attach(io: File.open("/path/to/face.jpg"), filename: "face.jpg", content_type: "image/jpg")
# person.avatar.attach(avatar_blob) # ActiveStorage::Blob object
def attach(attachable)
new_blob = create_blob_from(attachable)

if !attached? || new_blob != blob
write_attachment build_attachment(blob: new_blob)
if record.persisted? && !record.changed?
record.update(name => attachable)
else
record.public_send("#{name}=", attachable)
end
end

@@ -68,12 +72,6 @@ def purge_later
end

private
delegate :transaction, to: :record

def build_attachment(blob:)
Attachment.new(record: record, name: name, blob: blob)
end

def write_attachment(attachment)
record.public_send("#{name}_attachment=", attachment)
end
@@ -39,6 +39,73 @@ class ActiveStorage::ManyAttachedTest < ActiveSupport::TestCase
assert_equal "video.mp4", @user.highlights.second.filename.to_s
end

test "attaching existing blobs to an existing, changed record" do
@user.name = "Tina"
assert @user.changed?

@user.highlights.attach create_blob(filename: "funky.jpg"), create_blob(filename: "town.jpg")
assert_equal "funky.jpg", @user.highlights.first.filename.to_s
assert_equal "town.jpg", @user.highlights.second.filename.to_s
assert_not @user.highlights.first.persisted?
assert_not @user.highlights.second.persisted?
assert @user.will_save_change_to_name?

@user.save!
assert_equal "funky.jpg", @user.highlights.reload.first.filename.to_s
assert_equal "town.jpg", @user.highlights.second.filename.to_s
end

test "attaching existing blobs from signed IDs to an existing, changed record" do
@user.name = "Tina"
assert @user.changed?

@user.highlights.attach create_blob(filename: "funky.jpg").signed_id, create_blob(filename: "town.jpg").signed_id
assert_equal "funky.jpg", @user.highlights.first.filename.to_s
assert_equal "town.jpg", @user.highlights.second.filename.to_s
assert_not @user.highlights.first.persisted?
assert_not @user.highlights.second.persisted?
assert @user.will_save_change_to_name?

@user.save!
assert_equal "funky.jpg", @user.highlights.reload.first.filename.to_s
assert_equal "town.jpg", @user.highlights.second.filename.to_s
end

test "attaching new blobs from Hashes to an existing, changed record" do
@user.name = "Tina"
assert @user.changed?

@user.highlights.attach(
{ io: StringIO.new("STUFF"), filename: "funky.jpg", content_type: "image/jpg" },
{ io: StringIO.new("THINGS"), filename: "town.jpg", content_type: "image/jpeg" })

assert_equal "funky.jpg", @user.highlights.first.filename.to_s
assert_equal "town.jpg", @user.highlights.second.filename.to_s
assert_not @user.highlights.first.persisted?
assert_not @user.highlights.second.persisted?
assert @user.will_save_change_to_name?

@user.save!
assert_equal "funky.jpg", @user.highlights.reload.first.filename.to_s
assert_equal "town.jpg", @user.highlights.second.filename.to_s
end

test "attaching new blobs from uploaded files to an existing, changed record" do
@user.name = "Tina"
assert @user.changed?

@user.highlights.attach fixture_file_upload("racecar.jpg"), fixture_file_upload("video.mp4")
assert_equal "racecar.jpg", @user.highlights.first.filename.to_s
assert_equal "video.mp4", @user.highlights.second.filename.to_s
assert_not @user.highlights.first.persisted?
assert_not @user.highlights.second.persisted?
assert @user.will_save_change_to_name?

@user.save!
assert_equal "racecar.jpg", @user.highlights.reload.first.filename.to_s
assert_equal "video.mp4", @user.highlights.second.filename.to_s
end

test "updating an existing record to attach existing blobs" do
@user.update! highlights: [ create_file_blob(filename: "racecar.jpg"), create_file_blob(filename: "video.mp4") ]
assert_equal "racecar.jpg", @user.highlights.first.filename.to_s
@@ -220,7 +287,7 @@ class ActiveStorage::ManyAttachedTest < ActiveSupport::TestCase
end
end

test "attaching new blob from Hashes to a new record" do
test "attaching new blobs from Hashes to a new record" do
User.new(name: "Jason").tap do |user|
user.highlights.attach(
{ io: StringIO.new("STUFF"), filename: "funky.jpg", content_type: "image/jpg" },
@@ -229,16 +296,22 @@ class ActiveStorage::ManyAttachedTest < ActiveSupport::TestCase
assert user.new_record?
assert user.highlights.first.new_record?
assert user.highlights.second.new_record?
assert_not user.highlights.first.blob.new_record?
assert_not user.highlights.second.blob.new_record?
assert user.highlights.first.blob.new_record?
assert user.highlights.second.blob.new_record?
assert_equal "funky.jpg", user.highlights.first.filename.to_s
assert_equal "town.jpg", user.highlights.second.filename.to_s
assert ActiveStorage::Blob.service.exist?(user.highlights.first.key)
assert ActiveStorage::Blob.service.exist?(user.highlights.second.key)
assert_not ActiveStorage::Blob.service.exist?(user.highlights.first.key)
assert_not ActiveStorage::Blob.service.exist?(user.highlights.second.key)

user.save!
assert user.highlights.first.persisted?
assert user.highlights.second.persisted?
assert user.highlights.first.blob.persisted?
assert user.highlights.second.blob.persisted?
assert_equal "funky.jpg", user.reload.highlights.first.filename.to_s
assert_equal "town.jpg", user.highlights.second.filename.to_s
assert ActiveStorage::Blob.service.exist?(user.highlights.first.key)
assert ActiveStorage::Blob.service.exist?(user.highlights.second.key)
end
end

@@ -248,16 +321,22 @@ class ActiveStorage::ManyAttachedTest < ActiveSupport::TestCase
assert user.new_record?
assert user.highlights.first.new_record?
assert user.highlights.second.new_record?
assert_not user.highlights.first.blob.new_record?
assert_not user.highlights.second.blob.new_record?
assert user.highlights.first.blob.new_record?
assert user.highlights.second.blob.new_record?
assert_equal "racecar.jpg", user.highlights.first.filename.to_s
assert_equal "video.mp4", user.highlights.second.filename.to_s
assert ActiveStorage::Blob.service.exist?(user.highlights.first.key)
assert ActiveStorage::Blob.service.exist?(user.highlights.second.key)
assert_not ActiveStorage::Blob.service.exist?(user.highlights.first.key)
assert_not ActiveStorage::Blob.service.exist?(user.highlights.second.key)

user.save!
assert user.highlights.first.persisted?
assert user.highlights.second.persisted?
assert user.highlights.first.blob.persisted?
assert user.highlights.second.blob.persisted?
assert_equal "racecar.jpg", user.reload.highlights.first.filename.to_s
assert_equal "video.mp4", user.highlights.second.filename.to_s
assert ActiveStorage::Blob.service.exist?(user.highlights.first.key)
assert ActiveStorage::Blob.service.exist?(user.highlights.second.key)
end
end

@@ -32,6 +32,58 @@ class ActiveStorage::OneAttachedTest < ActiveSupport::TestCase
assert_equal "racecar.jpg", @user.avatar.filename.to_s
end

test "attaching an existing blob to an existing, changed record" do
@user.name = "Tina"
assert @user.changed?

@user.avatar.attach create_blob(filename: "funky.jpg")
assert_equal "funky.jpg", @user.avatar.filename.to_s
assert_not @user.avatar.persisted?
assert @user.will_save_change_to_name?

@user.save!
assert_equal "funky.jpg", @user.reload.avatar.filename.to_s
end

test "attaching an existing blob from a signed ID to an existing, changed record" do
@user.name = "Tina"
assert @user.changed?

@user.avatar.attach create_blob(filename: "funky.jpg").signed_id
assert_equal "funky.jpg", @user.avatar.filename.to_s
assert_not @user.avatar.persisted?
assert @user.will_save_change_to_name?

@user.save!
assert_equal "funky.jpg", @user.reload.avatar.filename.to_s
end

test "attaching a new blob from a Hash to an existing, changed record" do
@user.name = "Tina"
assert @user.changed?

@user.avatar.attach io: StringIO.new("STUFF"), filename: "town.jpg", content_type: "image/jpg"
assert_equal "town.jpg", @user.avatar.filename.to_s
assert_not @user.avatar.persisted?
assert @user.will_save_change_to_name?

@user.save!
assert_equal "town.jpg", @user.reload.avatar.filename.to_s
end

test "attaching a new blob from an uploaded file to an existing, changed record" do
@user.name = "Tina"
assert @user.changed?

@user.avatar.attach fixture_file_upload("racecar.jpg")
assert_equal "racecar.jpg", @user.avatar.filename.to_s
assert_not @user.avatar.persisted?
assert @user.will_save_change_to_name?

@user.save!
assert_equal "racecar.jpg", @user.reload.avatar.filename.to_s
end

test "updating an existing record to attach an existing blob" do
@user.update! avatar: create_blob(filename: "funky.jpg")
assert_equal "funky.jpg", @user.avatar.filename.to_s
@@ -71,7 +123,7 @@ class ActiveStorage::OneAttachedTest < ActiveSupport::TestCase
end
end

test "successfully replacing an existing, independent attachment on an existing record" do
test "replacing an existing, independent attachment on an existing record" do
@user.cover_photo.attach create_blob(filename: "funky.jpg")

assert_no_enqueued_jobs only: ActiveStorage::PurgeJob do
@@ -81,20 +133,7 @@ class ActiveStorage::OneAttachedTest < ActiveSupport::TestCase
assert_equal "town.jpg", @user.cover_photo.filename.to_s
end

test "unsuccessfully replacing an existing attachment on an existing record" do
@user.avatar.attach create_blob(filename: "funky.jpg")

assert_no_enqueued_jobs do
assert_raises do
@user.avatar.attach nil
end
end

assert_equal "funky.jpg", @user.avatar.filename.to_s
assert ActiveStorage::Blob.service.exist?(@user.avatar.key)
end

test "replacing an existing attachment on an existing record with the same blob" do
test "replacing an attached blob on an existing record with itself" do
create_blob(filename: "funky.jpg").tap do |blob|
@user.avatar.attach blob

@@ -149,7 +188,7 @@ class ActiveStorage::OneAttachedTest < ActiveSupport::TestCase
@user.avatar.attach blob

assert_no_enqueued_jobs do
assert_no_changes -> { @user.avatar_attachment.id } do
assert_no_changes -> { @user.reload.avatar_attachment.id } do
@user.update! avatar: blob
end
end
@@ -248,12 +287,15 @@ class ActiveStorage::OneAttachedTest < ActiveSupport::TestCase
user.avatar.attach io: StringIO.new("STUFF"), filename: "town.jpg", content_type: "image/jpg"
assert user.new_record?
assert user.avatar.attachment.new_record?
assert_not user.avatar.blob.new_record?
assert user.avatar.blob.new_record?
assert_equal "town.jpg", user.avatar.filename.to_s
assert ActiveStorage::Blob.service.exist?(user.avatar.key)
assert_not ActiveStorage::Blob.service.exist?(user.avatar.key)

user.save!
assert user.avatar.attachment.persisted?
assert user.avatar.blob.persisted?
assert_equal "town.jpg", user.reload.avatar.filename.to_s
assert ActiveStorage::Blob.service.exist?(user.avatar.key)
end
end

@@ -262,12 +304,15 @@ class ActiveStorage::OneAttachedTest < ActiveSupport::TestCase
user.avatar.attach fixture_file_upload("racecar.jpg")
assert user.new_record?
assert user.avatar.attachment.new_record?
assert_not user.avatar.blob.new_record?
assert user.avatar.blob.new_record?
assert_equal "racecar.jpg", user.avatar.filename.to_s
assert ActiveStorage::Blob.service.exist?(user.avatar.key)
assert_not ActiveStorage::Blob.service.exist?(user.avatar.key)

user.save!
assert user.avatar.attachment.persisted?
assert user.avatar.blob.persisted?
assert_equal "racecar.jpg", user.reload.avatar.filename.to_s
assert ActiveStorage::Blob.service.exist?(user.avatar.key)
end
end

0 comments on commit 28db8ba

Please sign in to comment.
You can’t perform that action at this time.