aboutsummaryrefslogtreecommitdiffstats
path: root/activestorage
diff options
context:
space:
mode:
Diffstat (limited to 'activestorage')
-rw-r--r--activestorage/activestorage.gemspec2
-rw-r--r--activestorage/app/models/active_storage/blob/identifiable.rb10
-rw-r--r--activestorage/app/models/active_storage/identification.rb35
-rw-r--r--activestorage/app/models/active_storage/preview.rb7
-rw-r--r--activestorage/lib/active_storage/attached/macros.rb18
-rw-r--r--activestorage/lib/active_storage/attached/many.rb18
-rw-r--r--activestorage/lib/active_storage/attached/one.rb32
-rw-r--r--activestorage/lib/active_storage/engine.rb5
-rw-r--r--activestorage/lib/active_storage/previewer/mupdf_previewer.rb36
-rw-r--r--activestorage/lib/active_storage/previewer/pdf_previewer.rb26
-rw-r--r--activestorage/lib/active_storage/previewer/poppler_pdf_previewer.rb35
-rw-r--r--activestorage/lib/active_storage/service.rb5
-rw-r--r--activestorage/lib/active_storage/service/azure_storage_service.rb7
-rw-r--r--activestorage/lib/active_storage/service/disk_service.rb28
-rw-r--r--activestorage/lib/active_storage/service/gcs_service.rb15
-rw-r--r--activestorage/lib/active_storage/service/mirror_service.rb2
-rw-r--r--activestorage/lib/active_storage/service/s3_service.rb6
-rw-r--r--activestorage/test/controllers/direct_uploads_controller_test.rb2
-rw-r--r--activestorage/test/models/attachments_test.rb61
-rw-r--r--activestorage/test/models/blob_test.rb2
-rw-r--r--activestorage/test/previewer/mupdf_previewer_test.rb (renamed from activestorage/test/previewer/pdf_previewer_test.rb)6
-rw-r--r--activestorage/test/previewer/poppler_pdf_previewer_test.rb23
-rw-r--r--activestorage/test/service/configurator_test.rb2
-rw-r--r--activestorage/test/service/shared_service_tests.rb5
-rw-r--r--activestorage/test/test_helper.rb6
25 files changed, 266 insertions, 128 deletions
diff --git a/activestorage/activestorage.gemspec b/activestorage/activestorage.gemspec
index df7801a671..cb1bb00a25 100644
--- a/activestorage/activestorage.gemspec
+++ b/activestorage/activestorage.gemspec
@@ -29,6 +29,4 @@ Gem::Specification.new do |s|
s.add_dependency "activerecord", version
s.add_dependency "marcel", "~> 0.3.1"
-
- s.add_development_dependency "webmock", "~> 3.2.1"
end
diff --git a/activestorage/app/models/active_storage/blob/identifiable.rb b/activestorage/app/models/active_storage/blob/identifiable.rb
index dbe03cfa6c..049e45dc3e 100644
--- a/activestorage/app/models/active_storage/blob/identifiable.rb
+++ b/activestorage/app/models/active_storage/blob/identifiable.rb
@@ -2,7 +2,7 @@
module ActiveStorage::Blob::Identifiable
def identify
- update!(content_type: identification.content_type, identified: true) unless identified?
+ update! content_type: identify_content_type, identified: true unless identified?
end
def identified?
@@ -10,7 +10,11 @@ module ActiveStorage::Blob::Identifiable
end
private
- def identification
- ActiveStorage::Identification.new self
+ def identify_content_type
+ Marcel::MimeType.for download_identifiable_chunk, name: filename.to_s, declared_type: content_type
+ end
+
+ def download_identifiable_chunk
+ service.download_chunk key, 0...4.kilobytes
end
end
diff --git a/activestorage/app/models/active_storage/identification.rb b/activestorage/app/models/active_storage/identification.rb
deleted file mode 100644
index 8d334ae1ea..0000000000
--- a/activestorage/app/models/active_storage/identification.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-# frozen_string_literal: true
-
-require "net/http"
-
-class ActiveStorage::Identification #:nodoc:
- attr_reader :blob
-
- def initialize(blob)
- @blob = blob
- end
-
- def content_type
- Marcel::MimeType.for(identifiable_chunk, name: filename, declared_type: declared_content_type)
- end
-
- private
- def identifiable_chunk
- Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") do |client|
- client.get(uri, "Range" => "bytes=0-4095").body
- end
- end
-
- def uri
- @uri ||= URI.parse(blob.service_url)
- end
-
-
- def filename
- blob.filename.to_s
- end
-
- def declared_content_type
- blob.content_type
- end
-end
diff --git a/activestorage/app/models/active_storage/preview.rb b/activestorage/app/models/active_storage/preview.rb
index 45efd26214..2b87897183 100644
--- a/activestorage/app/models/active_storage/preview.rb
+++ b/activestorage/app/models/active_storage/preview.rb
@@ -21,10 +21,9 @@
#
# Outside of a Rails application, modify +ActiveStorage.previewers+ instead.
#
-# The built-in previewers rely on third-party system libraries:
-#
-# * {ffmpeg}[https://www.ffmpeg.org]
-# * {mupdf}[https://mupdf.com] (version 1.8 or newer)
+# The built-in previewers rely on third-party system libraries. Specifically, the built-in video previewer requires
+# {ffmpeg}[https://www.ffmpeg.org]. Two PDF previewers are provided: one requires {Poppler}[https://poppler.freedesktop.org],
+# and the other requires {mupdf}[https://mupdf.com] (version 1.8 or newer). To preview PDFs, install either Poppler or mupdf.
#
# These libraries are not provided by Rails. You must install them yourself to use the built-in previewers. Before you
# install and use third-party software, make sure you understand the licensing implications of doing so.
diff --git a/activestorage/lib/active_storage/attached/macros.rb b/activestorage/lib/active_storage/attached/macros.rb
index c51efa9d6b..819f00cc06 100644
--- a/activestorage/lib/active_storage/attached/macros.rb
+++ b/activestorage/lib/active_storage/attached/macros.rb
@@ -38,13 +38,15 @@ module ActiveStorage
end
CODE
- has_one :"#{name}_attachment", -> { where(name: name) }, class_name: "ActiveStorage::Attachment", as: :record, inverse_of: :record
+ has_one :"#{name}_attachment", -> { where(name: name) }, class_name: "ActiveStorage::Attachment", as: :record, inverse_of: :record, dependent: false
has_one :"#{name}_blob", through: :"#{name}_attachment", class_name: "ActiveStorage::Blob", source: :blob
scope :"with_attached_#{name}", -> { includes("#{name}_attachment": :blob) }
if dependent == :purge_later
after_destroy_commit { public_send(name).purge_later }
+ else
+ before_destroy { public_send(name).detach }
end
end
@@ -83,13 +85,25 @@ module ActiveStorage
end
CODE
- has_many :"#{name}_attachments", -> { where(name: name) }, as: :record, class_name: "ActiveStorage::Attachment", inverse_of: :record
+ has_many :"#{name}_attachments", -> { where(name: name) }, as: :record, class_name: "ActiveStorage::Attachment", inverse_of: :record, dependent: false do
+ def purge
+ each(&:purge)
+ reset
+ end
+
+ def purge_later
+ each(&:purge_later)
+ reset
+ end
+ end
has_many :"#{name}_blobs", through: :"#{name}_attachments", class_name: "ActiveStorage::Blob", source: :blob
scope :"with_attached_#{name}", -> { includes("#{name}_attachments": :blob) }
if dependent == :purge_later
after_destroy_commit { public_send(name).purge_later }
+ else
+ before_destroy { public_send(name).detach }
end
end
end
diff --git a/activestorage/lib/active_storage/attached/many.rb b/activestorage/lib/active_storage/attached/many.rb
index 6eace65b79..d61acb6fad 100644
--- a/activestorage/lib/active_storage/attached/many.rb
+++ b/activestorage/lib/active_storage/attached/many.rb
@@ -44,20 +44,16 @@ module ActiveStorage
attachments.destroy_all if attached?
end
+ ##
+ # :method: purge
+ #
# Directly purges each associated attachment (i.e. destroys the blobs and
# attachments and deletes the files on the service).
- def purge
- if attached?
- attachments.each(&:purge)
- attachments.reload
- end
- end
+
+ ##
+ # :method: purge_later
+ #
# Purges each associated attachment through the queuing system.
- def purge_later
- if attached?
- attachments.each(&:purge_later)
- end
- end
end
end
diff --git a/activestorage/lib/active_storage/attached/one.rb b/activestorage/lib/active_storage/attached/one.rb
index 008f5a796b..a582ed0137 100644
--- a/activestorage/lib/active_storage/attached/one.rb
+++ b/activestorage/lib/active_storage/attached/one.rb
@@ -20,10 +20,16 @@ module ActiveStorage
# 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)
- if attached? && dependent == :purge_later
- replace attachable
- else
- write_attachment build_attachment_from(attachable)
+ blob_was = blob if attached?
+ blob = create_blob_from(attachable)
+
+ unless blob == blob_was
+ transaction do
+ detach
+ write_attachment build_attachment(blob: blob)
+ end
+
+ blob_was.purge_later if blob_was && dependent == :purge_later
end
end
@@ -63,23 +69,7 @@ module ActiveStorage
end
private
- def replace(attachable)
- blob_was = blob
- blob = create_blob_from(attachable)
-
- unless blob == blob_was
- transaction do
- detach
- write_attachment build_attachment(blob: blob)
- end
-
- blob_was.purge_later
- end
- end
-
- def build_attachment_from(attachable)
- build_attachment blob: create_blob_from(attachable)
- end
+ delegate :transaction, to: :record
def build_attachment(blob:)
ActiveStorage::Attachment.new(record: record, name: name, blob: blob)
diff --git a/activestorage/lib/active_storage/engine.rb b/activestorage/lib/active_storage/engine.rb
index 1e223f9f17..1385e2aa84 100644
--- a/activestorage/lib/active_storage/engine.rb
+++ b/activestorage/lib/active_storage/engine.rb
@@ -3,7 +3,8 @@
require "rails"
require "active_storage"
-require "active_storage/previewer/pdf_previewer"
+require "active_storage/previewer/poppler_pdf_previewer"
+require "active_storage/previewer/mupdf_previewer"
require "active_storage/previewer/video_previewer"
require "active_storage/analyzer/image_analyzer"
@@ -14,7 +15,7 @@ module ActiveStorage
isolate_namespace ActiveStorage
config.active_storage = ActiveSupport::OrderedOptions.new
- config.active_storage.previewers = [ ActiveStorage::Previewer::PDFPreviewer, ActiveStorage::Previewer::VideoPreviewer ]
+ config.active_storage.previewers = [ ActiveStorage::Previewer::PopplerPDFPreviewer, ActiveStorage::Previewer::MuPDFPreviewer, ActiveStorage::Previewer::VideoPreviewer ]
config.active_storage.analyzers = [ ActiveStorage::Analyzer::ImageAnalyzer, ActiveStorage::Analyzer::VideoAnalyzer ]
config.active_storage.paths = ActiveSupport::OrderedOptions.new
diff --git a/activestorage/lib/active_storage/previewer/mupdf_previewer.rb b/activestorage/lib/active_storage/previewer/mupdf_previewer.rb
new file mode 100644
index 0000000000..ae02a4889d
--- /dev/null
+++ b/activestorage/lib/active_storage/previewer/mupdf_previewer.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module ActiveStorage
+ class Previewer::MuPDFPreviewer < Previewer
+ class << self
+ def accept?(blob)
+ blob.content_type == "application/pdf" && mutool_exists?
+ end
+
+ def mutool_path
+ ActiveStorage.paths[:mutool] || "mutool"
+ end
+
+ def mutool_exists?
+ return @mutool_exists unless @mutool_exists.nil?
+
+ system mutool_path, out: File::NULL, err: File::NULL
+
+ @mutool_exists = $?.exitstatus == 1
+ end
+ end
+
+ def preview
+ download_blob_to_tempfile do |input|
+ draw_first_page_from input do |output|
+ yield io: output, filename: "#{blob.filename.base}.png", content_type: "image/png"
+ end
+ end
+ end
+
+ private
+ def draw_first_page_from(file, &block)
+ draw self.class.mutool_path, "draw", "-F", "png", "-o", "-", file.path, "1", &block
+ end
+ end
+end
diff --git a/activestorage/lib/active_storage/previewer/pdf_previewer.rb b/activestorage/lib/active_storage/previewer/pdf_previewer.rb
deleted file mode 100644
index 426ff51eb1..0000000000
--- a/activestorage/lib/active_storage/previewer/pdf_previewer.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-# frozen_string_literal: true
-
-module ActiveStorage
- class Previewer::PDFPreviewer < Previewer
- def self.accept?(blob)
- blob.content_type == "application/pdf"
- end
-
- def preview
- download_blob_to_tempfile do |input|
- draw_first_page_from input do |output|
- yield io: output, filename: "#{blob.filename.base}.png", content_type: "image/png"
- end
- end
- end
-
- private
- def draw_first_page_from(file, &block)
- draw mutool_path, "draw", "-F", "png", "-o", "-", file.path, "1", &block
- end
-
- def mutool_path
- ActiveStorage.paths[:mutool] || "mutool"
- end
- end
-end
diff --git a/activestorage/lib/active_storage/previewer/poppler_pdf_previewer.rb b/activestorage/lib/active_storage/previewer/poppler_pdf_previewer.rb
new file mode 100644
index 0000000000..2a787362cf
--- /dev/null
+++ b/activestorage/lib/active_storage/previewer/poppler_pdf_previewer.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module ActiveStorage
+ class Previewer::PopplerPDFPreviewer < Previewer
+ class << self
+ def accept?(blob)
+ blob.content_type == "application/pdf" && pdftoppm_exists?
+ end
+
+ def pdftoppm_path
+ ActiveStorage.paths[:pdftoppm] || "pdftoppm"
+ end
+
+ def pdftoppm_exists?
+ return @pdftoppm_exists unless @pdftoppm_exists.nil?
+
+ @pdftoppm_exists = system(pdftoppm_path, "-v", out: File::NULL, err: File::NULL)
+ end
+ end
+
+ def preview
+ download_blob_to_tempfile do |input|
+ draw_first_page_from input do |output|
+ yield io: output, filename: "#{blob.filename.base}.png", content_type: "image/png"
+ end
+ end
+ end
+
+ private
+ def draw_first_page_from(file, &block)
+ # use 72 dpi to match thumbnail dimesions of the PDF
+ draw self.class.pdftoppm_path, "-singlefile", "-r", "72", "-png", file.path, &block
+ end
+ end
+end
diff --git a/activestorage/lib/active_storage/service.rb b/activestorage/lib/active_storage/service.rb
index f2e1269f27..949969fc95 100644
--- a/activestorage/lib/active_storage/service.rb
+++ b/activestorage/lib/active_storage/service.rb
@@ -73,6 +73,11 @@ module ActiveStorage
raise NotImplementedError
end
+ # Return the partial content in the byte +range+ of the file at the +key+.
+ def download_chunk(key, range)
+ raise NotImplementedError
+ end
+
# Delete the file at the +key+.
def delete(key)
raise NotImplementedError
diff --git a/activestorage/lib/active_storage/service/azure_storage_service.rb b/activestorage/lib/active_storage/service/azure_storage_service.rb
index 0a9eb7f23d..5682087469 100644
--- a/activestorage/lib/active_storage/service/azure_storage_service.rb
+++ b/activestorage/lib/active_storage/service/azure_storage_service.rb
@@ -41,6 +41,13 @@ module ActiveStorage
end
end
+ def download_chunk(key, range)
+ instrument :download_chunk, key: key, range: range do
+ _, io = blobs.get_blob(container, key, start_range: range.begin, end_range: range.exclude_end? ? range.end - 1 : range.end)
+ io.force_encoding(Encoding::BINARY)
+ end
+ end
+
def delete(key)
instrument :delete, key: key do
begin
diff --git a/activestorage/lib/active_storage/service/disk_service.rb b/activestorage/lib/active_storage/service/disk_service.rb
index d17eea9046..75b66081c3 100644
--- a/activestorage/lib/active_storage/service/disk_service.rb
+++ b/activestorage/lib/active_storage/service/disk_service.rb
@@ -9,10 +9,10 @@ module ActiveStorage
# Wraps a local disk path as an Active Storage service. See ActiveStorage::Service for the generic API
# documentation that applies to all services.
class Service::DiskService < Service
- attr_reader :root, :host
+ attr_reader :root
- def initialize(root:, host: "http://localhost:3000")
- @root, @host = root, host
+ def initialize(root:)
+ @root = root
end
def upload(key, io, checksum: nil)
@@ -38,6 +38,15 @@ module ActiveStorage
end
end
+ def download_chunk(key, range)
+ instrument :download_chunk, key: key, range: range do
+ File.open(path_for(key), "rb") do |file|
+ file.seek range.begin
+ file.read range.size
+ end
+ end
+ end
+
def delete(key)
instrument :delete, key: key do
begin
@@ -69,12 +78,11 @@ module ActiveStorage
verified_key_with_expiration = ActiveStorage.verifier.generate(key, expires_in: expires_in, purpose: :blob_key)
generated_url =
- Rails.application.routes.url_helpers.rails_disk_service_url(
+ url_helpers.rails_disk_service_path(
verified_key_with_expiration,
filename: filename,
disposition: content_disposition_with(type: disposition, filename: filename),
- content_type: content_type,
- host: host
+ content_type: content_type
)
payload[:url] = generated_url
@@ -96,7 +104,7 @@ module ActiveStorage
purpose: :blob_token }
)
- generated_url = Rails.application.routes.url_helpers.update_rails_disk_service_url(verified_token_with_expiration, host: host)
+ generated_url = url_helpers.update_rails_disk_service_path(verified_token_with_expiration)
payload[:url] = generated_url
@@ -121,11 +129,17 @@ module ActiveStorage
path_for(key).tap { |path| FileUtils.mkdir_p File.dirname(path) }
end
+
def ensure_integrity_of(key, checksum)
unless Digest::MD5.file(path_for(key)).base64digest == checksum
delete key
raise ActiveStorage::IntegrityError
end
end
+
+
+ def url_helpers
+ @url_helpers ||= Rails.application.routes.url_helpers
+ end
end
end
diff --git a/activestorage/lib/active_storage/service/gcs_service.rb b/activestorage/lib/active_storage/service/gcs_service.rb
index d163605754..369c33cbdb 100644
--- a/activestorage/lib/active_storage/service/gcs_service.rb
+++ b/activestorage/lib/active_storage/service/gcs_service.rb
@@ -3,7 +3,10 @@
gem "google-cloud-storage", "~> 1.8"
require "google/cloud/storage"
+require "net/http"
+
require "active_support/core_ext/object/to_query"
+require "active_storage/filename"
module ActiveStorage
# Wraps the Google Cloud Storage as an Active Storage service. See ActiveStorage::Service for the generic API
@@ -43,6 +46,16 @@ module ActiveStorage
end
end
+ def download_chunk(key, range)
+ instrument :download_chunk, key: key, range: range do
+ uri = URI(url(key, expires_in: 30.seconds, filename: ActiveStorage::Filename.new(""), content_type: "application/octet-stream", disposition: "inline"))
+
+ Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") do |client|
+ client.get(uri, "Range" => "bytes=#{range.begin}-#{range.exclude_end? ? range.end - 1 : range.end}").body
+ end
+ end
+ end
+
def delete(key)
instrument :delete, key: key do
begin
@@ -91,7 +104,7 @@ module ActiveStorage
end
def headers_for_direct_upload(key, checksum:, **)
- { "Content-Type" => "", "Content-MD5" => checksum }
+ { "Content-MD5" => checksum }
end
private
diff --git a/activestorage/lib/active_storage/service/mirror_service.rb b/activestorage/lib/active_storage/service/mirror_service.rb
index 7eca8ce7f4..6002ef5a00 100644
--- a/activestorage/lib/active_storage/service/mirror_service.rb
+++ b/activestorage/lib/active_storage/service/mirror_service.rb
@@ -9,7 +9,7 @@ module ActiveStorage
class Service::MirrorService < Service
attr_reader :primary, :mirrors
- delegate :download, :exist?, :url, to: :primary
+ delegate :download, :download_chunk, :exist?, :url, to: :primary
# Stitch together from named services.
def self.build(primary:, mirrors:, configurator:, **options) #:nodoc:
diff --git a/activestorage/lib/active_storage/service/s3_service.rb b/activestorage/lib/active_storage/service/s3_service.rb
index c95672f338..8ab7a44131 100644
--- a/activestorage/lib/active_storage/service/s3_service.rb
+++ b/activestorage/lib/active_storage/service/s3_service.rb
@@ -38,6 +38,12 @@ module ActiveStorage
end
end
+ def download_chunk(key, range)
+ instrument :download_chunk, key: key, range: range do
+ object_for(key).get(range: "bytes=#{range.begin}-#{range.exclude_end? ? range.end - 1 : range.end}").body.read.force_encoding(Encoding::BINARY)
+ end
+ end
+
def delete(key)
instrument :delete, key: key do
object_for(key).delete
diff --git a/activestorage/test/controllers/direct_uploads_controller_test.rb b/activestorage/test/controllers/direct_uploads_controller_test.rb
index 5d57e37688..88d85e12ab 100644
--- a/activestorage/test/controllers/direct_uploads_controller_test.rb
+++ b/activestorage/test/controllers/direct_uploads_controller_test.rb
@@ -62,7 +62,7 @@ if SERVICE_CONFIGURATIONS[:gcs]
assert_equal checksum, details["checksum"]
assert_equal "text/plain", details["content_type"]
assert_match %r{storage\.googleapis\.com/#{@config[:bucket]}}, details["direct_upload"]["url"]
- assert_equal({ "Content-Type" => "", "Content-MD5" => checksum }, details["direct_upload"]["headers"])
+ assert_equal({ "Content-MD5" => checksum }, details["direct_upload"]["headers"])
end
end
end
diff --git a/activestorage/test/models/attachments_test.rb b/activestorage/test/models/attachments_test.rb
index 25e0352eca..29b83eb841 100644
--- a/activestorage/test/models/attachments_test.rb
+++ b/activestorage/test/models/attachments_test.rb
@@ -80,6 +80,20 @@ class ActiveStorage::AttachmentsTest < ActiveSupport::TestCase
end
end
+ test "replace independent attached blob" do
+ @user.cover_photo.attach create_blob(filename: "funky.jpg")
+
+ perform_enqueued_jobs do
+ assert_difference -> { ActiveStorage::Blob.count }, +1 do
+ assert_no_difference -> { ActiveStorage::Attachment.count } do
+ @user.cover_photo.attach create_blob(filename: "town.jpg")
+ end
+ end
+ end
+
+ assert_equal "town.jpg", @user.cover_photo.filename.to_s
+ end
+
test "attach blob to new record" do
user = User.new(name: "Jason")
@@ -126,7 +140,6 @@ class ActiveStorage::AttachmentsTest < ActiveSupport::TestCase
blob = create_blob_before_direct_upload(filename: "racecar.jpg", content_type: "application/octet-stream", byte_size: 1124062, checksum: "7GjDDNEQb4mzMzsW+MS0JQ==")
ActiveStorage::Blob.service.upload(blob.key, file_fixture("racecar.jpg").open)
- stub_request(:get, %r{localhost:3000/rails/active_storage/disk/.*}).to_return(body: file_fixture("racecar.jpg"))
@user.avatar.attach(blob)
assert_equal "image/jpeg", @user.avatar.reload.content_type
@@ -204,13 +217,20 @@ class ActiveStorage::AttachmentsTest < ActiveSupport::TestCase
avatar_key = @user.avatar.key
perform_enqueued_jobs do
- @user.destroy
+ @user.reload.destroy
assert_nil ActiveStorage::Blob.find_by(key: avatar_key)
assert_not ActiveStorage::Blob.service.exist?(avatar_key)
end
end
+ test "delete attachment for independent blob when record is destroyed" do
+ @user.cover_photo.attach create_blob(filename: "funky.jpg")
+
+ @user.destroy
+ assert_not ActiveStorage::Attachment.exists?(record: @user, name: "cover_photo")
+ end
+
test "find with attached blob" do
records = %w[alice bob].map do |name|
User.create!(name: name).tap do |user|
@@ -382,7 +402,7 @@ class ActiveStorage::AttachmentsTest < ActiveSupport::TestCase
highlight_keys = @user.highlights.collect(&:key)
perform_enqueued_jobs do
- @user.destroy
+ @user.reload.destroy
assert_nil ActiveStorage::Blob.find_by(key: highlight_keys.first)
assert_not ActiveStorage::Blob.service.exist?(highlight_keys.first)
@@ -391,4 +411,39 @@ class ActiveStorage::AttachmentsTest < ActiveSupport::TestCase
assert_not ActiveStorage::Blob.service.exist?(highlight_keys.second)
end
end
+
+ test "delete attachments for independent blobs when the record is destroyed" do
+ @user.vlogs.attach create_blob(filename: "funky.mp4"), create_blob(filename: "wonky.mp4")
+
+ @user.destroy
+ assert_not ActiveStorage::Attachment.exists?(record: @user, name: "vlogs")
+ end
+
+ test "selectively purge one attached blob of many" do
+ first_blob = create_blob(filename: "funky.jpg")
+ second_blob = create_blob(filename: "wonky.jpg")
+ attachments = @user.highlights.attach(first_blob, second_blob)
+
+ assert_difference -> { ActiveStorage::Blob.count }, -1 do
+ @user.highlights.where(id: attachments.first.id).purge
+ end
+
+ assert_not ActiveStorage::Blob.exists?(key: first_blob.key)
+ assert ActiveStorage::Blob.exists?(key: second_blob.key)
+ end
+
+ test "selectively purge one attached blob of many later" do
+ first_blob = create_blob(filename: "funky.jpg")
+ second_blob = create_blob(filename: "wonky.jpg")
+ attachments = @user.highlights.attach(first_blob, second_blob)
+
+ perform_enqueued_jobs do
+ assert_difference -> { ActiveStorage::Blob.count }, -1 do
+ @user.highlights.where(id: attachments.first.id).purge_later
+ end
+ end
+
+ assert_not ActiveStorage::Blob.exists?(key: first_blob.key)
+ assert ActiveStorage::Blob.exists?(key: second_blob.key)
+ end
end
diff --git a/activestorage/test/models/blob_test.rb b/activestorage/test/models/blob_test.rb
index 779e47ffb6..202d0fb093 100644
--- a/activestorage/test/models/blob_test.rb
+++ b/activestorage/test/models/blob_test.rb
@@ -140,6 +140,6 @@ class ActiveStorage::BlobTest < ActiveSupport::TestCase
def expected_url_for(blob, disposition: :inline, filename: nil)
filename ||= blob.filename
query_string = { content_type: blob.content_type, disposition: "#{disposition}; #{filename.parameters}" }.to_param
- "http://localhost:3000/rails/active_storage/disk/#{ActiveStorage.verifier.generate(blob.key, expires_in: 5.minutes, purpose: :blob_key)}/#{filename}?#{query_string}"
+ "/rails/active_storage/disk/#{ActiveStorage.verifier.generate(blob.key, expires_in: 5.minutes, purpose: :blob_key)}/#{filename}?#{query_string}"
end
end
diff --git a/activestorage/test/previewer/pdf_previewer_test.rb b/activestorage/test/previewer/mupdf_previewer_test.rb
index fe32f39be4..6c2db6fcbf 100644
--- a/activestorage/test/previewer/pdf_previewer_test.rb
+++ b/activestorage/test/previewer/mupdf_previewer_test.rb
@@ -3,15 +3,15 @@
require "test_helper"
require "database/setup"
-require "active_storage/previewer/pdf_previewer"
+require "active_storage/previewer/mupdf_previewer"
-class ActiveStorage::Previewer::PDFPreviewerTest < ActiveSupport::TestCase
+class ActiveStorage::Previewer::MuPDFPreviewerTest < ActiveSupport::TestCase
setup do
@blob = create_file_blob(filename: "report.pdf", content_type: "application/pdf")
end
test "previewing a PDF document" do
- ActiveStorage::Previewer::PDFPreviewer.new(@blob).preview do |attachable|
+ ActiveStorage::Previewer::MuPDFPreviewer.new(@blob).preview do |attachable|
assert_equal "image/png", attachable[:content_type]
assert_equal "report.png", attachable[:filename]
diff --git a/activestorage/test/previewer/poppler_pdf_previewer_test.rb b/activestorage/test/previewer/poppler_pdf_previewer_test.rb
new file mode 100644
index 0000000000..2b41c8b642
--- /dev/null
+++ b/activestorage/test/previewer/poppler_pdf_previewer_test.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require "test_helper"
+require "database/setup"
+
+require "active_storage/previewer/poppler_pdf_previewer"
+
+class ActiveStorage::Previewer::PopplerPDFPreviewerTest < ActiveSupport::TestCase
+ setup do
+ @blob = create_file_blob(filename: "report.pdf", content_type: "application/pdf")
+ end
+
+ test "previewing a PDF document" do
+ ActiveStorage::Previewer::PopplerPDFPreviewer.new(@blob).preview do |attachable|
+ assert_equal "image/png", attachable[:content_type]
+ assert_equal "report.png", attachable[:filename]
+
+ image = MiniMagick::Image.read(attachable[:io])
+ assert_equal 612, image.width
+ assert_equal 792, image.height
+ end
+ end
+end
diff --git a/activestorage/test/service/configurator_test.rb b/activestorage/test/service/configurator_test.rb
index fe8a637ad0..1c9c5c3aa0 100644
--- a/activestorage/test/service/configurator_test.rb
+++ b/activestorage/test/service/configurator_test.rb
@@ -5,10 +5,8 @@ require "service/shared_service_tests"
class ActiveStorage::Service::ConfiguratorTest < ActiveSupport::TestCase
test "builds correct service instance based on service name" do
service = ActiveStorage::Service::Configurator.build(:foo, foo: { service: "Disk", root: "path" })
-
assert_instance_of ActiveStorage::Service::DiskService, service
assert_equal "path", service.root
- assert_equal "http://localhost:3000", service.host
end
test "raises error when passing non-existent service name" do
diff --git a/activestorage/test/service/shared_service_tests.rb b/activestorage/test/service/shared_service_tests.rb
index ce28c4393a..24debe7f47 100644
--- a/activestorage/test/service/shared_service_tests.rb
+++ b/activestorage/test/service/shared_service_tests.rb
@@ -60,6 +60,11 @@ module ActiveStorage::Service::SharedServiceTests
assert_equal [ FIXTURE_DATA ], chunks
end
+ test "downloading partially" do
+ assert_equal "\x10\x00\x00", @service.download_chunk(FIXTURE_KEY, 19..21)
+ assert_equal "\x10\x00\x00", @service.download_chunk(FIXTURE_KEY, 19...22)
+ end
+
test "existing" do
assert @service.exist?(FIXTURE_KEY)
assert_not @service.exist?(FIXTURE_KEY + "nonsense")
diff --git a/activestorage/test/test_helper.rb b/activestorage/test/test_helper.rb
index 98fa44a604..2a8e153303 100644
--- a/activestorage/test/test_helper.rb
+++ b/activestorage/test/test_helper.rb
@@ -7,7 +7,6 @@ require "bundler/setup"
require "active_support"
require "active_support/test_case"
require "active_support/testing/autorun"
-require "webmock/minitest"
require "mini_magick"
begin
@@ -42,8 +41,6 @@ ActiveStorage.verifier = ActiveSupport::MessageVerifier.new("Testing")
class ActiveSupport::TestCase
self.file_fixture_path = File.expand_path("fixtures/files", __dir__)
- setup { WebMock.allow_net_connect! }
-
private
def create_blob(data: "Hello world!", filename: "hello.txt", content_type: "text/plain")
ActiveStorage::Blob.create_after_upload! io: StringIO.new(data), filename: filename, content_type: content_type
@@ -68,5 +65,8 @@ ActiveRecord::Base.send :include, GlobalID::Identification
class User < ActiveRecord::Base
has_one_attached :avatar
+ has_one_attached :cover_photo, dependent: false
+
has_many_attached :highlights
+ has_many_attached :vlogs, dependent: false
end