shrine 3.4.0 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -0
- data/README.md +11 -11
- data/doc/carrierwave.md +2 -2
- data/doc/changing_location.md +2 -1
- data/doc/external/articles.md +16 -16
- data/doc/external/extensions.md +1 -1
- data/doc/getting_started.md +69 -28
- data/doc/multiple_files.md +57 -22
- data/doc/plugins/backgrounding.md +4 -4
- data/doc/plugins/derivation_endpoint.md +24 -0
- data/doc/plugins/derivatives.md +11 -1
- data/doc/plugins/keep_files.md +6 -4
- data/doc/plugins/validation_helpers.md +1 -1
- data/doc/refile.md +3 -3
- data/doc/release_notes/2.1.0.md +1 -1
- data/doc/release_notes/3.5.0.md +63 -0
- data/doc/retrieving_uploads.md +1 -1
- data/doc/testing.md +45 -17
- data/doc/upgrading_to_3.md +3 -5
- data/lib/shrine/plugins/derivation_endpoint.rb +30 -29
- data/lib/shrine/plugins/derivatives.rb +23 -15
- data/lib/shrine/plugins/download_endpoint.rb +7 -0
- data/lib/shrine/plugins/entity.rb +8 -2
- data/lib/shrine/plugins/infer_extension.rb +4 -0
- data/lib/shrine/plugins/instrumentation.rb +5 -1
- data/lib/shrine/plugins/model.rb +3 -1
- data/lib/shrine/plugins/remove_attachment.rb +2 -0
- data/lib/shrine/plugins/validation_helpers.rb +1 -1
- data/lib/shrine/storage/s3.rb +17 -7
- data/lib/shrine/uploaded_file.rb +3 -2
- data/lib/shrine/version.rb +1 -1
- data/lib/shrine.rb +1 -1
- data/shrine.gemspec +2 -2
- metadata +8 -7
data/doc/plugins/keep_files.md
CHANGED
@@ -2,10 +2,11 @@
|
|
2
2
|
title: Keep Files
|
3
3
|
---
|
4
4
|
|
5
|
-
The [`keep_files`][keep_files] plugin prevents file
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
The [`keep_files`][keep_files] plugin prevents the attached file (and any of
|
6
|
+
its [derivatives]) from being deleted when the attachment would normally be
|
7
|
+
destroyed, which happens when the attachment is removed/replaced, or when the
|
8
|
+
record is deleted. This functionality is useful when implementing soft deletes,
|
9
|
+
versioning, or in general any scenario where you need to keep history.
|
9
10
|
|
10
11
|
```rb
|
11
12
|
plugin :keep_files
|
@@ -17,3 +18,4 @@ photo.image.exists? #=> true
|
|
17
18
|
```
|
18
19
|
|
19
20
|
[keep_files]: https://github.com/shrinerb/shrine/blob/master/lib/shrine/plugins/keep_files.rb
|
21
|
+
[derivatives]: https://shrinerb.com/docs/plugins/derivatives
|
data/doc/refile.md
CHANGED
@@ -458,7 +458,7 @@ Shrine.plugin :cached_attachment_data
|
|
458
458
|
```
|
459
459
|
```rb
|
460
460
|
form_for @user do |form|
|
461
|
-
form.hidden_field :profile_image, value: @user.cached_profile_image_data
|
461
|
+
form.hidden_field :profile_image, value: @user.cached_profile_image_data, id: nil
|
462
462
|
form.file_field :profile_image
|
463
463
|
end
|
464
464
|
```
|
@@ -475,7 +475,7 @@ Shrine.plugin :remove_attachment
|
|
475
475
|
```
|
476
476
|
```rb
|
477
477
|
form_for @user do |form|
|
478
|
-
form.hidden_field :profile_image, value: @user.cached_profile_image_data
|
478
|
+
form.hidden_field :profile_image, value: @user.cached_profile_image_data, id: nil
|
479
479
|
form.file_field :profile_image
|
480
480
|
form.check_box :remove_profile_image
|
481
481
|
end
|
@@ -491,7 +491,7 @@ Shrine.plugin :remote_url
|
|
491
491
|
```
|
492
492
|
```rb
|
493
493
|
form_for @user do |form|
|
494
|
-
form.hidden_field :profile_image, value: @user.cached_profile_image_data
|
494
|
+
form.hidden_field :profile_image, value: @user.cached_profile_image_data, id: nil
|
495
495
|
form.file_field :profile_image
|
496
496
|
form.text_field :profile_image_remote_url
|
497
497
|
end
|
data/doc/release_notes/2.1.0.md
CHANGED
@@ -0,0 +1,63 @@
|
|
1
|
+
---
|
2
|
+
title: Shrine 3.5.0
|
3
|
+
---
|
4
|
+
|
5
|
+
## New features
|
6
|
+
|
7
|
+
* The website has been migrated to Docusaurus v2. :sparkles:
|
8
|
+
|
9
|
+
* The `:signer` option has been added to the `derivation_endpoint` plugin, for when you want to use custom URL signing. This is useful when using `:expires_in`, and wanting to have expiring URLs work with CDN caching.
|
10
|
+
|
11
|
+
```rb
|
12
|
+
require "aws-sdk-cloudfront"
|
13
|
+
signer = Aws::CloudFront::UrlSigner.new(key_pair_id: "...", private_key: "...")
|
14
|
+
|
15
|
+
plugin :derivation_endpoint,
|
16
|
+
expires_in: 90,
|
17
|
+
signer: -> (url, expires_in:) do
|
18
|
+
signer.signed_url(url, expires: Time.now.to_i + expires_in)
|
19
|
+
end
|
20
|
+
```
|
21
|
+
|
22
|
+
* The S3 storage now supports `:max_multipart_parts` option for specifying the maximum number of concurrent parts in which a large file will get uploaded. This number defaults to `10_000`.
|
23
|
+
|
24
|
+
```rb
|
25
|
+
Shrine::Storage::S3.new(max_multipart_parts: 1000, ...)
|
26
|
+
```
|
27
|
+
|
28
|
+
* The `:encoding` option can now be passed to `S3#open`, which is applied to downloaded chunks.
|
29
|
+
|
30
|
+
```rb
|
31
|
+
io = uploaded_file.open(encoding: Encoding::UTF_8)
|
32
|
+
csv = CSV.new(io)
|
33
|
+
# ...
|
34
|
+
```
|
35
|
+
|
36
|
+
## Other improvements
|
37
|
+
|
38
|
+
* Passing a boolean value to the `#remove_attachment=` setter now works on Ruby 3.2. Previously this would raise an error, because Shrine would try to call `=~` on it, but `Object#=~` method has been removed in Ruby 3.2.
|
39
|
+
|
40
|
+
* When duplicating a model instance, the duplicated attacher now references the duplicated model instance instead of the original one.
|
41
|
+
|
42
|
+
* The download endpoint now returns a `400 Bad Request` response when the serialized file component is invalid.
|
43
|
+
|
44
|
+
* The `derivatives` plugin now supports passing `mutex: false` option to disable usage of a mutex. This makes the `Shrine::Attacher` object marshallable, which should enable using `Marshal.dump` and `Marshal.load` on model instances with attachments. This should be safe unless you're adding derivatives on the same attacher object concurrently.
|
45
|
+
|
46
|
+
* When loading the `derivatives` plugin with `versions_compatibility: true`, this setting doesn't leak to other uploaders anymore. Previously if other uploaders would load `derivatives` plugin without this option, versions compatibility would still get enabled for them. This change also fixes behavior on JRuby.
|
47
|
+
|
48
|
+
* When S3 storage copies files, the AWS tag are not inherited anymore. This allows passing the `:tagging` upload option when promoting from temporary to permanent storage, and have it take effect.
|
49
|
+
|
50
|
+
* The `UploadedFile#url` method doesn't call the obsolete `URI.regexp` method anymore, which should avoid warnings.
|
51
|
+
|
52
|
+
* The `infer_extension` plugin now defines `infer_extension` instance method (in addition to class method) on the uploader for convenience, so that it can be easily called at the uploader instance level.
|
53
|
+
|
54
|
+
```rb
|
55
|
+
class MyUploader < Shrine
|
56
|
+
plugin :infer_extension
|
57
|
+
|
58
|
+
def generate_location(io, metadata:, **)
|
59
|
+
extension = infer_extension(metadata["mime_type"])
|
60
|
+
# ...
|
61
|
+
end
|
62
|
+
end
|
63
|
+
```
|
data/doc/retrieving_uploads.md
CHANGED
@@ -124,7 +124,7 @@ end # underlying IO object is closed
|
|
124
124
|
```
|
125
125
|
|
126
126
|
`Shrine::UploadedFile#open` will return the result of a given block.
|
127
|
-
|
127
|
+
We can use that to safely retrieve the whole content of a file, without
|
128
128
|
leaving any temporary files lying around.
|
129
129
|
|
130
130
|
```rb
|
data/doc/testing.md
CHANGED
@@ -2,6 +2,9 @@
|
|
2
2
|
title: Testing with Shrine
|
3
3
|
---
|
4
4
|
|
5
|
+
import Tabs from '@theme/Tabs';
|
6
|
+
import TabItem from '@theme/TabItem';
|
7
|
+
|
5
8
|
The goal of this guide is to provide some useful tips for testing file
|
6
9
|
attachments implemented with Shrine in your application.
|
7
10
|
|
@@ -137,19 +140,26 @@ module TestData
|
|
137
140
|
end
|
138
141
|
end
|
139
142
|
```
|
140
|
-
|
141
|
-
|
143
|
+
|
144
|
+
<Tabs>
|
145
|
+
<TabItem value="factory_bot" label="FactoryBot">
|
146
|
+
|
142
147
|
```rb
|
143
148
|
factory :photo do
|
144
149
|
image_data { TestData.image_data }
|
145
150
|
end
|
146
151
|
```
|
147
|
-
|
152
|
+
|
153
|
+
</TabItem>
|
154
|
+
<TabItem value="fixtures" label="Rails YAML fixtures">
|
155
|
+
|
148
156
|
```erb
|
149
157
|
photo:
|
150
158
|
image_data: <%= TestData.image_data %>
|
151
159
|
```
|
152
|
-
|
160
|
+
|
161
|
+
</TabItem>
|
162
|
+
</Tabs>
|
153
163
|
|
154
164
|
## Unit tests
|
155
165
|
|
@@ -182,24 +192,33 @@ end
|
|
182
192
|
In acceptance tests you're testing your app end-to-end, and you likely want to
|
183
193
|
also test file attachments here. Here are examples for some common use cases:
|
184
194
|
|
185
|
-
|
186
|
-
|
195
|
+
<Tabs>
|
196
|
+
<TabItem value="capybara" label="Capybara">
|
197
|
+
|
187
198
|
```rb
|
188
199
|
attach_file("#image-field", "test/files/image.jpg")
|
189
200
|
```
|
190
|
-
|
201
|
+
|
202
|
+
</TabItem>
|
203
|
+
<TabItem value="rack-test" label="rack-test">
|
204
|
+
|
191
205
|
```rb
|
192
206
|
post "/photos", photo: {
|
193
207
|
image: Rack::Test::UploadedFile.new("test/files/image.jpg", "image/jpeg")
|
194
208
|
}
|
195
209
|
```
|
196
|
-
|
210
|
+
|
211
|
+
</TabItem>
|
212
|
+
</Tabs>
|
213
|
+
|
214
|
+
If you want to test requests with cached attachment data, you can do so as
|
215
|
+
follows:
|
216
|
+
|
197
217
|
```rb
|
198
|
-
|
199
|
-
|
200
|
-
}
|
218
|
+
cached_file = Shrine.upload(file, :cache)
|
219
|
+
|
220
|
+
post "/photos", photo: { image: cached_file.to_json }
|
201
221
|
```
|
202
|
-
<!--END_DOCUSAURUS_CODE_TABS-->
|
203
222
|
|
204
223
|
## Background jobs
|
205
224
|
|
@@ -207,21 +226,30 @@ If you're using background jobs with Shrine, you probably want to make them
|
|
207
226
|
synchronous in tests. See your backgrounding library docs for how to make jobs
|
208
227
|
synchronous.
|
209
228
|
|
210
|
-
|
211
|
-
|
229
|
+
<Tabs>
|
230
|
+
<TabItem value="activejob" label="Active Job">
|
231
|
+
|
212
232
|
```rb
|
213
233
|
ActiveJob::Base.queue_adapter = :inline
|
214
234
|
```
|
215
|
-
|
235
|
+
|
236
|
+
</TabItem>
|
237
|
+
<TabItem value="sidekiq" label="Sidekiq">
|
238
|
+
|
216
239
|
```rb
|
217
240
|
require "sidekiq/testing"
|
218
241
|
Sidekiq::Testing.inline!
|
219
242
|
```
|
220
|
-
|
243
|
+
|
244
|
+
</TabItem>
|
245
|
+
<TabItem value="sucker_punch" label="SuckerPunch">
|
246
|
+
|
221
247
|
```rb
|
222
248
|
require "sucker_punch/testing/inline"
|
223
249
|
```
|
224
|
-
|
250
|
+
|
251
|
+
</TabItem>
|
252
|
+
</Tabs>
|
225
253
|
|
226
254
|
## Processing
|
227
255
|
|
data/doc/upgrading_to_3.md
CHANGED
@@ -7,9 +7,6 @@ This guide provides instructions for upgrading Shrine in your apps to version
|
|
7
7
|
3.x. If you're looking for a full list of changes, see the **[3.0 release
|
8
8
|
notes]**.
|
9
9
|
|
10
|
-
If you would like assistance with the upgrade, I'm available for consultation,
|
11
|
-
you can email me at <janko.marohnic@gmail.com>.
|
12
|
-
|
13
10
|
## Attacher
|
14
11
|
|
15
12
|
The `Shrine::Attacher` class has been rewritten in Shrine 3.0, though much of
|
@@ -447,7 +444,7 @@ class PromoteJob
|
|
447
444
|
attacher.create_derivatives # call derivatives processor
|
448
445
|
attacher.atomic_promote
|
449
446
|
rescue Shrine::AttachmentChanged, ActiveRecord::RecordNotFound
|
450
|
-
# attachment has changed or record has
|
447
|
+
# attachment has changed or record has been deleted, nothing to do
|
451
448
|
end
|
452
449
|
end
|
453
450
|
```
|
@@ -649,7 +646,8 @@ attacher.copy(other_attacher)
|
|
649
646
|
with
|
650
647
|
|
651
648
|
```rb
|
652
|
-
attacher.set
|
649
|
+
attacher.set nil # clear original attachment
|
650
|
+
attacher.attach other_attacher.file, storage: other_attacher.file.storage_key
|
653
651
|
attacher.add_derivatives other_attacher.derivatives # if using derivatives
|
654
652
|
```
|
655
653
|
|
@@ -28,8 +28,8 @@ class Shrine
|
|
28
28
|
uploader.opts[:derivation_endpoint] ||= { options: {}, derivations: {} }
|
29
29
|
uploader.opts[:derivation_endpoint][:options].merge!(opts)
|
30
30
|
|
31
|
-
|
32
|
-
fail Error, "must provide :secret_key option to derivation_endpoint plugin"
|
31
|
+
if !uploader.opts[:derivation_endpoint][:options][:secret_key] && !uploader.opts[:derivation_endpoint][:options][:signer]
|
32
|
+
fail Error, "must provide :secret_key option to derivation_endpoint plugin when no custom signer is set"
|
33
33
|
end
|
34
34
|
|
35
35
|
# instrumentation plugin integration
|
@@ -197,6 +197,7 @@ class Shrine
|
|
197
197
|
option :metadata, default: -> { [] }
|
198
198
|
option :prefix
|
199
199
|
option :secret_key
|
200
|
+
option :signer
|
200
201
|
option :type
|
201
202
|
option :upload, default: -> { false }
|
202
203
|
option :upload_location, default: -> { default_upload_location }, result: -> (o) { upload_location(o) }
|
@@ -216,7 +217,7 @@ class Shrine
|
|
216
217
|
option_definition = self.class.options.fetch(name)
|
217
218
|
|
218
219
|
value = options.fetch(name) { shrine_class.derivation_options[name] }
|
219
|
-
value = instance_exec(&value) if value.is_a?(Proc)
|
220
|
+
value = instance_exec(&value) if value.is_a?(Proc) && value.arity == 0
|
220
221
|
|
221
222
|
if value.nil?
|
222
223
|
default = option_definition[:default]
|
@@ -300,20 +301,36 @@ class Shrine
|
|
300
301
|
end
|
301
302
|
|
302
303
|
class Derivation::Url < Derivation::Command
|
303
|
-
delegate :name, :args, :source, :secret_key
|
304
|
+
delegate :name, :args, :source, :secret_key, :signer
|
304
305
|
|
305
306
|
def call(host: nil, prefix: nil, **options)
|
306
|
-
[host, *prefix
|
307
|
+
base_url = [host, *prefix].join("/")
|
308
|
+
path = path_identifier(metadata: options.delete(:metadata))
|
309
|
+
|
310
|
+
if signer
|
311
|
+
url = [base_url, path].join("/")
|
312
|
+
signer.call(url, **options)
|
313
|
+
else
|
314
|
+
signed_part = signed_url("#{path}?#{query(**options)}")
|
315
|
+
[base_url, signed_part].join("/")
|
316
|
+
end
|
307
317
|
end
|
308
318
|
|
309
319
|
private
|
310
320
|
|
311
|
-
def
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
321
|
+
def path_identifier(metadata: [])
|
322
|
+
[
|
323
|
+
name,
|
324
|
+
*args,
|
325
|
+
source.urlsafe_dump(metadata: metadata)
|
326
|
+
].map{|component| Rack::Utils.escape_path(component.to_s)}.join('/')
|
327
|
+
end
|
328
|
+
|
329
|
+
def query(expires_in: nil,
|
330
|
+
type: nil,
|
331
|
+
filename: nil,
|
332
|
+
disposition: nil,
|
333
|
+
version: nil)
|
317
334
|
|
318
335
|
params = {}
|
319
336
|
params[:expires_at] = (Time.now + expires_in).to_i if expires_in
|
@@ -322,23 +339,7 @@ class Shrine
|
|
322
339
|
params[:filename] = filename if filename
|
323
340
|
params[:disposition] = disposition if disposition
|
324
341
|
|
325
|
-
|
326
|
-
source_component = source.urlsafe_dump(metadata: metadata)
|
327
|
-
|
328
|
-
# generate plain URL
|
329
|
-
url = plain_url(name, *args, source_component, params)
|
330
|
-
|
331
|
-
# generate signed URL
|
332
|
-
signed_url(url)
|
333
|
-
end
|
334
|
-
|
335
|
-
def plain_url(*components, params)
|
336
|
-
# When using Rack < 2, Rack::Utils#escape_path will escape '/'.
|
337
|
-
# Escape each component and then join them together.
|
338
|
-
path = components.map{|component| Rack::Utils.escape_path(component.to_s)}.join('/')
|
339
|
-
query = Rack::Utils.build_query(params)
|
340
|
-
|
341
|
-
"#{path}?#{query}"
|
342
|
+
Rack::Utils.build_query(params)
|
342
343
|
end
|
343
344
|
|
344
345
|
def signed_url(url)
|
@@ -379,7 +380,7 @@ class Shrine
|
|
379
380
|
# Returns "404 Not Found" if derivation block is not defined, or if source
|
380
381
|
# file was not found on the storage.
|
381
382
|
def handle_request(request)
|
382
|
-
verify_signature!(request)
|
383
|
+
verify_signature!(request) if secret_key
|
383
384
|
check_expiry!(request)
|
384
385
|
|
385
386
|
name, *args, serialized_file = request.path_info.split("/")[1..-1]
|
@@ -12,18 +12,18 @@ class Shrine
|
|
12
12
|
}.inspect}"
|
13
13
|
end
|
14
14
|
|
15
|
-
def self.load_dependencies(uploader,
|
15
|
+
def self.load_dependencies(uploader, **)
|
16
16
|
uploader.plugin :default_url
|
17
|
-
|
18
|
-
AttacherMethods.prepend(VersionsCompatibility) if versions_compatibility
|
19
17
|
end
|
20
18
|
|
21
|
-
def self.configure(uploader, log_subscriber: LOG_SUBSCRIBER, **opts)
|
22
|
-
uploader.opts[:derivatives] ||= { processors: {}, processor_settings: {}, storage: proc { store_key } }
|
19
|
+
def self.configure(uploader, log_subscriber: LOG_SUBSCRIBER, versions_compatibility: false, **opts)
|
20
|
+
uploader.opts[:derivatives] ||= { processors: {}, processor_settings: {}, storage: proc { store_key }, mutex: true }
|
23
21
|
uploader.opts[:derivatives].merge!(opts)
|
24
22
|
|
25
23
|
# instrumentation plugin integration
|
26
24
|
uploader.subscribe(:derivatives, &log_subscriber) if uploader.respond_to?(:subscribe)
|
25
|
+
|
26
|
+
uploader::Attacher.include(VersionsCompatibility) if versions_compatibility
|
27
27
|
end
|
28
28
|
|
29
29
|
module AttachmentMethods
|
@@ -109,7 +109,7 @@ class Shrine
|
|
109
109
|
super(**options)
|
110
110
|
|
111
111
|
@derivatives = derivatives
|
112
|
-
@derivatives_mutex = Mutex.new
|
112
|
+
@derivatives_mutex = Mutex.new if shrine_class.derivatives_options[:mutex]
|
113
113
|
end
|
114
114
|
|
115
115
|
# Convenience method for accessing derivatives.
|
@@ -139,7 +139,7 @@ class Shrine
|
|
139
139
|
# Allows generating a URL to the derivative by passing the derivative
|
140
140
|
# name.
|
141
141
|
#
|
142
|
-
# attacher.add_derivatives(thumb: thumb)
|
142
|
+
# attacher.add_derivatives({ thumb: thumb })
|
143
143
|
# attacher.url(:thumb) #=> "https://example.org/thumb.jpg"
|
144
144
|
def url(*path, **options)
|
145
145
|
return super if path.empty?
|
@@ -180,7 +180,7 @@ class Shrine
|
|
180
180
|
|
181
181
|
# In addition to deleting the main file it also deletes any derivatives.
|
182
182
|
#
|
183
|
-
# attacher.add_derivatives(thumb: thumb)
|
183
|
+
# attacher.add_derivatives({ thumb: thumb })
|
184
184
|
# attacher.derivatives[:thumb].exists? #=> true
|
185
185
|
# attacher.destroy
|
186
186
|
# attacher.derivatives[:thumb].exists? #=> false
|
@@ -208,7 +208,7 @@ class Shrine
|
|
208
208
|
# # {
|
209
209
|
# # thumb: #<Shrine::UploadedFile>,
|
210
210
|
# # }
|
211
|
-
# attacher.add_derivatives(cropped: cropped)
|
211
|
+
# attacher.add_derivatives({ cropped: cropped })
|
212
212
|
# attacher.derivatives #=>
|
213
213
|
# # {
|
214
214
|
# # thumb: #<Shrine::UploadedFile>,
|
@@ -239,7 +239,7 @@ class Shrine
|
|
239
239
|
|
240
240
|
# Uploads given hash of files.
|
241
241
|
#
|
242
|
-
# hash = attacher.upload_derivatives(thumb: thumb)
|
242
|
+
# hash = attacher.upload_derivatives({ thumb: thumb })
|
243
243
|
# hash[:thumb] #=> #<Shrine::UploadedFile>
|
244
244
|
def upload_derivatives(files, **options)
|
245
245
|
map_derivative(files) do |path, file|
|
@@ -298,10 +298,10 @@ class Shrine
|
|
298
298
|
# Deep merges given uploaded derivatives with current derivatives.
|
299
299
|
#
|
300
300
|
# attacher.derivatives #=> { one: #<Shrine::UploadedFile> }
|
301
|
-
# attacher.merge_derivatives(two: uploaded_file)
|
301
|
+
# attacher.merge_derivatives({ two: uploaded_file })
|
302
302
|
# attacher.derivatives #=> { one: #<Shrine::UploadedFile>, two: #<Shrine::UploadedFile> }
|
303
303
|
def merge_derivatives(new_derivatives)
|
304
|
-
|
304
|
+
derivatives_synchronize do
|
305
305
|
merged_derivatives = deep_merge_derivatives(derivatives, new_derivatives)
|
306
306
|
set_derivatives(merged_derivatives)
|
307
307
|
end
|
@@ -379,7 +379,7 @@ class Shrine
|
|
379
379
|
|
380
380
|
# Deletes given hash of uploaded files.
|
381
381
|
#
|
382
|
-
# attacher.delete_derivatives(thumb: uploaded_file)
|
382
|
+
# attacher.delete_derivatives({ thumb: uploaded_file })
|
383
383
|
# uploaded_file.exists? #=> false
|
384
384
|
def delete_derivatives(derivatives = self.derivatives)
|
385
385
|
map_derivative(derivatives) { |_, derivative| derivative.delete }
|
@@ -387,7 +387,7 @@ class Shrine
|
|
387
387
|
|
388
388
|
# Sets the given hash of uploaded files as derivatives.
|
389
389
|
#
|
390
|
-
# attacher.set_derivatives(thumb: uploaded_file)
|
390
|
+
# attacher.set_derivatives({ thumb: uploaded_file })
|
391
391
|
# attacher.derivatives #=> { thumb: #<Shrine::UploadedFile> }
|
392
392
|
def set_derivatives(derivatives)
|
393
393
|
self.derivatives = derivatives
|
@@ -398,7 +398,7 @@ class Shrine
|
|
398
398
|
# Adds derivative data into the hash.
|
399
399
|
#
|
400
400
|
# attacher.attach(io)
|
401
|
-
# attacher.add_derivatives(thumb: thumb)
|
401
|
+
# attacher.add_derivatives({ thumb: thumb })
|
402
402
|
# attacher.data
|
403
403
|
# #=>
|
404
404
|
# # {
|
@@ -544,6 +544,14 @@ class Shrine
|
|
544
544
|
def create_derivatives_on_promote?
|
545
545
|
shrine_class.derivatives_options[:create_on_promote]
|
546
546
|
end
|
547
|
+
|
548
|
+
def derivatives_synchronize
|
549
|
+
if @derivatives_mutex
|
550
|
+
@derivatives_mutex.synchronize { yield }
|
551
|
+
else
|
552
|
+
yield
|
553
|
+
end
|
554
|
+
end
|
547
555
|
end
|
548
556
|
|
549
557
|
module ClassMethods
|
@@ -182,12 +182,19 @@ class Shrine
|
|
182
182
|
@shrine_class::UploadedFile.urlsafe_load(serialized)
|
183
183
|
rescue Shrine::Error # storage not found
|
184
184
|
not_found!
|
185
|
+
rescue JSON::ParserError, ArgumentError => error # invalid serialized component
|
186
|
+
raise if error.is_a?(ArgumentError) && error.message != "invalid base64"
|
187
|
+
bad_request!("Invalid serialized file")
|
185
188
|
end
|
186
189
|
|
187
190
|
def not_found!
|
188
191
|
error!(404, "File Not Found")
|
189
192
|
end
|
190
193
|
|
194
|
+
def bad_request!(message)
|
195
|
+
error!(400, message)
|
196
|
+
end
|
197
|
+
|
191
198
|
# Halts the request with the error message.
|
192
199
|
def error!(status, message)
|
193
200
|
throw :halt, [status, { "Content-Type" => "text/plain" }, [message]]
|
@@ -36,8 +36,14 @@ class Shrine
|
|
36
36
|
attachment = self
|
37
37
|
|
38
38
|
# Returns the attached file.
|
39
|
-
|
40
|
-
|
39
|
+
if shrine_class::Attacher.instance_method(:get).arity == 0
|
40
|
+
define_method :"#{name}" do
|
41
|
+
send(:"#{name}_attacher").get
|
42
|
+
end
|
43
|
+
else # derivatives
|
44
|
+
define_method :"#{name}" do |*args|
|
45
|
+
send(:"#{name}_attacher").get(*args)
|
46
|
+
end
|
41
47
|
end
|
42
48
|
|
43
49
|
# Returns the URL to the attached file.
|
@@ -12,7 +12,11 @@ class Shrine
|
|
12
12
|
def self.configure(uploader, log_subscriber: LOG_SUBSCRIBER, **opts)
|
13
13
|
uploader.opts[:instrumentation] ||= { log_events: EVENTS, subscribers: {} }
|
14
14
|
uploader.opts[:instrumentation].merge!(opts)
|
15
|
-
|
15
|
+
begin
|
16
|
+
uploader.opts[:instrumentation][:notifications] ||= ::ActiveSupport::Notifications
|
17
|
+
rescue NameError
|
18
|
+
fail Error, "default notifications library is ActiveSupport::Notifications, but activesupport gem is not installed"
|
19
|
+
end
|
16
20
|
|
17
21
|
uploader.opts[:instrumentation][:log_events].each do |event_name|
|
18
22
|
uploader.subscribe(event_name, &log_subscriber)
|
data/lib/shrine/plugins/model.rb
CHANGED
@@ -48,7 +48,9 @@ class Shrine
|
|
48
48
|
# The copy constructor that's called on #dup and #clone.
|
49
49
|
define_method :initialize_copy do |other|
|
50
50
|
super(other)
|
51
|
-
|
51
|
+
attacher_copy = instance_variable_get(:"@#{name}_attacher")&.dup
|
52
|
+
attacher_copy.set_entity(self, name) if attacher_copy
|
53
|
+
instance_variable_set(:"@#{name}_attacher", attacher_copy)
|
52
54
|
self
|
53
55
|
end
|
54
56
|
private :initialize_copy
|
@@ -145,7 +145,7 @@ class Shrine
|
|
145
145
|
|
146
146
|
# Validates that the dimensions are not smaller than specified.
|
147
147
|
#
|
148
|
-
#
|
148
|
+
# validate_min_dimensions [100, 100]
|
149
149
|
def validate_min_dimensions((min_width, min_height), message: nil)
|
150
150
|
fail Error, "width and/or height metadata is missing" unless file["width"] && file["height"]
|
151
151
|
|