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/lib/shrine/attacher.rb
CHANGED
@@ -18,253 +18,349 @@ class Shrine
|
|
18
18
|
"#{shrine_class.inspect}::Attacher"
|
19
19
|
end
|
20
20
|
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
# Shrine::
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
def validate(&block)
|
30
|
-
define_method(:validate_block, &block)
|
31
|
-
private :validate_block
|
21
|
+
# Initializes the attacher from a data hash generated from `Attacher#data`.
|
22
|
+
#
|
23
|
+
# attacher = Attacher.from_data({ "id" => "...", "storage" => "...", "metadata" => { ... } })
|
24
|
+
# attacher.file #=> #<Shrine::UploadedFile>
|
25
|
+
def from_data(data, **options)
|
26
|
+
attacher = new(**options)
|
27
|
+
attacher.load_data(data)
|
28
|
+
attacher
|
32
29
|
end
|
33
30
|
end
|
34
31
|
|
35
32
|
module InstanceMethods
|
36
|
-
# Returns the
|
37
|
-
attr_reader :
|
33
|
+
# Returns the attached uploaded file.
|
34
|
+
attr_reader :file
|
38
35
|
|
39
|
-
# Returns
|
40
|
-
|
41
|
-
|
42
|
-
# Returns the context that will be sent to the uploader when uploading
|
43
|
-
# and deleting. Can be modified with additional data to be sent to the
|
44
|
-
# uploader.
|
36
|
+
# Returns options that are automatically forwarded to the uploader.
|
37
|
+
# Can be modified with additional data.
|
45
38
|
attr_reader :context
|
46
39
|
|
47
|
-
#
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
@cache = shrine_class.new(cache)
|
54
|
-
@store = shrine_class.new(store)
|
55
|
-
@context = { record: record, name: name }
|
56
|
-
@errors = []
|
40
|
+
# Initializes the attached file, temporary and permanent storage.
|
41
|
+
def initialize(file: nil, cache: :cache, store: :store)
|
42
|
+
@file = file
|
43
|
+
@cache = cache
|
44
|
+
@store = store
|
45
|
+
@context = {}
|
57
46
|
end
|
58
47
|
|
59
|
-
# Returns the
|
60
|
-
def
|
48
|
+
# Returns the temporary storage identifier.
|
49
|
+
def cache_key; @cache; end
|
50
|
+
# Returns the permanent storage identifier.
|
51
|
+
def store_key; @store; end
|
61
52
|
|
62
|
-
# Returns the
|
63
|
-
def
|
53
|
+
# Returns the uploader that is used for the temporary storage.
|
54
|
+
def cache; shrine_class.new(cache_key); end
|
55
|
+
# Returns the uploader that is used for the permanent storage.
|
56
|
+
def store; shrine_class.new(store_key); end
|
64
57
|
|
65
|
-
#
|
66
|
-
#
|
67
|
-
#
|
68
|
-
#
|
58
|
+
# Calls #attach_cached, but skips if value is an empty string (this is
|
59
|
+
# useful when the uploaded file comes from form fields). Forwards any
|
60
|
+
# additional options to #attach_cached.
|
61
|
+
#
|
62
|
+
# attacher.assign(File.open(...))
|
63
|
+
# attacher.assign(File.open(...), metadata: { "foo" => "bar" })
|
64
|
+
# attacher.assign('{"id":"...","storage":"cache","metadata":{...}}')
|
65
|
+
# attacher.assign({ "id" => "...", "storage" => "cache", "metadata" => {} })
|
66
|
+
#
|
67
|
+
# # ignores the assignment when a blank string is given
|
68
|
+
# attacher.assign("")
|
69
69
|
def assign(value, **options)
|
70
|
-
if value
|
71
|
-
return if value == "" || !cached?(uploaded_file(value))
|
72
|
-
assign_cached(uploaded_file(value))
|
73
|
-
else
|
74
|
-
uploaded_file = cache!(value, action: :cache, **options) if value
|
75
|
-
set(uploaded_file)
|
76
|
-
end
|
77
|
-
end
|
70
|
+
return if value == "" # skip empty hidden field
|
78
71
|
|
79
|
-
|
80
|
-
# attribute. It then runs file validations, and records that the
|
81
|
-
# attachment has changed.
|
82
|
-
def set(uploaded_file)
|
83
|
-
file = get
|
84
|
-
@old = file unless uploaded_file == file
|
85
|
-
_set(uploaded_file)
|
86
|
-
validate
|
72
|
+
attach_cached(value, **options)
|
87
73
|
end
|
88
74
|
|
89
|
-
#
|
90
|
-
|
91
|
-
|
92
|
-
|
75
|
+
# Sets an existing cached file, or uploads an IO object to temporary
|
76
|
+
# storage and sets it via #attach. Forwards any additional options to
|
77
|
+
# #attach.
|
78
|
+
#
|
79
|
+
# # upload file to temporary storage and set the uploaded file.
|
80
|
+
# attacher.attach_cached(File.open(...))
|
81
|
+
#
|
82
|
+
# # foward additional options to the uploader
|
83
|
+
# attacher.attach_cached(File.open(...), metadata: { "foo" => "bar" })
|
84
|
+
#
|
85
|
+
# # sets an existing cached file from JSON data
|
86
|
+
# attacher.attach_cached('{"id":"...","storage":"cache","metadata":{...}}')
|
87
|
+
#
|
88
|
+
# # sets an existing cached file from Hash data
|
89
|
+
# attacher.attach_cached({ "id" => "...", "storage" => "cache", "metadata" => {} })
|
90
|
+
def attach_cached(value, **options)
|
91
|
+
if value.is_a?(String) || value.is_a?(Hash)
|
92
|
+
change(cached(value, **options), **options)
|
93
|
+
else
|
94
|
+
attach(value, storage: cache_key, action: :cache, **options)
|
95
|
+
end
|
93
96
|
end
|
94
97
|
|
95
|
-
#
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
98
|
+
# Uploads given IO object and changes the uploaded file.
|
99
|
+
#
|
100
|
+
# # uploads the file to permanent storage
|
101
|
+
# attacher.attach(io)
|
102
|
+
#
|
103
|
+
# # uploads the file to specified storage
|
104
|
+
# attacher.attach(io, storage: :other_store)
|
105
|
+
#
|
106
|
+
# # forwards additional options to the uploader
|
107
|
+
# attacher.attach(io, upload_options: { acl: "public-read" }, metadata: { "foo" => "bar" })
|
108
|
+
#
|
109
|
+
# # removes the attachment
|
110
|
+
# attacher.attach(nil)
|
111
|
+
def attach(io, storage: store_key, **options)
|
112
|
+
file = upload(io, storage, **options) if io
|
100
113
|
|
101
|
-
|
102
|
-
# save.
|
103
|
-
def save
|
114
|
+
change(file, **options)
|
104
115
|
end
|
105
116
|
|
106
|
-
# Deletes
|
107
|
-
#
|
117
|
+
# Deletes any previous file and promotes newly attached cached file.
|
118
|
+
# It also clears any dirty tracking.
|
119
|
+
#
|
120
|
+
# # promoting cached file
|
121
|
+
# attacher.assign(io)
|
122
|
+
# attacher.cached? #=> true
|
123
|
+
# attacher.finalize
|
124
|
+
# attacher.stored?
|
125
|
+
#
|
126
|
+
# # deleting previous file
|
127
|
+
# previous_file = attacher.file
|
128
|
+
# previous_file.exists? #=> true
|
129
|
+
# attacher.assign(io)
|
130
|
+
# attacher.finalize
|
131
|
+
# previous_file.exists? #=> false
|
132
|
+
#
|
133
|
+
# # clearing dirty tracking
|
134
|
+
# attacher.assign(io)
|
135
|
+
# attacher.changed? #=> true
|
136
|
+
# attacher.finalize
|
137
|
+
# attacher.changed? #=> false
|
108
138
|
def finalize
|
109
|
-
|
110
|
-
|
111
|
-
remove_instance_variable(:@
|
112
|
-
_promote(action: :store) if cached?
|
139
|
+
destroy_previous
|
140
|
+
promote_cached
|
141
|
+
remove_instance_variable(:@previous) if changed?
|
113
142
|
end
|
114
143
|
|
115
|
-
#
|
116
|
-
|
117
|
-
|
144
|
+
# Plugins can override this if they want something to be done in a
|
145
|
+
# "before save" callback.
|
146
|
+
def save
|
118
147
|
end
|
119
148
|
|
120
|
-
#
|
121
|
-
#
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
149
|
+
# If a new cached file has been attached, uploads it to permanent storage.
|
150
|
+
# Any additional options are forwarded to #promote.
|
151
|
+
#
|
152
|
+
# attacher.assign(io)
|
153
|
+
# attacher.cached? #=> true
|
154
|
+
# attacher.promote_cached
|
155
|
+
# attacher.stored? #=> true
|
156
|
+
def promote_cached(**options)
|
157
|
+
promote(action: :store, **options) if changed? && cached?
|
126
158
|
end
|
127
159
|
|
128
|
-
#
|
129
|
-
#
|
130
|
-
|
131
|
-
|
132
|
-
|
160
|
+
# Uploads current file to permanent storage and sets the stored file.
|
161
|
+
#
|
162
|
+
# attacher.cached? #=> true
|
163
|
+
# attacher.promote
|
164
|
+
# attacher.stored? #=> true
|
165
|
+
def promote(storage: store_key, **options)
|
166
|
+
set upload(file, storage, **options)
|
133
167
|
end
|
134
168
|
|
135
|
-
#
|
136
|
-
#
|
137
|
-
|
138
|
-
|
169
|
+
# Delegates to `Shrine.upload`, passing the #context.
|
170
|
+
#
|
171
|
+
# # upload file to specified storage
|
172
|
+
# attacher.upload(io, :store) #=> #<Shrine::UploadedFile>
|
173
|
+
#
|
174
|
+
# # pass additional options for the uploader
|
175
|
+
# attacher.upload(io, :store, metadata: { "foo" => "bar" })
|
176
|
+
def upload(io, storage = store_key, **options)
|
177
|
+
shrine_class.upload(io, storage, **context, **options)
|
139
178
|
end
|
140
179
|
|
141
|
-
#
|
142
|
-
#
|
143
|
-
|
144
|
-
|
145
|
-
|
180
|
+
# If a new file was attached, deletes previously attached file if any.
|
181
|
+
#
|
182
|
+
# previous_file = attacher.file
|
183
|
+
# attacher.attach(file)
|
184
|
+
# attacher.destroy_previous
|
185
|
+
# previous_file.exists? #=> false
|
186
|
+
def destroy_previous(**options)
|
187
|
+
@previous.destroy_attached(**options) if changed?
|
146
188
|
end
|
147
189
|
|
148
|
-
#
|
149
|
-
|
150
|
-
|
190
|
+
# Destroys the attached file if it exists and is uploaded to permanent
|
191
|
+
# storage.
|
192
|
+
#
|
193
|
+
# attacher.file.exists? #=> true
|
194
|
+
# attacher.destroy_attached
|
195
|
+
# attacher.file.exists? #=> false
|
196
|
+
def destroy_attached(**options)
|
197
|
+
destroy(**options) if attached? && !cached?
|
151
198
|
end
|
152
199
|
|
153
|
-
#
|
154
|
-
#
|
155
|
-
|
156
|
-
|
200
|
+
# Destroys the attachment.
|
201
|
+
#
|
202
|
+
# attacher.file.exists? #=> true
|
203
|
+
# attacher.destroy
|
204
|
+
# attacher.file.exists? #=> false
|
205
|
+
def destroy(**options)
|
206
|
+
file&.delete
|
157
207
|
end
|
158
208
|
|
159
|
-
#
|
160
|
-
|
161
|
-
|
209
|
+
# Sets the uploaded file with dirty tracking, and runs validations.
|
210
|
+
#
|
211
|
+
# attacher.change(uploaded_file)
|
212
|
+
# attacher.file #=> #<Shrine::UploadedFile>
|
213
|
+
# attacher.changed? #=> true
|
214
|
+
def change(file, **)
|
215
|
+
@previous = dup unless @file == file
|
216
|
+
set(file)
|
162
217
|
end
|
163
218
|
|
164
|
-
#
|
165
|
-
|
166
|
-
|
219
|
+
# Sets the uploaded file.
|
220
|
+
#
|
221
|
+
# attacher.set(uploaded_file)
|
222
|
+
# attacher.file #=> #<Shrine::UploadedFile>
|
223
|
+
# attacher.changed? #=> false
|
224
|
+
def set(file)
|
225
|
+
self.file = file
|
167
226
|
end
|
168
227
|
|
169
|
-
# Returns
|
170
|
-
#
|
228
|
+
# Returns the attached file.
|
229
|
+
#
|
230
|
+
# # when a file is attached
|
231
|
+
# attacher.get #=> #<Shrine::UploadedFile>
|
232
|
+
#
|
233
|
+
# # when no file is attached
|
234
|
+
# attacher.get #=> nil
|
171
235
|
def get
|
172
|
-
|
236
|
+
file
|
173
237
|
end
|
174
238
|
|
175
|
-
#
|
176
|
-
#
|
177
|
-
|
178
|
-
|
179
|
-
|
239
|
+
# If a file is attached, returns the uploaded file URL, otherwise returns
|
240
|
+
# nil. Any options are forwarded to the storage.
|
241
|
+
#
|
242
|
+
# attacher.file = file
|
243
|
+
# attacher.url #=> "https://..."
|
244
|
+
#
|
245
|
+
# attacher.file = nil
|
246
|
+
# attacher.url #=> nil
|
247
|
+
def url(**options)
|
248
|
+
file&.url(**options)
|
180
249
|
end
|
181
250
|
|
182
|
-
#
|
183
|
-
|
184
|
-
|
185
|
-
|
251
|
+
# Returns whether the attachment has changed.
|
252
|
+
#
|
253
|
+
# attacher.changed? #=> false
|
254
|
+
# attacher.attach(file)
|
255
|
+
# attacher.changed? #=> true
|
256
|
+
def changed?
|
257
|
+
instance_variable_defined?(:@previous)
|
186
258
|
end
|
187
259
|
|
188
|
-
#
|
189
|
-
|
190
|
-
|
191
|
-
|
260
|
+
# Returns whether a file is attached.
|
261
|
+
#
|
262
|
+
# attacher.attach(io)
|
263
|
+
# attacher.attached? #=> true
|
264
|
+
#
|
265
|
+
# attacher.attach(nil)
|
266
|
+
# attacher.attached? #=> false
|
267
|
+
def attached?
|
268
|
+
!!file
|
192
269
|
end
|
193
270
|
|
194
|
-
#
|
195
|
-
|
196
|
-
|
197
|
-
|
271
|
+
# Returns whether the file is uploaded to temporary storage.
|
272
|
+
#
|
273
|
+
# attacher.cached? # checks current file
|
274
|
+
# attacher.cached?(file) # checks given file
|
275
|
+
def cached?(file = self.file)
|
276
|
+
uploaded?(file, cache_key)
|
198
277
|
end
|
199
278
|
|
200
|
-
#
|
201
|
-
#
|
202
|
-
|
203
|
-
|
279
|
+
# Returns whether the file is uploaded to permanent storage.
|
280
|
+
#
|
281
|
+
# attacher.stored? # checks current file
|
282
|
+
# attacher.stored?(file) # checks given file
|
283
|
+
def stored?(file = self.file)
|
284
|
+
uploaded?(file, store_key)
|
204
285
|
end
|
205
286
|
|
206
|
-
#
|
207
|
-
#
|
208
|
-
|
209
|
-
|
287
|
+
# Generates serializable data for the attachment.
|
288
|
+
#
|
289
|
+
# attacher.data #=> { "id" => "...", "storage" => "...", "metadata": { ... } }
|
290
|
+
def data
|
291
|
+
file&.data
|
210
292
|
end
|
211
293
|
|
212
|
-
#
|
213
|
-
#
|
214
|
-
|
215
|
-
|
294
|
+
# Loads the uploaded file from data generated by `Attacher#data`.
|
295
|
+
#
|
296
|
+
# attacher.file #=> nil
|
297
|
+
# attacher.load_data({ "id" => "...", "storage" => "...", "metadata" => { ... } })
|
298
|
+
# attacher.file #=> #<Shrine::UploadedFile>
|
299
|
+
def load_data(data)
|
300
|
+
@file = data && uploaded_file(data)
|
216
301
|
end
|
217
302
|
|
218
|
-
|
219
|
-
|
220
|
-
#
|
221
|
-
|
222
|
-
|
223
|
-
|
303
|
+
# Saves the given uploaded file to an instance variable.
|
304
|
+
#
|
305
|
+
# attacher.file = uploaded_file
|
306
|
+
# attacher.file #=> #<Shrine::UploadedFile>
|
307
|
+
def file=(file)
|
308
|
+
unless file.is_a?(Shrine::UploadedFile) || file.nil?
|
309
|
+
fail ArgumentError, "expected file to be a Shrine::UploadedFile or nil, got #{file.inspect}"
|
310
|
+
end
|
224
311
|
|
225
|
-
|
226
|
-
# plugins to additionally save the model instance.
|
227
|
-
def update(uploaded_file)
|
228
|
-
_set(uploaded_file)
|
312
|
+
@file = file
|
229
313
|
end
|
230
314
|
|
231
|
-
#
|
232
|
-
|
233
|
-
|
315
|
+
# Returns attached file or raises an exception if no file is attached.
|
316
|
+
def file!
|
317
|
+
file or fail Error, "no file is attached"
|
234
318
|
end
|
235
319
|
|
236
|
-
# Converts
|
237
|
-
#
|
238
|
-
|
239
|
-
|
240
|
-
|
320
|
+
# Converts JSON or Hash data into a Shrine::UploadedFile object.
|
321
|
+
#
|
322
|
+
# attacher.uploaded_file('{"id":"...","storage":"...","metadata":{...}}')
|
323
|
+
# #=> #<Shrine::UploadedFile ...>
|
324
|
+
#
|
325
|
+
# attacher.uploaded_file({ "id" => "...", "storage" => "...", "metadata" => {} })
|
326
|
+
# #=> #<Shrine::UploadedFile ...>
|
327
|
+
def uploaded_file(value)
|
328
|
+
shrine_class.uploaded_file(value)
|
241
329
|
end
|
242
330
|
|
243
|
-
#
|
244
|
-
|
245
|
-
|
331
|
+
# Returns the Shrine class that this attacher's class is namespaced
|
332
|
+
# under.
|
333
|
+
def shrine_class
|
334
|
+
self.class.shrine_class
|
246
335
|
end
|
247
336
|
|
248
|
-
|
249
|
-
def convert_to_data(uploaded_file)
|
250
|
-
uploaded_file.data
|
251
|
-
end
|
337
|
+
private
|
252
338
|
|
253
|
-
#
|
254
|
-
|
255
|
-
|
256
|
-
|
339
|
+
# Converts a String or Hash value into an UploadedFile object and ensures
|
340
|
+
# it's uploaded to temporary storage.
|
341
|
+
#
|
342
|
+
# # from JSON data
|
343
|
+
# attacher.cached('{"id":"...","storage":"cache","metadata":{...}}')
|
344
|
+
# #=> #<Shrine::UploadedFile>
|
345
|
+
#
|
346
|
+
# # from Hash data
|
347
|
+
# attacher.cached({ "id" => "...", "storage" => "cache", "metadata" => { ... } })
|
348
|
+
# #=> #<Shrine::UploadedFile>
|
349
|
+
def cached(value, **)
|
350
|
+
uploaded_file = uploaded_file(value)
|
351
|
+
|
352
|
+
# reject files not uploaded to temporary storage, because otherwise
|
353
|
+
# attackers could hijack other users' attachments
|
354
|
+
unless cached?(uploaded_file)
|
355
|
+
fail Shrine::Error, "expected cached file, got #{value.inspect}"
|
356
|
+
end
|
257
357
|
|
258
|
-
|
259
|
-
def convert_after_read(value)
|
260
|
-
value
|
358
|
+
uploaded_file
|
261
359
|
end
|
262
360
|
|
263
|
-
#
|
264
|
-
def
|
265
|
-
|
266
|
-
options[:action] = options[:phase] if options.key?(:phase)
|
267
|
-
options
|
361
|
+
# Returns whether the file is uploaded to specified storage.
|
362
|
+
def uploaded?(file, storage_key)
|
363
|
+
file&.storage_key == storage_key
|
268
364
|
end
|
269
365
|
end
|
270
366
|
|