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.

Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +299 -11
  3. data/README.md +9 -3
  4. data/doc/advantages.md +1 -1
  5. data/doc/carrierwave.md +4 -4
  6. data/doc/creating_persistence_plugins.md +172 -0
  7. data/doc/creating_plugins.md +1 -1
  8. data/doc/creating_storages.md +3 -1
  9. data/doc/design.md +2 -2
  10. data/doc/direct_s3.md +0 -22
  11. data/doc/paperclip.md +3 -3
  12. data/doc/plugins/activerecord.md +211 -42
  13. data/doc/plugins/atomic_helpers.md +153 -0
  14. data/doc/plugins/column.md +90 -0
  15. data/doc/plugins/derivation_endpoint.md +54 -62
  16. data/doc/plugins/derivatives.md +752 -0
  17. data/doc/plugins/entity.md +204 -0
  18. data/doc/plugins/infer_extension.md +8 -8
  19. data/doc/plugins/instrumentation.md +33 -13
  20. data/doc/plugins/keep_files.md +5 -15
  21. data/doc/plugins/model.md +157 -0
  22. data/doc/plugins/presign_endpoint.md +2 -1
  23. data/doc/plugins/refresh_metadata.md +44 -7
  24. data/doc/plugins/sequel.md +190 -33
  25. data/doc/plugins/{default_url_options.md → url_options.md} +5 -5
  26. data/doc/processing.md +1 -1
  27. data/doc/release_notes/1.1.0.md +2 -2
  28. data/doc/release_notes/2.15.0.md +1 -1
  29. data/doc/storage/s3.md +2 -2
  30. data/doc/testing.md +1 -1
  31. data/lib/shrine.rb +72 -138
  32. data/lib/shrine/attacher.rb +272 -176
  33. data/lib/shrine/attachment.rb +2 -42
  34. data/lib/shrine/plugins/activerecord.rb +103 -26
  35. data/lib/shrine/plugins/add_metadata.rb +9 -10
  36. data/lib/shrine/plugins/atomic_helpers.rb +111 -0
  37. data/lib/shrine/plugins/attacher_options.rb +55 -0
  38. data/lib/shrine/plugins/backgrounding.rb +147 -115
  39. data/lib/shrine/plugins/cached_attachment_data.rb +6 -9
  40. data/lib/shrine/plugins/column.rb +104 -0
  41. data/lib/shrine/plugins/data_uri.rb +35 -38
  42. data/lib/shrine/plugins/default_storage.rb +18 -12
  43. data/lib/shrine/plugins/default_url.rb +11 -21
  44. data/lib/shrine/plugins/default_url_options.rb +3 -30
  45. data/lib/shrine/plugins/delete_raw.rb +9 -13
  46. data/lib/shrine/plugins/derivation_endpoint.rb +75 -114
  47. data/lib/shrine/plugins/derivatives.rb +576 -0
  48. data/lib/shrine/plugins/determine_mime_type.rb +3 -15
  49. data/lib/shrine/plugins/download_endpoint.rb +83 -131
  50. data/lib/shrine/plugins/dynamic_storage.rb +4 -8
  51. data/lib/shrine/plugins/entity.rb +128 -0
  52. data/lib/shrine/plugins/form_assign.rb +107 -0
  53. data/lib/shrine/plugins/included.rb +4 -3
  54. data/lib/shrine/plugins/infer_extension.rb +10 -17
  55. data/lib/shrine/plugins/instrumentation.rb +45 -25
  56. data/lib/shrine/plugins/keep_files.rb +2 -12
  57. data/lib/shrine/plugins/metadata_attributes.rb +15 -14
  58. data/lib/shrine/plugins/model.rb +137 -0
  59. data/lib/shrine/plugins/module_include.rb +2 -0
  60. data/lib/shrine/plugins/presign_endpoint.rb +1 -15
  61. data/lib/shrine/plugins/pretty_location.rb +5 -5
  62. data/lib/shrine/plugins/processing.rb +21 -6
  63. data/lib/shrine/plugins/rack_file.rb +1 -39
  64. data/lib/shrine/plugins/rack_response.rb +14 -7
  65. data/lib/shrine/plugins/recache.rb +5 -2
  66. data/lib/shrine/plugins/refresh_metadata.rb +12 -8
  67. data/lib/shrine/plugins/remote_url.rb +44 -53
  68. data/lib/shrine/plugins/remove_attachment.rb +7 -2
  69. data/lib/shrine/plugins/remove_invalid.rb +8 -4
  70. data/lib/shrine/plugins/restore_cached_data.rb +12 -4
  71. data/lib/shrine/plugins/sequel.rb +115 -27
  72. data/lib/shrine/plugins/signature.rb +2 -7
  73. data/lib/shrine/plugins/store_dimensions.rb +13 -27
  74. data/lib/shrine/plugins/upload_endpoint.rb +14 -15
  75. data/lib/shrine/plugins/upload_options.rb +9 -8
  76. data/lib/shrine/plugins/url_options.rb +33 -0
  77. data/lib/shrine/plugins/validation.rb +87 -0
  78. data/lib/shrine/plugins/validation_helpers.rb +33 -54
  79. data/lib/shrine/plugins/versions.rb +106 -84
  80. data/lib/shrine/storage/file_system.rb +32 -57
  81. data/lib/shrine/storage/linter.rb +9 -1
  82. data/lib/shrine/storage/memory.rb +42 -0
  83. data/lib/shrine/storage/s3.rb +38 -146
  84. data/lib/shrine/uploaded_file.rb +22 -29
  85. data/lib/shrine/version.rb +4 -4
  86. data/shrine.gemspec +2 -3
  87. metadata +27 -54
  88. data/doc/plugins/backup.md +0 -31
  89. data/doc/plugins/copy.md +0 -24
  90. data/doc/plugins/delete_promoted.md +0 -12
  91. data/doc/plugins/direct_upload.md +0 -172
  92. data/doc/plugins/hooks.md +0 -58
  93. data/doc/plugins/logging.md +0 -42
  94. data/doc/plugins/migration_helpers.md +0 -60
  95. data/doc/plugins/moving.md +0 -19
  96. data/doc/plugins/multi_delete.md +0 -20
  97. data/doc/plugins/parallelize.md +0 -16
  98. data/doc/plugins/parsed_json.md +0 -23
  99. data/lib/shrine/plugins/background_helpers.rb +0 -5
  100. data/lib/shrine/plugins/backup.rb +0 -90
  101. data/lib/shrine/plugins/copy.rb +0 -50
  102. data/lib/shrine/plugins/delete_promoted.rb +0 -20
  103. data/lib/shrine/plugins/direct_upload.rb +0 -217
  104. data/lib/shrine/plugins/hooks.rb +0 -90
  105. data/lib/shrine/plugins/logging.rb +0 -142
  106. data/lib/shrine/plugins/migration_helpers.rb +0 -70
  107. data/lib/shrine/plugins/moving.rb +0 -57
  108. data/lib/shrine/plugins/multi_delete.rb +0 -32
  109. data/lib/shrine/plugins/parallelize.rb +0 -78
  110. 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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