shrine 2.19.4 → 3.0.0.alpha
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of shrine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +299 -11
- data/README.md +9 -3
- data/doc/advantages.md +1 -1
- data/doc/carrierwave.md +4 -4
- data/doc/creating_persistence_plugins.md +172 -0
- data/doc/creating_plugins.md +1 -1
- data/doc/creating_storages.md +3 -1
- data/doc/design.md +2 -2
- data/doc/direct_s3.md +0 -22
- data/doc/paperclip.md +3 -3
- data/doc/plugins/activerecord.md +211 -42
- data/doc/plugins/atomic_helpers.md +153 -0
- data/doc/plugins/column.md +90 -0
- data/doc/plugins/derivation_endpoint.md +54 -62
- data/doc/plugins/derivatives.md +752 -0
- data/doc/plugins/entity.md +204 -0
- data/doc/plugins/infer_extension.md +8 -8
- data/doc/plugins/instrumentation.md +33 -13
- data/doc/plugins/keep_files.md +5 -15
- data/doc/plugins/model.md +157 -0
- data/doc/plugins/presign_endpoint.md +2 -1
- data/doc/plugins/refresh_metadata.md +44 -7
- data/doc/plugins/sequel.md +190 -33
- data/doc/plugins/{default_url_options.md → url_options.md} +5 -5
- data/doc/processing.md +1 -1
- data/doc/release_notes/1.1.0.md +2 -2
- data/doc/release_notes/2.15.0.md +1 -1
- data/doc/storage/s3.md +2 -2
- data/doc/testing.md +1 -1
- data/lib/shrine.rb +72 -138
- data/lib/shrine/attacher.rb +272 -176
- data/lib/shrine/attachment.rb +2 -42
- data/lib/shrine/plugins/activerecord.rb +103 -26
- data/lib/shrine/plugins/add_metadata.rb +9 -10
- data/lib/shrine/plugins/atomic_helpers.rb +111 -0
- data/lib/shrine/plugins/attacher_options.rb +55 -0
- data/lib/shrine/plugins/backgrounding.rb +147 -115
- data/lib/shrine/plugins/cached_attachment_data.rb +6 -9
- data/lib/shrine/plugins/column.rb +104 -0
- data/lib/shrine/plugins/data_uri.rb +35 -38
- data/lib/shrine/plugins/default_storage.rb +18 -12
- data/lib/shrine/plugins/default_url.rb +11 -21
- data/lib/shrine/plugins/default_url_options.rb +3 -30
- data/lib/shrine/plugins/delete_raw.rb +9 -13
- data/lib/shrine/plugins/derivation_endpoint.rb +75 -114
- data/lib/shrine/plugins/derivatives.rb +576 -0
- data/lib/shrine/plugins/determine_mime_type.rb +3 -15
- data/lib/shrine/plugins/download_endpoint.rb +83 -131
- data/lib/shrine/plugins/dynamic_storage.rb +4 -8
- data/lib/shrine/plugins/entity.rb +128 -0
- data/lib/shrine/plugins/form_assign.rb +107 -0
- data/lib/shrine/plugins/included.rb +4 -3
- data/lib/shrine/plugins/infer_extension.rb +10 -17
- data/lib/shrine/plugins/instrumentation.rb +45 -25
- data/lib/shrine/plugins/keep_files.rb +2 -12
- data/lib/shrine/plugins/metadata_attributes.rb +15 -14
- data/lib/shrine/plugins/model.rb +137 -0
- data/lib/shrine/plugins/module_include.rb +2 -0
- data/lib/shrine/plugins/presign_endpoint.rb +1 -15
- data/lib/shrine/plugins/pretty_location.rb +5 -5
- data/lib/shrine/plugins/processing.rb +21 -6
- data/lib/shrine/plugins/rack_file.rb +1 -39
- data/lib/shrine/plugins/rack_response.rb +14 -7
- data/lib/shrine/plugins/recache.rb +5 -2
- data/lib/shrine/plugins/refresh_metadata.rb +12 -8
- data/lib/shrine/plugins/remote_url.rb +44 -53
- data/lib/shrine/plugins/remove_attachment.rb +7 -2
- data/lib/shrine/plugins/remove_invalid.rb +8 -4
- data/lib/shrine/plugins/restore_cached_data.rb +12 -4
- data/lib/shrine/plugins/sequel.rb +115 -27
- data/lib/shrine/plugins/signature.rb +2 -7
- data/lib/shrine/plugins/store_dimensions.rb +13 -27
- data/lib/shrine/plugins/upload_endpoint.rb +14 -15
- data/lib/shrine/plugins/upload_options.rb +9 -8
- data/lib/shrine/plugins/url_options.rb +33 -0
- data/lib/shrine/plugins/validation.rb +87 -0
- data/lib/shrine/plugins/validation_helpers.rb +33 -54
- data/lib/shrine/plugins/versions.rb +106 -84
- data/lib/shrine/storage/file_system.rb +32 -57
- data/lib/shrine/storage/linter.rb +9 -1
- data/lib/shrine/storage/memory.rb +42 -0
- data/lib/shrine/storage/s3.rb +38 -146
- data/lib/shrine/uploaded_file.rb +22 -29
- data/lib/shrine/version.rb +4 -4
- data/shrine.gemspec +2 -3
- metadata +27 -54
- 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/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
data/doc/plugins/hooks.md
DELETED
@@ -1,58 +0,0 @@
|
|
1
|
-
# Hooks
|
2
|
-
|
3
|
-
The [`hooks`][hooks] plugin allows you to trigger some code around
|
4
|
-
processing/storing/deleting of each file.
|
5
|
-
|
6
|
-
```rb
|
7
|
-
plugin :hooks
|
8
|
-
```
|
9
|
-
|
10
|
-
Shrine uses instance methods for hooks. To define a hook for an uploader, you
|
11
|
-
just add an instance method to the uploader:
|
12
|
-
|
13
|
-
```rb
|
14
|
-
class ImageUploader < Shrine
|
15
|
-
def around_process(io, context)
|
16
|
-
super
|
17
|
-
rescue
|
18
|
-
ExceptionNotifier.processing_failed(io, context)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
```
|
22
|
-
|
23
|
-
Each hook will be called with 2 arguments, `io` and `context`. You should
|
24
|
-
always call `super` when overriding a hook, as other plugins may be using hooks
|
25
|
-
internally, and without `super` those wouldn't get executed.
|
26
|
-
|
27
|
-
Shrine calls hooks in the following order when uploading a file:
|
28
|
-
|
29
|
-
* `before_upload`
|
30
|
-
* `around_upload`
|
31
|
-
- `before_process`
|
32
|
-
- `around_process`
|
33
|
-
- `after_process`
|
34
|
-
- `before_store`
|
35
|
-
- `around_store`
|
36
|
-
- `after_store`
|
37
|
-
* `after_upload`
|
38
|
-
|
39
|
-
Shrine calls hooks in the following order when deleting a file:
|
40
|
-
|
41
|
-
* `before_delete`
|
42
|
-
* `around_delete`
|
43
|
-
* `after_delete`
|
44
|
-
|
45
|
-
By default every `around_*` hook returns the result of the corresponding
|
46
|
-
operation:
|
47
|
-
|
48
|
-
```rb
|
49
|
-
class ImageUploader < Shrine
|
50
|
-
def around_store(io, context)
|
51
|
-
result = super
|
52
|
-
result.class #=> Shrine::UploadedFile
|
53
|
-
result # it's good to always return the result for consistent behaviour
|
54
|
-
end
|
55
|
-
end
|
56
|
-
```
|
57
|
-
|
58
|
-
[hooks]: /lib/shrine/plugins/hooks.rb
|
data/doc/plugins/logging.md
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
# Logging
|
2
|
-
|
3
|
-
The [`logging`][logging] plugin logs any storing/processing/deleting that is
|
4
|
-
performed.
|
5
|
-
|
6
|
-
```rb
|
7
|
-
plugin :logging
|
8
|
-
```
|
9
|
-
|
10
|
-
This plugin is useful when you want to have overview of what exactly is going
|
11
|
-
on, or you simply want to have it logged for future debugging. By default the
|
12
|
-
logging output looks something like this:
|
13
|
-
|
14
|
-
```
|
15
|
-
2015-10-09T20:06:06.676Z #25602: STORE[cache] ImageUploader[:avatar] User[29543] 1 file (0.1s)
|
16
|
-
2015-10-09T20:06:06.854Z #25602: PROCESS[store]: ImageUploader[:avatar] User[29543] 1-3 files (0.22s)
|
17
|
-
2015-10-09T20:06:07.133Z #25602: DELETE[destroyed]: ImageUploader[:avatar] User[29543] 3 files (0.07s)
|
18
|
-
```
|
19
|
-
|
20
|
-
The plugin accepts the following options:
|
21
|
-
|
22
|
-
| Option | Description |
|
23
|
-
| :-------- | :---------- |
|
24
|
-
| `:format` | This allows you to change the logging output into something that may be easier to grep. Accepts `:human` (default), `:json` and `:logfmt`. |
|
25
|
-
| `:stream` | The default logging stream is `$stdout`, but you may want to change it, e.g. if you log into a file. This option is passed directly to `Logger.new` (from the "logger" Ruby standard library). |
|
26
|
-
| `:logger` | This allows you to change the logger entirely. This is useful for example in Rails applications, where you might want to assign this option to `Rails.logger`. |
|
27
|
-
|
28
|
-
The default format is probably easiest to read, but may not be easiest to grep.
|
29
|
-
If this is important to you, you can switch to another format:
|
30
|
-
|
31
|
-
```rb
|
32
|
-
plugin :logging, format: :json
|
33
|
-
# {"action":"upload","phase":"cache","uploader":"ImageUploader","attachment":"avatar",...}
|
34
|
-
|
35
|
-
plugin :logging, format: :logfmt
|
36
|
-
# action=upload phase=cache uploader=ImageUploader attachment=avatar record_class=User ...
|
37
|
-
```
|
38
|
-
|
39
|
-
Logging is by default disabled in tests, but you can enable it by setting
|
40
|
-
`Shrine.logger.level = Logger::INFO`.
|
41
|
-
|
42
|
-
[logging]: /lib/shrine/plugins/logging.rb
|
@@ -1,60 +0,0 @@
|
|
1
|
-
# Migration Helpers
|
2
|
-
|
3
|
-
The [`migration_helpers`][migration_helpers] plugin gives the attacher
|
4
|
-
additional helper methods which are convenient when doing file migrations.
|
5
|
-
|
6
|
-
The plugin also allows convenient delegating to these methods through the
|
7
|
-
model, by setting `:delegate`:
|
8
|
-
|
9
|
-
```rb
|
10
|
-
plugin :migration_helpers, delegate: true
|
11
|
-
```
|
12
|
-
|
13
|
-
## `update_stored`
|
14
|
-
|
15
|
-
This method updates the record's attachment with the result of the given block.
|
16
|
-
|
17
|
-
```rb
|
18
|
-
user.avatar_attacher.update_stored do |avatar|
|
19
|
-
user.avatar_attacher.store.upload(avatar) # saved to the record
|
20
|
-
end
|
21
|
-
|
22
|
-
# with model delegation
|
23
|
-
user.update_avatar do |avatar|
|
24
|
-
user.avatar_store.upload(avatar) # saved to the record
|
25
|
-
end
|
26
|
-
```
|
27
|
-
|
28
|
-
The block will get triggered _only_ if the attachment is present and not
|
29
|
-
cached, *and* will save the record only if the record's attachment hasn't
|
30
|
-
changed in the time it took to execute the block. This method is most useful
|
31
|
-
for adding/removing versions and changing locations of files.
|
32
|
-
|
33
|
-
## `cached?` and `stored?`
|
34
|
-
|
35
|
-
These methods return true if attachment exists and is cached/stored:
|
36
|
-
|
37
|
-
```rb
|
38
|
-
user.avatar_attacher.cached? # user.avatar && user.avatar_attacher.cache.uploaded?(user.avatar)
|
39
|
-
user.avatar_attacher.stored? # user.avatar && user.avatar_attacher.store.uploaded?(user.avatar)
|
40
|
-
|
41
|
-
# with model delegation
|
42
|
-
user.avatar_cached?
|
43
|
-
user.avatar_stored?
|
44
|
-
```
|
45
|
-
|
46
|
-
## `attachment_cache` and `attachment_store`
|
47
|
-
|
48
|
-
These methods return cache and store uploaders used by the underlying attacher:
|
49
|
-
|
50
|
-
```rb
|
51
|
-
# these methods already exist without migration_helpers
|
52
|
-
user.avatar_attacher.cache #=> #<Shrine @storage_key=:cache @storage=#<Shrine::Storage::FileSystem @directory=public/uploads>>
|
53
|
-
user.avatar_attacher.store #=> #<Shrine @storage_key=:store @storage=#<Shrine::Storage::S3:0x007fb8343397c8 @bucket=#<Aws::S3::Bucket name="foo">>>
|
54
|
-
|
55
|
-
# with model delegation
|
56
|
-
user.avatar_cache
|
57
|
-
user.avatar_store
|
58
|
-
```
|
59
|
-
|
60
|
-
[migration_helpers]: /lib/shrine/plugins/migration_helpers.rb
|
data/doc/plugins/moving.md
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
# Moving
|
2
|
-
|
3
|
-
The [`moving`][moving] plugin will *move* files to storages instead of copying
|
4
|
-
them, when the storage supports it. For FileSystem this will issue a `mv`
|
5
|
-
command, which is instantaneous regardless of the filesize, so in that case
|
6
|
-
loading this plugin can significantly speed up the attachment process.
|
7
|
-
|
8
|
-
```rb
|
9
|
-
plugin :moving
|
10
|
-
```
|
11
|
-
|
12
|
-
By default files will be moved whenever the storage supports it. If you want
|
13
|
-
moving to happen only for certain storages, you can set `:storages`:
|
14
|
-
|
15
|
-
```rb
|
16
|
-
plugin :moving, storages: [:cache]
|
17
|
-
```
|
18
|
-
|
19
|
-
[moving]: /lib/shrine/plugins/moving.rb
|
data/doc/plugins/multi_delete.md
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
# Multi Delete
|
2
|
-
|
3
|
-
The [`multi_delete`][multi_delete] plugins allows you to leverage your
|
4
|
-
storage's multi delete capabilities.
|
5
|
-
|
6
|
-
```rb
|
7
|
-
plugin :multi_delete
|
8
|
-
```
|
9
|
-
|
10
|
-
This plugin allows you pass an array of files to `Shrine#delete`.
|
11
|
-
|
12
|
-
```rb
|
13
|
-
uploader.delete([file1, file2, file3])
|
14
|
-
```
|
15
|
-
|
16
|
-
Now if you're using Storage::S3, deleting an array of files will issue a single
|
17
|
-
HTTP request. Some other storages may support multi deletes as well. The
|
18
|
-
`versions` plugin uses this plugin for deleting multiple versions at once.
|
19
|
-
|
20
|
-
[multi_delete]: /lib/shrine/plugins/multi_delete.rb
|
data/doc/plugins/parallelize.md
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
# Parallelize
|
2
|
-
|
3
|
-
The [`parallelize`][parallelize] plugin parallelizes uploads and deletes of
|
4
|
-
multiple versions using threads.
|
5
|
-
|
6
|
-
```rb
|
7
|
-
plugin :parallelize
|
8
|
-
```
|
9
|
-
|
10
|
-
By default a pool of 3 threads will be used, but you can change that:
|
11
|
-
|
12
|
-
```rb
|
13
|
-
plugin :parallelize, threads: 5
|
14
|
-
```
|
15
|
-
|
16
|
-
[parallelize]: /lib/shrine/plugins/parallelize.rb
|
data/doc/plugins/parsed_json.md
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
# Parsed JSON
|
2
|
-
|
3
|
-
The [`parsed_json`][parsed_json] plugin is suitable for the case when your
|
4
|
-
framework is automatically parsing JSON query parameters, allowing you to
|
5
|
-
assign cached files with hashes/arrays.
|
6
|
-
|
7
|
-
```rb
|
8
|
-
plugin :parsed_json
|
9
|
-
```
|
10
|
-
|
11
|
-
```rb
|
12
|
-
photo.image = {
|
13
|
-
"id" => "sdf90s2443.jpg",
|
14
|
-
"storage" => "cache",
|
15
|
-
"metadata" => {
|
16
|
-
"filename" => "nature.jpg",
|
17
|
-
"size" => 29475,
|
18
|
-
"mime_type" => "image/jpeg",
|
19
|
-
}
|
20
|
-
}
|
21
|
-
```
|
22
|
-
|
23
|
-
[parsed_json]: /lib/shrine/plugins/parsed_json.rb
|
@@ -1,5 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
Shrine.deprecation("The background_helpers plugin has been renamed to \"backgrounding\". Loading the plugin through \"background_helpers\" will stop working in Shrine 3.")
|
4
|
-
require "shrine/plugins/backgrounding"
|
5
|
-
Shrine::Plugins.register_plugin(:background_helpers, Shrine::Plugins::Backgrounding)
|
@@ -1,90 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
Shrine.deprecation("The backup plugin has been deprecated, the new preferred way to implement mirroring is via the instrumentation plugin – see https://github.com/shrinerb/shrine/wiki/Mirroring-Uploads. The backup plugin will be removed in Shrine 3.")
|
4
|
-
|
5
|
-
class Shrine
|
6
|
-
module Plugins
|
7
|
-
# Documentation lives in [doc/plugins/backup.md] on GitHub.
|
8
|
-
#
|
9
|
-
# [doc/plugins/backup.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/backup.md
|
10
|
-
module Backup
|
11
|
-
def self.configure(uploader, opts = {})
|
12
|
-
uploader.opts[:backup_storage] = opts.fetch(:storage, uploader.opts[:backup_storage])
|
13
|
-
uploader.opts[:backup_delete] = opts.fetch(:delete, uploader.opts.fetch(:backup_delete, true))
|
14
|
-
|
15
|
-
raise Error, "The :storage option is required for backup plugin" if uploader.opts[:backup_storage].nil?
|
16
|
-
end
|
17
|
-
|
18
|
-
module AttacherMethods
|
19
|
-
# Backs up the stored file after promoting.
|
20
|
-
def promote(*)
|
21
|
-
result = super
|
22
|
-
store_backup!(result) if result
|
23
|
-
result
|
24
|
-
end
|
25
|
-
|
26
|
-
# Deletes the backup file in addition to the stored file.
|
27
|
-
def replace
|
28
|
-
result = super
|
29
|
-
delete_backup!(@old) if result && delete_backup?
|
30
|
-
result
|
31
|
-
end
|
32
|
-
|
33
|
-
# Deletes the backup file in addition to the stored file.
|
34
|
-
def destroy
|
35
|
-
result = super
|
36
|
-
delete_backup!(get) if result && delete_backup?
|
37
|
-
result
|
38
|
-
end
|
39
|
-
|
40
|
-
# Returns a copy of the given uploaded file with storage changed to
|
41
|
-
# backup storage.
|
42
|
-
def backup_file(uploaded_file)
|
43
|
-
uploaded_file(uploaded_file.to_json) do |file|
|
44
|
-
file.data["storage"] = backup_storage.to_s
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
private
|
49
|
-
|
50
|
-
# Upload the stored file to the backup storage.
|
51
|
-
def store_backup!(stored_file)
|
52
|
-
options = _equalize_phase_and_action(action: :backup, move: false)
|
53
|
-
backup_store.upload(stored_file, context.merge(options))
|
54
|
-
end
|
55
|
-
|
56
|
-
# Deleted the stored file from the backup storage.
|
57
|
-
def delete_backup!(deleted_file)
|
58
|
-
_delete(backup_file(deleted_file), action: :backup)
|
59
|
-
end
|
60
|
-
|
61
|
-
def backup_store
|
62
|
-
@backup_store ||= shrine_class.new(backup_storage)
|
63
|
-
end
|
64
|
-
|
65
|
-
def backup_storage
|
66
|
-
shrine_class.opts[:backup_storage]
|
67
|
-
end
|
68
|
-
|
69
|
-
def delete_backup?
|
70
|
-
shrine_class.opts[:backup_delete]
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
module InstanceMethods
|
75
|
-
private
|
76
|
-
|
77
|
-
# We preserve the location when uploading from store to backup.
|
78
|
-
def get_location(io, context)
|
79
|
-
if context[:action] == :backup
|
80
|
-
io.id
|
81
|
-
else
|
82
|
-
super
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
register_plugin(:backup, Backup)
|
89
|
-
end
|
90
|
-
end
|
data/lib/shrine/plugins/copy.rb
DELETED
@@ -1,50 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
Shrine.deprecation("The copy plugin is deprecated and will be removed in Shrine 3.")
|
4
|
-
|
5
|
-
class Shrine
|
6
|
-
module Plugins
|
7
|
-
# Documentation lives in [doc/plugins/copy.md] on GitHub.
|
8
|
-
#
|
9
|
-
# [doc/plugins/copy.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/copy.md
|
10
|
-
module Copy
|
11
|
-
module AttachmentMethods
|
12
|
-
def initialize(*)
|
13
|
-
super
|
14
|
-
|
15
|
-
name = attachment_name
|
16
|
-
|
17
|
-
define_method :initialize_copy do |record|
|
18
|
-
super(record)
|
19
|
-
instance_variable_set(:"@#{name}_attacher", nil) # reload the attacher
|
20
|
-
attacher = send(:"#{name}_attacher")
|
21
|
-
attacher.send(:write, nil) # remove original attachment
|
22
|
-
attacher.copy(record.public_send(:"#{name}_attacher"))
|
23
|
-
end
|
24
|
-
|
25
|
-
# Fix for JRuby
|
26
|
-
private :initialize_copy
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
module AttacherMethods
|
31
|
-
def copy(attacher)
|
32
|
-
options = {action: :copy, move: false}
|
33
|
-
|
34
|
-
copied_attachment = if attacher.cached?
|
35
|
-
cache!(attacher.get, **options)
|
36
|
-
elsif attacher.stored?
|
37
|
-
store!(attacher.get, **options)
|
38
|
-
else
|
39
|
-
nil
|
40
|
-
end
|
41
|
-
|
42
|
-
@old = get
|
43
|
-
_set(copied_attachment)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
register_plugin(:copy, Copy)
|
49
|
-
end
|
50
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Shrine
|
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
|
8
|
-
module DeletePromoted
|
9
|
-
module AttacherMethods
|
10
|
-
def promote(uploaded_file = get, **options)
|
11
|
-
result = super
|
12
|
-
_delete(uploaded_file, action: :promote)
|
13
|
-
result
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
register_plugin(:delete_promoted, DeletePromoted)
|
19
|
-
end
|
20
|
-
end
|
@@ -1,217 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
Shrine.deprecation("The direct_upload plugin has been deprecated in favor of upload_endpoint and presign_endpoint plugins. The direct_upload plugin will be removed in Shrine 3.")
|
4
|
-
|
5
|
-
require "roda"
|
6
|
-
require "json"
|
7
|
-
|
8
|
-
class Shrine
|
9
|
-
module Plugins
|
10
|
-
# Documentation lives in [doc/plugins/direct_upload.md] on GitHub.
|
11
|
-
#
|
12
|
-
# [doc/plugins/direct_upload.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/direct_upload.md
|
13
|
-
module DirectUpload
|
14
|
-
def self.load_dependencies(uploader, *)
|
15
|
-
uploader.plugin :rack_file
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.configure(uploader, opts = {})
|
19
|
-
uploader.opts[:direct_upload_allowed_storages] = opts.fetch(:allowed_storages, uploader.opts.fetch(:direct_upload_allowed_storages, [:cache]))
|
20
|
-
uploader.opts[:direct_upload_presign_options] = opts.fetch(:presign_options, uploader.opts.fetch(:direct_upload_presign_options, {}))
|
21
|
-
uploader.opts[:direct_upload_presign_location] = opts.fetch(:presign_location, uploader.opts[:direct_upload_presign_location])
|
22
|
-
uploader.opts[:direct_upload_max_size] = opts.fetch(:max_size, uploader.opts[:direct_upload_max_size])
|
23
|
-
|
24
|
-
uploader.assign_upload_endpoint(App) unless uploader.const_defined?(:UploadEndpoint)
|
25
|
-
end
|
26
|
-
|
27
|
-
module ClassMethods
|
28
|
-
# Assigns the subclass a copy of the upload endpoint class.
|
29
|
-
def inherited(subclass)
|
30
|
-
super
|
31
|
-
subclass.assign_upload_endpoint(self::UploadEndpoint)
|
32
|
-
end
|
33
|
-
|
34
|
-
# Assigns the subclassed endpoint as the `UploadEndpoint` constant.
|
35
|
-
def assign_upload_endpoint(klass)
|
36
|
-
endpoint_class = Class.new(klass)
|
37
|
-
endpoint_class.opts[:shrine_class] = self
|
38
|
-
const_set(:UploadEndpoint, endpoint_class)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
# Routes incoming requests. It first asserts that the storage is existent
|
43
|
-
# and allowed, then the filesize isn't too large. Afterwards it proceeds
|
44
|
-
# with the file upload and returns the uploaded file as JSON.
|
45
|
-
class App < Roda
|
46
|
-
plugin :default_headers, "Content-Type"=>"application/json"
|
47
|
-
plugin :placeholder_string_matchers if Gem::Version.new(Roda::RodaVersion) >= Gem::Version.new("3.0.0")
|
48
|
-
|
49
|
-
route do |r|
|
50
|
-
r.on ":storage" do |storage_key|
|
51
|
-
@uploader = get_uploader(storage_key)
|
52
|
-
|
53
|
-
r.post ["upload", ":name"] do |name|
|
54
|
-
file = get_file
|
55
|
-
context = get_context(name)
|
56
|
-
|
57
|
-
uploaded_file = upload(file, context)
|
58
|
-
|
59
|
-
json uploaded_file
|
60
|
-
end
|
61
|
-
|
62
|
-
r.get "presign" do
|
63
|
-
location = get_presign_location
|
64
|
-
options = get_presign_options
|
65
|
-
|
66
|
-
presign_data = generate_presign(location, options)
|
67
|
-
response.headers["Cache-Control"] = "no-store"
|
68
|
-
|
69
|
-
json presign_data
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
private
|
75
|
-
|
76
|
-
attr_reader :uploader
|
77
|
-
|
78
|
-
# Instantiates the uploader, checking first if the storage is allowed.
|
79
|
-
def get_uploader(storage_key)
|
80
|
-
allow_storage!(storage_key)
|
81
|
-
shrine_class.new(storage_key.to_sym)
|
82
|
-
end
|
83
|
-
|
84
|
-
# Retrieves the context for the upload.
|
85
|
-
def get_context(name)
|
86
|
-
context = {action: :cache, phase: :cache}
|
87
|
-
|
88
|
-
if name != "upload"
|
89
|
-
Shrine.deprecation("The \"POST /:storage/:name\" route of the direct_upload plugin is deprecated, and it will be removed in Shrine 3. Use \"POST /:storage/upload\" instead.")
|
90
|
-
context[:name] = name
|
91
|
-
end
|
92
|
-
|
93
|
-
unless presign_storage?
|
94
|
-
context[:location] = request.params["key"]
|
95
|
-
end
|
96
|
-
|
97
|
-
context
|
98
|
-
end
|
99
|
-
|
100
|
-
# Uploads the file to the requested storage.
|
101
|
-
def upload(file, context)
|
102
|
-
uploader.upload(file, context)
|
103
|
-
end
|
104
|
-
|
105
|
-
# Generates a unique location, or calls `:presign_location`.
|
106
|
-
def get_presign_location
|
107
|
-
if presign_location
|
108
|
-
presign_location.call(request)
|
109
|
-
else
|
110
|
-
extension = request.params["extension"]
|
111
|
-
extension.prepend(".") if extension && !extension.start_with?(".")
|
112
|
-
uploader.send(:generate_uid, nil) + extension.to_s
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
# Returns dynamic options for generating the presign.
|
117
|
-
def get_presign_options
|
118
|
-
options = presign_options
|
119
|
-
options = options.call(request) if options.respond_to?(:call)
|
120
|
-
options || {}
|
121
|
-
end
|
122
|
-
|
123
|
-
# Generates the presign hash for the request.
|
124
|
-
def generate_presign(location, options)
|
125
|
-
if presign_storage?
|
126
|
-
generate_real_presign(location, options)
|
127
|
-
else
|
128
|
-
generate_fake_presign(location, options)
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
# Generates a presign by calling the storage.
|
133
|
-
def generate_real_presign(location, options)
|
134
|
-
signature = uploader.storage.presign(location, options)
|
135
|
-
{url: signature.url, fields: signature.fields}
|
136
|
-
end
|
137
|
-
|
138
|
-
# Generates a presign that points to the direct upload endpoint.
|
139
|
-
def generate_fake_presign(location, options)
|
140
|
-
url = request.url.sub(/presign[^\/]*$/, "upload")
|
141
|
-
{url: url, fields: {key: location}}
|
142
|
-
end
|
143
|
-
|
144
|
-
# Returns true if the storage supports presigns.
|
145
|
-
def presign_storage?
|
146
|
-
uploader.storage.respond_to?(:presign)
|
147
|
-
end
|
148
|
-
|
149
|
-
# Halts the request if storage is not allowed.
|
150
|
-
def allow_storage!(storage_key)
|
151
|
-
if !allowed_storages.map(&:to_s).include?(storage_key)
|
152
|
-
error! 403, "Storage #{storage_key.inspect} is not allowed."
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
# Returns the Rack file wrapped in an IO-like object. If "file" is
|
157
|
-
# missing or is too big, the request is halted.
|
158
|
-
def get_file
|
159
|
-
file = require_param!("file")
|
160
|
-
error! 400, "The \"file\" query parameter is not a file." if !(file.is_a?(Hash) && file.key?(:tempfile))
|
161
|
-
check_filesize!(file[:tempfile]) if max_size
|
162
|
-
|
163
|
-
RackFile::UploadedFile.new(file)
|
164
|
-
end
|
165
|
-
|
166
|
-
# If the file is too big, deletes the file and halts the request.
|
167
|
-
def check_filesize!(file)
|
168
|
-
if file.size > max_size
|
169
|
-
file.delete
|
170
|
-
megabytes = max_size.to_f / 1024 / 1024
|
171
|
-
error! 413, "The file is too big (maximum size is #{megabytes} MB)."
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
# Loudly requires the param.
|
176
|
-
def require_param!(name)
|
177
|
-
request.params.fetch(name)
|
178
|
-
rescue KeyError
|
179
|
-
error! 400, "Missing query parameter: #{name.inspect}"
|
180
|
-
end
|
181
|
-
|
182
|
-
# Halts the request with the error message.
|
183
|
-
def error!(status, message)
|
184
|
-
response.status = status
|
185
|
-
response.write({error: message}.to_json)
|
186
|
-
request.halt
|
187
|
-
end
|
188
|
-
|
189
|
-
def json(object)
|
190
|
-
object.to_json
|
191
|
-
end
|
192
|
-
|
193
|
-
def shrine_class
|
194
|
-
opts[:shrine_class]
|
195
|
-
end
|
196
|
-
|
197
|
-
def allowed_storages
|
198
|
-
shrine_class.opts[:direct_upload_allowed_storages]
|
199
|
-
end
|
200
|
-
|
201
|
-
def presign_options
|
202
|
-
shrine_class.opts[:direct_upload_presign_options]
|
203
|
-
end
|
204
|
-
|
205
|
-
def presign_location
|
206
|
-
shrine_class.opts[:direct_upload_presign_location]
|
207
|
-
end
|
208
|
-
|
209
|
-
def max_size
|
210
|
-
shrine_class.opts[:direct_upload_max_size]
|
211
|
-
end
|
212
|
-
end
|
213
|
-
end
|
214
|
-
|
215
|
-
register_plugin(:direct_upload, DirectUpload)
|
216
|
-
end
|
217
|
-
end
|