mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Pass optional record in blob finder methods
Allow record to be optionally passed to blob finders to make sharding easier.
This commit is contained in:
parent
9894f652ad
commit
7fd006de0b
6 changed files with 61 additions and 17 deletions
|
@ -1,3 +1,8 @@
|
||||||
|
* Allow record to be optionally passed to blob finders to make sharding
|
||||||
|
easier.
|
||||||
|
|
||||||
|
*Gannon McGibbon*
|
||||||
|
|
||||||
* Switch from `azure-storage` gem to `azure-storage-blob` gem for Azure service.
|
* Switch from `azure-storage` gem to `azure-storage-blob` gem for Azure service.
|
||||||
|
|
||||||
*Peter Zhu*
|
*Peter Zhu*
|
||||||
|
|
|
@ -45,19 +45,19 @@ class ActiveStorage::Blob < ActiveRecord::Base
|
||||||
# that was created ahead of the upload itself on form submission.
|
# that was created ahead of the upload itself on form submission.
|
||||||
#
|
#
|
||||||
# The signed ID is also used to create stable URLs for the blob through the BlobsController.
|
# The signed ID is also used to create stable URLs for the blob through the BlobsController.
|
||||||
def find_signed(id)
|
def find_signed(id, record: nil)
|
||||||
find ActiveStorage.verifier.verify(id, purpose: :blob_id)
|
find ActiveStorage.verifier.verify(id, purpose: :blob_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a new, unsaved blob instance after the +io+ has been uploaded to the service.
|
# Returns a new, unsaved blob instance after the +io+ has been uploaded to the service.
|
||||||
# When providing a content type, pass <tt>identify: false</tt> to bypass automatic content type inference.
|
# When providing a content type, pass <tt>identify: false</tt> to bypass automatic content type inference.
|
||||||
def build_after_upload(io:, filename:, content_type: nil, metadata: nil, identify: true)
|
def build_after_upload(io:, filename:, content_type: nil, metadata: nil, identify: true, record: nil)
|
||||||
new(filename: filename, content_type: content_type, metadata: metadata).tap do |blob|
|
new(filename: filename, content_type: content_type, metadata: metadata).tap do |blob|
|
||||||
blob.upload(io, identify: identify)
|
blob.upload(io, identify: identify)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_after_unfurling(io:, filename:, content_type: nil, metadata: nil, identify: true) #:nodoc:
|
def build_after_unfurling(io:, filename:, content_type: nil, metadata: nil, identify: true, record: nil) #:nodoc:
|
||||||
new(filename: filename, content_type: content_type, metadata: metadata).tap do |blob|
|
new(filename: filename, content_type: content_type, metadata: metadata).tap do |blob|
|
||||||
blob.unfurl(io, identify: identify)
|
blob.unfurl(io, identify: identify)
|
||||||
end
|
end
|
||||||
|
@ -67,7 +67,7 @@ class ActiveStorage::Blob < ActiveRecord::Base
|
||||||
# then the +io+ is uploaded, then the blob is saved. This is done this way to avoid uploading (which may take
|
# then the +io+ is uploaded, then the blob is saved. This is done this way to avoid uploading (which may take
|
||||||
# time), while having an open database transaction.
|
# time), while having an open database transaction.
|
||||||
# When providing a content type, pass <tt>identify: false</tt> to bypass automatic content type inference.
|
# When providing a content type, pass <tt>identify: false</tt> to bypass automatic content type inference.
|
||||||
def create_after_upload!(io:, filename:, content_type: nil, metadata: nil, identify: true)
|
def create_after_upload!(io:, filename:, content_type: nil, metadata: nil, identify: true, record: nil)
|
||||||
build_after_upload(io: io, filename: filename, content_type: content_type, metadata: metadata, identify: identify).tap(&:save!)
|
build_after_upload(io: io, filename: filename, content_type: content_type, metadata: metadata, identify: identify).tap(&:save!)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ class ActiveStorage::Blob < ActiveRecord::Base
|
||||||
# in order to produce the signed URL for uploading. This signed URL points to the key generated by the blob.
|
# in order to produce the signed URL for uploading. This signed URL points to the key generated by the blob.
|
||||||
# Once the form using the direct upload is submitted, the blob can be associated with the right record using
|
# Once the form using the direct upload is submitted, the blob can be associated with the right record using
|
||||||
# the signed ID.
|
# the signed ID.
|
||||||
def create_before_direct_upload!(filename:, byte_size:, checksum:, content_type: nil, metadata: nil)
|
def create_before_direct_upload!(filename:, byte_size:, checksum:, content_type: nil, metadata: nil, record: nil)
|
||||||
create! filename: filename, byte_size: byte_size, checksum: checksum, content_type: content_type, metadata: metadata
|
create! filename: filename, byte_size: byte_size, checksum: checksum, content_type: content_type, metadata: metadata
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -53,14 +53,16 @@ module ActiveStorage
|
||||||
when ActiveStorage::Blob
|
when ActiveStorage::Blob
|
||||||
attachable
|
attachable
|
||||||
when ActionDispatch::Http::UploadedFile, Rack::Test::UploadedFile
|
when ActionDispatch::Http::UploadedFile, Rack::Test::UploadedFile
|
||||||
ActiveStorage::Blob.build_after_unfurling \
|
ActiveStorage::Blob.build_after_unfurling(
|
||||||
io: attachable.open,
|
io: attachable.open,
|
||||||
filename: attachable.original_filename,
|
filename: attachable.original_filename,
|
||||||
content_type: attachable.content_type
|
content_type: attachable.content_type,
|
||||||
|
record: record,
|
||||||
|
)
|
||||||
when Hash
|
when Hash
|
||||||
ActiveStorage::Blob.build_after_unfurling(attachable)
|
ActiveStorage::Blob.build_after_unfurling(attachable.merge(record: record))
|
||||||
when String
|
when String
|
||||||
ActiveStorage::Blob.find_signed(attachable)
|
ActiveStorage::Blob.find_signed(attachable, record: record)
|
||||||
else
|
else
|
||||||
raise ArgumentError, "Could not find or build blob: expected attachable, got #{attachable.inspect}"
|
raise ArgumentError, "Could not find or build blob: expected attachable, got #{attachable.inspect}"
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,9 +2,11 @@
|
||||||
|
|
||||||
require "test_helper"
|
require "test_helper"
|
||||||
require "database/setup"
|
require "database/setup"
|
||||||
|
require "active_support/testing/method_call_assertions"
|
||||||
|
|
||||||
class ActiveStorage::OneAttachedTest < ActiveSupport::TestCase
|
class ActiveStorage::OneAttachedTest < ActiveSupport::TestCase
|
||||||
include ActiveJob::TestHelper
|
include ActiveJob::TestHelper
|
||||||
|
include ActiveSupport::Testing::MethodCallAssertions
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
@user = User.create!(name: "Josh")
|
@user = User.create!(name: "Josh")
|
||||||
|
@ -25,16 +27,45 @@ class ActiveStorage::OneAttachedTest < ActiveSupport::TestCase
|
||||||
assert_equal "funky.jpg", @user.avatar.filename.to_s
|
assert_equal "funky.jpg", @user.avatar.filename.to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "attaching an existing blob from a signed ID passes record" do
|
||||||
|
blob = create_blob(filename: "funky.jpg")
|
||||||
|
arguments = [blob.signed_id, record: @user]
|
||||||
|
assert_called_with(ActiveStorage::Blob, :find_signed, arguments, returns: blob) do
|
||||||
|
@user.avatar.attach blob.signed_id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
test "attaching a new blob from a Hash to an existing record" do
|
test "attaching a new blob from a Hash to an existing record" do
|
||||||
@user.avatar.attach io: StringIO.new("STUFF"), filename: "town.jpg", content_type: "image/jpg"
|
@user.avatar.attach io: StringIO.new("STUFF"), filename: "town.jpg", content_type: "image/jpg"
|
||||||
assert_equal "town.jpg", @user.avatar.filename.to_s
|
assert_equal "town.jpg", @user.avatar.filename.to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "attaching a new blob from a Hash to an existing record passes record" do
|
||||||
|
hash = { io: StringIO.new("STUFF"), filename: "town.jpg", content_type: "image/jpg" }
|
||||||
|
blob = ActiveStorage::Blob.build_after_unfurling(hash)
|
||||||
|
arguments = [hash.merge(record: @user)]
|
||||||
|
assert_called_with(ActiveStorage::Blob, :build_after_unfurling, arguments, returns: blob) do
|
||||||
|
@user.avatar.attach hash
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
test "attaching a new blob from an uploaded file to an existing record" do
|
test "attaching a new blob from an uploaded file to an existing record" do
|
||||||
@user.avatar.attach fixture_file_upload("racecar.jpg")
|
@user.avatar.attach fixture_file_upload("racecar.jpg")
|
||||||
assert_equal "racecar.jpg", @user.avatar.filename.to_s
|
assert_equal "racecar.jpg", @user.avatar.filename.to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "attaching a new blob from an uploaded file to an existing record passes record" do
|
||||||
|
upload = fixture_file_upload("racecar.jpg")
|
||||||
|
def upload.open
|
||||||
|
@io ||= StringIO.new("")
|
||||||
|
end
|
||||||
|
arguments = [io: upload.open, filename: upload.original_filename, content_type: upload.content_type, record: @user]
|
||||||
|
blob = ActiveStorage::Blob.build_after_unfurling(*arguments)
|
||||||
|
assert_called_with(ActiveStorage::Blob, :build_after_unfurling, arguments, returns: blob) do
|
||||||
|
@user.avatar.attach upload
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
test "attaching an existing blob to an existing, changed record" do
|
test "attaching an existing blob to an existing, changed record" do
|
||||||
@user.name = "Tina"
|
@user.name = "Tina"
|
||||||
assert @user.changed?
|
assert @user.changed?
|
||||||
|
|
|
@ -51,6 +51,12 @@ class ActiveStorage::BlobTest < ActiveSupport::TestCase
|
||||||
assert_match(/^[a-z0-9]{28}$/, create_blob.key)
|
assert_match(/^[a-z0-9]{28}$/, create_blob.key)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "create after upload accepts a record for overrides" do
|
||||||
|
assert_nothing_raised do
|
||||||
|
create_blob(record: User.new)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
test "image?" do
|
test "image?" do
|
||||||
blob = create_file_blob filename: "racecar.jpg"
|
blob = create_file_blob filename: "racecar.jpg"
|
||||||
assert_predicate blob, :image?
|
assert_predicate blob, :image?
|
||||||
|
|
|
@ -51,24 +51,24 @@ class ActiveSupport::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def create_blob(data: "Hello world!", filename: "hello.txt", content_type: "text/plain", identify: true)
|
def create_blob(data: "Hello world!", filename: "hello.txt", content_type: "text/plain", identify: true, record: nil)
|
||||||
ActiveStorage::Blob.create_after_upload! io: StringIO.new(data), filename: filename, content_type: content_type, identify: identify
|
ActiveStorage::Blob.create_after_upload! io: StringIO.new(data), filename: filename, content_type: content_type, identify: identify, record: record
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_file_blob(filename: "racecar.jpg", content_type: "image/jpeg", metadata: nil)
|
def create_file_blob(filename: "racecar.jpg", content_type: "image/jpeg", metadata: nil, record: nil)
|
||||||
ActiveStorage::Blob.create_after_upload! io: file_fixture(filename).open, filename: filename, content_type: content_type, metadata: metadata
|
ActiveStorage::Blob.create_after_upload! io: file_fixture(filename).open, filename: filename, content_type: content_type, metadata: metadata, record: record
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_blob_before_direct_upload(filename: "hello.txt", byte_size:, checksum:, content_type: "text/plain")
|
def create_blob_before_direct_upload(filename: "hello.txt", byte_size:, checksum:, content_type: "text/plain", record: nil)
|
||||||
ActiveStorage::Blob.create_before_direct_upload! filename: filename, byte_size: byte_size, checksum: checksum, content_type: content_type
|
ActiveStorage::Blob.create_before_direct_upload! filename: filename, byte_size: byte_size, checksum: checksum, content_type: content_type, record: record
|
||||||
end
|
end
|
||||||
|
|
||||||
def directly_upload_file_blob(filename: "racecar.jpg", content_type: "image/jpeg")
|
def directly_upload_file_blob(filename: "racecar.jpg", content_type: "image/jpeg", record: nil)
|
||||||
file = file_fixture(filename)
|
file = file_fixture(filename)
|
||||||
byte_size = file.size
|
byte_size = file.size
|
||||||
checksum = Digest::MD5.file(file).base64digest
|
checksum = Digest::MD5.file(file).base64digest
|
||||||
|
|
||||||
create_blob_before_direct_upload(filename: filename, byte_size: byte_size, checksum: checksum, content_type: content_type).tap do |blob|
|
create_blob_before_direct_upload(filename: filename, byte_size: byte_size, checksum: checksum, content_type: content_type, record: record).tap do |blob|
|
||||||
service = ActiveStorage::Blob.service.try(:primary) || ActiveStorage::Blob.service
|
service = ActiveStorage::Blob.service.try(:primary) || ActiveStorage::Blob.service
|
||||||
service.upload(blob.key, file.open)
|
service.upload(blob.key, file.open)
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue