diff options
Diffstat (limited to 'activestorage/test')
17 files changed, 320 insertions, 58 deletions
diff --git a/activestorage/test/analyzer/image_analyzer_test.rb b/activestorage/test/analyzer/image_analyzer_test.rb index f8a38f001a..55bb5e7280 100644 --- a/activestorage/test/analyzer/image_analyzer_test.rb +++ b/activestorage/test/analyzer/image_analyzer_test.rb @@ -29,9 +29,4 @@ class ActiveStorage::Analyzer::ImageAnalyzerTest < ActiveSupport::TestCase assert_equal 792, metadata[:width] assert_equal 584, metadata[:height] end - - private - def extract_metadata_from(blob) - blob.tap(&:analyze).metadata - end end diff --git a/activestorage/test/analyzer/video_analyzer_test.rb b/activestorage/test/analyzer/video_analyzer_test.rb index 2612006551..d30f49315a 100644 --- a/activestorage/test/analyzer/video_analyzer_test.rb +++ b/activestorage/test/analyzer/video_analyzer_test.rb @@ -51,9 +51,4 @@ class ActiveStorage::Analyzer::VideoAnalyzerTest < ActiveSupport::TestCase metadata = extract_metadata_from(blob) assert_equal({ "analyzed" => true, "identified" => true }, metadata) end - - private - def extract_metadata_from(blob) - blob.tap(&:analyze).metadata - end end diff --git a/activestorage/test/controllers/disk_controller_test.rb b/activestorage/test/controllers/disk_controller_test.rb index 940dbf5918..9af7c83bdf 100644 --- a/activestorage/test/controllers/disk_controller_test.rb +++ b/activestorage/test/controllers/disk_controller_test.rb @@ -8,16 +8,18 @@ class ActiveStorage::DiskControllerTest < ActionDispatch::IntegrationTest blob = create_blob get blob.service_url - assert_equal "inline; filename=\"hello.txt\"; filename*=UTF-8''hello.txt", @response.headers["Content-Disposition"] - assert_equal "text/plain", @response.headers["Content-Type"] + assert_equal "inline; filename=\"hello.txt\"; filename*=UTF-8''hello.txt", response.headers["Content-Disposition"] + assert_equal "text/plain", response.headers["Content-Type"] + assert_equal "Hello world!", response.body end test "showing blob as attachment" do blob = create_blob get blob.service_url(disposition: :attachment) - assert_equal "attachment; filename=\"hello.txt\"; filename*=UTF-8''hello.txt", @response.headers["Content-Disposition"] - assert_equal "text/plain", @response.headers["Content-Type"] + assert_equal "attachment; filename=\"hello.txt\"; filename*=UTF-8''hello.txt", response.headers["Content-Disposition"] + assert_equal "text/plain", response.headers["Content-Type"] + assert_equal "Hello world!", response.body end diff --git a/activestorage/test/models/attached_test.rb b/activestorage/test/models/attached_test.rb new file mode 100644 index 0000000000..b10d2bebe3 --- /dev/null +++ b/activestorage/test/models/attached_test.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require "test_helper" +require "database/setup" + +class ActiveStorage::AttachmentsTest < ActiveSupport::TestCase + setup do + @user = User.create!(name: "Josh") + end + + teardown { ActiveStorage::Blob.all.each(&:purge) } + + test "overriding has_one_attached methods works" do + # attach blob before messing with getter, which breaks `#attach` + @user.avatar.attach create_blob(filename: "funky.jpg") + + # inherited only + assert_equal "funky.jpg", @user.avatar.filename.to_s + + begin + User.class_eval do + def avatar + super.filename.to_s.reverse + end + end + + # override with super + assert_equal "funky.jpg".reverse, @user.avatar + ensure + User.send(:remove_method, :avatar) + end + end + + test "overriding has_many_attached methods works" do + # attach blobs before messing with getter, which breaks `#attach` + @user.highlights.attach create_blob(filename: "funky.jpg"), create_blob(filename: "wonky.jpg") + + # inherited only + assert_equal "funky.jpg", @user.highlights.first.filename.to_s + assert_equal "wonky.jpg", @user.highlights.second.filename.to_s + + begin + User.class_eval do + def highlights + super.reverse + end + end + + # override with super + assert_equal "wonky.jpg", @user.highlights.first.filename.to_s + assert_equal "funky.jpg", @user.highlights.second.filename.to_s + ensure + User.send(:remove_method, :highlights) + end + end +end diff --git a/activestorage/test/models/blob_test.rb b/activestorage/test/models/blob_test.rb index fead17d33a..40b30acd3e 100644 --- a/activestorage/test/models/blob_test.rb +++ b/activestorage/test/models/blob_test.rb @@ -43,6 +43,16 @@ class ActiveStorage::BlobTest < ActiveSupport::TestCase assert_equal "text/plain", blob.content_type end + test "create after upload extracts content_type from io when no content_type given and identify: false" do + blob = create_blob content_type: nil, identify: false + assert_equal "text/plain", blob.content_type + end + + test "create after upload uses content_type when identify: false" do + blob = create_blob data: "Article,dates,analysis\n1, 2, 3", filename: "table.csv", content_type: "text/csv", identify: false + assert_equal "text/csv", blob.content_type + end + test "image?" do blob = create_file_blob filename: "racecar.jpg" assert_predicate blob, :image? @@ -62,7 +72,7 @@ class ActiveStorage::BlobTest < ActiveSupport::TestCase end test "download yields chunks" do - blob = create_blob data: "a" * 75.kilobytes + blob = create_blob data: "a" * 5.0625.megabytes chunks = [] blob.download do |chunk| @@ -70,8 +80,42 @@ class ActiveStorage::BlobTest < ActiveSupport::TestCase end assert_equal 2, chunks.size - assert_equal "a" * 64.kilobytes, chunks.first - assert_equal "a" * 11.kilobytes, chunks.second + assert_equal "a" * 5.megabytes, chunks.first + assert_equal "a" * 64.kilobytes, chunks.second + end + + test "open with integrity" do + create_file_blob(filename: "racecar.jpg").tap do |blob| + blob.open do |file| + assert file.binmode? + assert_equal 0, file.pos + assert File.basename(file.path).starts_with?("ActiveStorage-#{blob.id}-") + assert file.path.ends_with?(".jpg") + assert_equal file_fixture("racecar.jpg").binread, file.read, "Expected downloaded file to match fixture file" + end + end + end + + test "open without integrity" do + create_blob(data: "Hello, world!").tap do |blob| + blob.update! checksum: Digest::MD5.base64digest("Goodbye, world!") + + assert_raises ActiveStorage::IntegrityError do + blob.open { |file| flunk "Expected integrity check to fail" } + end + end + end + + test "open in a custom tempdir" do + tempdir = Dir.mktmpdir + + create_file_blob(filename: "racecar.jpg").open(tempdir: tempdir) do |file| + assert file.binmode? + assert_equal 0, file.pos + assert_match(/\.jpg\z/, file.path) + assert file.path.starts_with?(tempdir) + assert_equal file_fixture("racecar.jpg").binread, file.read, "Expected downloaded file to match fixture file" + end end test "urls expiring in 5 minutes" do @@ -107,16 +151,16 @@ class ActiveStorage::BlobTest < ActiveSupport::TestCase test "urls allow for custom options" do blob = create_blob(filename: "original.txt") - options = [ + arguments = [ blob.key, - expires_in: blob.service.url_expires_in, + expires_in: ActiveStorage.service_urls_expire_in, disposition: :inline, content_type: blob.content_type, filename: blob.filename, thumb_size: "300x300", thumb_mode: "crop" ] - assert_called_with(blob.service, :url, options) do + assert_called_with(blob.service, :url, arguments) do blob.service_url(thumb_size: "300x300", thumb_mode: "crop") end end diff --git a/activestorage/test/models/presence_validation_test.rb b/activestorage/test/models/presence_validation_test.rb new file mode 100644 index 0000000000..aa804506dd --- /dev/null +++ b/activestorage/test/models/presence_validation_test.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require "test_helper" +require "database/setup" + +class ActiveStorage::PresenceValidationTest < ActiveSupport::TestCase + class Admin < User; end + + teardown do + Admin.clear_validators! + end + + test "validates_presence_of has_one_attached" do + Admin.validates_presence_of :avatar + a = Admin.new + assert_predicate a, :invalid? + + a.avatar.attach create_blob(filename: "funky.jpg") + assert_predicate a, :valid? + end + + test "validates_presence_of has_many_attached" do + Admin.validates_presence_of :highlights + a = Admin.new + assert_predicate a, :invalid? + + a.highlights.attach create_blob(filename: "funky.jpg") + assert_predicate a, :valid? + end +end diff --git a/activestorage/test/models/preview_test.rb b/activestorage/test/models/preview_test.rb index 55fdc228c8..e7ae399fb7 100644 --- a/activestorage/test/models/preview_test.rb +++ b/activestorage/test/models/preview_test.rb @@ -22,8 +22,8 @@ class ActiveStorage::PreviewTest < ActiveSupport::TestCase preview = blob.preview(resize: "640x280").processed assert_predicate preview.image, :attached? - assert_equal "video.png", preview.image.filename.to_s - assert_equal "image/png", preview.image.content_type + assert_equal "video.jpg", preview.image.filename.to_s + assert_equal "image/jpeg", preview.image.content_type image = read_image(preview.image) assert_equal 640, image.width diff --git a/activestorage/test/models/reflection_test.rb b/activestorage/test/models/reflection_test.rb new file mode 100644 index 0000000000..da866ca996 --- /dev/null +++ b/activestorage/test/models/reflection_test.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require "test_helper" + +class ActiveStorage::ReflectionTest < ActiveSupport::TestCase + test "allows reflecting for all attachment" do + expected_classes = + User.reflect_on_all_attachments.all? do |reflection| + reflection.is_a?(ActiveStorage::Reflection::HasOneAttachedReflection) || + reflection.is_a?(ActiveStorage::Reflection::HasManyAttachedReflection) + end + + assert expected_classes + end + + test "allows reflecting on a singular has_one_attached attachment" do + reflection = User.reflect_on_attachment(:avatar) + + assert_equal :avatar, reflection.name + assert_equal :has_one_attached, reflection.macro + end + + test "allows reflecting on a singular has_many_attached attachment" do + reflection = User.reflect_on_attachment(:highlights) + + assert_equal :highlights, reflection.name + assert_equal :has_many_attached, reflection.macro + end +end diff --git a/activestorage/test/models/variant_test.rb b/activestorage/test/models/variant_test.rb index 0f3ada25c0..6577f1cd9f 100644 --- a/activestorage/test/models/variant_test.rb +++ b/activestorage/test/models/variant_test.rb @@ -25,13 +25,91 @@ class ActiveStorage::VariantTest < ActiveSupport::TestCase assert_match(/Gray/, image.colorspace) end - test "center-weighted crop of JPEG blob" do + test "monochrome with default variant_processor" do + begin + ActiveStorage.variant_processor = nil + + blob = create_file_blob(filename: "racecar.jpg") + variant = blob.variant(monochrome: true).processed + image = read_image(variant) + assert_match(/Gray/, image.colorspace) + ensure + ActiveStorage.variant_processor = :mini_magick + end + end + + test "disabled variation of JPEG blob" do blob = create_file_blob(filename: "racecar.jpg") - variant = blob.variant(combine_options: { - gravity: "center", - resize: "100x100^", - crop: "100x100+0+0", - }).processed + variant = blob.variant(resize: "100x100", monochrome: false).processed + assert_match(/racecar\.jpg/, variant.service_url) + + image = read_image(variant) + assert_equal 100, image.width + assert_equal 67, image.height + assert_match(/RGB/, image.colorspace) + end + + test "disabled variation of JPEG blob with :combine_options" do + blob = create_file_blob(filename: "racecar.jpg") + variant = ActiveSupport::Deprecation.silence do + blob.variant(combine_options: { + resize: "100x100", + monochrome: false + }).processed + end + assert_match(/racecar\.jpg/, variant.service_url) + + image = read_image(variant) + assert_equal 100, image.width + assert_equal 67, image.height + assert_match(/RGB/, image.colorspace) + end + + test "disabled variation using :combine_options" do + begin + ActiveStorage.variant_processor = nil + blob = create_file_blob(filename: "racecar.jpg") + variant = ActiveSupport::Deprecation.silence do + blob.variant(combine_options: { + crop: "100x100+0+0", + monochrome: false + }).processed + end + assert_match(/racecar\.jpg/, variant.service_url) + + image = read_image(variant) + assert_equal 100, image.width + assert_equal 100, image.height + assert_match(/RGB/, image.colorspace) + ensure + ActiveStorage.variant_processor = :mini_magick + end + end + + test "center-weighted crop of JPEG blob using :combine_options" do + begin + ActiveStorage.variant_processor = nil + blob = create_file_blob(filename: "racecar.jpg") + variant = ActiveSupport::Deprecation.silence do + blob.variant(combine_options: { + gravity: "center", + resize: "100x100^", + crop: "100x100+0+0", + }).processed + end + assert_match(/racecar\.jpg/, variant.service_url) + + image = read_image(variant) + assert_equal 100, image.width + assert_equal 100, image.height + ensure + ActiveStorage.variant_processor = :mini_magick + end + end + + test "center-weighted crop of JPEG blob using :resize_to_fill" do + blob = create_file_blob(filename: "racecar.jpg") + variant = blob.variant(resize_to_fill: [100, 100]).processed assert_match(/racecar\.jpg/, variant.service_url) image = read_image(variant) @@ -80,4 +158,20 @@ class ActiveStorage::VariantTest < ActiveSupport::TestCase variant = blob.variant(font: "a" * 10_000).processed assert_operator variant.service_url.length, :<, 525 end + + test "works for vips processor" do + begin + ActiveStorage.variant_processor = :vips + blob = create_file_blob(filename: "racecar.jpg") + variant = blob.variant(thumbnail_image: 100).processed + + image = read_image(variant) + assert_equal 100, image.width + assert_equal 67, image.height + rescue LoadError + # libvips not installed + ensure + ActiveStorage.variant_processor = :mini_magick + end + end end diff --git a/activestorage/test/previewer/video_previewer_test.rb b/activestorage/test/previewer/video_previewer_test.rb index dba9b0d7e2..9dc350205b 100644 --- a/activestorage/test/previewer/video_previewer_test.rb +++ b/activestorage/test/previewer/video_previewer_test.rb @@ -12,12 +12,13 @@ class ActiveStorage::Previewer::VideoPreviewerTest < ActiveSupport::TestCase test "previewing an MP4 video" do ActiveStorage::Previewer::VideoPreviewer.new(@blob).preview do |attachable| - assert_equal "image/png", attachable[:content_type] - assert_equal "video.png", attachable[:filename] + assert_equal "image/jpeg", attachable[:content_type] + assert_equal "video.jpg", attachable[:filename] image = MiniMagick::Image.read(attachable[:io]) assert_equal 640, image.width assert_equal 480, image.height + assert_equal "image/jpeg", image.mime_type end end end diff --git a/activestorage/test/service/azure_storage_service_test.rb b/activestorage/test/service/azure_storage_service_test.rb index be31bbe858..76920850d1 100644 --- a/activestorage/test/service/azure_storage_service_test.rb +++ b/activestorage/test/service/azure_storage_service_test.rb @@ -10,7 +10,7 @@ if SERVICE_CONFIGURATIONS[:azure] include ActiveStorage::Service::SharedServiceTests test "signed URL generation" do - url = @service.url(FIXTURE_KEY, expires_in: 5.minutes, + url = @service.url(@key, expires_in: 5.minutes, disposition: :inline, filename: ActiveStorage::Filename.new("avatar.png"), content_type: "image/png") assert_match(/(\S+)&rscd=inline%3B\+filename%3D%22avatar\.png%22%3B\+filename\*%3DUTF-8%27%27avatar\.png&rsct=image%2Fpng/, url) diff --git a/activestorage/test/service/disk_service_test.rb b/activestorage/test/service/disk_service_test.rb index d7142de458..a0218bff1c 100644 --- a/activestorage/test/service/disk_service_test.rb +++ b/activestorage/test/service/disk_service_test.rb @@ -9,6 +9,10 @@ class ActiveStorage::Service::DiskServiceTest < ActiveSupport::TestCase test "url generation" do assert_match(/^https:\/\/example.com\/rails\/active_storage\/disk\/.*\/avatar\.png\?content_type=image%2Fpng&disposition=inline/, - @service.url(FIXTURE_KEY, expires_in: 5.minutes, disposition: :inline, filename: ActiveStorage::Filename.new("avatar.png"), content_type: "image/png")) + @service.url(@key, expires_in: 5.minutes, disposition: :inline, filename: ActiveStorage::Filename.new("avatar.png"), content_type: "image/png")) + end + + test "headers_for_direct_upload generation" do + assert_equal({ "Content-Type" => "application/json" }, @service.headers_for_direct_upload(@key, content_type: "application/json")) end end diff --git a/activestorage/test/service/gcs_service_test.rb b/activestorage/test/service/gcs_service_test.rb index fc2d9d0fa7..2ba2f8b346 100644 --- a/activestorage/test/service/gcs_service_test.rb +++ b/activestorage/test/service/gcs_service_test.rb @@ -33,7 +33,7 @@ if SERVICE_CONFIGURATIONS[:gcs] test "signed URL generation" do assert_match(/storage\.googleapis\.com\/.*response-content-disposition=inline.*test\.txt.*response-content-type=text%2Fplain/, - @service.url(FIXTURE_KEY, expires_in: 2.minutes, disposition: :inline, filename: ActiveStorage::Filename.new("test.txt"), content_type: "text/plain")) + @service.url(@key, expires_in: 2.minutes, disposition: :inline, filename: ActiveStorage::Filename.new("test.txt"), content_type: "text/plain")) end test "signed URL response headers" do @@ -44,7 +44,7 @@ if SERVICE_CONFIGURATIONS[:gcs] url = @service.url(key, expires_in: 2.minutes, disposition: :inline, filename: ActiveStorage::Filename.new("test.txt"), content_type: "text/plain") response = Net::HTTP.get_response(URI(url)) - assert_equal "text/plain", response.header["Content-Type"] + assert_equal "text/plain", response.content_type ensure @service.delete key end diff --git a/activestorage/test/service/mirror_service_test.rb b/activestorage/test/service/mirror_service_test.rb index 87306644c5..bb502dde60 100644 --- a/activestorage/test/service/mirror_service_test.rb +++ b/activestorage/test/service/mirror_service_test.rb @@ -47,11 +47,11 @@ class ActiveStorage::Service::MirrorServiceTest < ActiveSupport::TestCase end test "deleting from all services" do - @service.delete FIXTURE_KEY + @service.delete @key - assert_not SERVICE.primary.exist?(FIXTURE_KEY) + assert_not SERVICE.primary.exist?(@key) SERVICE.mirrors.each do |mirror| - assert_not mirror.exist?(FIXTURE_KEY) + assert_not mirror.exist?(@key) end end @@ -59,8 +59,8 @@ class ActiveStorage::Service::MirrorServiceTest < ActiveSupport::TestCase filename = ActiveStorage::Filename.new("test.txt") freeze_time do - assert_equal @service.primary.url(FIXTURE_KEY, expires_in: 2.minutes, disposition: :inline, filename: filename, content_type: "text/plain"), - @service.url(FIXTURE_KEY, expires_in: 2.minutes, disposition: :inline, filename: filename, content_type: "text/plain") + assert_equal @service.primary.url(@key, expires_in: 2.minutes, disposition: :inline, filename: filename, content_type: "text/plain"), + @service.url(@key, expires_in: 2.minutes, disposition: :inline, filename: filename, content_type: "text/plain") end end end diff --git a/activestorage/test/service/s3_service_test.rb b/activestorage/test/service/s3_service_test.rb index 7833e51122..4bfcda017f 100644 --- a/activestorage/test/service/s3_service_test.rb +++ b/activestorage/test/service/s3_service_test.rb @@ -32,7 +32,7 @@ if SERVICE_CONFIGURATIONS[:s3] end test "signed URL generation" do - url = @service.url(FIXTURE_KEY, expires_in: 5.minutes, + url = @service.url(@key, expires_in: 5.minutes, disposition: :inline, filename: ActiveStorage::Filename.new("avatar.png"), content_type: "image/png") assert_match(/s3(-[-a-z0-9]+)?\.(\S+)?amazonaws.com.*response-content-disposition=inline.*avatar\.png.*response-content-type=image%2Fpng/, url) diff --git a/activestorage/test/service/shared_service_tests.rb b/activestorage/test/service/shared_service_tests.rb index 24debe7f47..30cfca4e36 100644 --- a/activestorage/test/service/shared_service_tests.rb +++ b/activestorage/test/service/shared_service_tests.rb @@ -6,17 +6,17 @@ require "active_support/core_ext/securerandom" module ActiveStorage::Service::SharedServiceTests extend ActiveSupport::Concern - FIXTURE_KEY = SecureRandom.base58(24) FIXTURE_DATA = "\211PNG\r\n\032\n\000\000\000\rIHDR\000\000\000\020\000\000\000\020\001\003\000\000\000%=m\"\000\000\000\006PLTE\000\000\000\377\377\377\245\331\237\335\000\000\0003IDATx\234c\370\377\237\341\377_\206\377\237\031\016\2603\334?\314p\1772\303\315\315\f7\215\031\356\024\203\320\275\317\f\367\201R\314\f\017\300\350\377\177\000Q\206\027(\316]\233P\000\000\000\000IEND\256B`\202".dup.force_encoding(Encoding::BINARY) included do setup do + @key = SecureRandom.base58(24) @service = self.class.const_get(:SERVICE) - @service.upload FIXTURE_KEY, StringIO.new(FIXTURE_DATA) + @service.upload @key, StringIO.new(FIXTURE_DATA) end teardown do - @service.delete FIXTURE_KEY + @service.delete @key end test "uploading with integrity" do @@ -47,32 +47,40 @@ module ActiveStorage::Service::SharedServiceTests end test "downloading" do - assert_equal FIXTURE_DATA, @service.download(FIXTURE_KEY) + assert_equal FIXTURE_DATA, @service.download(@key) end test "downloading in chunks" do - chunks = [] + key = SecureRandom.base58(24) + expected_chunks = [ "a" * 5.megabytes, "b" ] + actual_chunks = [] - @service.download(FIXTURE_KEY) do |chunk| - chunks << chunk - end + begin + @service.upload key, StringIO.new(expected_chunks.join) + + @service.download key do |chunk| + actual_chunks << chunk + end - assert_equal [ FIXTURE_DATA ], chunks + assert_equal expected_chunks, actual_chunks, "Downloaded chunks did not match uploaded data" + ensure + @service.delete key + end 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) + assert_equal "\x10\x00\x00", @service.download_chunk(@key, 19..21) + assert_equal "\x10\x00\x00", @service.download_chunk(@key, 19...22) end test "existing" do - assert @service.exist?(FIXTURE_KEY) - assert_not @service.exist?(FIXTURE_KEY + "nonsense") + assert @service.exist?(@key) + assert_not @service.exist?(@key + "nonsense") end test "deleting" do - @service.delete FIXTURE_KEY - assert_not @service.exist?(FIXTURE_KEY) + @service.delete @key + assert_not @service.exist?(@key) end test "deleting nonexistent key" do diff --git a/activestorage/test/test_helper.rb b/activestorage/test/test_helper.rb index 028874f374..573a8e0b0b 100644 --- a/activestorage/test/test_helper.rb +++ b/activestorage/test/test_helper.rb @@ -7,7 +7,7 @@ require "bundler/setup" require "active_support" require "active_support/test_case" require "active_support/testing/autorun" -require "mini_magick" +require "image_processing/mini_magick" begin require "byebug" @@ -50,8 +50,8 @@ class ActiveSupport::TestCase end 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 + def create_blob(data: "Hello world!", filename: "hello.txt", content_type: "text/plain", identify: true) + ActiveStorage::Blob.create_after_upload! io: StringIO.new(data), filename: filename, content_type: content_type, identify: identify end def create_file_blob(filename: "racecar.jpg", content_type: "image/jpeg", metadata: nil) @@ -75,6 +75,10 @@ class ActiveSupport::TestCase def read_image(blob_or_variant) MiniMagick::Image.open blob_or_variant.service.send(:path_for, blob_or_variant.key) end + + def extract_metadata_from(blob) + blob.tap(&:analyze).metadata + end end require "global_id" |