shrine 2.8.0 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of shrine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +681 -0
- data/README.md +73 -21
- data/doc/carrierwave.md +75 -20
- data/doc/creating_storages.md +15 -26
- data/doc/direct_s3.md +113 -31
- data/doc/multiple_files.md +4 -8
- data/doc/paperclip.md +98 -31
- data/doc/refile.md +4 -6
- data/doc/testing.md +24 -21
- data/lib/shrine.rb +32 -20
- data/lib/shrine/plugins/activerecord.rb +2 -0
- data/lib/shrine/plugins/add_metadata.rb +2 -0
- data/lib/shrine/plugins/background_helpers.rb +2 -0
- data/lib/shrine/plugins/backgrounding.rb +11 -4
- data/lib/shrine/plugins/backup.rb +2 -0
- data/lib/shrine/plugins/cached_attachment_data.rb +2 -0
- data/lib/shrine/plugins/copy.rb +2 -0
- data/lib/shrine/plugins/data_uri.rb +20 -12
- data/lib/shrine/plugins/default_storage.rb +2 -0
- data/lib/shrine/plugins/default_url.rb +2 -0
- data/lib/shrine/plugins/default_url_options.rb +2 -0
- data/lib/shrine/plugins/delete_promoted.rb +2 -0
- data/lib/shrine/plugins/delete_raw.rb +2 -0
- data/lib/shrine/plugins/determine_mime_type.rb +18 -2
- data/lib/shrine/plugins/direct_upload.rb +6 -6
- data/lib/shrine/plugins/download_endpoint.rb +2 -0
- data/lib/shrine/plugins/dynamic_storage.rb +2 -0
- data/lib/shrine/plugins/hooks.rb +2 -0
- data/lib/shrine/plugins/included.rb +2 -0
- data/lib/shrine/plugins/infer_extension.rb +131 -0
- data/lib/shrine/plugins/keep_files.rb +2 -0
- data/lib/shrine/plugins/logging.rb +6 -4
- data/lib/shrine/plugins/metadata_attributes.rb +2 -0
- data/lib/shrine/plugins/migration_helpers.rb +2 -0
- data/lib/shrine/plugins/module_include.rb +2 -0
- data/lib/shrine/plugins/moving.rb +2 -0
- data/lib/shrine/plugins/multi_delete.rb +4 -0
- data/lib/shrine/plugins/parallelize.rb +2 -0
- data/lib/shrine/plugins/parsed_json.rb +2 -0
- data/lib/shrine/plugins/presign_endpoint.rb +7 -7
- data/lib/shrine/plugins/pretty_location.rb +2 -0
- data/lib/shrine/plugins/processing.rb +2 -0
- data/lib/shrine/plugins/rack_file.rb +2 -0
- data/lib/shrine/plugins/rack_response.rb +2 -0
- data/lib/shrine/plugins/recache.rb +2 -0
- data/lib/shrine/plugins/refresh_metadata.rb +2 -0
- data/lib/shrine/plugins/remote_url.rb +12 -1
- data/lib/shrine/plugins/remove_attachment.rb +2 -0
- data/lib/shrine/plugins/remove_invalid.rb +2 -0
- data/lib/shrine/plugins/restore_cached_data.rb +2 -0
- data/lib/shrine/plugins/sequel.rb +2 -0
- data/lib/shrine/plugins/signature.rb +10 -8
- data/lib/shrine/plugins/store_dimensions.rb +5 -3
- data/lib/shrine/plugins/upload_endpoint.rb +7 -8
- data/lib/shrine/plugins/upload_options.rb +2 -0
- data/lib/shrine/plugins/validation_helpers.rb +2 -0
- data/lib/shrine/plugins/versions.rb +72 -31
- data/lib/shrine/storage/file_system.rb +11 -4
- data/lib/shrine/storage/linter.rb +5 -13
- data/lib/shrine/storage/s3.rb +16 -13
- data/lib/shrine/version.rb +3 -1
- data/shrine.gemspec +7 -6
- metadata +26 -10
data/lib/shrine/plugins/copy.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "base64"
|
2
4
|
require "strscan"
|
3
5
|
require "cgi"
|
@@ -32,6 +34,18 @@ class Shrine
|
|
32
34
|
# plugin :data_uri, error_message: "data URI was invalid"
|
33
35
|
# plugin :data_uri, error_message: ->(uri) { I18n.t("errors.data_uri_invalid") }
|
34
36
|
#
|
37
|
+
# ## File extension
|
38
|
+
#
|
39
|
+
# A data URI doesn't convey any information about the file extension, so
|
40
|
+
# when attaching from a data URI, the uploaded file location will be
|
41
|
+
# missing an extension. If you want the upload location to always have an
|
42
|
+
# extension, you can load the `infer_extension` plugin to infer it from the
|
43
|
+
# MIME type.
|
44
|
+
#
|
45
|
+
# plugin :infer_extension
|
46
|
+
#
|
47
|
+
# ## `Shrine.data_uri`
|
48
|
+
#
|
35
49
|
# If you just want to parse the data URI and create an IO object from it,
|
36
50
|
# you can do that with `Shrine.data_uri`. If the data URI cannot be parsed,
|
37
51
|
# a `Shrine::Plugins::DataUri::ParseError` will be raised.
|
@@ -50,19 +64,11 @@ class Shrine
|
|
50
64
|
# io.size #=> 11
|
51
65
|
# io.read #=> "raw content"
|
52
66
|
#
|
53
|
-
#
|
54
|
-
# have a filename), but you can generate a filename based on the content
|
55
|
-
# type of the data URI:
|
56
|
-
#
|
57
|
-
# require "mime/types"
|
67
|
+
# ## `UploadedFile#data_uri` and `UploadedFile#base64`
|
58
68
|
#
|
59
|
-
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
# end
|
63
|
-
#
|
64
|
-
# This plugin also adds a `UploadedFile#data_uri` method (and `#base64`),
|
65
|
-
# which returns a base64-encoded data URI of any UploadedFile:
|
69
|
+
# This plugin also adds UploadedFile#data_uri method, which returns a
|
70
|
+
# base64-encoded data URI of the file content, and UploadedFile#base64,
|
71
|
+
# which simply returns the file content base64-encoded.
|
66
72
|
#
|
67
73
|
# uploaded_file.data_uri #=> "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA"
|
68
74
|
# uploaded_file.base64 #=> "iVBORw0KGgoAAAANSUhEUgAAAAUA"
|
@@ -81,6 +87,8 @@ class Shrine
|
|
81
87
|
def self.configure(uploader, opts = {})
|
82
88
|
uploader.opts[:data_uri_filename] = opts.fetch(:filename, uploader.opts[:data_uri_filename])
|
83
89
|
uploader.opts[:data_uri_error_message] = opts.fetch(:error_message, uploader.opts[:data_uri_error_message])
|
90
|
+
|
91
|
+
Shrine.deprecation("The :filename option is deprecated for the data_uri plugin, and will be removed in Shrine 3. Use the infer_extension plugin instead.") if opts[:filename]
|
84
92
|
end
|
85
93
|
|
86
94
|
module ClassMethods
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class Shrine
|
2
4
|
module Plugins
|
3
5
|
# The `determine_mime_type` plugin allows you to determine and store the
|
@@ -28,6 +30,13 @@ class Shrine
|
|
28
30
|
# Unlike ruby-filemagic, mimemagic is a pure-ruby solution, so it will
|
29
31
|
# work across all Ruby implementations.
|
30
32
|
#
|
33
|
+
# :marcel
|
34
|
+
# : Uses the [marcel] gem to determine the MIME type from file contents.
|
35
|
+
# Marcel is Basecamp's wrapper around mimemagic, it adds priority logic
|
36
|
+
# (preferring magic over name when given both), some extra type
|
37
|
+
# definitions, and common type subclasses (including Keynote, Pages,
|
38
|
+
# etc).
|
39
|
+
#
|
31
40
|
# :mime_types
|
32
41
|
# : Uses the [mime-types] gem to determine the MIME type from the file
|
33
42
|
# extension. Note that unlike other solutions, this analyzer is not
|
@@ -49,7 +58,7 @@ class Shrine
|
|
49
58
|
# correctly determine MIME type of .css, .js, .json, .csv, .xml, or similar
|
50
59
|
# text-based files, you can combine `file` and `mime_types` analyzers:
|
51
60
|
#
|
52
|
-
# plugin :determine_mime_type, analyzer: ->(io, analyzers) do
|
61
|
+
# plugin :determine_mime_type, analyzer: -> (io, analyzers) do
|
53
62
|
# mime_type = analyzers[:file].call(io)
|
54
63
|
# mime_type = analyzers[:mime_types].call(io) if mime_type == "text/plain"
|
55
64
|
# mime_type
|
@@ -69,6 +78,7 @@ class Shrine
|
|
69
78
|
# [Windows equivalent]: http://gnuwin32.sourceforge.net/packages/file.htm
|
70
79
|
# [ruby-filemagic]: https://github.com/blackwinter/ruby-filemagic
|
71
80
|
# [mimemagic]: https://github.com/minad/mimemagic
|
81
|
+
# [marcel]: https://github.com/basecamp/marcel
|
72
82
|
# [mime-types]: https://github.com/mime-types/ruby-mime-types
|
73
83
|
# [mini_mime]: https://github.com/discourse/mini_mime
|
74
84
|
module DetermineMimeType
|
@@ -125,7 +135,7 @@ class Shrine
|
|
125
135
|
end
|
126
136
|
|
127
137
|
class MimeTypeAnalyzer
|
128
|
-
SUPPORTED_TOOLS = [:file, :filemagic, :mimemagic, :mime_types, :mini_mime]
|
138
|
+
SUPPORTED_TOOLS = [:file, :filemagic, :mimemagic, :marcel, :mime_types, :mini_mime]
|
129
139
|
MAGIC_NUMBER = 256 * 1024
|
130
140
|
|
131
141
|
def initialize(tool)
|
@@ -181,6 +191,12 @@ class Shrine
|
|
181
191
|
mime.type if mime
|
182
192
|
end
|
183
193
|
|
194
|
+
def extract_with_marcel(io)
|
195
|
+
require "marcel"
|
196
|
+
|
197
|
+
Marcel::MimeType.for(io)
|
198
|
+
end
|
199
|
+
|
184
200
|
def extract_with_mime_types(io)
|
185
201
|
require "mime/types"
|
186
202
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "roda"
|
2
4
|
require "json"
|
3
5
|
|
@@ -28,9 +30,9 @@ class Shrine
|
|
28
30
|
# end
|
29
31
|
#
|
30
32
|
# Now your application will get `POST /images/cache/upload` and `GET
|
31
|
-
# /images/cache/presign` routes.
|
32
|
-
#
|
33
|
-
#
|
33
|
+
# /images/cache/presign` routes. On the client side it is recommended to
|
34
|
+
# use [Uppy] for uploading files to the app or directly to the 3rd-party
|
35
|
+
# service.
|
34
36
|
#
|
35
37
|
# ## Uploads
|
36
38
|
#
|
@@ -155,9 +157,7 @@ class Shrine
|
|
155
157
|
# configuration.
|
156
158
|
#
|
157
159
|
# [Roda]: https://github.com/jeremyevans/roda
|
158
|
-
# [
|
159
|
-
# [Dropzone]: https://github.com/enyo/dropzone
|
160
|
-
# [FineUploader]: https://github.com/FineUploader/fine-uploader
|
160
|
+
# [Uppy]: https://uppy.io
|
161
161
|
# [Roda request]: http://roda.jeremyevans.net/rdoc/classes/Roda/RodaPlugins/Base/RequestMethods.html
|
162
162
|
# [Direct Uploads to S3]: http://shrinerb.com/rdoc/files/doc/direct_s3_md.html
|
163
163
|
module DirectUpload
|
data/lib/shrine/plugins/hooks.rb
CHANGED
@@ -0,0 +1,131 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Shrine
|
4
|
+
module Plugins
|
5
|
+
# The `infer_extension` plugin allows deducing the appropriate file
|
6
|
+
# extension for the upload location based on the MIME type of the file.
|
7
|
+
# This is useful when using `data_uri` and `remote_url` plugins, where the
|
8
|
+
# file extension is not or might not be known.
|
9
|
+
#
|
10
|
+
# plugin :infer_extension
|
11
|
+
#
|
12
|
+
# The upload location will gain the inferred extension only if couldn't be
|
13
|
+
# determined from the filename. By default `Rack::Mime` will be used for
|
14
|
+
# inferring the extension, but you can also choose a different inferrer:
|
15
|
+
#
|
16
|
+
# plugin :infer_extension, inferrer: :mime_types
|
17
|
+
#
|
18
|
+
# The following inferrers are accepted:
|
19
|
+
#
|
20
|
+
# :rack_mime
|
21
|
+
# : (Default). Uses the `Rack::Mime` module to infer the appropriate
|
22
|
+
# extension from MIME type.
|
23
|
+
#
|
24
|
+
# :mime_types
|
25
|
+
# : Uses the [mime-types] gem to infer the appropriate extension from MIME
|
26
|
+
# type.
|
27
|
+
#
|
28
|
+
# :mini_mime
|
29
|
+
# : Uses the [mini_mime] gem to infer the appropriate extension from MIME
|
30
|
+
# type.
|
31
|
+
#
|
32
|
+
# You can also define your own inferrer, with the possibility to call the
|
33
|
+
# built-in inferrers:
|
34
|
+
#
|
35
|
+
# plugin :infer_extension, inferrer: -> (mime_type, inferrers) do
|
36
|
+
# # don't add extension if the file is a text file
|
37
|
+
# inferrrs[:rack_mime].call(mime_type) unless mime_type == "text/plain"
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# You can also use methods for inferring extension directly:
|
41
|
+
#
|
42
|
+
# Shrine.infer_extension("image/jpeg")
|
43
|
+
# # => ".jpeg"
|
44
|
+
#
|
45
|
+
# Shrine.extension_inferrers[:mime_types].call("image/jpeg")
|
46
|
+
# # => ".jpeg"
|
47
|
+
#
|
48
|
+
# [mime-types]: https://github.com/mime-types/ruby-mime-types
|
49
|
+
# [mini_mime]: https://github.com/discourse/mini_mime
|
50
|
+
module InferExtension
|
51
|
+
def self.configure(uploader, opts = {})
|
52
|
+
uploader.opts[:extension_inferrer] = opts.fetch(:inferrer, uploader.opts.fetch(:infer_extension_inferrer, :rack_mime))
|
53
|
+
end
|
54
|
+
|
55
|
+
module ClassMethods
|
56
|
+
def infer_extension(mime_type)
|
57
|
+
inferrer = opts[:extension_inferrer]
|
58
|
+
inferrer = extension_inferrers[inferrer] if inferrer.is_a?(Symbol)
|
59
|
+
args = [mime_type, extension_inferrers].take(inferrer.arity.abs)
|
60
|
+
|
61
|
+
inferrer.call(*args)
|
62
|
+
end
|
63
|
+
|
64
|
+
def extension_inferrers
|
65
|
+
@extension_inferrers ||= ExtensionInferrer::SUPPORTED_TOOLS.inject({}) do |hash, tool|
|
66
|
+
hash.merge!(tool => ExtensionInferrer.new(tool).method(:call))
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
module InstanceMethods
|
72
|
+
def generate_location(io, context = {})
|
73
|
+
mime_type = (context[:metadata] || {})["mime_type"]
|
74
|
+
|
75
|
+
location = super
|
76
|
+
location += infer_extension(mime_type) if File.extname(location).empty?
|
77
|
+
location
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def infer_extension(mime_type)
|
83
|
+
self.class.infer_extension(mime_type).to_s
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class ExtensionInferrer
|
88
|
+
SUPPORTED_TOOLS = [:rack_mime, :mime_types, :mini_mime]
|
89
|
+
|
90
|
+
def initialize(tool)
|
91
|
+
raise ArgumentError, "unsupported extension inferrer tool: #{tool}" unless SUPPORTED_TOOLS.include?(tool)
|
92
|
+
|
93
|
+
@tool = tool
|
94
|
+
end
|
95
|
+
|
96
|
+
def call(mime_type)
|
97
|
+
return nil if mime_type.nil?
|
98
|
+
|
99
|
+
extension = send(:"infer_with_#{@tool}", mime_type)
|
100
|
+
extension = ".#{extension}" unless extension.nil? || extension.start_with?(".")
|
101
|
+
extension
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def infer_with_rack_mime(mime_type)
|
107
|
+
require "rack/mime"
|
108
|
+
|
109
|
+
mime_types = Rack::Mime::MIME_TYPES
|
110
|
+
mime_types.key(mime_type)
|
111
|
+
end
|
112
|
+
|
113
|
+
def infer_with_mime_types(mime_type)
|
114
|
+
require "mime/types"
|
115
|
+
|
116
|
+
mime_type = MIME::Types[mime_type].first
|
117
|
+
mime_type.preferred_extension if mime_type
|
118
|
+
end
|
119
|
+
|
120
|
+
def infer_with_mini_mime(mime_type)
|
121
|
+
require "mini_mime"
|
122
|
+
|
123
|
+
info = MiniMime.lookup_by_content_type(mime_type)
|
124
|
+
info.extension if info
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
register_plugin(:infer_extension, InferExtension)
|
130
|
+
end
|
131
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "logger"
|
2
4
|
require "json"
|
3
5
|
require "time"
|
@@ -74,7 +76,7 @@ class Shrine
|
|
74
76
|
# doesn't output timestamps if on Heroku.
|
75
77
|
def pretty_formatter
|
76
78
|
proc do |severity, time, program_name, message|
|
77
|
-
output = "#{Process.pid}: #{message}\n"
|
79
|
+
output = "#{Process.pid}: #{message}\n".dup
|
78
80
|
output.prepend "#{time.utc.iso8601(3)} " unless ENV["DYNO"]
|
79
81
|
output
|
80
82
|
end
|
@@ -123,11 +125,11 @@ class Shrine
|
|
123
125
|
def _log_message_human(data)
|
124
126
|
components = []
|
125
127
|
components << "#{data[:action].upcase}"
|
126
|
-
components
|
128
|
+
components[-1] += "[#{data[:phase]}]" if data[:phase]
|
127
129
|
components << "#{data[:uploader]}"
|
128
|
-
components
|
130
|
+
components[-1] += "[:#{data[:attachment]}]" if data[:attachment]
|
129
131
|
components << "#{data[:record_class]}" if data[:record_class]
|
130
|
-
components
|
132
|
+
components[-1] += "[#{data[:record_id]}]" if data[:record_id]
|
131
133
|
components << "#{Array(data[:files]).join("-")} #{"file#{"s" if Array(data[:files]).any?{|n| n > 1}}"}"
|
132
134
|
components << "(#{data[:duration]}s)"
|
133
135
|
components.join(" ")
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "rack"
|
2
4
|
|
3
5
|
require "json"
|
@@ -6,10 +8,10 @@ class Shrine
|
|
6
8
|
module Plugins
|
7
9
|
# The `presign_endpoint` plugin provides a Rack endpoint which generates
|
8
10
|
# the URL, fields, and headers that can be used to upload files directly to
|
9
|
-
# a storage service.
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
11
|
+
# a storage service. On the client side it's recommended to use [Uppy] for
|
12
|
+
# asynchronous uploads. Storage services that support direct uploads
|
13
|
+
# include [Amazon S3], [Google Cloud Storage], [Microsoft Azure Storage]
|
14
|
+
# and more.
|
13
15
|
#
|
14
16
|
# plugin :presign_endpoint
|
15
17
|
#
|
@@ -112,9 +114,7 @@ class Shrine
|
|
112
114
|
#
|
113
115
|
# Shrine.presign_endpoint(:cache, presign_location: "${filename}")
|
114
116
|
#
|
115
|
-
# [
|
116
|
-
# [Dropzone]: https://github.com/enyo/dropzone
|
117
|
-
# [jQuery-File-Upload]: https://github.com/blueimp/jQuery-File-Upload
|
117
|
+
# [Uppy]: https://uppy.io
|
118
118
|
# [Amazon S3]: https://aws.amazon.com/s3/
|
119
119
|
# [Google Cloud Storage]: https://cloud.google.com/storage/
|
120
120
|
# [Microsoft Azure Storage]: https://azure.microsoft.com/en-us/services/storage/
|