shrine 2.19.3 → 3.6.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 +523 -41
- data/LICENSE.txt +1 -1
- data/README.md +83 -979
- data/doc/advantages.md +231 -204
- data/doc/attacher.md +304 -153
- data/doc/carrierwave.md +297 -226
- data/doc/changing_derivatives.md +308 -0
- data/doc/changing_location.md +103 -21
- data/doc/changing_storage.md +110 -0
- data/doc/creating_persistence_plugins.md +132 -0
- data/doc/creating_plugins.md +43 -23
- data/doc/creating_storages.md +19 -5
- data/doc/design.md +147 -97
- data/doc/direct_s3.md +38 -28
- data/doc/external/articles.md +63 -0
- data/doc/external/extensions.md +53 -0
- data/doc/external/misc.md +32 -0
- data/doc/getting_started.md +1156 -0
- data/doc/metadata.md +190 -109
- data/doc/multiple_files.md +93 -30
- data/doc/paperclip.md +384 -262
- data/doc/plugins/activerecord.md +177 -46
- data/doc/plugins/add_metadata.md +139 -38
- data/doc/plugins/atomic_helpers.md +217 -0
- data/doc/plugins/backgrounding.md +156 -98
- data/doc/plugins/cached_attachment_data.md +7 -5
- data/doc/plugins/column.md +121 -0
- data/doc/plugins/data_uri.md +23 -22
- data/doc/plugins/default_storage.md +36 -10
- data/doc/plugins/default_url.md +30 -13
- data/doc/plugins/delete_raw.md +4 -2
- data/doc/plugins/derivation_endpoint.md +186 -101
- data/doc/plugins/derivatives.md +839 -0
- data/doc/plugins/determine_mime_type.md +4 -2
- data/doc/plugins/download_endpoint.md +64 -8
- data/doc/plugins/dynamic_storage.md +5 -3
- data/doc/plugins/entity.md +263 -0
- data/doc/plugins/form_assign.md +55 -0
- data/doc/plugins/included.md +31 -8
- data/doc/plugins/infer_extension.md +21 -10
- data/doc/plugins/instrumentation.md +38 -16
- data/doc/plugins/keep_files.md +16 -17
- data/doc/plugins/metadata_attributes.md +42 -13
- data/doc/plugins/mirroring.md +118 -0
- data/doc/plugins/model.md +210 -0
- data/doc/plugins/module_include.md +4 -2
- data/doc/plugins/multi_cache.md +24 -0
- data/doc/plugins/persistence.md +101 -0
- data/doc/plugins/presign_endpoint.md +9 -4
- data/doc/plugins/pretty_location.md +16 -3
- data/doc/plugins/processing.md +4 -2
- data/doc/plugins/rack_file.md +8 -2
- data/doc/plugins/rack_response.md +6 -2
- data/doc/plugins/recache.md +4 -2
- data/doc/plugins/refresh_metadata.md +49 -9
- data/doc/plugins/remote_url.md +84 -47
- data/doc/plugins/remove_attachment.md +27 -6
- data/doc/plugins/remove_invalid.md +21 -6
- data/doc/plugins/restore_cached_data.md +11 -3
- data/doc/plugins/sequel.md +159 -35
- data/doc/plugins/signature.md +16 -5
- data/doc/plugins/store_dimensions.md +14 -2
- data/doc/plugins/tempfile.md +4 -2
- data/doc/plugins/type_predicates.md +96 -0
- data/doc/plugins/upload_endpoint.md +13 -13
- data/doc/plugins/upload_options.md +6 -4
- data/doc/plugins/{default_url_options.md → url_options.md} +9 -7
- data/doc/plugins/validation.md +97 -0
- data/doc/plugins/validation_helpers.md +16 -13
- data/doc/plugins/versions.md +15 -19
- data/doc/processing.md +438 -221
- data/doc/refile.md +188 -170
- data/doc/release_notes/1.0.0.md +4 -0
- data/doc/release_notes/1.1.0.md +6 -2
- data/doc/release_notes/1.2.0.md +4 -0
- data/doc/release_notes/1.3.0.md +4 -0
- data/doc/release_notes/1.4.0.md +4 -0
- data/doc/release_notes/1.4.1.md +4 -0
- data/doc/release_notes/1.4.2.md +4 -0
- data/doc/release_notes/2.0.0.md +4 -0
- data/doc/release_notes/2.0.1.md +4 -0
- data/doc/release_notes/2.1.0.md +5 -1
- data/doc/release_notes/2.1.1.md +4 -0
- data/doc/release_notes/2.10.0.md +4 -0
- data/doc/release_notes/2.10.1.md +4 -0
- data/doc/release_notes/2.11.0.md +4 -0
- data/doc/release_notes/2.12.0.md +4 -0
- data/doc/release_notes/2.13.0.md +4 -0
- data/doc/release_notes/2.14.0.md +5 -1
- data/doc/release_notes/2.15.0.md +11 -7
- data/doc/release_notes/2.16.0.md +4 -0
- data/doc/release_notes/2.17.0.md +4 -0
- data/doc/release_notes/2.18.0.md +4 -0
- data/doc/release_notes/2.19.0.md +6 -3
- data/doc/release_notes/2.2.0.md +4 -0
- data/doc/release_notes/2.3.0.md +4 -0
- data/doc/release_notes/2.3.1.md +4 -0
- data/doc/release_notes/2.4.0.md +4 -0
- data/doc/release_notes/2.4.1.md +4 -0
- data/doc/release_notes/2.5.0.md +4 -0
- data/doc/release_notes/2.6.0.md +4 -0
- data/doc/release_notes/2.6.1.md +4 -0
- data/doc/release_notes/2.7.0.md +4 -0
- data/doc/release_notes/2.8.0.md +4 -0
- data/doc/release_notes/2.9.0.md +4 -0
- data/doc/release_notes/3.0.0.md +981 -0
- 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 +31 -0
- data/doc/release_notes/3.2.2.md +14 -0
- data/doc/release_notes/3.3.0.md +105 -0
- data/doc/release_notes/3.4.0.md +35 -0
- data/doc/release_notes/3.5.0.md +63 -0
- data/doc/release_notes/3.6.0.md +23 -0
- data/doc/retrieving_uploads.md +5 -2
- data/doc/securing_uploads.md +60 -37
- data/doc/storage/file_system.md +20 -3
- data/doc/storage/memory.md +19 -0
- data/doc/storage/s3.md +122 -78
- data/doc/testing.md +141 -133
- data/doc/upgrading_to_3.md +708 -0
- data/doc/validation.md +54 -90
- data/lib/shrine/attacher.rb +292 -169
- data/lib/shrine/attachment.rb +13 -46
- data/lib/shrine/plugins/_persistence.rb +93 -0
- data/lib/shrine/plugins/activerecord.rb +77 -34
- data/lib/shrine/plugins/add_metadata.rb +25 -17
- data/lib/shrine/plugins/atomic_helpers.rb +119 -0
- data/lib/shrine/plugins/backgrounding.rb +77 -113
- data/lib/shrine/plugins/cached_attachment_data.rb +6 -15
- data/lib/shrine/plugins/column.rb +102 -0
- data/lib/shrine/plugins/data_uri.rb +38 -36
- data/lib/shrine/plugins/default_storage.rb +45 -15
- data/lib/shrine/plugins/default_url.rb +12 -24
- data/lib/shrine/plugins/default_url_options.rb +3 -30
- data/lib/shrine/plugins/delete_raw.rb +10 -16
- data/lib/shrine/plugins/derivation_endpoint.rb +130 -171
- data/lib/shrine/plugins/derivatives.rb +645 -0
- data/lib/shrine/plugins/determine_mime_type.rb +9 -21
- data/lib/shrine/plugins/download_endpoint.rb +118 -133
- data/lib/shrine/plugins/dynamic_storage.rb +5 -11
- data/lib/shrine/plugins/entity.rb +158 -0
- data/lib/shrine/plugins/form_assign.rb +108 -0
- data/lib/shrine/plugins/included.rb +6 -6
- data/lib/shrine/plugins/infer_extension.rb +17 -20
- data/lib/shrine/plugins/instrumentation.rb +59 -43
- data/lib/shrine/plugins/keep_files.rb +3 -15
- data/lib/shrine/plugins/metadata_attributes.rb +28 -19
- data/lib/shrine/plugins/mirroring.rb +142 -0
- data/lib/shrine/plugins/model.rb +160 -0
- data/lib/shrine/plugins/module_include.rb +3 -3
- data/lib/shrine/plugins/multi_cache.rb +27 -0
- data/lib/shrine/plugins/presign_endpoint.rb +27 -28
- data/lib/shrine/plugins/pretty_location.rb +15 -9
- data/lib/shrine/plugins/processing.rb +22 -9
- data/lib/shrine/plugins/rack_file.rb +2 -42
- data/lib/shrine/plugins/rack_response.rb +21 -10
- data/lib/shrine/plugins/recache.rb +6 -5
- data/lib/shrine/plugins/refresh_metadata.rb +13 -11
- data/lib/shrine/plugins/remote_url.rb +49 -49
- data/lib/shrine/plugins/remove_attachment.rb +12 -6
- data/lib/shrine/plugins/remove_invalid.rb +19 -8
- data/lib/shrine/plugins/restore_cached_data.rb +13 -7
- data/lib/shrine/plugins/sequel.rb +86 -36
- data/lib/shrine/plugins/signature.rb +10 -16
- data/lib/shrine/plugins/store_dimensions.rb +35 -40
- data/lib/shrine/plugins/tempfile.rb +1 -3
- data/lib/shrine/plugins/type_predicates.rb +113 -0
- data/lib/shrine/plugins/upload_endpoint.rb +28 -24
- data/lib/shrine/plugins/upload_options.rb +14 -15
- data/lib/shrine/plugins/url_options.rb +31 -0
- data/lib/shrine/plugins/validation.rb +80 -0
- data/lib/shrine/plugins/validation_helpers.rb +35 -58
- data/lib/shrine/plugins/versions.rb +107 -87
- data/lib/shrine/plugins.rb +22 -0
- data/lib/shrine/storage/file_system.rb +46 -64
- data/lib/shrine/storage/linter.rb +42 -7
- data/lib/shrine/storage/memory.rb +49 -0
- data/lib/shrine/storage/s3.rb +173 -160
- data/lib/shrine/uploaded_file.rb +32 -32
- data/lib/shrine/version.rb +3 -3
- data/lib/shrine.rb +87 -150
- data/shrine.gemspec +11 -12
- metadata +92 -82
- data/doc/migrating_storage.md +0 -76
- 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/doc/regenerating_versions.md +0 -143
- 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
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Shrine
|
|
4
|
+
module Plugins
|
|
5
|
+
# Documentation can be found on https://shrinerb.com/docs/plugins/form_assign
|
|
6
|
+
module FormAssign
|
|
7
|
+
def self.load_dependencies(uploader)
|
|
8
|
+
uploader.plugin :entity
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.configure(uploader, **opts)
|
|
12
|
+
uploader.opts[:form_assign] ||= { result: :params }
|
|
13
|
+
uploader.opts[:form_assign].merge!(opts)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
module AttacherMethods
|
|
17
|
+
# Helper for setting the attachment from form fields. Returns normalized
|
|
18
|
+
# fields.
|
|
19
|
+
#
|
|
20
|
+
# attacher = Shrine::Attacher.from_entity(photo, :image)
|
|
21
|
+
#
|
|
22
|
+
# attacher.form_assign({ image: file, title: "Title" })
|
|
23
|
+
# #=> { image: '{...}', title: "Title" }
|
|
24
|
+
#
|
|
25
|
+
# attacher.form_assign({ image: "", image_remote_url: "...", title: "Title" })
|
|
26
|
+
# #=> { image: '{...}', title: "Title" }
|
|
27
|
+
#
|
|
28
|
+
# attacher.form_assign({ image: "", title: "Title" })
|
|
29
|
+
# #=> { title: "Title" }
|
|
30
|
+
#
|
|
31
|
+
# You can also return the result in form of attributes to be used for
|
|
32
|
+
# database record creation.
|
|
33
|
+
#
|
|
34
|
+
# attacher.form_assign({ image: file, title: "Title" }, result: :attributes)
|
|
35
|
+
# #=> { image_data: '{...}', title: "Title" }
|
|
36
|
+
def form_assign(fields, result: shrine_class.opts[:form_assign][:result])
|
|
37
|
+
form = create_form_object
|
|
38
|
+
fields = form_write(form, fields)
|
|
39
|
+
|
|
40
|
+
form_attach(form)
|
|
41
|
+
|
|
42
|
+
form_result(fields, result)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
# Assigns form params to the form object using Shrine's attachment
|
|
48
|
+
# writers.
|
|
49
|
+
def form_write(form, fields)
|
|
50
|
+
result = fields.dup
|
|
51
|
+
|
|
52
|
+
fields.each do |key, value|
|
|
53
|
+
if form.respond_to?(:"#{key}=")
|
|
54
|
+
form.send(:"#{key}=", value)
|
|
55
|
+
|
|
56
|
+
result.delete(key)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
result
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Attaches the file from the form object if atachment has changed.
|
|
64
|
+
def form_attach(form)
|
|
65
|
+
return unless form.send(:"#{name}_attacher").changed?
|
|
66
|
+
|
|
67
|
+
file = form.send(:"#{name}_attacher").file
|
|
68
|
+
|
|
69
|
+
if file
|
|
70
|
+
change uploaded_file(file.data) # use our UploadedFile class
|
|
71
|
+
else
|
|
72
|
+
change nil
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Adds attached file data to the fields if attachment has changed.
|
|
77
|
+
def form_result(fields, result_type)
|
|
78
|
+
return fields unless changed?
|
|
79
|
+
|
|
80
|
+
case result_type
|
|
81
|
+
when :params then fields[name] = file&.to_json
|
|
82
|
+
when :attributes then fields[attribute] = column_data
|
|
83
|
+
else
|
|
84
|
+
fail ArgumentError, "unrecognized result type: #{result_type.inspect}"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
fields
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Creates a disposable form object with model plugin loaded.
|
|
91
|
+
def create_form_object
|
|
92
|
+
# load the model plugin into a disposable Shrine subclass
|
|
93
|
+
shrine_subclass = Class.new(shrine_class)
|
|
94
|
+
shrine_subclass.plugin :model
|
|
95
|
+
|
|
96
|
+
# create a model class with attachment methods
|
|
97
|
+
form_class = Struct.new(attribute)
|
|
98
|
+
form_class.include shrine_subclass::Attachment(name)
|
|
99
|
+
|
|
100
|
+
# instantiate form object
|
|
101
|
+
form_class.new(column_data)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
register_plugin(:form_assign, FormAssign)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -2,18 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
class Shrine
|
|
4
4
|
module Plugins
|
|
5
|
-
# Documentation
|
|
6
|
-
#
|
|
7
|
-
# [doc/plugins/included.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/included.md
|
|
5
|
+
# Documentation can be found on https://shrinerb.com/docs/plugins/included
|
|
8
6
|
module Included
|
|
9
7
|
def self.configure(uploader, &block)
|
|
10
|
-
uploader.opts[:
|
|
8
|
+
uploader.opts[:included] ||= {}
|
|
9
|
+
uploader.opts[:included][:block] = block
|
|
11
10
|
end
|
|
12
11
|
|
|
13
12
|
module AttachmentMethods
|
|
14
|
-
def included(
|
|
13
|
+
def included(klass)
|
|
15
14
|
super
|
|
16
|
-
|
|
15
|
+
|
|
16
|
+
klass.instance_exec(@name, &shrine_class.opts[:included][:block])
|
|
17
17
|
end
|
|
18
18
|
end
|
|
19
19
|
end
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "pathname"
|
|
4
|
+
|
|
3
5
|
class Shrine
|
|
4
6
|
module Plugins
|
|
5
|
-
# Documentation
|
|
6
|
-
#
|
|
7
|
-
# [doc/plugins/infer_extension.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/infer_extension.md
|
|
7
|
+
# Documentation can be found on https://shrinerb.com/docs/plugins/infer_extension
|
|
8
8
|
module InferExtension
|
|
9
9
|
LOG_SUBSCRIBER = -> (event) do
|
|
10
10
|
Shrine.logger.info "Extension (#{event.duration}ms) – #{{
|
|
@@ -13,14 +13,12 @@ class Shrine
|
|
|
13
13
|
}.inspect}"
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
def self.configure(uploader,
|
|
17
|
-
uploader.opts[:infer_extension] ||= { inferrer: :
|
|
16
|
+
def self.configure(uploader, log_subscriber: LOG_SUBSCRIBER, **opts)
|
|
17
|
+
uploader.opts[:infer_extension] ||= { inferrer: :mini_mime }
|
|
18
18
|
uploader.opts[:infer_extension].merge!(opts)
|
|
19
19
|
|
|
20
20
|
# instrumentation plugin integration
|
|
21
|
-
if uploader.respond_to?(:subscribe)
|
|
22
|
-
uploader.subscribe(:extension, &uploader.opts[:infer_extension][:log_subscriber])
|
|
23
|
-
end
|
|
21
|
+
uploader.subscribe(:extension, &log_subscriber) if uploader.respond_to?(:subscribe)
|
|
24
22
|
end
|
|
25
23
|
|
|
26
24
|
module ClassMethods
|
|
@@ -53,22 +51,21 @@ class Shrine
|
|
|
53
51
|
end
|
|
54
52
|
|
|
55
53
|
module InstanceMethods
|
|
56
|
-
def
|
|
57
|
-
|
|
58
|
-
current_extension = File.extname(location)
|
|
59
|
-
|
|
60
|
-
if current_extension.empty? || opts[:infer_extension][:force]
|
|
61
|
-
inferred_extension = infer_extension(metadata["mime_type"])
|
|
62
|
-
location = location.chomp(current_extension) << inferred_extension unless inferred_extension.empty?
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
location
|
|
54
|
+
def infer_extension(mime_type)
|
|
55
|
+
self.class.infer_extension(mime_type)
|
|
66
56
|
end
|
|
67
57
|
|
|
68
58
|
private
|
|
69
59
|
|
|
70
|
-
def
|
|
71
|
-
|
|
60
|
+
def basic_location(io, metadata:)
|
|
61
|
+
location = Pathname(super)
|
|
62
|
+
|
|
63
|
+
if location.extname.empty? || opts[:infer_extension][:force]
|
|
64
|
+
inferred_extension = self.class.infer_extension(metadata["mime_type"])
|
|
65
|
+
location = location.sub_ext(inferred_extension) if inferred_extension
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
location.to_s
|
|
72
69
|
end
|
|
73
70
|
end
|
|
74
71
|
|
|
@@ -2,25 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
class Shrine
|
|
4
4
|
module Plugins
|
|
5
|
-
# Documentation
|
|
6
|
-
#
|
|
7
|
-
# [doc/plugins/instrumentation.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/instrumentation.md
|
|
5
|
+
# Documentation can be found on https://shrinerb.com/docs/plugins/instrumentation
|
|
8
6
|
module Instrumentation
|
|
9
|
-
EVENTS = %i[upload download exists delete metadata].freeze
|
|
7
|
+
EVENTS = %i[upload download open exists delete metadata].freeze
|
|
10
8
|
|
|
11
9
|
# We use a proc in order to be able identify listeners.
|
|
12
10
|
LOG_SUBSCRIBER = -> (event) { LogSubscriber.call(event) }
|
|
13
11
|
|
|
14
|
-
def self.configure(uploader,
|
|
15
|
-
uploader.opts[:instrumentation] ||= {
|
|
12
|
+
def self.configure(uploader, log_subscriber: LOG_SUBSCRIBER, **opts)
|
|
13
|
+
uploader.opts[:instrumentation] ||= { log_events: EVENTS, subscribers: {} }
|
|
16
14
|
uploader.opts[:instrumentation].merge!(opts)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
21
20
|
|
|
22
21
|
uploader.opts[:instrumentation][:log_events].each do |event_name|
|
|
23
|
-
uploader.subscribe(event_name, &
|
|
22
|
+
uploader.subscribe(event_name, &log_subscriber)
|
|
24
23
|
end
|
|
25
24
|
end
|
|
26
25
|
|
|
@@ -48,12 +47,13 @@ class Shrine
|
|
|
48
47
|
# end
|
|
49
48
|
def subscribe(event_name, &subscriber)
|
|
50
49
|
return if subscriber.nil?
|
|
51
|
-
return if subscribers[event_name]
|
|
50
|
+
return if subscribers[event_name]&.include?(subscriber)
|
|
52
51
|
|
|
53
52
|
notifications.subscribe("#{event_name}.shrine") do |event|
|
|
54
53
|
subscriber.call(event) if event[:uploader] <= self
|
|
55
54
|
end
|
|
56
55
|
|
|
56
|
+
subscribers[event_name] ||= []
|
|
57
57
|
subscribers[event_name] << subscriber
|
|
58
58
|
end
|
|
59
59
|
|
|
@@ -63,12 +63,8 @@ class Shrine
|
|
|
63
63
|
Notifications.new(opts[:instrumentation][:notifications])
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
-
def log_subscriber
|
|
67
|
-
opts[:instrumentation][:log_subscriber]
|
|
68
|
-
end
|
|
69
|
-
|
|
70
66
|
def subscribers
|
|
71
|
-
opts[:
|
|
67
|
+
opts[:instrumentation][:subscribers]
|
|
72
68
|
end
|
|
73
69
|
end
|
|
74
70
|
|
|
@@ -76,57 +72,68 @@ class Shrine
|
|
|
76
72
|
private
|
|
77
73
|
|
|
78
74
|
# Sends a `upload.shrine` event.
|
|
79
|
-
def
|
|
80
|
-
self.class.instrument(
|
|
81
|
-
:upload,
|
|
75
|
+
def _upload(io, location:, metadata:, upload_options: {}, **options)
|
|
76
|
+
self.class.instrument(:upload, {
|
|
82
77
|
storage: storage_key,
|
|
83
|
-
location:
|
|
78
|
+
location: location,
|
|
84
79
|
io: io,
|
|
85
|
-
upload_options:
|
|
86
|
-
|
|
87
|
-
|
|
80
|
+
upload_options: upload_options,
|
|
81
|
+
metadata: metadata,
|
|
82
|
+
options: options,
|
|
83
|
+
}) { super }
|
|
88
84
|
end
|
|
89
85
|
|
|
90
86
|
# Sends a `metadata.shrine` event.
|
|
91
|
-
def get_metadata(io,
|
|
92
|
-
return super if io.is_a?(UploadedFile) &&
|
|
87
|
+
def get_metadata(io, metadata: nil, **options)
|
|
88
|
+
return super if io.is_a?(UploadedFile) && metadata != true || metadata == false
|
|
93
89
|
|
|
94
|
-
self.class.instrument(
|
|
95
|
-
:metadata,
|
|
90
|
+
self.class.instrument(:metadata, {
|
|
96
91
|
storage: storage_key,
|
|
97
92
|
io: io,
|
|
98
|
-
options:
|
|
99
|
-
) { super }
|
|
93
|
+
options: options,
|
|
94
|
+
}) { super }
|
|
100
95
|
end
|
|
101
96
|
end
|
|
102
97
|
|
|
103
98
|
module FileMethods
|
|
104
99
|
# Sends a `download.shrine` event.
|
|
105
|
-
def
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
100
|
+
def stream(destination, **options)
|
|
101
|
+
return super if opened?
|
|
102
|
+
|
|
103
|
+
shrine_class.instrument(:download, {
|
|
104
|
+
storage: storage_key,
|
|
109
105
|
location: id,
|
|
110
106
|
download_options: options,
|
|
111
|
-
) { super }
|
|
107
|
+
}) { super(destination, **options, instrument: false) }
|
|
112
108
|
end
|
|
113
109
|
|
|
114
110
|
# Sends a `exists.shrine` event.
|
|
115
111
|
def exists?
|
|
116
|
-
shrine_class.instrument(
|
|
117
|
-
:
|
|
118
|
-
storage: storage_key.to_sym,
|
|
112
|
+
shrine_class.instrument(:exists, {
|
|
113
|
+
storage: storage_key,
|
|
119
114
|
location: id,
|
|
120
|
-
) { super }
|
|
115
|
+
}) { super }
|
|
121
116
|
end
|
|
122
117
|
|
|
123
118
|
# Sends a `delete.shrine` event.
|
|
124
119
|
def delete
|
|
125
|
-
shrine_class.instrument(
|
|
126
|
-
:
|
|
127
|
-
storage: storage_key.to_sym,
|
|
120
|
+
shrine_class.instrument(:delete, {
|
|
121
|
+
storage: storage_key,
|
|
128
122
|
location: id,
|
|
129
|
-
) { super }
|
|
123
|
+
}) { super }
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
private
|
|
127
|
+
|
|
128
|
+
# Sends an `open.shrine` event.
|
|
129
|
+
def _open(instrument: true, **options)
|
|
130
|
+
return super(**options) unless instrument
|
|
131
|
+
|
|
132
|
+
shrine_class.instrument(:open, {
|
|
133
|
+
storage: storage_key,
|
|
134
|
+
location: id,
|
|
135
|
+
download_options: options,
|
|
136
|
+
}) { super(**options) }
|
|
130
137
|
end
|
|
131
138
|
end
|
|
132
139
|
|
|
@@ -255,6 +262,15 @@ class Shrine
|
|
|
255
262
|
)}"
|
|
256
263
|
end
|
|
257
264
|
|
|
265
|
+
def on_open(event)
|
|
266
|
+
log "Open (#{event.duration}ms) – #{format(
|
|
267
|
+
storage: event[:storage],
|
|
268
|
+
location: event[:location],
|
|
269
|
+
download_options: event[:download_options],
|
|
270
|
+
uploader: event[:uploader],
|
|
271
|
+
)}"
|
|
272
|
+
end
|
|
273
|
+
|
|
258
274
|
def on_exists(event)
|
|
259
275
|
log "Exists (#{event.duration}ms) – #{format(
|
|
260
276
|
storage: event[:storage],
|
|
@@ -2,23 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
class Shrine
|
|
4
4
|
module Plugins
|
|
5
|
-
# Documentation
|
|
6
|
-
#
|
|
7
|
-
# [doc/plugins/keep_files.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/keep_files.md
|
|
5
|
+
# Documentation can be found on https://shrinerb.com/docs/plugins/keep_files
|
|
8
6
|
module KeepFiles
|
|
9
|
-
def self.configure(uploader, opts = {})
|
|
10
|
-
keep_files = (uploader.opts[:keep_files] ||= [])
|
|
11
|
-
opts[:destroyed] ? keep_files << :destroyed : keep_files.delete(:destroyed) if opts.key?(:destroyed)
|
|
12
|
-
opts[:replaced] ? keep_files << :replaced : keep_files.delete(:replaced) if opts.key?(:replaced)
|
|
13
|
-
end
|
|
14
|
-
|
|
15
7
|
module AttacherMethods
|
|
16
|
-
def
|
|
17
|
-
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def destroy
|
|
21
|
-
super unless shrine_class.opts[:keep_files].include?(:destroyed)
|
|
8
|
+
def destroy?
|
|
9
|
+
false
|
|
22
10
|
end
|
|
23
11
|
end
|
|
24
12
|
end
|
|
@@ -2,37 +2,46 @@
|
|
|
2
2
|
|
|
3
3
|
class Shrine
|
|
4
4
|
module Plugins
|
|
5
|
-
# Documentation
|
|
6
|
-
#
|
|
7
|
-
# [doc/plugins/metadata_attributes.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/metadata_attributes.md
|
|
5
|
+
# Documentation can be found on https://shrinerb.com/docs/plugins/metadata_attributes
|
|
8
6
|
module MetadataAttributes
|
|
9
|
-
def self.
|
|
10
|
-
uploader.
|
|
11
|
-
|
|
7
|
+
def self.load_dependencies(uploader, *)
|
|
8
|
+
uploader.plugin :entity
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.configure(uploader, **opts)
|
|
12
|
+
uploader.opts[:metadata_attributes] ||= {}
|
|
13
|
+
uploader.opts[:metadata_attributes].merge!(opts)
|
|
12
14
|
end
|
|
13
15
|
|
|
14
16
|
module AttacherClassMethods
|
|
15
|
-
def metadata_attributes(mappings)
|
|
16
|
-
|
|
17
|
+
def metadata_attributes(mappings = nil)
|
|
18
|
+
if mappings
|
|
19
|
+
shrine_class.opts[:metadata_attributes].merge!(mappings)
|
|
20
|
+
else
|
|
21
|
+
shrine_class.opts[:metadata_attributes]
|
|
22
|
+
end
|
|
17
23
|
end
|
|
18
24
|
end
|
|
19
25
|
|
|
20
26
|
module AttacherMethods
|
|
21
|
-
def
|
|
22
|
-
super
|
|
23
|
-
|
|
27
|
+
def column_values
|
|
28
|
+
super.merge(metadata_attributes)
|
|
29
|
+
end
|
|
24
30
|
|
|
25
|
-
|
|
26
|
-
attribute_name = destination.is_a?(Symbol) ? :"#{name}_#{destination}" : :"#{destination}"
|
|
31
|
+
private
|
|
27
32
|
|
|
28
|
-
|
|
33
|
+
def metadata_attributes
|
|
34
|
+
values = {}
|
|
29
35
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
self.class.metadata_attributes.each do |source, destination|
|
|
37
|
+
metadata_attribute = destination.is_a?(Symbol) ? :"#{name}_#{destination}" : :"#{destination}"
|
|
38
|
+
|
|
39
|
+
next unless record.respond_to?(metadata_attribute)
|
|
40
|
+
|
|
41
|
+
values[metadata_attribute] = file && file.metadata[source.to_s]
|
|
35
42
|
end
|
|
43
|
+
|
|
44
|
+
values
|
|
36
45
|
end
|
|
37
46
|
end
|
|
38
47
|
end
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Shrine
|
|
4
|
+
module Plugins
|
|
5
|
+
# Documentation can be found on https://shrinerb.com/docs/plugins/mirroring
|
|
6
|
+
module Mirroring
|
|
7
|
+
def self.configure(uploader, **opts)
|
|
8
|
+
uploader.opts[:mirroring] ||= { upload: true, delete: true }
|
|
9
|
+
uploader.opts[:mirroring].merge!(opts)
|
|
10
|
+
|
|
11
|
+
fail Error, ":mirror option is required for mirroring plugin" unless uploader.opts[:mirroring][:mirror]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
module ClassMethods
|
|
15
|
+
def mirrors(storage_key = nil)
|
|
16
|
+
if storage_key
|
|
17
|
+
mirrors = opts[:mirroring][:mirror][storage_key]
|
|
18
|
+
|
|
19
|
+
fail Error, "no mirrors registered for storage #{storage_key.inspect}" unless mirrors
|
|
20
|
+
|
|
21
|
+
Array(mirrors)
|
|
22
|
+
else
|
|
23
|
+
opts[:mirroring][:mirror]
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def mirror_upload_block(&block)
|
|
28
|
+
if block
|
|
29
|
+
opts[:mirroring][:upload_block] = block
|
|
30
|
+
else
|
|
31
|
+
opts[:mirroring][:upload_block]
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def mirror_delete_block(&block)
|
|
36
|
+
if block
|
|
37
|
+
opts[:mirroring][:delete_block] = block
|
|
38
|
+
else
|
|
39
|
+
opts[:mirroring][:delete_block]
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def mirror_upload?
|
|
44
|
+
opts[:mirroring][:upload]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def mirror_delete?
|
|
48
|
+
opts[:mirroring][:delete]
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
module InstanceMethods
|
|
53
|
+
# Mirrors upload to other mirror storages.
|
|
54
|
+
def upload(io, mirror: true, **options)
|
|
55
|
+
file = super(io, **options)
|
|
56
|
+
file.trigger_mirror_upload(**options) if mirror
|
|
57
|
+
file
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
module FileMethods
|
|
62
|
+
# Mirrors upload if mirrors are defined. Calls mirror block if
|
|
63
|
+
# registered, otherwise mirrors synchronously.
|
|
64
|
+
def trigger_mirror_upload(**options)
|
|
65
|
+
return unless shrine_class.mirrors[storage_key] && shrine_class.mirror_upload?
|
|
66
|
+
|
|
67
|
+
if shrine_class.mirror_upload_block
|
|
68
|
+
mirror_upload_background(**options)
|
|
69
|
+
else
|
|
70
|
+
mirror_upload(**options)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Calls mirror upload block.
|
|
75
|
+
def mirror_upload_background(**options)
|
|
76
|
+
fail Error, "mirror upload block is not registered" unless shrine_class.mirror_upload_block
|
|
77
|
+
|
|
78
|
+
shrine_class.mirror_upload_block.call(self, **options)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Uploads the file to each mirror storage.
|
|
82
|
+
def mirror_upload(**options)
|
|
83
|
+
previously_opened = opened?
|
|
84
|
+
|
|
85
|
+
each_mirror do |mirror|
|
|
86
|
+
rewind if opened?
|
|
87
|
+
|
|
88
|
+
shrine_class.upload(self, mirror, **options, location: id, close: false, action: :mirror)
|
|
89
|
+
end
|
|
90
|
+
ensure
|
|
91
|
+
if opened? && !previously_opened
|
|
92
|
+
close
|
|
93
|
+
@io = nil
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Mirrors delete to other mirror storages.
|
|
98
|
+
def delete(mirror: true)
|
|
99
|
+
result = super()
|
|
100
|
+
trigger_mirror_delete if mirror
|
|
101
|
+
result
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Mirrors delete if mirrors are defined. Calls mirror block if
|
|
105
|
+
# registered, otherwise mirrors synchronously.
|
|
106
|
+
def trigger_mirror_delete
|
|
107
|
+
return unless shrine_class.mirrors[storage_key] && shrine_class.mirror_delete?
|
|
108
|
+
|
|
109
|
+
if shrine_class.mirror_delete_block
|
|
110
|
+
mirror_delete_background
|
|
111
|
+
else
|
|
112
|
+
mirror_delete
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Calls mirror delete block.
|
|
117
|
+
def mirror_delete_background
|
|
118
|
+
fail Error, "mirror delete block is not registered" unless shrine_class.mirror_delete_block
|
|
119
|
+
|
|
120
|
+
shrine_class.mirror_delete_block.call(self)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Deletes the file from each mirror storage.
|
|
124
|
+
def mirror_delete
|
|
125
|
+
each_mirror do |mirror|
|
|
126
|
+
self.class.new(id: id, storage: mirror).delete
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
private
|
|
131
|
+
|
|
132
|
+
# Iterates over mirror storages.
|
|
133
|
+
def each_mirror(&block)
|
|
134
|
+
mirrors = shrine_class.mirrors(storage_key)
|
|
135
|
+
mirrors.map(&block)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
register_plugin(:mirroring, Mirroring)
|
|
141
|
+
end
|
|
142
|
+
end
|