shrine 3.4.0 → 3.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|