shrine 2.19.3 → 3.6.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +523 -41
- data/LICENSE.txt +1 -1
- data/README.md +83 -979
- data/doc/advantages.md +231 -204
- data/doc/attacher.md +304 -153
- data/doc/carrierwave.md +297 -226
- data/doc/changing_derivatives.md +308 -0
- data/doc/changing_location.md +103 -21
- data/doc/changing_storage.md +110 -0
- data/doc/creating_persistence_plugins.md +132 -0
- data/doc/creating_plugins.md +43 -23
- data/doc/creating_storages.md +19 -5
- data/doc/design.md +147 -97
- data/doc/direct_s3.md +38 -28
- data/doc/external/articles.md +63 -0
- data/doc/external/extensions.md +53 -0
- data/doc/external/misc.md +32 -0
- data/doc/getting_started.md +1156 -0
- data/doc/metadata.md +190 -109
- data/doc/multiple_files.md +93 -30
- data/doc/paperclip.md +384 -262
- data/doc/plugins/activerecord.md +177 -46
- data/doc/plugins/add_metadata.md +139 -38
- data/doc/plugins/atomic_helpers.md +217 -0
- data/doc/plugins/backgrounding.md +156 -98
- data/doc/plugins/cached_attachment_data.md +7 -5
- data/doc/plugins/column.md +121 -0
- data/doc/plugins/data_uri.md +23 -22
- data/doc/plugins/default_storage.md +36 -10
- data/doc/plugins/default_url.md +30 -13
- data/doc/plugins/delete_raw.md +4 -2
- data/doc/plugins/derivation_endpoint.md +186 -101
- data/doc/plugins/derivatives.md +839 -0
- data/doc/plugins/determine_mime_type.md +4 -2
- data/doc/plugins/download_endpoint.md +64 -8
- data/doc/plugins/dynamic_storage.md +5 -3
- data/doc/plugins/entity.md +263 -0
- data/doc/plugins/form_assign.md +55 -0
- data/doc/plugins/included.md +31 -8
- data/doc/plugins/infer_extension.md +21 -10
- data/doc/plugins/instrumentation.md +38 -16
- data/doc/plugins/keep_files.md +16 -17
- data/doc/plugins/metadata_attributes.md +42 -13
- data/doc/plugins/mirroring.md +118 -0
- data/doc/plugins/model.md +210 -0
- data/doc/plugins/module_include.md +4 -2
- data/doc/plugins/multi_cache.md +24 -0
- data/doc/plugins/persistence.md +101 -0
- data/doc/plugins/presign_endpoint.md +9 -4
- data/doc/plugins/pretty_location.md +16 -3
- data/doc/plugins/processing.md +4 -2
- data/doc/plugins/rack_file.md +8 -2
- data/doc/plugins/rack_response.md +6 -2
- data/doc/plugins/recache.md +4 -2
- data/doc/plugins/refresh_metadata.md +49 -9
- data/doc/plugins/remote_url.md +84 -47
- data/doc/plugins/remove_attachment.md +27 -6
- data/doc/plugins/remove_invalid.md +21 -6
- data/doc/plugins/restore_cached_data.md +11 -3
- data/doc/plugins/sequel.md +159 -35
- data/doc/plugins/signature.md +16 -5
- data/doc/plugins/store_dimensions.md +14 -2
- data/doc/plugins/tempfile.md +4 -2
- data/doc/plugins/type_predicates.md +96 -0
- data/doc/plugins/upload_endpoint.md +13 -13
- data/doc/plugins/upload_options.md +6 -4
- data/doc/plugins/{default_url_options.md → url_options.md} +9 -7
- data/doc/plugins/validation.md +97 -0
- data/doc/plugins/validation_helpers.md +16 -13
- data/doc/plugins/versions.md +15 -19
- data/doc/processing.md +438 -221
- data/doc/refile.md +188 -170
- data/doc/release_notes/1.0.0.md +4 -0
- data/doc/release_notes/1.1.0.md +6 -2
- data/doc/release_notes/1.2.0.md +4 -0
- data/doc/release_notes/1.3.0.md +4 -0
- data/doc/release_notes/1.4.0.md +4 -0
- data/doc/release_notes/1.4.1.md +4 -0
- data/doc/release_notes/1.4.2.md +4 -0
- data/doc/release_notes/2.0.0.md +4 -0
- data/doc/release_notes/2.0.1.md +4 -0
- data/doc/release_notes/2.1.0.md +5 -1
- data/doc/release_notes/2.1.1.md +4 -0
- data/doc/release_notes/2.10.0.md +4 -0
- data/doc/release_notes/2.10.1.md +4 -0
- data/doc/release_notes/2.11.0.md +4 -0
- data/doc/release_notes/2.12.0.md +4 -0
- data/doc/release_notes/2.13.0.md +4 -0
- data/doc/release_notes/2.14.0.md +5 -1
- data/doc/release_notes/2.15.0.md +11 -7
- data/doc/release_notes/2.16.0.md +4 -0
- data/doc/release_notes/2.17.0.md +4 -0
- data/doc/release_notes/2.18.0.md +4 -0
- data/doc/release_notes/2.19.0.md +6 -3
- data/doc/release_notes/2.2.0.md +4 -0
- data/doc/release_notes/2.3.0.md +4 -0
- data/doc/release_notes/2.3.1.md +4 -0
- data/doc/release_notes/2.4.0.md +4 -0
- data/doc/release_notes/2.4.1.md +4 -0
- data/doc/release_notes/2.5.0.md +4 -0
- data/doc/release_notes/2.6.0.md +4 -0
- data/doc/release_notes/2.6.1.md +4 -0
- data/doc/release_notes/2.7.0.md +4 -0
- data/doc/release_notes/2.8.0.md +4 -0
- data/doc/release_notes/2.9.0.md +4 -0
- data/doc/release_notes/3.0.0.md +981 -0
- data/doc/release_notes/3.0.1.md +22 -0
- data/doc/release_notes/3.1.0.md +73 -0
- data/doc/release_notes/3.2.0.md +96 -0
- data/doc/release_notes/3.2.1.md +31 -0
- data/doc/release_notes/3.2.2.md +14 -0
- data/doc/release_notes/3.3.0.md +105 -0
- data/doc/release_notes/3.4.0.md +35 -0
- data/doc/release_notes/3.5.0.md +63 -0
- data/doc/release_notes/3.6.0.md +23 -0
- data/doc/retrieving_uploads.md +5 -2
- data/doc/securing_uploads.md +60 -37
- data/doc/storage/file_system.md +20 -3
- data/doc/storage/memory.md +19 -0
- data/doc/storage/s3.md +122 -78
- data/doc/testing.md +141 -133
- data/doc/upgrading_to_3.md +708 -0
- data/doc/validation.md +54 -90
- data/lib/shrine/attacher.rb +292 -169
- data/lib/shrine/attachment.rb +13 -46
- data/lib/shrine/plugins/_persistence.rb +93 -0
- data/lib/shrine/plugins/activerecord.rb +77 -34
- data/lib/shrine/plugins/add_metadata.rb +25 -17
- data/lib/shrine/plugins/atomic_helpers.rb +119 -0
- data/lib/shrine/plugins/backgrounding.rb +77 -113
- data/lib/shrine/plugins/cached_attachment_data.rb +6 -15
- data/lib/shrine/plugins/column.rb +102 -0
- data/lib/shrine/plugins/data_uri.rb +38 -36
- data/lib/shrine/plugins/default_storage.rb +45 -15
- data/lib/shrine/plugins/default_url.rb +12 -24
- data/lib/shrine/plugins/default_url_options.rb +3 -30
- data/lib/shrine/plugins/delete_raw.rb +10 -16
- data/lib/shrine/plugins/derivation_endpoint.rb +130 -171
- data/lib/shrine/plugins/derivatives.rb +645 -0
- data/lib/shrine/plugins/determine_mime_type.rb +9 -21
- data/lib/shrine/plugins/download_endpoint.rb +118 -133
- data/lib/shrine/plugins/dynamic_storage.rb +5 -11
- data/lib/shrine/plugins/entity.rb +158 -0
- data/lib/shrine/plugins/form_assign.rb +108 -0
- data/lib/shrine/plugins/included.rb +6 -6
- data/lib/shrine/plugins/infer_extension.rb +17 -20
- data/lib/shrine/plugins/instrumentation.rb +59 -43
- data/lib/shrine/plugins/keep_files.rb +3 -15
- data/lib/shrine/plugins/metadata_attributes.rb +28 -19
- data/lib/shrine/plugins/mirroring.rb +142 -0
- data/lib/shrine/plugins/model.rb +160 -0
- data/lib/shrine/plugins/module_include.rb +3 -3
- data/lib/shrine/plugins/multi_cache.rb +27 -0
- data/lib/shrine/plugins/presign_endpoint.rb +27 -28
- data/lib/shrine/plugins/pretty_location.rb +15 -9
- data/lib/shrine/plugins/processing.rb +22 -9
- data/lib/shrine/plugins/rack_file.rb +2 -42
- data/lib/shrine/plugins/rack_response.rb +21 -10
- data/lib/shrine/plugins/recache.rb +6 -5
- data/lib/shrine/plugins/refresh_metadata.rb +13 -11
- data/lib/shrine/plugins/remote_url.rb +49 -49
- data/lib/shrine/plugins/remove_attachment.rb +12 -6
- data/lib/shrine/plugins/remove_invalid.rb +19 -8
- data/lib/shrine/plugins/restore_cached_data.rb +13 -7
- data/lib/shrine/plugins/sequel.rb +86 -36
- data/lib/shrine/plugins/signature.rb +10 -16
- data/lib/shrine/plugins/store_dimensions.rb +35 -40
- data/lib/shrine/plugins/tempfile.rb +1 -3
- data/lib/shrine/plugins/type_predicates.rb +113 -0
- data/lib/shrine/plugins/upload_endpoint.rb +28 -24
- data/lib/shrine/plugins/upload_options.rb +14 -15
- data/lib/shrine/plugins/url_options.rb +31 -0
- data/lib/shrine/plugins/validation.rb +80 -0
- data/lib/shrine/plugins/validation_helpers.rb +35 -58
- data/lib/shrine/plugins/versions.rb +107 -87
- data/lib/shrine/plugins.rb +22 -0
- data/lib/shrine/storage/file_system.rb +46 -64
- data/lib/shrine/storage/linter.rb +42 -7
- data/lib/shrine/storage/memory.rb +49 -0
- data/lib/shrine/storage/s3.rb +173 -160
- data/lib/shrine/uploaded_file.rb +32 -32
- data/lib/shrine/version.rb +3 -3
- data/lib/shrine.rb +87 -150
- data/shrine.gemspec +11 -12
- metadata +92 -82
- data/doc/migrating_storage.md +0 -76
- data/doc/plugins/backup.md +0 -31
- data/doc/plugins/copy.md +0 -24
- data/doc/plugins/delete_promoted.md +0 -12
- data/doc/plugins/direct_upload.md +0 -172
- data/doc/plugins/hooks.md +0 -58
- data/doc/plugins/logging.md +0 -42
- data/doc/plugins/migration_helpers.md +0 -60
- data/doc/plugins/moving.md +0 -19
- data/doc/plugins/multi_delete.md +0 -20
- data/doc/plugins/parallelize.md +0 -16
- data/doc/plugins/parsed_json.md +0 -23
- data/doc/regenerating_versions.md +0 -143
- data/lib/shrine/plugins/background_helpers.rb +0 -5
- data/lib/shrine/plugins/backup.rb +0 -90
- data/lib/shrine/plugins/copy.rb +0 -50
- data/lib/shrine/plugins/delete_promoted.rb +0 -20
- data/lib/shrine/plugins/direct_upload.rb +0 -217
- data/lib/shrine/plugins/hooks.rb +0 -90
- data/lib/shrine/plugins/logging.rb +0 -142
- data/lib/shrine/plugins/migration_helpers.rb +0 -70
- data/lib/shrine/plugins/moving.rb +0 -57
- data/lib/shrine/plugins/multi_delete.rb +0 -32
- data/lib/shrine/plugins/parallelize.rb +0 -78
- data/lib/shrine/plugins/parsed_json.rb +0 -29
|
@@ -5,40 +5,35 @@ require "content_disposition"
|
|
|
5
5
|
|
|
6
6
|
require "openssl"
|
|
7
7
|
require "tempfile"
|
|
8
|
+
require "pathname"
|
|
8
9
|
|
|
9
10
|
class Shrine
|
|
10
11
|
module Plugins
|
|
11
|
-
# Documentation
|
|
12
|
-
#
|
|
13
|
-
# [doc/plugins/derivation_endpoint.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/derivation_endpoint.md
|
|
12
|
+
# Documentation can be found on https://shrinerb.com/docs/plugins/derivation_endpoint
|
|
14
13
|
module DerivationEndpoint
|
|
15
14
|
LOG_SUBSCRIBER = -> (event) do
|
|
16
15
|
Shrine.logger.info "Derivation (#{event.duration}ms) – #{{
|
|
17
|
-
name: event[:name
|
|
18
|
-
args: event[:args
|
|
16
|
+
name: event[:derivation].name,
|
|
17
|
+
args: event[:derivation].args,
|
|
19
18
|
uploader: event[:uploader],
|
|
20
19
|
}.inspect}"
|
|
21
20
|
end
|
|
22
21
|
|
|
23
|
-
def self.load_dependencies(uploader,
|
|
22
|
+
def self.load_dependencies(uploader, **)
|
|
24
23
|
uploader.plugin :rack_response
|
|
25
24
|
uploader.plugin :_urlsafe_serialization
|
|
26
25
|
end
|
|
27
26
|
|
|
28
|
-
def self.configure(uploader,
|
|
29
|
-
uploader.opts[:
|
|
30
|
-
uploader.opts[:
|
|
31
|
-
|
|
32
|
-
uploader.opts[:derivation_endpoint_derivations] ||= {}
|
|
27
|
+
def self.configure(uploader, log_subscriber: LOG_SUBSCRIBER, **opts)
|
|
28
|
+
uploader.opts[:derivation_endpoint] ||= { options: {}, derivations: {} }
|
|
29
|
+
uploader.opts[:derivation_endpoint][:options].merge!(opts)
|
|
33
30
|
|
|
34
|
-
|
|
35
|
-
fail Error, "must provide :secret_key option to derivation_endpoint plugin"
|
|
31
|
+
if !uploader.opts[:derivation_endpoint][:options][:secret_key] && !uploader.opts[:derivation_endpoint][:options][:signer]
|
|
32
|
+
fail Error, "must provide :secret_key option to derivation_endpoint plugin when no custom signer is set"
|
|
36
33
|
end
|
|
37
34
|
|
|
38
35
|
# instrumentation plugin integration
|
|
39
|
-
if uploader.respond_to?(:subscribe)
|
|
40
|
-
uploader.subscribe(:derivation, &uploader.opts[:derivation_endpoint_options][:log_subscriber])
|
|
41
|
-
end
|
|
36
|
+
uploader.subscribe(:derivation, &log_subscriber) if uploader.respond_to?(:subscribe)
|
|
42
37
|
end
|
|
43
38
|
|
|
44
39
|
module ClassMethods
|
|
@@ -76,15 +71,15 @@ class Shrine
|
|
|
76
71
|
# Registers a derivation block, which is called when the corresponding
|
|
77
72
|
# derivation URL is requested.
|
|
78
73
|
def derivation(name, &block)
|
|
79
|
-
derivations[name] = block
|
|
74
|
+
derivations[name.to_sym] = block
|
|
80
75
|
end
|
|
81
76
|
|
|
82
77
|
def derivations
|
|
83
|
-
opts[:
|
|
78
|
+
opts[:derivation_endpoint][:derivations]
|
|
84
79
|
end
|
|
85
80
|
|
|
86
81
|
def derivation_options
|
|
87
|
-
opts[:
|
|
82
|
+
opts[:derivation_endpoint][:options]
|
|
88
83
|
end
|
|
89
84
|
end
|
|
90
85
|
|
|
@@ -127,7 +122,7 @@ class Shrine
|
|
|
127
122
|
attr_reader :name, :args, :source, :options
|
|
128
123
|
|
|
129
124
|
def initialize(name:, args:, source:, options:)
|
|
130
|
-
@name = name
|
|
125
|
+
@name = name.to_sym
|
|
131
126
|
@args = args
|
|
132
127
|
@source = source
|
|
133
128
|
@options = options
|
|
@@ -163,16 +158,22 @@ class Shrine
|
|
|
163
158
|
|
|
164
159
|
# Uploads the derivation result to a dedicated destination on the specified
|
|
165
160
|
# Shrine storage.
|
|
166
|
-
def upload(file = nil)
|
|
167
|
-
Derivation::Upload.new(self).call(file)
|
|
161
|
+
def upload(file = nil, **options)
|
|
162
|
+
Derivation::Upload.new(self).call(file, **options)
|
|
168
163
|
end
|
|
169
164
|
|
|
170
|
-
# Returns a Shrine::UploadedFile object pointing to the uploaded
|
|
171
|
-
#
|
|
165
|
+
# Returns a Shrine::UploadedFile object pointing to the uploaded derivative
|
|
166
|
+
# if it exists.
|
|
172
167
|
def retrieve
|
|
173
168
|
Derivation::Retrieve.new(self).call
|
|
174
169
|
end
|
|
175
170
|
|
|
171
|
+
# Returns opened Shrine::UploadedFile object pointing to the uploaded
|
|
172
|
+
# derivative if it exists.
|
|
173
|
+
def opened
|
|
174
|
+
Derivation::Opened.new(self).call
|
|
175
|
+
end
|
|
176
|
+
|
|
176
177
|
# Deletes the derivation result from the storage.
|
|
177
178
|
def delete
|
|
178
179
|
Derivation::Delete.new(self).call
|
|
@@ -189,15 +190,14 @@ class Shrine
|
|
|
189
190
|
option :cache_control, default: -> { default_cache_control }
|
|
190
191
|
option :disposition, default: -> { "inline" }
|
|
191
192
|
option :download, default: -> { true }
|
|
192
|
-
option :download_errors, default: -> { [] }
|
|
193
193
|
option :download_options, default: -> { {} }
|
|
194
194
|
option :expires_in
|
|
195
195
|
option :filename, default: -> { default_filename }
|
|
196
196
|
option :host
|
|
197
|
-
option :include_uploaded_file, default: -> { false }
|
|
198
197
|
option :metadata, default: -> { [] }
|
|
199
198
|
option :prefix
|
|
200
199
|
option :secret_key
|
|
200
|
+
option :signer
|
|
201
201
|
option :type
|
|
202
202
|
option :upload, default: -> { false }
|
|
203
203
|
option :upload_location, default: -> { default_upload_location }, result: -> (o) { upload_location(o) }
|
|
@@ -217,7 +217,7 @@ class Shrine
|
|
|
217
217
|
option_definition = self.class.options.fetch(name)
|
|
218
218
|
|
|
219
219
|
value = options.fetch(name) { shrine_class.derivation_options[name] }
|
|
220
|
-
value = instance_exec(&value) if value.is_a?(Proc)
|
|
220
|
+
value = instance_exec(&value) if value.is_a?(Proc) && value.arity == 0
|
|
221
221
|
|
|
222
222
|
if value.nil?
|
|
223
223
|
default = option_definition[:default]
|
|
@@ -260,7 +260,7 @@ class Shrine
|
|
|
260
260
|
|
|
261
261
|
# The source uploaded file storage is the default derivative storage.
|
|
262
262
|
def default_upload_storage
|
|
263
|
-
source.storage_key
|
|
263
|
+
source.storage_key
|
|
264
264
|
end
|
|
265
265
|
|
|
266
266
|
# Allows caching for 1 year or until the URL expires.
|
|
@@ -301,20 +301,36 @@ class Shrine
|
|
|
301
301
|
end
|
|
302
302
|
|
|
303
303
|
class Derivation::Url < Derivation::Command
|
|
304
|
-
delegate :name, :args, :source, :secret_key
|
|
304
|
+
delegate :name, :args, :source, :secret_key, :signer
|
|
305
305
|
|
|
306
306
|
def call(host: nil, prefix: nil, **options)
|
|
307
|
-
[host, *prefix
|
|
307
|
+
base_url = [host, *prefix].join("/")
|
|
308
|
+
path = path_identifier(metadata: options.delete(:metadata))
|
|
309
|
+
|
|
310
|
+
if signer
|
|
311
|
+
url = [base_url, path].join("/")
|
|
312
|
+
signer.call(url, **options)
|
|
313
|
+
else
|
|
314
|
+
signed_part = signed_url("#{path}?#{query(**options)}")
|
|
315
|
+
[base_url, signed_part].join("/")
|
|
316
|
+
end
|
|
308
317
|
end
|
|
309
318
|
|
|
310
319
|
private
|
|
311
320
|
|
|
312
|
-
def
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
321
|
+
def path_identifier(metadata: [])
|
|
322
|
+
[
|
|
323
|
+
name,
|
|
324
|
+
*args,
|
|
325
|
+
source.urlsafe_dump(metadata: metadata)
|
|
326
|
+
].map{|component| Rack::Utils.escape_path(component.to_s)}.join('/')
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def query(expires_in: nil,
|
|
330
|
+
type: nil,
|
|
331
|
+
filename: nil,
|
|
332
|
+
disposition: nil,
|
|
333
|
+
version: nil)
|
|
318
334
|
|
|
319
335
|
params = {}
|
|
320
336
|
params[:expires_at] = (Time.now + expires_in).to_i if expires_in
|
|
@@ -323,23 +339,7 @@ class Shrine
|
|
|
323
339
|
params[:filename] = filename if filename
|
|
324
340
|
params[:disposition] = disposition if disposition
|
|
325
341
|
|
|
326
|
-
|
|
327
|
-
source_component = source.urlsafe_dump(metadata: metadata)
|
|
328
|
-
|
|
329
|
-
# generate plain URL
|
|
330
|
-
url = plain_url(name, *args, source_component, params)
|
|
331
|
-
|
|
332
|
-
# generate signed URL
|
|
333
|
-
signed_url(url)
|
|
334
|
-
end
|
|
335
|
-
|
|
336
|
-
def plain_url(*components, params)
|
|
337
|
-
# When using Rack < 2, Rack::Utils#escape_path will escape '/'.
|
|
338
|
-
# Escape each component and then join them together.
|
|
339
|
-
path = components.map{|component| Rack::Utils.escape_path(component.to_s)}.join('/')
|
|
340
|
-
query = Rack::Utils.build_query(params)
|
|
341
|
-
|
|
342
|
-
"#{path}?#{query}"
|
|
342
|
+
Rack::Utils.build_query(params)
|
|
343
343
|
end
|
|
344
344
|
|
|
345
345
|
def signed_url(url)
|
|
@@ -365,7 +365,9 @@ class Shrine
|
|
|
365
365
|
handle_request(request)
|
|
366
366
|
end
|
|
367
367
|
|
|
368
|
-
headers[
|
|
368
|
+
headers = Rack::Headers[headers] if Rack.release >= "3"
|
|
369
|
+
headers["Content-Length"] ||= body.respond_to?(:bytesize) ? body.bytesize.to_s :
|
|
370
|
+
body.map(&:bytesize).inject(0, :+).to_s
|
|
369
371
|
|
|
370
372
|
[status, headers, body]
|
|
371
373
|
end
|
|
@@ -380,7 +382,7 @@ class Shrine
|
|
|
380
382
|
# Returns "404 Not Found" if derivation block is not defined, or if source
|
|
381
383
|
# file was not found on the storage.
|
|
382
384
|
def handle_request(request)
|
|
383
|
-
verify_signature!(request)
|
|
385
|
+
verify_signature!(request) if secret_key
|
|
384
386
|
check_expiry!(request)
|
|
385
387
|
|
|
386
388
|
name, *args, serialized_file = request.path_info.split("/")[1..-1]
|
|
@@ -393,16 +395,17 @@ class Shrine
|
|
|
393
395
|
options[:type] = request.params["type"] if request.params["type"]
|
|
394
396
|
options[:disposition] = request.params["disposition"] if request.params["disposition"]
|
|
395
397
|
options[:filename] = request.params["filename"] if request.params["filename"]
|
|
398
|
+
options[:version] = request.params["version"] if request.params["version"]
|
|
396
399
|
options[:expires_in] = expires_in(request) if request.params["expires_at"]
|
|
397
400
|
|
|
398
401
|
derivation = uploaded_file.derivation(name, *args, **options)
|
|
399
402
|
|
|
400
403
|
begin
|
|
401
404
|
status, headers, body = derivation.response(request.env)
|
|
402
|
-
rescue Derivation::NotFound
|
|
403
|
-
error!(404, "Unknown derivation \"#{name}\"")
|
|
404
405
|
rescue Derivation::SourceNotFound
|
|
405
406
|
error!(404, "Source file not found")
|
|
407
|
+
rescue Derivation::NotFound
|
|
408
|
+
error!(404, "Unknown derivation \"#{name}\"")
|
|
406
409
|
end
|
|
407
410
|
|
|
408
411
|
# tell clients to cache the derivation result if it was successful
|
|
@@ -476,23 +479,22 @@ class Shrine
|
|
|
476
479
|
file_response(derivative, env)
|
|
477
480
|
end
|
|
478
481
|
|
|
479
|
-
# Generates a Rack response triple from a local file
|
|
480
|
-
#
|
|
481
|
-
#
|
|
482
|
+
# Generates a Rack response triple from a local file. Fills in
|
|
483
|
+
# `Content-Type` and `Content-Disposition` response headers from derivation
|
|
484
|
+
# options and file extension of the derivation result.
|
|
482
485
|
def file_response(file, env)
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
status = response[0]
|
|
486
|
+
status, headers, body = rack_file_response(file.path, env)
|
|
486
487
|
|
|
488
|
+
headers = Rack::Headers[headers] if Rack.release >= "3"
|
|
487
489
|
headers = {
|
|
488
|
-
"Content-Type" => type ||
|
|
489
|
-
"Content-Length" =>
|
|
490
|
+
"Content-Type" => type || headers["Content-Type"],
|
|
491
|
+
"Content-Length" => headers["Content-Length"],
|
|
490
492
|
"Content-Disposition" => content_disposition(file),
|
|
491
|
-
"Content-Range" =>
|
|
493
|
+
"Content-Range" => headers["Content-Range"],
|
|
492
494
|
"Accept-Ranges" => "bytes",
|
|
493
495
|
}.compact
|
|
494
496
|
|
|
495
|
-
body = Rack::BodyProxy.new(
|
|
497
|
+
body = Rack::BodyProxy.new(body) { File.delete(file.path) }
|
|
496
498
|
|
|
497
499
|
file.close
|
|
498
500
|
|
|
@@ -504,28 +506,23 @@ class Shrine
|
|
|
504
506
|
# uploads the result. If the derivation result is already uploaded, uses
|
|
505
507
|
# the `rack_response` plugin to generate a Rack response triple.
|
|
506
508
|
def upload_response(env)
|
|
507
|
-
uploaded_file = derivation.retrieve
|
|
509
|
+
uploaded_file = upload_redirect ? derivation.retrieve : derivation.opened
|
|
508
510
|
|
|
509
511
|
unless uploaded_file
|
|
510
512
|
derivative = derivation.generate
|
|
511
|
-
uploaded_file = derivation.upload(derivative)
|
|
513
|
+
uploaded_file = derivation.upload(derivative, delete: upload_redirect)
|
|
512
514
|
end
|
|
513
515
|
|
|
514
516
|
if upload_redirect
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
File.delete(derivative.path)
|
|
519
|
-
end
|
|
520
|
-
|
|
521
|
-
redirect_url = uploaded_file.url(upload_redirect_url_options)
|
|
517
|
+
redirect_url = uploaded_file.url(**upload_redirect_url_options)
|
|
518
|
+
headers = { "Location" => redirect_url }
|
|
519
|
+
headers = Rack::Headers[headers] if Rack.release >= "3"
|
|
522
520
|
|
|
523
|
-
[302,
|
|
521
|
+
[302, headers, []]
|
|
524
522
|
else
|
|
525
523
|
if derivative && File.exist?(derivative.path)
|
|
526
524
|
file_response(derivative, env)
|
|
527
525
|
else
|
|
528
|
-
uploaded_file.open(**upload_open_options)
|
|
529
526
|
uploaded_file.to_rack_response(
|
|
530
527
|
type: type,
|
|
531
528
|
disposition: disposition,
|
|
@@ -536,16 +533,22 @@ class Shrine
|
|
|
536
533
|
end
|
|
537
534
|
end
|
|
538
535
|
|
|
539
|
-
# We call `Rack::
|
|
536
|
+
# We call `Rack::Files` with no default `Content-Type`, and make sure we
|
|
540
537
|
# stay compatible with both Rack 2.x and 1.6.x.
|
|
541
538
|
def rack_file_response(path, env)
|
|
542
|
-
|
|
539
|
+
if Rack.release >= "2.1"
|
|
540
|
+
server = Rack::Files.new("", {}, nil)
|
|
541
|
+
else
|
|
542
|
+
server = Rack::File.new("", {}, nil)
|
|
543
|
+
end
|
|
543
544
|
|
|
544
545
|
if Rack.release > "2"
|
|
545
546
|
server.serving(Rack::Request.new(env), path)
|
|
546
547
|
else
|
|
548
|
+
# :nocov:
|
|
547
549
|
server.path = path
|
|
548
550
|
server.serving(env)
|
|
551
|
+
# :nocov:
|
|
549
552
|
end
|
|
550
553
|
end
|
|
551
554
|
|
|
@@ -572,9 +575,7 @@ class Shrine
|
|
|
572
575
|
end
|
|
573
576
|
|
|
574
577
|
class Derivation::Generate < Derivation::Command
|
|
575
|
-
delegate :name, :args, :source,
|
|
576
|
-
:download, :download_errors, :download_options,
|
|
577
|
-
:include_uploaded_file
|
|
578
|
+
delegate :name, :args, :source, :download, :download_options
|
|
578
579
|
|
|
579
580
|
def call(file = nil)
|
|
580
581
|
derivative = generate(file)
|
|
@@ -589,22 +590,16 @@ class Shrine
|
|
|
589
590
|
# file.
|
|
590
591
|
def generate(file)
|
|
591
592
|
if download
|
|
592
|
-
with_downloaded(file)
|
|
593
|
-
if include_uploaded_file
|
|
594
|
-
derive(file, source, *args)
|
|
595
|
-
else
|
|
596
|
-
derive(file, *args)
|
|
597
|
-
end
|
|
598
|
-
end
|
|
593
|
+
with_downloaded(file) { |file| derive(file, *args) }
|
|
599
594
|
else
|
|
600
|
-
derive(
|
|
595
|
+
derive(*args)
|
|
601
596
|
end
|
|
602
597
|
end
|
|
603
598
|
|
|
604
599
|
# Calls the derivation block.
|
|
605
600
|
def derive(*args)
|
|
606
601
|
instrument_derivation do
|
|
607
|
-
|
|
602
|
+
derivation.instance_exec(*args, &derivation_block)
|
|
608
603
|
end
|
|
609
604
|
end
|
|
610
605
|
|
|
@@ -612,63 +607,37 @@ class Shrine
|
|
|
612
607
|
def instrument_derivation(&block)
|
|
613
608
|
return yield unless shrine_class.respond_to?(:instrument)
|
|
614
609
|
|
|
615
|
-
shrine_class.instrument(
|
|
616
|
-
:derivation,
|
|
617
|
-
name: derivation.name,
|
|
618
|
-
args: derivation.args,
|
|
619
|
-
derivation: derivation,
|
|
620
|
-
&block
|
|
621
|
-
)
|
|
610
|
+
shrine_class.instrument(:derivation, { derivation: derivation }, &block)
|
|
622
611
|
end
|
|
623
612
|
|
|
624
613
|
# Massages the derivation result, ensuring it's opened in binary mode,
|
|
625
614
|
# rewinded and flushed to disk.
|
|
626
|
-
def normalize(
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
elsif derivative.is_a?(File)
|
|
630
|
-
derivative.close
|
|
631
|
-
derivative = File.open(derivative.path)
|
|
632
|
-
elsif derivative.is_a?(String)
|
|
633
|
-
derivative = File.open(derivative)
|
|
634
|
-
elsif defined?(Pathname) && derivative.is_a?(Pathname)
|
|
635
|
-
derivative = derivative.open
|
|
636
|
-
else
|
|
637
|
-
fail Error, "unexpected derivation result: #{derivation.inspect} (expected File, Tempfile, String, or Pathname object)"
|
|
615
|
+
def normalize(file)
|
|
616
|
+
unless file.is_a?(File) || file.is_a?(Tempfile)
|
|
617
|
+
fail Error, "expected File or Tempfile object as derivation result, got #{file.inspect}"
|
|
638
618
|
end
|
|
639
619
|
|
|
640
|
-
|
|
641
|
-
|
|
620
|
+
file.open if file.is_a?(Tempfile) # refresh file descriptor
|
|
621
|
+
file.binmode # ensure binary mode
|
|
622
|
+
file
|
|
642
623
|
end
|
|
643
624
|
|
|
644
625
|
def with_downloaded(file, &block)
|
|
645
|
-
if file
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
download_source(&block)
|
|
649
|
-
end
|
|
626
|
+
return yield(file) if file
|
|
627
|
+
|
|
628
|
+
download_source(&block)
|
|
650
629
|
end
|
|
651
630
|
|
|
652
631
|
# Downloads the source uploaded file from the storage.
|
|
653
|
-
def download_source
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
raise Derivation::SourceNotFound, "source file \"#{source.id}\" was not found on storage :#{source.storage_key}"
|
|
658
|
-
end
|
|
659
|
-
|
|
660
|
-
yield file
|
|
661
|
-
ensure
|
|
662
|
-
file.close! if file
|
|
632
|
+
def download_source(&block)
|
|
633
|
+
source.download(**download_options, &block)
|
|
634
|
+
rescue Shrine::FileNotFound
|
|
635
|
+
raise Derivation::SourceNotFound, "source file \"#{source.id}\" was not found on storage :#{source.storage_key}"
|
|
663
636
|
end
|
|
664
637
|
|
|
665
638
|
def derivation_block
|
|
666
639
|
shrine_class.derivations[name] or fail Derivation::NotFound, "derivation #{name.inspect} is not defined"
|
|
667
640
|
end
|
|
668
|
-
|
|
669
|
-
def uploader
|
|
670
|
-
source.uploader
|
|
671
|
-
end
|
|
672
641
|
end
|
|
673
642
|
|
|
674
643
|
class Derivation::Upload < Derivation::Command
|
|
@@ -677,59 +646,49 @@ class Shrine
|
|
|
677
646
|
# Uploads the derivation result to the dedicated location on the storage.
|
|
678
647
|
# If a file object is given, uploads that to the storage, otherwise calls
|
|
679
648
|
# the derivation block and uploads the result.
|
|
680
|
-
def call(derivative = nil)
|
|
681
|
-
with_derivative(derivative) do |uploadable|
|
|
682
|
-
uploader.upload uploadable,
|
|
683
|
-
location: upload_location,
|
|
684
|
-
upload_options: upload_options
|
|
685
|
-
end
|
|
686
|
-
end
|
|
687
|
-
|
|
688
|
-
private
|
|
689
|
-
|
|
690
|
-
def with_derivative(derivative)
|
|
649
|
+
def call(derivative = nil, **options)
|
|
691
650
|
if derivative
|
|
692
|
-
|
|
693
|
-
# we want to keep the provided file open and rewinded
|
|
694
|
-
File.open(derivative.path, binmode: true) do |file|
|
|
695
|
-
yield file
|
|
696
|
-
end
|
|
697
|
-
ensure
|
|
698
|
-
# close the file handler if the file was deleted during upload
|
|
699
|
-
derivative.close if !File.exist?(derivative.path)
|
|
700
|
-
end
|
|
651
|
+
upload(derivative, **options)
|
|
701
652
|
else
|
|
702
|
-
|
|
703
|
-
begin
|
|
704
|
-
file = derivation.generate
|
|
705
|
-
yield file
|
|
706
|
-
ensure
|
|
707
|
-
file.close
|
|
708
|
-
File.delete(file.path)
|
|
709
|
-
end
|
|
653
|
+
upload(derivation.generate, delete: true, **options)
|
|
710
654
|
end
|
|
711
655
|
end
|
|
712
656
|
|
|
713
|
-
|
|
714
|
-
|
|
657
|
+
private
|
|
658
|
+
|
|
659
|
+
def upload(io, **options)
|
|
660
|
+
shrine_class.upload io, upload_storage,
|
|
661
|
+
location: upload_location,
|
|
662
|
+
upload_options: upload_options,
|
|
663
|
+
action: :derivation,
|
|
664
|
+
**options
|
|
715
665
|
end
|
|
716
666
|
end
|
|
717
667
|
|
|
718
668
|
class Derivation::Retrieve < Derivation::Command
|
|
719
|
-
delegate :
|
|
669
|
+
delegate :upload_storage, :upload_location
|
|
720
670
|
|
|
721
|
-
# Returns a Shrine::UploadedFile object pointing to the uploaded
|
|
722
|
-
#
|
|
671
|
+
# Returns a Shrine::UploadedFile object pointing to the uploaded derivative
|
|
672
|
+
# if it exists on the storage.
|
|
723
673
|
def call
|
|
724
|
-
uploaded_file = shrine_class
|
|
725
|
-
"storage" => upload_storage.to_s,
|
|
726
|
-
"id" => upload_location,
|
|
727
|
-
)
|
|
728
|
-
|
|
674
|
+
uploaded_file = shrine_class.uploaded_file(storage: upload_storage, id: upload_location)
|
|
729
675
|
uploaded_file if uploaded_file.exists?
|
|
730
676
|
end
|
|
731
677
|
end
|
|
732
678
|
|
|
679
|
+
class Derivation::Opened < Derivation::Command
|
|
680
|
+
delegate :upload_storage, :upload_location, :upload_open_options
|
|
681
|
+
|
|
682
|
+
# Returns opened Shrine::UploadedFile object pointing to the uploaded if
|
|
683
|
+
# it exists on the storage.
|
|
684
|
+
def call
|
|
685
|
+
uploaded_file = shrine_class.uploaded_file(storage: upload_storage, id: upload_location)
|
|
686
|
+
uploaded_file.open(**upload_open_options)
|
|
687
|
+
uploaded_file
|
|
688
|
+
rescue Shrine::FileNotFound
|
|
689
|
+
end
|
|
690
|
+
end
|
|
691
|
+
|
|
733
692
|
class Derivation::Delete < Derivation::Command
|
|
734
693
|
delegate :upload_location, :upload_storage
|
|
735
694
|
|
|
@@ -784,7 +743,7 @@ class Shrine
|
|
|
784
743
|
def verify_signature(string, signature)
|
|
785
744
|
if signature.nil?
|
|
786
745
|
fail InvalidSignature, "missing \"signature\" param"
|
|
787
|
-
elsif signature
|
|
746
|
+
elsif !Rack::Utils.secure_compare(signature, generate_signature(string))
|
|
788
747
|
fail InvalidSignature, "provided signature does not match the calculated signature"
|
|
789
748
|
end
|
|
790
749
|
end
|