Skip to content

Commit

Permalink
Add refresh_metadata plugin for re-extracting metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
janko committed Apr 2, 2017
1 parent f491195 commit fd39291
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 21 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## HEAD

* Add `refresh_metadata` plugin for re-extracting metadata from an uploaded file (@janko-m)

* Allow S3 storage to use parallelized multipart upload for files from FileSystem storage as well (@janko-m)

* Improve default multipart copy threshold for S3 storage (@janko-m)
Expand Down
29 changes: 29 additions & 0 deletions lib/shrine/plugins/refresh_metadata.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
class Shrine
module Plugins
# The `refresh_metadata` plugin allows you to re-extract metadata from an
# uploaded file.
#
# plugin :refresh_metadata
#
# It provides `UploadedFile#refresh_metadata!` method, which calls
# `Shrine#extract_metadata` with the uploaded file opened for reading,
# and updates the existing metadata hash with the results.
#
# uploaded_file.refresh_metadata!
# uploaded_file.metadata # re-extracted metadata
#
# For remote storages this will make an HTTP request to open the file for
# reading, but only the portion of the file needed for extracting each
# metadata value will be downloaded.
module RefreshMetadata
module FileMethods
def refresh_metadata!(context = {})
refreshed_metadata = open { uploader.extract_metadata(self, context) }
metadata.merge!(refreshed_metadata)
end
end
end

register_plugin(:refresh_metadata, RefreshMetadata)
end
end
23 changes: 10 additions & 13 deletions lib/shrine/plugins/restore_cached_data.rb
Original file line number Diff line number Diff line change
@@ -1,28 +1,25 @@
class Shrine
module Plugins
# The `restore_cached_data` plugin re-extracts cached file's metadata on
# assignment. This happens when an uploaded file is retained on validation
# errors, or when assigning direct uploaded files. In both cases you
# usually want to re-extract metadata on the server side, mainly to prevent
# The `restore_cached_data` plugin re-extracts metadata when assigning
# already cached files, i.e. when the attachment has been retained on
# validation errors or assigned from a direct upload. In both cases you may
# want to re-extract metadata on the server side, mainly to prevent
# tempering, but also in case of direct uploads to obtain metadata that
# couldn't be extracted on the client side.
#
# plugin :restore_cached_data
#
# This will give an opened `UploadedFile` for metadata extraction. For
# remote storages this will make an HTTP request, and since metadata is
# typically found in the beginning of the file, Shrine will download only
# the amount of bytes necessary for extracting the metadata.
# It uses the `refresh_metadata` plugin to re-extract metadata.
module RestoreCachedData
def self.load_dependencies(uploader, *)
uploader.plugin :refresh_metadata
end

module AttacherMethods
private

def assign_cached(cached_file)
uploaded_file(cached_file) do |file|
real_metadata = file.open { cache.extract_metadata(file, context) }
file.metadata.update(real_metadata)
end

uploaded_file(cached_file) { |file| file.refresh_metadata!(context) }
super(cached_file)
end
end
Expand Down
40 changes: 40 additions & 0 deletions test/plugin/refresh_metadata_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
require "test_helper"
require "shrine/plugins/refresh_metadata"

describe Shrine::Plugins::RefreshMetadata do
before do
@uploader = uploader { plugin :refresh_metadata }
end

it "re-extracts metadata" do
uploaded_file = @uploader.upload(fakeio("content", filename: "file.txt", content_type: "text/plain"))
uploaded_file.metadata.delete("size")
uploaded_file.refresh_metadata!
assert_equal 7, uploaded_file.metadata["size"]
assert_equal "file.txt", uploaded_file.metadata["filename"]
assert_equal "text/plain", uploaded_file.metadata["mime_type"]
end

it "keeps any custom metadata" do
uploaded_file = @uploader.upload(fakeio)
uploaded_file.metadata["custom"] = "custom"
uploaded_file.refresh_metadata!
assert_equal "custom", uploaded_file.metadata["custom"]
end

it "forwards a Shrine::UploadedFile" do
uploaded_file = @uploader.upload(fakeio)
@uploader.class.plugin :add_metadata
@uploader.class.add_metadata(:uploaded_file) { |io| io.is_a?(Shrine::UploadedFile) }
uploaded_file.refresh_metadata!
assert_equal true, uploaded_file.metadata["uploaded_file"]
end

it "accepts additional context and forwards it" do
uploaded_file = @uploader.upload(fakeio)
@uploader.class.plugin :add_metadata
@uploader.class.add_metadata(:context) { |io, context| context.to_s }
uploaded_file.refresh_metadata!(foo: "bar")
assert_equal '{:foo=>"bar"}', uploaded_file.metadata["context"]
end
end
17 changes: 9 additions & 8 deletions test/plugin/restore_cached_data_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,17 @@
assert_equal 1024, @attacher.get.metadata["size"]
end

it "keeps any custom metadata" do
cached_file = @attacher.cache!(fakeio("image"))
cached_file.metadata["custom"] = "custom"
@attacher.assign(cached_file.to_json)
assert_equal "custom", @attacher.get.metadata["custom"]
end

it "skips extracting if the file is not cached" do
stored_file = @attacher.store!(fakeio("image"))
stored_file = @attacher.store!(fakeio)
@attacher.cache.expects(:extract_metadata).never
@attacher.assign(stored_file.to_json)
end

it "forwards the context" do
cached_file = @attacher.cache!(fakeio)
@attacher.shrine_class.plugin :add_metadata
@attacher.shrine_class.add_metadata(:context) { |io, context| context.keys.to_s }
@attacher.assign(cached_file.to_json)
assert_equal "[:record, :name]", @attacher.get.metadata["context"]
end
end
4 changes: 4 additions & 0 deletions www/_data/plugins.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ Metadata:
name: determine_mime_type
path: DetermineMimeType.html
description: Allows you to determine the actual MIME type from file contents.
-
name: refresh_metadata
path: RefreshMetadata.html
description: Allows re-extracting metadata of an uploaded file.
-
name: restore_cached_data
path: RestoreCachedData.html
Expand Down

0 comments on commit fd39291

Please sign in to comment.