shrine 2.15.0 → 2.16.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 +26 -0
- data/doc/advantages.md +4 -0
- data/doc/plugins/activerecord.md +3 -2
- data/doc/plugins/add_metadata.md +4 -2
- data/doc/plugins/backgrounding.md +6 -4
- data/doc/plugins/backup.md +4 -2
- data/doc/plugins/cached_attachment_data.md +5 -3
- data/doc/plugins/copy.md +3 -1
- data/doc/plugins/data_uri.md +3 -2
- data/doc/plugins/default_storage.md +5 -2
- data/doc/plugins/default_url.md +4 -2
- data/doc/plugins/default_url_options.md +5 -2
- data/doc/plugins/delete_promoted.md +6 -4
- data/doc/plugins/delete_raw.md +12 -3
- data/doc/plugins/derivation_endpoint.md +17 -6
- data/doc/plugins/determine_mime_type.md +3 -2
- data/doc/plugins/direct_upload.md +4 -2
- data/doc/plugins/download_endpoint.md +46 -6
- data/doc/plugins/dynamic_storage.md +5 -2
- data/doc/plugins/hooks.md +3 -1
- data/doc/plugins/included.md +5 -2
- data/doc/plugins/infer_extension.md +5 -4
- data/doc/plugins/keep_files.md +5 -3
- data/doc/plugins/logging.md +4 -1
- data/doc/plugins/metadata_attribues.md +6 -3
- data/doc/plugins/migration_helpers.md +4 -2
- data/doc/plugins/module_include.md +4 -2
- data/doc/plugins/moving.md +6 -4
- data/doc/plugins/multi_delete.md +4 -2
- data/doc/plugins/parallelize.md +4 -2
- data/doc/plugins/parsed_json.md +5 -3
- data/doc/plugins/presign_endpoint.md +7 -5
- data/doc/plugins/pretty_location.md +4 -2
- data/doc/plugins/processing.md +3 -2
- data/doc/plugins/rack_file.md +4 -2
- data/doc/plugins/rack_response.md +26 -10
- data/doc/plugins/recache.md +7 -5
- data/doc/plugins/refresh_metadata.md +4 -2
- data/doc/plugins/remote_url.md +3 -1
- data/doc/plugins/remove_attachment.md +4 -2
- data/doc/plugins/remove_invalid.md +6 -3
- data/doc/plugins/restore_cached_data.md +8 -6
- data/doc/plugins/sequel.md +4 -1
- data/doc/plugins/signature.md +5 -3
- data/doc/plugins/store_dimensions.md +4 -2
- data/doc/plugins/tempfile.md +4 -2
- data/doc/plugins/upload_endpoint.md +4 -3
- data/doc/plugins/upload_options.md +4 -2
- data/doc/plugins/validation_helpers.md +4 -2
- data/doc/plugins/versions.md +3 -2
- data/doc/release_notes/2.15.0.md +2 -2
- data/doc/release_notes/2.16.0.md +52 -0
- data/doc/storage/s3.md +2 -2
- data/lib/shrine/plugins/_urlsafe_serialization.rb +2 -0
- data/lib/shrine/plugins/activerecord.rb +3 -0
- data/lib/shrine/plugins/add_metadata.rb +3 -0
- data/lib/shrine/plugins/backgrounding.rb +3 -0
- data/lib/shrine/plugins/backup.rb +3 -0
- data/lib/shrine/plugins/cached_attachment_data.rb +3 -0
- data/lib/shrine/plugins/copy.rb +3 -0
- data/lib/shrine/plugins/data_uri.rb +3 -0
- data/lib/shrine/plugins/default_storage.rb +3 -0
- data/lib/shrine/plugins/default_url.rb +3 -0
- data/lib/shrine/plugins/default_url_options.rb +3 -0
- data/lib/shrine/plugins/delete_promoted.rb +3 -0
- data/lib/shrine/plugins/delete_raw.rb +4 -1
- data/lib/shrine/plugins/derivation_endpoint.rb +144 -47
- data/lib/shrine/plugins/determine_mime_type.rb +3 -0
- data/lib/shrine/plugins/direct_upload.rb +3 -0
- data/lib/shrine/plugins/download_endpoint.rb +29 -29
- data/lib/shrine/plugins/dynamic_storage.rb +3 -0
- data/lib/shrine/plugins/hooks.rb +3 -0
- data/lib/shrine/plugins/included.rb +3 -0
- data/lib/shrine/plugins/infer_extension.rb +3 -0
- data/lib/shrine/plugins/keep_files.rb +3 -0
- data/lib/shrine/plugins/logging.rb +3 -0
- data/lib/shrine/plugins/metadata_attributes.rb +3 -0
- data/lib/shrine/plugins/migration_helpers.rb +3 -0
- data/lib/shrine/plugins/module_include.rb +3 -0
- data/lib/shrine/plugins/moving.rb +3 -0
- data/lib/shrine/plugins/multi_delete.rb +3 -0
- data/lib/shrine/plugins/parallelize.rb +5 -1
- data/lib/shrine/plugins/parsed_json.rb +3 -0
- data/lib/shrine/plugins/presign_endpoint.rb +3 -0
- data/lib/shrine/plugins/pretty_location.rb +3 -0
- data/lib/shrine/plugins/processing.rb +3 -0
- data/lib/shrine/plugins/rack_file.rb +3 -0
- data/lib/shrine/plugins/rack_response.rb +14 -14
- data/lib/shrine/plugins/recache.rb +3 -0
- data/lib/shrine/plugins/refresh_metadata.rb +3 -0
- data/lib/shrine/plugins/remote_url.rb +3 -0
- data/lib/shrine/plugins/remove_attachment.rb +3 -0
- data/lib/shrine/plugins/remove_invalid.rb +3 -0
- data/lib/shrine/plugins/restore_cached_data.rb +3 -0
- data/lib/shrine/plugins/sequel.rb +3 -0
- data/lib/shrine/plugins/signature.rb +3 -0
- data/lib/shrine/plugins/store_dimensions.rb +4 -1
- data/lib/shrine/plugins/tempfile.rb +5 -0
- data/lib/shrine/plugins/upload_endpoint.rb +3 -0
- data/lib/shrine/plugins/upload_options.rb +3 -0
- data/lib/shrine/plugins/validation_helpers.rb +3 -0
- data/lib/shrine/plugins/versions.rb +3 -0
- data/lib/shrine/uploaded_file.rb +15 -7
- data/lib/shrine/version.rb +1 -1
- metadata +3 -2
@@ -1,14 +1,16 @@
|
|
1
1
|
# Restore Cached Data
|
2
2
|
|
3
|
-
The `restore_cached_data` plugin re-extracts metadata
|
4
|
-
cached files, i.e. when the attachment has been retained
|
5
|
-
or assigned from a direct upload. In both cases you may
|
6
|
-
metadata on the server side, mainly to prevent tempering,
|
7
|
-
direct uploads to obtain metadata that couldn't be
|
8
|
-
side.
|
3
|
+
The [`restore_cached_data`][restore_cached_data] plugin re-extracts metadata
|
4
|
+
when assigning already cached files, i.e. when the attachment has been retained
|
5
|
+
on validation errors or assigned from a direct upload. In both cases you may
|
6
|
+
want to re-extract metadata on the server side, mainly to prevent tempering,
|
7
|
+
but also in case of direct uploads to obtain metadata that couldn't be
|
8
|
+
extracted on the client side.
|
9
9
|
|
10
10
|
```rb
|
11
11
|
plugin :restore_cached_data
|
12
12
|
```
|
13
13
|
|
14
14
|
It uses the `refresh_metadata` plugin to re-extract metadata.
|
15
|
+
|
16
|
+
[restore_cached_data]: /lib/shrine/plugins/restore_cached_data.rb
|
data/doc/plugins/sequel.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# Sequel
|
2
2
|
|
3
|
-
The `sequel` plugin extends the "attachment" interface with support
|
3
|
+
The [`sequel`][sequel] plugin extends the "attachment" interface with support
|
4
|
+
for Sequel.
|
4
5
|
|
5
6
|
```rb
|
6
7
|
plugin :sequel
|
@@ -62,3 +63,5 @@ errors, you can disable it:
|
|
62
63
|
```rb
|
63
64
|
plugin :sequel, validations: false
|
64
65
|
```
|
66
|
+
|
67
|
+
[sequel]: /lib/shrine/plugins/sequel.rb
|
data/doc/plugins/signature.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# Signature
|
2
2
|
|
3
|
-
The `signature` plugin provides the ability to calculate a hash
|
4
|
-
content. This hash can be used as a checksum or just as a unique
|
5
|
-
the uploaded file.
|
3
|
+
The [`signature`][signature] plugin provides the ability to calculate a hash
|
4
|
+
from file content. This hash can be used as a checksum or just as a unique
|
5
|
+
signature for the uploaded file.
|
6
6
|
|
7
7
|
```rb
|
8
8
|
Shrine.plugin :signature
|
@@ -47,3 +47,5 @@ Shrine.calculate_signature(io, :sha256, format: :base64)
|
|
47
47
|
```
|
48
48
|
|
49
49
|
The supported encoding formats are `hex` (default), `base64`, and `none`.
|
50
|
+
|
51
|
+
[signature]: /lib/shrine/plugins/signature.rb
|
@@ -1,7 +1,8 @@
|
|
1
1
|
# Store Dimensions
|
2
2
|
|
3
|
-
The `store_dimensions` plugin extracts dimensions of
|
4
|
-
them into the metadata hash (by default it uses the
|
3
|
+
The [`store_dimensions`][store_dimensions] plugin extracts dimensions of
|
4
|
+
uploaded images and stores them into the metadata hash (by default it uses the
|
5
|
+
[fastimage] gem).
|
5
6
|
|
6
7
|
```rb
|
7
8
|
plugin :store_dimensions
|
@@ -63,6 +64,7 @@ Shrine.dimensions_analyzers[:fastimage].call(io) # calls a built-in analyzer
|
|
63
64
|
#=> [300, 400]
|
64
65
|
```
|
65
66
|
|
67
|
+
[store_dimensions]: /lib/shrine/plugins/store_dimensions.rb
|
66
68
|
[fastimage]: https://github.com/sdsykes/fastimage
|
67
69
|
[mini_magick]: https://github.com/minimagick/minimagick
|
68
70
|
[ruby-vips]: https://github.com/libvips/ruby-vips
|
data/doc/plugins/tempfile.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Tempfile
|
2
2
|
|
3
|
-
The `tempfile` plugin makes it easier to reuse a single copy of an
|
4
|
-
file on disk.
|
3
|
+
The [`tempfile`][tempfile] plugin makes it easier to reuse a single copy of an
|
4
|
+
uploaded file on disk.
|
5
5
|
|
6
6
|
```rb
|
7
7
|
Shrine.plugin :tempfile
|
@@ -38,3 +38,5 @@ This plugin also modifies `Shrine.with_file` to call `UploadedFile#tempfile`
|
|
38
38
|
when the given IO object is an open `UploadedFile`. Since `Shrine.with_file` is
|
39
39
|
typically called on the `Shrine` class directly, it's recommended to load this
|
40
40
|
plugin globally.
|
41
|
+
|
42
|
+
[tempfile]: /lib/shrine/plugins/tempfile.rb
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# Upload Endpoint
|
2
2
|
|
3
|
-
The `upload_endpoint` plugin provides a Rack endpoint which
|
4
|
-
uploads and forwards them to specified storage. On the client side
|
5
|
-
recommended to use [Uppy] for asynchronous uploads.
|
3
|
+
The [`upload_endpoint`][upload_endpoint] plugin provides a Rack endpoint which
|
4
|
+
accepts file uploads and forwards them to specified storage. On the client side
|
5
|
+
it's recommended to use [Uppy] for asynchronous uploads.
|
6
6
|
|
7
7
|
```rb
|
8
8
|
plugin :upload_endpoint
|
@@ -120,4 +120,5 @@ You can override any of the options above when creating the endpoint:
|
|
120
120
|
Shrine.upload_endpoint(:cache, max_size: 20*1024*1024)
|
121
121
|
```
|
122
122
|
|
123
|
+
[upload_endpoint]: /lib/shrine/plugins/upload_endpoint.rb
|
123
124
|
[Uppy]: https://uppy.io
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# Upload Options
|
2
2
|
|
3
|
-
The `upload_options` plugin allows you to automatically pass
|
4
|
-
options to storage on every upload:
|
3
|
+
The [`upload_options`][upload_options] plugin allows you to automatically pass
|
4
|
+
additional upload options to storage on every upload:
|
5
5
|
|
6
6
|
```rb
|
7
7
|
plugin :upload_options, cache: { acl: "private" }
|
@@ -26,3 +26,5 @@ the uploader.
|
|
26
26
|
```rb
|
27
27
|
uploader.upload(file, upload_options: { acl: "public-read" })
|
28
28
|
```
|
29
|
+
|
30
|
+
[upload_options]: /lib/shrine/plugins/upload_options.rb
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# Validation Helpers
|
2
2
|
|
3
|
-
The `validation_helpers` plugin provides helper methods
|
4
|
-
files based on extracted metadata.
|
3
|
+
The [`validation_helpers`][validation_helpers] plugin provides helper methods
|
4
|
+
for validating attached files based on extracted metadata.
|
5
5
|
|
6
6
|
```rb
|
7
7
|
plugin :validation_helpers
|
@@ -127,3 +127,5 @@ Attacher.validate do
|
|
127
127
|
validate_mime_type_inclusion %w[image/jpeg image/png image/gif], message: "must be JPEG, PNG or GIF"
|
128
128
|
end
|
129
129
|
```
|
130
|
+
|
131
|
+
[validation_helpers]: /lib/shrine/plugins/validation_helpers.rb
|
data/doc/plugins/versions.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Versions
|
2
2
|
|
3
|
-
The `versions` plugin enables your uploader to deal with versions,
|
4
|
-
you to return a Hash of files when processing.
|
3
|
+
The [`versions`][versions] plugin enables your uploader to deal with versions,
|
4
|
+
by allowing you to return a Hash of files when processing.
|
5
5
|
|
6
6
|
```rb
|
7
7
|
plugin :versions
|
@@ -175,5 +175,6 @@ end
|
|
175
175
|
If you want to re-create a single or all versions, refer to the [reprocessing
|
176
176
|
versions] guide for details.
|
177
177
|
|
178
|
+
[versions]: /lib/shrine/plugins/versions.rb
|
178
179
|
[reprocessing versions]: /doc/regenerating_versions.md#readme
|
179
180
|
[image_processing]: https://github.com/janko/image_processing
|
data/doc/release_notes/2.15.0.md
CHANGED
@@ -53,7 +53,7 @@
|
|
53
53
|
host], change response headers of derivatives ([`Content-Type`],
|
54
54
|
[`Content-Disposition`]), add [URL expiration], [cache][uploading]
|
55
55
|
generated derivatives to a Shrine storage and more. Check out the
|
56
|
-
[documentation] for more details.
|
56
|
+
[documentation][derivation_endpoint] for more details.
|
57
57
|
|
58
58
|
## Other improvements
|
59
59
|
|
@@ -74,7 +74,7 @@
|
|
74
74
|
relying on multiple invocations returning the same object, you will need to
|
75
75
|
modify your code.
|
76
76
|
|
77
|
-
[derivation_endpoint]: /doc/plugins/derivation_endpoint.md
|
77
|
+
[derivation_endpoint]: /doc/plugins/derivation_endpoint.md#readme
|
78
78
|
[CDN host]: /doc/plugins/derivation_endpoint.md#host
|
79
79
|
[`Content-Type`]: /doc/plugins/derivation_endpoint.md#content-type
|
80
80
|
[`Content-Disposition`]: /doc/plugins/derivation_endpoint.md#content-disposition
|
@@ -0,0 +1,52 @@
|
|
1
|
+
## New Features
|
2
|
+
|
3
|
+
* The `:download_options` option has been added to the `download_endpoint`
|
4
|
+
plugin, for specifying options passed to `Storage#open`.
|
5
|
+
|
6
|
+
```rb
|
7
|
+
plugin :download_endpoint,
|
8
|
+
download_options: {
|
9
|
+
sse_customer_algorithm: "AES256",
|
10
|
+
sse_customer_key: "secret_key",
|
11
|
+
sse_customer_key_md5: "secret_key_md5",
|
12
|
+
}
|
13
|
+
```
|
14
|
+
|
15
|
+
* The `:upload_open_options` option has been added to the `derivation_endpoint`
|
16
|
+
plugin, for specifying options passed to `Storage#open` when downloading a
|
17
|
+
cached derivation result.
|
18
|
+
|
19
|
+
```rb
|
20
|
+
plugin :download_endpoint,
|
21
|
+
upload: true,
|
22
|
+
upload_open_options: { response_content_encoding: "gzip" }
|
23
|
+
```
|
24
|
+
|
25
|
+
## Other improvements
|
26
|
+
|
27
|
+
* The `rack_response` and `derivation_endpoint` plugins now don't return any
|
28
|
+
`Content-Type` response header if the MIME type could not be determined from
|
29
|
+
the file extension. Previously it the `Content-Type` header would default to
|
30
|
+
`application/octet-stream`, which would force the browser to view the file
|
31
|
+
as generic binary content, as opposed to doing its own MIME type sniffing.
|
32
|
+
|
33
|
+
* Fixed `delete_raw` plugin breaking `derivation_endpoint` when `:upload` was
|
34
|
+
enabled.
|
35
|
+
|
36
|
+
* Fixed a few things in the `Shrine::Derivation` API:
|
37
|
+
|
38
|
+
* `Derivation#upload` doesn't close the input file anymore
|
39
|
+
* `Derivation#upload` now requires input file to respond to `#path`
|
40
|
+
* `Derivation#upload` now deletes the internally generated derivation result
|
41
|
+
* `Derivation#processed` now works when derivation result is a `File` object
|
42
|
+
|
43
|
+
* The official demo app now shows the `derivation_endpoint` plugin.
|
44
|
+
|
45
|
+
* The `#to_rack_response` method from the `rack_response` plugin now always
|
46
|
+
opens the `UploadedFile`, and does so upfront. This means if ther are any
|
47
|
+
download errors, they will bubble up from `#to_rack_response` as opposed to
|
48
|
+
when the response body is iterated over.
|
49
|
+
|
50
|
+
* When `store_dimensions` plugin was overriding `Shrine#extract_metadata`, it
|
51
|
+
made the second argument (the `context` hash) mandatory. This has been fixed,
|
52
|
+
now the second argument is optional again.
|
data/doc/storage/s3.md
CHANGED
@@ -65,7 +65,7 @@ Shrine::Storage::S3.new(prefix: "cache", **s3_options)
|
|
65
65
|
## Upload options
|
66
66
|
|
67
67
|
Sometimes you'll want to add additional upload options to all S3 uploads. You
|
68
|
-
can do that by passing the `:
|
68
|
+
can do that by passing the `:upload_options` option:
|
69
69
|
|
70
70
|
```rb
|
71
71
|
Shrine::Storage::S3.new(upload_options: { acl: "private" }, **s3_options)
|
@@ -288,6 +288,6 @@ can scale exponentially by using more prefixes.
|
|
288
288
|
[presigning]: http://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Object.html#presigned_post-instance_method
|
289
289
|
[aws-sdk-s3]: https://github.com/aws/aws-sdk-ruby/tree/master/gems/aws-sdk-s3
|
290
290
|
[Transfer Acceleration]: http://docs.aws.amazon.com/AmazonS3/latest/dev/transfer-acceleration.html
|
291
|
-
[object lifecycle]: http://docs.aws.amazon.com/
|
291
|
+
[object lifecycle]: http://docs.aws.amazon.com/AmazonS3/latest/UG/lifecycle-configuration-bucket-no-versioning.html
|
292
292
|
[serve private content via CloudFront]: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/PrivateContent.html
|
293
293
|
[`Aws::CloudFront::UrlSigner`]: https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/CloudFront/UrlSigner.html
|
@@ -4,6 +4,9 @@ require "active_record"
|
|
4
4
|
|
5
5
|
class Shrine
|
6
6
|
module Plugins
|
7
|
+
# Documentation lives in [doc/plugins/activerecord.md] on GitHub.
|
8
|
+
#
|
9
|
+
# [doc/plugins/activerecord.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/activerecord.md
|
7
10
|
module Activerecord
|
8
11
|
def self.configure(uploader, opts = {})
|
9
12
|
uploader.opts[:activerecord_callbacks] = opts.fetch(:callbacks, uploader.opts.fetch(:activerecord_callbacks, true))
|
@@ -2,6 +2,9 @@
|
|
2
2
|
|
3
3
|
class Shrine
|
4
4
|
module Plugins
|
5
|
+
# Documentation lives in [doc/plugins/add_metadata.md] on GitHub.
|
6
|
+
#
|
7
|
+
# [doc/plugins/add_metadata.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/add_metadata.md
|
5
8
|
module AddMetadata
|
6
9
|
def self.configure(uploader)
|
7
10
|
uploader.opts[:metadata] ||= []
|
@@ -2,6 +2,9 @@
|
|
2
2
|
|
3
3
|
class Shrine
|
4
4
|
module Plugins
|
5
|
+
# Documentation lives in [doc/plugins/backgrounding.md] on GitHub.
|
6
|
+
#
|
7
|
+
# [doc/plugins/backgrounding.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/backgrounding.md
|
5
8
|
module Backgrounding
|
6
9
|
module AttacherClassMethods
|
7
10
|
# If block is passed in, stores it to be called on promotion. Otherwise
|
@@ -2,6 +2,9 @@
|
|
2
2
|
|
3
3
|
class Shrine
|
4
4
|
module Plugins
|
5
|
+
# Documentation lives in [doc/plugins/backup.md] on GitHub.
|
6
|
+
#
|
7
|
+
# [doc/plugins/backup.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/backup.md
|
5
8
|
module Backup
|
6
9
|
def self.configure(uploader, opts = {})
|
7
10
|
uploader.opts[:backup_storage] = opts.fetch(:storage, uploader.opts[:backup_storage])
|
@@ -2,6 +2,9 @@
|
|
2
2
|
|
3
3
|
class Shrine
|
4
4
|
module Plugins
|
5
|
+
# Documentation lives in [doc/plugins/cached_attachment_data.md] on GitHub.
|
6
|
+
#
|
7
|
+
# [doc/plugins/cached_attachment_data.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/cached_attachment_data.md
|
5
8
|
module CachedAttachmentData
|
6
9
|
module AttachmentMethods
|
7
10
|
def initialize(*)
|
data/lib/shrine/plugins/copy.rb
CHANGED
@@ -8,6 +8,9 @@ require "forwardable"
|
|
8
8
|
|
9
9
|
class Shrine
|
10
10
|
module Plugins
|
11
|
+
# Documentation lives in [doc/plugins/data_uri.md] on GitHub.
|
12
|
+
#
|
13
|
+
# [doc/plugins/data_uri.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/data_uri.md
|
11
14
|
module DataUri
|
12
15
|
class ParseError < Error; end
|
13
16
|
|
@@ -2,6 +2,9 @@
|
|
2
2
|
|
3
3
|
class Shrine
|
4
4
|
module Plugins
|
5
|
+
# Documentation lives in [doc/plugins/default_storage.md] on GitHub.
|
6
|
+
#
|
7
|
+
# [doc/plugins/default_storage.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/default_storage.md
|
5
8
|
module DefaultStorage
|
6
9
|
def self.configure(uploader, opts = {})
|
7
10
|
uploader.opts[:default_storage_cache] = opts.fetch(:cache, uploader.opts[:default_storage_cache])
|
@@ -2,6 +2,9 @@
|
|
2
2
|
|
3
3
|
class Shrine
|
4
4
|
module Plugins
|
5
|
+
# Documentation lives in [doc/plugins/default_url.md] on GitHub.
|
6
|
+
#
|
7
|
+
# [doc/plugins/default_url.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/default_url.md
|
5
8
|
module DefaultUrl
|
6
9
|
def self.configure(uploader, &block)
|
7
10
|
if block
|
@@ -2,6 +2,9 @@
|
|
2
2
|
|
3
3
|
class Shrine
|
4
4
|
module Plugins
|
5
|
+
# Documentation lives in [doc/plugins/default_url_options.md] on GitHub.
|
6
|
+
#
|
7
|
+
# [doc/plugins/default_url_options.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/default_url_options.md
|
5
8
|
module DefaultUrlOptions
|
6
9
|
def self.configure(uploader, options = {})
|
7
10
|
uploader.opts[:default_url_options] ||= {}
|
@@ -2,6 +2,9 @@
|
|
2
2
|
|
3
3
|
class Shrine
|
4
4
|
module Plugins
|
5
|
+
# Documentation lives in [doc/plugins/delete_promoted.md] on GitHub.
|
6
|
+
#
|
7
|
+
# [doc/plugins/delete_promoted.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/delete_promoted.md
|
5
8
|
module DeletePromoted
|
6
9
|
module AttacherMethods
|
7
10
|
def promote(uploaded_file = get, **options)
|
@@ -2,6 +2,9 @@
|
|
2
2
|
|
3
3
|
class Shrine
|
4
4
|
module Plugins
|
5
|
+
# Documentation lives in [doc/plugins/delete_raw.md] on GitHub.
|
6
|
+
#
|
7
|
+
# [doc/plugins/delete_raw.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/delete_raw.md
|
5
8
|
module DeleteRaw
|
6
9
|
def self.configure(uploader, opts = {})
|
7
10
|
uploader.opts[:delete_raw_storages] = opts.fetch(:storages, uploader.opts[:delete_raw_storages])
|
@@ -13,7 +16,7 @@ class Shrine
|
|
13
16
|
# Deletes the file that was uploaded, unless it's an UploadedFile.
|
14
17
|
def copy(io, context)
|
15
18
|
super
|
16
|
-
if io.respond_to?(:path) && io.path && delete_raw?
|
19
|
+
if io.respond_to?(:path) && io.path && delete_raw? && context[:delete] != false
|
17
20
|
begin
|
18
21
|
File.delete(io.path)
|
19
22
|
rescue Errno::ENOENT
|
@@ -8,6 +8,9 @@ require "tempfile"
|
|
8
8
|
|
9
9
|
class Shrine
|
10
10
|
module Plugins
|
11
|
+
# Documentation lives in [doc/plugins/derivation_endpoint.md] on GitHub.
|
12
|
+
#
|
13
|
+
# [doc/plugins/derivation_endpoint.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/derivation_endpoint.md
|
11
14
|
module DerivationEndpoint
|
12
15
|
def self.load_dependencies(uploader, opts = {})
|
13
16
|
uploader.plugin :rack_response
|
@@ -26,10 +29,17 @@ class Shrine
|
|
26
29
|
end
|
27
30
|
|
28
31
|
module ClassMethods
|
32
|
+
# Returns a mountable Rack app that handles derivation requests.
|
29
33
|
def derivation_endpoint(**options)
|
30
34
|
Shrine::DerivationEndpoint.new(shrine_class: self, options: options)
|
31
35
|
end
|
32
36
|
|
37
|
+
# Calls the derivation endpoint passing the request information, and
|
38
|
+
# returns the Rack response triple.
|
39
|
+
#
|
40
|
+
# It uses a trick where it removes the derivation path prefix from the
|
41
|
+
# path info before calling the Rack app, which is what web framework
|
42
|
+
# routers do before they're calling a mounted Rack app.
|
33
43
|
def derivation_response(env, **options)
|
34
44
|
script_name = env["SCRIPT_NAME"]
|
35
45
|
path_info = env["PATH_INFO"]
|
@@ -50,6 +60,8 @@ class Shrine
|
|
50
60
|
end
|
51
61
|
end
|
52
62
|
|
63
|
+
# Registers a derivation block, which is called when the corresponding
|
64
|
+
# derivation URL is requested.
|
53
65
|
def derivation(name, &block)
|
54
66
|
derivations[name] = block
|
55
67
|
end
|
@@ -64,14 +76,23 @@ class Shrine
|
|
64
76
|
end
|
65
77
|
|
66
78
|
module FileMethods
|
79
|
+
# Generates a URL to a derivation with the receiver as the source file.
|
80
|
+
# Any arguments provided will be included in the URL and passed to the
|
81
|
+
# derivation block. Accepts additional URL options.
|
67
82
|
def derivation_url(name, *args, **options)
|
68
83
|
derivation(name, *args).url(**options)
|
69
84
|
end
|
70
85
|
|
86
|
+
# Calls the specified derivation with the receiver as the source file,
|
87
|
+
# returning a Rack response triple. The derivation endpoint ultimately
|
88
|
+
# calls this method.
|
71
89
|
def derivation_response(name, *args, env:, **options)
|
72
90
|
derivation(name, *args, **options).response(env)
|
73
91
|
end
|
74
92
|
|
93
|
+
# Returns a Shrine::Derivation object created from the provided
|
94
|
+
# arguments. This object offers additional methods for operating with
|
95
|
+
# derivatives on a lower level.
|
75
96
|
def derivation(name, *args, **options)
|
76
97
|
Shrine::Derivation.new(
|
77
98
|
name: name,
|
@@ -99,6 +120,7 @@ class Shrine
|
|
99
120
|
@options = options
|
100
121
|
end
|
101
122
|
|
123
|
+
# Returns an URL to the derivation.
|
102
124
|
def url(**options)
|
103
125
|
Derivation::Url.new(self).call(
|
104
126
|
host: option(:host),
|
@@ -110,26 +132,35 @@ class Shrine
|
|
110
132
|
)
|
111
133
|
end
|
112
134
|
|
135
|
+
# Returns the derivation result in form of a Rack response triple.
|
113
136
|
def response(env)
|
114
137
|
Derivation::Response.new(self).call(env)
|
115
138
|
end
|
116
139
|
|
140
|
+
# Returns the derivation result as a File/Tempfile or a
|
141
|
+
# Shrine::UploadedFile object.
|
117
142
|
def processed
|
118
143
|
Derivation::Processed.new(self).call
|
119
144
|
end
|
120
145
|
|
146
|
+
# Calls the derivation block and returns the direct result.
|
121
147
|
def generate(file = nil)
|
122
148
|
Derivation::Generate.new(self).call(file)
|
123
149
|
end
|
124
150
|
|
151
|
+
# Uploads the derivation result to a dedicated destination on the specified
|
152
|
+
# Shrine storage.
|
125
153
|
def upload(file = nil)
|
126
154
|
Derivation::Upload.new(self).call(file)
|
127
155
|
end
|
128
156
|
|
157
|
+
# Returns a Shrine::UploadedFile object pointing to the uploaded derivation
|
158
|
+
# result.
|
129
159
|
def retrieve
|
130
160
|
Derivation::Retrieve.new(self).call
|
131
161
|
end
|
132
162
|
|
163
|
+
# Deletes the derivation result from the storage.
|
133
164
|
def delete
|
134
165
|
Derivation::Delete.new(self).call
|
135
166
|
end
|
@@ -157,12 +188,18 @@ class Shrine
|
|
157
188
|
option :type
|
158
189
|
option :upload, default: -> { false }
|
159
190
|
option :upload_location, default: -> { default_upload_location }, result: -> (o) { upload_location(o) }
|
191
|
+
option :upload_open_options, default: -> { {} }
|
160
192
|
option :upload_options, default: -> { {} }
|
161
193
|
option :upload_redirect, default: -> { false }
|
162
194
|
option :upload_redirect_url_options, default: -> { {} }
|
163
195
|
option :upload_storage, default: -> { source.storage_key.to_sym }
|
164
196
|
option :version
|
165
197
|
|
198
|
+
# Retrieves the value of a derivation option.
|
199
|
+
#
|
200
|
+
# * If specified as a raw value, returns that value
|
201
|
+
# * If specified as a block, evaluates that it and returns the result
|
202
|
+
# * If unspecified, returns the default value
|
166
203
|
def option(name)
|
167
204
|
option_definition = self.class.options.fetch(name)
|
168
205
|
|
@@ -186,16 +223,21 @@ class Shrine
|
|
186
223
|
|
187
224
|
private
|
188
225
|
|
189
|
-
#
|
226
|
+
# When bumping the version, we also append it to the upload location to
|
227
|
+
# ensure we're not retrieving old derivatives.
|
190
228
|
def upload_location(location)
|
191
229
|
location = location.sub(/(?=(\.\w+)?$)/, "-#{option(:version)}") if option(:version)
|
192
230
|
location
|
193
231
|
end
|
194
232
|
|
233
|
+
# For derivation "thumbnail" with arguments "600/400" and source id of
|
234
|
+
# "1f6375ad.ext", returns "thumbnail-600-400-1f6375ad".
|
195
235
|
def default_filename
|
196
236
|
[name, *args, File.basename(source.id, ".*")].join("-")
|
197
237
|
end
|
198
238
|
|
239
|
+
# For derivation "thumbnail" with arguments "600/400" and source id of
|
240
|
+
# "1f6375ad.ext", returns "1f6375ad/thumbnail-600-400".
|
199
241
|
def default_upload_location
|
200
242
|
directory = source.id.sub(/\.[^\/]+/, "")
|
201
243
|
filename = [name, *args].join("-")
|
@@ -203,6 +245,7 @@ class Shrine
|
|
203
245
|
[directory, filename].join("/")
|
204
246
|
end
|
205
247
|
|
248
|
+
# Allows caching for 1 year or until the URL expires.
|
206
249
|
def default_cache_control
|
207
250
|
if option(:expires_in)
|
208
251
|
"public, max-age=#{option(:expires_in)}"
|
@@ -218,6 +261,7 @@ class Shrine
|
|
218
261
|
@derivation = derivation
|
219
262
|
end
|
220
263
|
|
264
|
+
# Creates methods that delegate to derivation parameters.
|
221
265
|
def self.delegate(*names)
|
222
266
|
names.each do |name|
|
223
267
|
protected define_method(name) {
|
@@ -255,14 +299,16 @@ class Shrine
|
|
255
299
|
metadata: [])
|
256
300
|
|
257
301
|
params = {}
|
258
|
-
params[:expires_at] = (Time.now
|
302
|
+
params[:expires_at] = (Time.now + expires_in).to_i if expires_in
|
259
303
|
params[:version] = version if version
|
260
304
|
params[:type] = type if type
|
261
305
|
params[:filename] = filename if filename
|
262
306
|
params[:disposition] = disposition if disposition
|
263
307
|
|
308
|
+
# serializes the source uploaded file into an URL-safe format
|
264
309
|
source_component = source.urlsafe_dump(metadata: metadata)
|
265
310
|
|
311
|
+
# generate signed URL
|
266
312
|
signed_url(name, *args, source_component, params)
|
267
313
|
end
|
268
314
|
|
@@ -294,6 +340,15 @@ class Shrine
|
|
294
340
|
[status, headers, body]
|
295
341
|
end
|
296
342
|
|
343
|
+
# Verifies validity of the URL, then extracts parameters from it (such as
|
344
|
+
# derivation name, arguments and source file), and generates a derivation
|
345
|
+
# response.
|
346
|
+
#
|
347
|
+
# Returns "403 Forbidden" if signature is invalid, or if the URL has
|
348
|
+
# expired.
|
349
|
+
#
|
350
|
+
# Returns "404 Not Found" if derivation block is not defined, or if source
|
351
|
+
# file was not found on the storage.
|
297
352
|
def handle_request(request)
|
298
353
|
verify_signature!(request)
|
299
354
|
check_expiry!(request)
|
@@ -305,12 +360,10 @@ class Shrine
|
|
305
360
|
|
306
361
|
# request params override statically configured options
|
307
362
|
options = self.options.dup
|
308
|
-
|
309
363
|
options[:type] = request.params["type"] if request.params["type"]
|
310
364
|
options[:disposition] = request.params["disposition"] if request.params["disposition"]
|
311
365
|
options[:filename] = request.params["filename"] if request.params["filename"]
|
312
|
-
|
313
|
-
options[:expires_in] = expires_in(request) if request.params["expires_at"]
|
366
|
+
options[:expires_in] = expires_in(request) if request.params["expires_at"]
|
314
367
|
|
315
368
|
derivation = uploaded_file.derivation(name, *args, **options)
|
316
369
|
|
@@ -322,6 +375,7 @@ class Shrine
|
|
322
375
|
error!(404, "Source file not found")
|
323
376
|
end
|
324
377
|
|
378
|
+
# tell clients to cache the derivation result if it was successful
|
325
379
|
if status == 200 || status == 206
|
326
380
|
headers["Cache-Control"] = derivation.option(:cache_control)
|
327
381
|
end
|
@@ -331,6 +385,7 @@ class Shrine
|
|
331
385
|
|
332
386
|
private
|
333
387
|
|
388
|
+
# Return an error response if the signature is invalid.
|
334
389
|
def verify_signature!(request)
|
335
390
|
signer = UrlSigner.new(secret_key)
|
336
391
|
signer.verify_url("#{request.path_info[1..-1]}?#{request.query_string}")
|
@@ -338,6 +393,7 @@ class Shrine
|
|
338
393
|
error!(403, error.message.capitalize)
|
339
394
|
end
|
340
395
|
|
396
|
+
# Return an error response if URL has expired.
|
341
397
|
def check_expiry!(request)
|
342
398
|
if request.params["expires_at"]
|
343
399
|
error!(403, "Request has expired") if expires_in(request) <= 0
|
@@ -366,7 +422,8 @@ class Shrine
|
|
366
422
|
|
367
423
|
class Derivation::Response < Derivation::Command
|
368
424
|
delegate :type, :disposition, :filename,
|
369
|
-
:upload, :
|
425
|
+
:upload, :upload_open_options,
|
426
|
+
:upload_redirect, :upload_redirect_url_options
|
370
427
|
|
371
428
|
def call(env)
|
372
429
|
if upload
|
@@ -384,27 +441,39 @@ class Shrine
|
|
384
441
|
file_response(derivative, env)
|
385
442
|
end
|
386
443
|
|
444
|
+
# Generates a Rack response triple from a local file using `Rack::File`.
|
445
|
+
# Fills in `Content-Type` and `Content-Disposition` response headers from
|
446
|
+
# derivation options and file extension of the derivation result.
|
387
447
|
def file_response(file, env)
|
388
|
-
file.close
|
389
448
|
response = rack_file_response(file.path, env)
|
390
449
|
|
391
450
|
status = response[0]
|
392
451
|
|
452
|
+
content_type = type || response[1]["Content-Type"]
|
453
|
+
content_length = response[1]["Content-Length"]
|
454
|
+
content_range = response[1]["Content-Range"]
|
455
|
+
|
393
456
|
filename = self.filename
|
394
457
|
filename += File.extname(file.path) if File.extname(filename).empty?
|
395
458
|
|
396
459
|
headers = {}
|
397
|
-
headers["Content-Type"] =
|
460
|
+
headers["Content-Type"] = content_type if content_type
|
398
461
|
headers["Content-Disposition"] = content_disposition(filename)
|
399
|
-
headers["Content-Length"] =
|
400
|
-
headers["Content-Range"] =
|
462
|
+
headers["Content-Length"] = content_length
|
463
|
+
headers["Content-Range"] = content_range if content_range
|
401
464
|
headers["Accept-Ranges"] = "bytes"
|
402
465
|
|
403
466
|
body = Rack::BodyProxy.new(response[2]) { File.delete(file.path) }
|
404
467
|
|
468
|
+
file.close
|
469
|
+
|
405
470
|
[status, headers, body]
|
406
471
|
end
|
407
472
|
|
473
|
+
# This is called when `:upload` is enabled. Checks the storage for already
|
474
|
+
# uploaded derivation result, otherwise calls the derivation block and
|
475
|
+
# uploads the result. If the derivation result is already uploaded, uses
|
476
|
+
# the `rack_response` plugin to generate a Rack response triple.
|
408
477
|
def upload_response(env)
|
409
478
|
uploaded_file = derivation.retrieve
|
410
479
|
|
@@ -414,7 +483,11 @@ class Shrine
|
|
414
483
|
end
|
415
484
|
|
416
485
|
if upload_redirect
|
417
|
-
|
486
|
+
# we don't need the local derivation result here
|
487
|
+
if derivative
|
488
|
+
derivative.close
|
489
|
+
File.delete(derivative.path)
|
490
|
+
end
|
418
491
|
|
419
492
|
redirect_url = uploaded_file.url(upload_redirect_url_options)
|
420
493
|
|
@@ -423,6 +496,7 @@ class Shrine
|
|
423
496
|
if derivative
|
424
497
|
file_response(derivative, env)
|
425
498
|
else
|
499
|
+
uploaded_file.open(**upload_open_options)
|
426
500
|
uploaded_file.to_rack_response(
|
427
501
|
type: type,
|
428
502
|
disposition: disposition,
|
@@ -433,8 +507,10 @@ class Shrine
|
|
433
507
|
end
|
434
508
|
end
|
435
509
|
|
510
|
+
# We call `Rack::File` with no default `Content-Type`, and make sure we
|
511
|
+
# stay compatible with both Rack 2.x and 1.6.x.
|
436
512
|
def rack_file_response(path, env)
|
437
|
-
server = Rack::File.new("", {},
|
513
|
+
server = Rack::File.new("", {}, nil)
|
438
514
|
|
439
515
|
if Rack.release > "2"
|
440
516
|
server.serving(Rack::Request.new(env), path)
|
@@ -445,6 +521,8 @@ class Shrine
|
|
445
521
|
end
|
446
522
|
end
|
447
523
|
|
524
|
+
# Returns disposition and filename formatted for the `Content-Disposition`
|
525
|
+
# header.
|
448
526
|
def content_disposition(filename)
|
449
527
|
ContentDisposition.format(disposition: disposition, filename: filename)
|
450
528
|
end
|
@@ -455,29 +533,10 @@ class Shrine
|
|
455
533
|
|
456
534
|
def call
|
457
535
|
if upload
|
458
|
-
|
536
|
+
derivation.retrieve || derivation.upload
|
459
537
|
else
|
460
|
-
|
461
|
-
end
|
462
|
-
end
|
463
|
-
|
464
|
-
private
|
465
|
-
|
466
|
-
def local_result
|
467
|
-
derivation.generate
|
468
|
-
end
|
469
|
-
|
470
|
-
def upload_result
|
471
|
-
uploaded_file = derivation.retrieve
|
472
|
-
|
473
|
-
unless uploaded_file
|
474
|
-
derivative = derivation.generate
|
475
|
-
uploaded_file = derivation.upload(derivative)
|
476
|
-
|
477
|
-
derivative.unlink
|
538
|
+
derivation.generate
|
478
539
|
end
|
479
|
-
|
480
|
-
uploaded_file
|
481
540
|
end
|
482
541
|
end
|
483
542
|
|
@@ -494,6 +553,9 @@ class Shrine
|
|
494
553
|
|
495
554
|
private
|
496
555
|
|
556
|
+
# Calls the derivation block with the source file and derivation arguments.
|
557
|
+
# If a file object is given, passes that as the source file, otherwise
|
558
|
+
# downloads the source uploaded file.
|
497
559
|
def generate(file)
|
498
560
|
if download
|
499
561
|
with_downloaded(file) do |file|
|
@@ -508,6 +570,8 @@ class Shrine
|
|
508
570
|
end
|
509
571
|
end
|
510
572
|
|
573
|
+
# Massages the derivation result, ensuring it's opened in binary mode,
|
574
|
+
# rewinded and flushed to disk.
|
511
575
|
def normalize(derivative)
|
512
576
|
if derivative.is_a?(Tempfile)
|
513
577
|
derivative.open
|
@@ -534,17 +598,17 @@ class Shrine
|
|
534
598
|
end
|
535
599
|
end
|
536
600
|
|
601
|
+
# Downloads the source uploaded file from the storage.
|
537
602
|
def download_source
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
downloaded = true
|
543
|
-
yield file
|
603
|
+
begin
|
604
|
+
file = source.download(**download_options)
|
605
|
+
rescue *download_errors
|
606
|
+
raise Derivation::SourceNotFound, "source file \"#{source.id}\" was not found on storage :#{source.storage_key}"
|
544
607
|
end
|
545
|
-
|
546
|
-
|
547
|
-
|
608
|
+
|
609
|
+
yield file
|
610
|
+
ensure
|
611
|
+
file.close! if file
|
548
612
|
end
|
549
613
|
|
550
614
|
def derivation_block
|
@@ -559,16 +623,38 @@ class Shrine
|
|
559
623
|
class Derivation::Upload < Derivation::Command
|
560
624
|
delegate :upload_location, :upload_storage, :upload_options
|
561
625
|
|
626
|
+
# Uploads the derivation result to the dedicated location on the storage.
|
627
|
+
# If a file object is given, uploads that to the storage, otherwise calls
|
628
|
+
# the derivation block and uploads the result.
|
562
629
|
def call(derivative = nil)
|
563
|
-
derivative
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
630
|
+
with_derivative(derivative) do |uploadable|
|
631
|
+
uploader.upload uploadable,
|
632
|
+
location: upload_location,
|
633
|
+
upload_options: upload_options,
|
634
|
+
delete: false # disable delete_raw plugin
|
635
|
+
end
|
568
636
|
end
|
569
637
|
|
570
638
|
private
|
571
639
|
|
640
|
+
def with_derivative(derivative)
|
641
|
+
if derivative
|
642
|
+
# we want to keep the provided file open and rewinded
|
643
|
+
File.open(derivative.path, binmode: true) do |file|
|
644
|
+
yield file
|
645
|
+
end
|
646
|
+
else
|
647
|
+
# generate the derivative and delete it afterwards
|
648
|
+
begin
|
649
|
+
file = derivation.generate
|
650
|
+
yield file
|
651
|
+
ensure
|
652
|
+
file.close
|
653
|
+
File.delete(file.path)
|
654
|
+
end
|
655
|
+
end
|
656
|
+
end
|
657
|
+
|
572
658
|
def uploader
|
573
659
|
shrine_class.new(upload_storage)
|
574
660
|
end
|
@@ -577,6 +663,8 @@ class Shrine
|
|
577
663
|
class Derivation::Retrieve < Derivation::Command
|
578
664
|
delegate :upload_location, :upload_storage
|
579
665
|
|
666
|
+
# Returns a Shrine::UploadedFile object pointing to the uploaded derivation
|
667
|
+
# result it exists on the storage.
|
580
668
|
def call
|
581
669
|
if storage.exists?(upload_location)
|
582
670
|
shrine_class::UploadedFile.new(
|
@@ -596,6 +684,7 @@ class Shrine
|
|
596
684
|
class Derivation::Delete < Derivation::Command
|
597
685
|
delegate :upload_location, :upload_storage
|
598
686
|
|
687
|
+
# Deletes the uploaded derivation result from the storage.
|
599
688
|
def call
|
600
689
|
storage.delete(upload_location)
|
601
690
|
end
|
@@ -616,6 +705,8 @@ class Shrine
|
|
616
705
|
@secret_key = secret_key
|
617
706
|
end
|
618
707
|
|
708
|
+
# Returns a URL with the `signature` query parameter generated from the
|
709
|
+
# given path components and query parameters.
|
619
710
|
def signed_url(*components, params)
|
620
711
|
path = Rack::Utils.escape_path(components.join("/"))
|
621
712
|
query = Rack::Utils.build_query(params)
|
@@ -627,6 +718,10 @@ class Shrine
|
|
627
718
|
"#{path}?#{query}"
|
628
719
|
end
|
629
720
|
|
721
|
+
# Calculcates the signature from the URL and checks whether it matches the
|
722
|
+
# value in the `signature` query parameter. Raises `InvalidSignature` if
|
723
|
+
# the `signature` parameter is missing or its value doesn't match the
|
724
|
+
# calculated signature.
|
630
725
|
def verify_url(path_with_query)
|
631
726
|
path, query = path_with_query.split("?")
|
632
727
|
|
@@ -645,6 +740,8 @@ class Shrine
|
|
645
740
|
end
|
646
741
|
end
|
647
742
|
|
743
|
+
# Uses HMAC-SHA-256 algorithm to generate a signature from the given string
|
744
|
+
# using the secret key.
|
648
745
|
def generate_signature(string)
|
649
746
|
OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, secret_key, string)
|
650
747
|
end
|