shrine 3.0.0 → 3.2.2
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.
Potentially problematic release.
This version of shrine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +87 -33
- data/LICENSE.txt +1 -1
- data/README.md +94 -4
- data/doc/advantages.md +35 -18
- data/doc/attacher.md +16 -17
- data/doc/carrierwave.md +75 -34
- data/doc/changing_derivatives.md +39 -39
- data/doc/design.md +134 -85
- data/doc/external/articles.md +56 -41
- data/doc/external/extensions.md +38 -34
- data/doc/getting_started.md +182 -112
- data/doc/metadata.md +79 -43
- data/doc/multiple_files.md +5 -3
- data/doc/paperclip.md +110 -42
- data/doc/plugins/activerecord.md +5 -5
- data/doc/plugins/add_metadata.md +92 -35
- data/doc/plugins/backgrounding.md +12 -2
- data/doc/plugins/column.md +36 -7
- data/doc/plugins/data_uri.md +2 -2
- data/doc/plugins/default_url.md +6 -3
- data/doc/plugins/derivation_endpoint.md +26 -28
- data/doc/plugins/derivatives.md +205 -169
- data/doc/plugins/determine_mime_type.md +2 -2
- data/doc/plugins/entity.md +3 -3
- data/doc/plugins/form_assign.md +5 -5
- data/doc/plugins/included.md +25 -5
- data/doc/plugins/infer_extension.md +2 -2
- data/doc/plugins/instrumentation.md +1 -1
- data/doc/plugins/metadata_attributes.md +21 -10
- data/doc/plugins/model.md +4 -4
- data/doc/plugins/persistence.md +1 -0
- data/doc/plugins/refresh_metadata.md +5 -4
- data/doc/plugins/remote_url.md +8 -3
- data/doc/plugins/remove_invalid.md +9 -1
- data/doc/plugins/sequel.md +4 -4
- data/doc/plugins/signature.md +11 -2
- data/doc/plugins/store_dimensions.md +2 -2
- data/doc/plugins/type_predicates.md +96 -0
- data/doc/plugins/upload_endpoint.md +7 -11
- data/doc/plugins/upload_options.md +1 -1
- data/doc/plugins/url_options.md +2 -2
- data/doc/plugins/validation.md +14 -4
- data/doc/plugins/validation_helpers.md +3 -3
- data/doc/plugins/versions.md +11 -11
- data/doc/processing.md +289 -125
- data/doc/refile.md +39 -18
- data/doc/release_notes/2.19.0.md +1 -1
- data/doc/release_notes/3.0.0.md +275 -258
- data/doc/release_notes/3.0.1.md +22 -0
- data/doc/release_notes/3.1.0.md +73 -0
- data/doc/release_notes/3.2.0.md +96 -0
- data/doc/release_notes/3.2.1.md +32 -0
- data/doc/release_notes/3.2.2.md +14 -0
- data/doc/securing_uploads.md +3 -3
- data/doc/storage/file_system.md +1 -1
- data/doc/storage/memory.md +19 -0
- data/doc/storage/s3.md +105 -86
- data/doc/testing.md +2 -2
- data/doc/upgrading_to_3.md +115 -33
- data/doc/validation.md +3 -2
- data/lib/shrine.rb +8 -8
- data/lib/shrine/attacher.rb +19 -14
- data/lib/shrine/attachment.rb +5 -5
- data/lib/shrine/plugins.rb +22 -0
- data/lib/shrine/plugins/add_metadata.rb +12 -3
- data/lib/shrine/plugins/default_storage.rb +6 -6
- data/lib/shrine/plugins/default_url.rb +1 -1
- data/lib/shrine/plugins/derivation_endpoint.rb +10 -6
- data/lib/shrine/plugins/derivatives.rb +19 -17
- data/lib/shrine/plugins/determine_mime_type.rb +3 -3
- data/lib/shrine/plugins/entity.rb +6 -6
- data/lib/shrine/plugins/metadata_attributes.rb +1 -1
- data/lib/shrine/plugins/model.rb +3 -3
- data/lib/shrine/plugins/presign_endpoint.rb +2 -2
- data/lib/shrine/plugins/pretty_location.rb +1 -1
- data/lib/shrine/plugins/processing.rb +1 -1
- data/lib/shrine/plugins/refresh_metadata.rb +2 -2
- data/lib/shrine/plugins/remote_url.rb +3 -3
- data/lib/shrine/plugins/remove_invalid.rb +10 -5
- data/lib/shrine/plugins/signature.rb +7 -6
- data/lib/shrine/plugins/store_dimensions.rb +18 -9
- data/lib/shrine/plugins/type_predicates.rb +113 -0
- data/lib/shrine/plugins/upload_endpoint.rb +3 -3
- data/lib/shrine/plugins/upload_options.rb +2 -2
- data/lib/shrine/plugins/url_options.rb +2 -2
- data/lib/shrine/plugins/validation.rb +9 -7
- data/lib/shrine/storage/linter.rb +4 -4
- data/lib/shrine/storage/s3.rb +62 -38
- data/lib/shrine/uploaded_file.rb +5 -1
- data/lib/shrine/version.rb +2 -2
- data/shrine.gemspec +6 -7
- metadata +23 -29
data/lib/shrine/plugins.rb
CHANGED
@@ -18,6 +18,28 @@ class Shrine
|
|
18
18
|
plugin
|
19
19
|
end
|
20
20
|
|
21
|
+
# Delegate call to the plugin in a way that works across Ruby versions.
|
22
|
+
def self.load_dependencies(plugin, uploader, *args, **kwargs, &block)
|
23
|
+
return unless plugin.respond_to?(:load_dependencies)
|
24
|
+
|
25
|
+
if kwargs.any?
|
26
|
+
plugin.load_dependencies(uploader, *args, **kwargs, &block)
|
27
|
+
else
|
28
|
+
plugin.load_dependencies(uploader, *args, &block)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Delegate call to the plugin in a way that works across Ruby versions.
|
33
|
+
def self.configure(plugin, uploader, *args, **kwargs, &block)
|
34
|
+
return unless plugin.respond_to?(:configure)
|
35
|
+
|
36
|
+
if kwargs.any?
|
37
|
+
plugin.configure(uploader, *args, **kwargs, &block)
|
38
|
+
else
|
39
|
+
plugin.configure(uploader, *args, &block)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
21
43
|
# Register the given plugin with Shrine, so that it can be loaded using
|
22
44
|
# `Shrine.plugin` with a symbol. Should be used by plugin files. Example:
|
23
45
|
#
|
@@ -22,7 +22,7 @@ class Shrine
|
|
22
22
|
private
|
23
23
|
|
24
24
|
def _metadata_method(name)
|
25
|
-
|
25
|
+
self::UploadedFile.send(:define_method, name) do
|
26
26
|
metadata[name.to_s]
|
27
27
|
end
|
28
28
|
end
|
@@ -41,7 +41,7 @@ class Shrine
|
|
41
41
|
|
42
42
|
def extract_custom_metadata(io, **options)
|
43
43
|
opts[:add_metadata][:definitions].each do |name, block|
|
44
|
-
result = instance_exec(io, options, &block)
|
44
|
+
result = instance_exec(io, **options, &block)
|
45
45
|
|
46
46
|
if name
|
47
47
|
options[:metadata].merge! name.to_s => result
|
@@ -55,8 +55,17 @@ class Shrine
|
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
+
module AttacherMethods
|
59
|
+
def add_metadata(new_metadata, &block)
|
60
|
+
file!.add_metadata(new_metadata, &block)
|
61
|
+
set(file) # trigger model write
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
58
65
|
module FileMethods
|
59
|
-
|
66
|
+
def add_metadata(new_metadata, &block)
|
67
|
+
@metadata = @metadata.merge(new_metadata, &block)
|
68
|
+
end
|
60
69
|
end
|
61
70
|
end
|
62
71
|
|
@@ -34,12 +34,12 @@ class Shrine
|
|
34
34
|
if @cache.respond_to?(:call)
|
35
35
|
if @cache.arity == 2
|
36
36
|
Shrine.deprecation("Passing record & name argument to default storage block is deprecated and will be removed in Shrine 4. Use a block without arguments instead.")
|
37
|
-
@cache.call(record, name)
|
37
|
+
@cache.call(record, name).to_sym
|
38
38
|
else
|
39
|
-
instance_exec(&@cache)
|
39
|
+
instance_exec(&@cache).to_sym
|
40
40
|
end
|
41
41
|
else
|
42
|
-
|
42
|
+
super
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
@@ -47,12 +47,12 @@ class Shrine
|
|
47
47
|
if @store.respond_to?(:call)
|
48
48
|
if @store.arity == 2
|
49
49
|
Shrine.deprecation("Passing record & name argument to default storage block is deprecated and will be removed in Shrine 4. Use a block without arguments instead.")
|
50
|
-
@store.call(record, name)
|
50
|
+
@store.call(record, name).to_sym
|
51
51
|
else
|
52
|
-
instance_exec(&@store)
|
52
|
+
instance_exec(&@store).to_sym
|
53
53
|
end
|
54
54
|
else
|
55
|
-
|
55
|
+
super
|
56
56
|
end
|
57
57
|
end
|
58
58
|
end
|
@@ -475,9 +475,9 @@ class Shrine
|
|
475
475
|
file_response(derivative, env)
|
476
476
|
end
|
477
477
|
|
478
|
-
# Generates a Rack response triple from a local file
|
479
|
-
#
|
480
|
-
#
|
478
|
+
# Generates a Rack response triple from a local file. Fills in
|
479
|
+
# `Content-Type` and `Content-Disposition` response headers from derivation
|
480
|
+
# options and file extension of the derivation result.
|
481
481
|
def file_response(file, env)
|
482
482
|
response = rack_file_response(file.path, env)
|
483
483
|
|
@@ -511,7 +511,7 @@ class Shrine
|
|
511
511
|
end
|
512
512
|
|
513
513
|
if upload_redirect
|
514
|
-
redirect_url = uploaded_file.url(upload_redirect_url_options)
|
514
|
+
redirect_url = uploaded_file.url(**upload_redirect_url_options)
|
515
515
|
|
516
516
|
[302, { "Location" => redirect_url }, []]
|
517
517
|
else
|
@@ -528,10 +528,14 @@ class Shrine
|
|
528
528
|
end
|
529
529
|
end
|
530
530
|
|
531
|
-
# We call `Rack::
|
531
|
+
# We call `Rack::Files` with no default `Content-Type`, and make sure we
|
532
532
|
# stay compatible with both Rack 2.x and 1.6.x.
|
533
533
|
def rack_file_response(path, env)
|
534
|
-
|
534
|
+
if Rack.release >= "2.1"
|
535
|
+
server = Rack::Files.new("", {}, nil)
|
536
|
+
else
|
537
|
+
server = Rack::File.new("", {}, nil)
|
538
|
+
end
|
535
539
|
|
536
540
|
if Rack.release > "2"
|
537
541
|
server.serving(Rack::Request.new(env), path)
|
@@ -40,8 +40,8 @@ class Shrine
|
|
40
40
|
def define_model_methods(name)
|
41
41
|
super if defined?(super)
|
42
42
|
|
43
|
-
define_method(:"#{name}_derivatives!") do |*args|
|
44
|
-
send(:"#{name}_attacher").create_derivatives(*args)
|
43
|
+
define_method(:"#{name}_derivatives!") do |*args, **options|
|
44
|
+
send(:"#{name}_attacher").create_derivatives(*args, **options)
|
45
45
|
end
|
46
46
|
end
|
47
47
|
end
|
@@ -64,6 +64,7 @@ class Shrine
|
|
64
64
|
processor
|
65
65
|
end
|
66
66
|
end
|
67
|
+
alias derivatives derivatives_processor
|
67
68
|
|
68
69
|
# Specifies default storage to which derivatives will be uploaded.
|
69
70
|
#
|
@@ -179,9 +180,9 @@ class Shrine
|
|
179
180
|
# end
|
180
181
|
#
|
181
182
|
# attacher.create_derivatives(:my_processor)
|
182
|
-
def create_derivatives(*args)
|
183
|
-
files = process_derivatives(*args)
|
184
|
-
add_derivatives(files)
|
183
|
+
def create_derivatives(*args, storage: nil, **options)
|
184
|
+
files = process_derivatives(*args, **options)
|
185
|
+
add_derivatives(files, storage: storage)
|
185
186
|
end
|
186
187
|
|
187
188
|
# Uploads given hash of files and adds uploaded files to the
|
@@ -226,8 +227,6 @@ class Shrine
|
|
226
227
|
# hash[:thumb] #=> #<Shrine::UploadedFile>
|
227
228
|
def upload_derivatives(files, **options)
|
228
229
|
map_derivative(files) do |path, file|
|
229
|
-
path = derivative_path(path)
|
230
|
-
|
231
230
|
upload_derivative(path, file, **options)
|
232
231
|
end
|
233
232
|
end
|
@@ -237,6 +236,7 @@ class Shrine
|
|
237
236
|
# hash = attacher.upload_derivative(:thumb, thumb)
|
238
237
|
# hash[:thumb] #=> #<Shrine::UploadedFile>
|
239
238
|
def upload_derivative(path, file, storage: nil, **options)
|
239
|
+
path = derivative_path(path)
|
240
240
|
storage ||= derivative_storage(path)
|
241
241
|
|
242
242
|
file.open if file.is_a?(Tempfile) # refresh file descriptor
|
@@ -373,7 +373,7 @@ class Shrine
|
|
373
373
|
# attacher.derivatives #=> { thumb: #<Shrine::UploadedFile> }
|
374
374
|
def set_derivatives(derivatives)
|
375
375
|
self.derivatives = derivatives
|
376
|
-
set file # trigger model
|
376
|
+
set file # trigger model write
|
377
377
|
derivatives
|
378
378
|
end
|
379
379
|
|
@@ -441,7 +441,7 @@ class Shrine
|
|
441
441
|
# attacher.derivatives #=> { thumb: #<Shrine::UploadedFile> }
|
442
442
|
# attacher.change(file)
|
443
443
|
# attacher.derivatives #=> {}
|
444
|
-
def change(*
|
444
|
+
def change(*)
|
445
445
|
result = super
|
446
446
|
set_derivatives({})
|
447
447
|
result
|
@@ -462,8 +462,8 @@ class Shrine
|
|
462
462
|
# Iterates through nested derivatives and maps results.
|
463
463
|
#
|
464
464
|
# attacher.map_derivative(derivatives) { |path, file| ... }
|
465
|
-
def map_derivative(
|
466
|
-
shrine_class.map_derivative(
|
465
|
+
def map_derivative(derivatives, **options, &block)
|
466
|
+
shrine_class.map_derivative(derivatives, **options, &block)
|
467
467
|
end
|
468
468
|
|
469
469
|
private
|
@@ -472,7 +472,7 @@ class Shrine
|
|
472
472
|
def _process_derivatives(processor_name, source, **options)
|
473
473
|
processor = self.class.derivatives_processor(processor_name)
|
474
474
|
|
475
|
-
result = instrument_derivatives(processor_name, options) do
|
475
|
+
result = instrument_derivatives(processor_name, source, options) do
|
476
476
|
instance_exec(source, **options, &processor)
|
477
477
|
end
|
478
478
|
|
@@ -484,20 +484,22 @@ class Shrine
|
|
484
484
|
end
|
485
485
|
|
486
486
|
# Sends a `derivatives.shrine` event for instrumentation plugin.
|
487
|
-
def instrument_derivatives(processor_name, processor_options, &block)
|
487
|
+
def instrument_derivatives(processor_name, source, processor_options, &block)
|
488
488
|
return yield unless shrine_class.respond_to?(:instrument)
|
489
489
|
|
490
490
|
shrine_class.instrument(
|
491
491
|
:derivatives,
|
492
492
|
processor: processor_name,
|
493
493
|
processor_options: processor_options,
|
494
|
+
io: source,
|
495
|
+
attacher: self,
|
494
496
|
&block
|
495
497
|
)
|
496
498
|
end
|
497
499
|
|
498
500
|
# Returns symbolized array or single key.
|
499
501
|
def derivative_path(path)
|
500
|
-
path = path.map { |key| key.is_a?(String) ? key.to_sym : key }
|
502
|
+
path = Array(path).map { |key| key.is_a?(String) ? key.to_sym : key }
|
501
503
|
path = path.first if path.one?
|
502
504
|
path
|
503
505
|
end
|
@@ -525,13 +527,13 @@ class Shrine
|
|
525
527
|
# Converts data into a Hash of derivatives.
|
526
528
|
#
|
527
529
|
# Shrine.derivatives('{"thumb":{"id":"foo","storage":"store","metadata":{}}}')
|
528
|
-
# #=> { thumb: #<Shrine::UploadedFile
|
530
|
+
# #=> { thumb: #<Shrine::UploadedFile id="foo" storage=:store metadata={}> }
|
529
531
|
#
|
530
532
|
# Shrine.derivatives({ "thumb" => { "id" => "foo", "storage" => "store", "metadata" => {} } })
|
531
|
-
# #=> { thumb: #<Shrine::UploadedFile
|
533
|
+
# #=> { thumb: #<Shrine::UploadedFile id="foo" storage=:store metadata={}> }
|
532
534
|
#
|
533
535
|
# Shrine.derivatives({ thumb: { id: "foo", storage: "store", metadata: {} } })
|
534
|
-
# #=> { thumb: #<Shrine::UploadedFile
|
536
|
+
# #=> { thumb: #<Shrine::UploadedFile id="foo" storage=:store metadata={}> }
|
535
537
|
def derivatives(object)
|
536
538
|
if object.is_a?(String)
|
537
539
|
derivatives JSON.parse(object)
|
@@ -141,7 +141,7 @@ class Shrine
|
|
141
141
|
require "mimemagic"
|
142
142
|
|
143
143
|
mime = MimeMagic.by_magic(io)
|
144
|
-
mime
|
144
|
+
mime&.type
|
145
145
|
end
|
146
146
|
|
147
147
|
def extract_with_marcel(io, options)
|
@@ -158,7 +158,7 @@ class Shrine
|
|
158
158
|
|
159
159
|
if filename = extract_filename(io)
|
160
160
|
mime_type = MIME::Types.of(filename).first
|
161
|
-
mime_type
|
161
|
+
mime_type&.content_type
|
162
162
|
end
|
163
163
|
end
|
164
164
|
|
@@ -167,7 +167,7 @@ class Shrine
|
|
167
167
|
|
168
168
|
if filename = extract_filename(io)
|
169
169
|
info = MiniMime.lookup_by_filename(filename)
|
170
|
-
info
|
170
|
+
info&.content_type
|
171
171
|
end
|
172
172
|
end
|
173
173
|
|
@@ -41,27 +41,27 @@ class Shrine
|
|
41
41
|
end
|
42
42
|
|
43
43
|
# Returns the URL to the attached file.
|
44
|
-
define_method :"#{name}_url" do |*args|
|
45
|
-
send(:"#{name}_attacher").url(*args)
|
44
|
+
define_method :"#{name}_url" do |*args, **options|
|
45
|
+
send(:"#{name}_attacher").url(*args, **options)
|
46
46
|
end
|
47
47
|
|
48
48
|
# Returns an attacher instance.
|
49
49
|
define_method :"#{name}_attacher" do |**options|
|
50
|
-
attachment.send(:attacher, self, options)
|
50
|
+
attachment.send(:attacher, self, **options)
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
54
|
# Returns the class attacher instance with loaded entity. It's not
|
55
55
|
# memoized because the entity object could be frozen.
|
56
|
-
def attacher(record, options)
|
57
|
-
attacher = class_attacher(options)
|
56
|
+
def attacher(record, **options)
|
57
|
+
attacher = class_attacher(**options)
|
58
58
|
attacher.load_entity(record, @name)
|
59
59
|
attacher
|
60
60
|
end
|
61
61
|
|
62
62
|
# Creates an instance of the corresponding attacher class with set
|
63
63
|
# name.
|
64
|
-
def class_attacher(options)
|
64
|
+
def class_attacher(**options)
|
65
65
|
attacher = shrine_class::Attacher.new(**@options, **options)
|
66
66
|
attacher.instance_variable_set(:@name, @name)
|
67
67
|
attacher
|
data/lib/shrine/plugins/model.rb
CHANGED
@@ -55,11 +55,11 @@ class Shrine
|
|
55
55
|
end
|
56
56
|
|
57
57
|
# Memoizes the attacher instance into an instance variable.
|
58
|
-
def attacher(record, options)
|
58
|
+
def attacher(record, **options)
|
59
59
|
return super unless model?
|
60
60
|
|
61
61
|
if !record.instance_variable_get(:"@#{@name}_attacher") || options.any?
|
62
|
-
attacher = class_attacher(options)
|
62
|
+
attacher = class_attacher(**options)
|
63
63
|
attacher.load_model(record, @name)
|
64
64
|
|
65
65
|
record.instance_variable_set(:"@#{@name}_attacher", attacher)
|
@@ -118,7 +118,7 @@ class Shrine
|
|
118
118
|
end
|
119
119
|
|
120
120
|
# Writes uploaded file data into the model.
|
121
|
-
def set(*
|
121
|
+
def set(*)
|
122
122
|
result = super
|
123
123
|
write if model?
|
124
124
|
result
|
@@ -8,7 +8,7 @@ class Shrine
|
|
8
8
|
module Plugins
|
9
9
|
# Documentation can be found on https://shrinerb.com/docs/plugins/presign_endpoint
|
10
10
|
module PresignEndpoint
|
11
|
-
def self.configure(uploader, opts
|
11
|
+
def self.configure(uploader, **opts)
|
12
12
|
uploader.opts[:presign_endpoint] ||= {}
|
13
13
|
uploader.opts[:presign_endpoint].merge!(opts)
|
14
14
|
end
|
@@ -135,7 +135,7 @@ class Shrine
|
|
135
135
|
if @presign
|
136
136
|
data = @presign.call(location, options, request)
|
137
137
|
else
|
138
|
-
data = storage.presign(location, options)
|
138
|
+
data = storage.presign(location, **options)
|
139
139
|
end
|
140
140
|
|
141
141
|
{ fields: {}, headers: {} }.merge(data.to_h)
|
@@ -11,7 +11,7 @@ class Shrine
|
|
11
11
|
|
12
12
|
module InstanceMethods
|
13
13
|
def generate_location(io, **options)
|
14
|
-
pretty_location(io, options)
|
14
|
+
pretty_location(io, **options)
|
15
15
|
end
|
16
16
|
|
17
17
|
def pretty_location(io, name: nil, record: nil, version: nil, derivative: nil, identifier: nil, metadata: {}, **)
|
@@ -33,7 +33,7 @@ class Shrine
|
|
33
33
|
def process(io, **options)
|
34
34
|
pipeline = processing_pipeline(options[:action])
|
35
35
|
pipeline.inject(io) do |input, processor|
|
36
|
-
instance_exec(input, options, &processor) || input
|
36
|
+
instance_exec(input, **options, &processor) || input
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
@@ -16,7 +16,7 @@ class Shrine
|
|
16
16
|
}.inspect}"
|
17
17
|
end
|
18
18
|
|
19
|
-
DOWNLOADER = -> (url, options) { Down.download(url, options) }
|
19
|
+
DOWNLOADER = -> (url, **options) { Down.download(url, **options) }
|
20
20
|
|
21
21
|
def self.load_dependencies(uploader, *)
|
22
22
|
uploader.plugin :validation
|
@@ -63,7 +63,7 @@ class Shrine
|
|
63
63
|
private
|
64
64
|
|
65
65
|
def download_remote_url(url, options)
|
66
|
-
opts[:remote_url][:downloader].call(url, options)
|
66
|
+
opts[:remote_url][:downloader].call(url, **options)
|
67
67
|
rescue Down::TooLarge
|
68
68
|
fail DownloadError, "remote file too large"
|
69
69
|
rescue Down::Error
|
@@ -87,7 +87,7 @@ class Shrine
|
|
87
87
|
def assign_remote_url(url, downloader: {}, **options)
|
88
88
|
return if url == "" || url.nil?
|
89
89
|
|
90
|
-
downloaded_file = shrine_class.remote_url(url, downloader)
|
90
|
+
downloaded_file = shrine_class.remote_url(url, **downloader)
|
91
91
|
attach_cached(downloaded_file, **options)
|
92
92
|
rescue DownloadError => error
|
93
93
|
errors.clear << remote_url_error_message(url, error)
|
@@ -9,18 +9,23 @@ class Shrine
|
|
9
9
|
end
|
10
10
|
|
11
11
|
module AttacherMethods
|
12
|
-
def
|
12
|
+
def validate(*)
|
13
13
|
super
|
14
14
|
ensure
|
15
|
-
|
15
|
+
deassign if errors.any?
|
16
16
|
end
|
17
17
|
|
18
18
|
private
|
19
19
|
|
20
|
-
def
|
20
|
+
def deassign
|
21
21
|
destroy
|
22
|
-
|
23
|
-
|
22
|
+
|
23
|
+
if changed?
|
24
|
+
load_data @previous.data
|
25
|
+
@previous = nil
|
26
|
+
else
|
27
|
+
load_data nil
|
28
|
+
end
|
24
29
|
end
|
25
30
|
end
|
26
31
|
end
|