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
|
@@ -4,10 +4,10 @@ require "down"
|
|
|
4
4
|
|
|
5
5
|
class Shrine
|
|
6
6
|
module Plugins
|
|
7
|
-
# Documentation
|
|
8
|
-
#
|
|
9
|
-
# [doc/plugins/remote_url.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/remote_url.md
|
|
7
|
+
# Documentation can be found on https://shrinerb.com/docs/plugins/remote_url
|
|
10
8
|
module RemoteUrl
|
|
9
|
+
class DownloadError < Error; end
|
|
10
|
+
|
|
11
11
|
LOG_SUBSCRIBER = -> (event) do
|
|
12
12
|
Shrine.logger.info "Remote URL (#{event.duration}ms) – #{{
|
|
13
13
|
remote_url: event[:remote_url],
|
|
@@ -16,8 +16,14 @@ class Shrine
|
|
|
16
16
|
}.inspect}"
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
DOWNLOADER = -> (url, **options) { Down.download(url, **options) }
|
|
20
|
+
|
|
21
|
+
def self.load_dependencies(uploader, *)
|
|
22
|
+
uploader.plugin :validation
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.configure(uploader, log_subscriber: LOG_SUBSCRIBER, **opts)
|
|
26
|
+
uploader.opts[:remote_url] ||= { downloader: DOWNLOADER }
|
|
21
27
|
uploader.opts[:remote_url].merge!(opts)
|
|
22
28
|
|
|
23
29
|
unless uploader.opts[:remote_url].key?(:max_size)
|
|
@@ -25,8 +31,20 @@ class Shrine
|
|
|
25
31
|
end
|
|
26
32
|
|
|
27
33
|
# instrumentation plugin integration
|
|
28
|
-
if uploader.respond_to?(:subscribe)
|
|
29
|
-
|
|
34
|
+
uploader.subscribe(:remote_url, &log_subscriber) if uploader.respond_to?(:subscribe)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
module AttachmentMethods
|
|
38
|
+
def define_model_methods(name)
|
|
39
|
+
super if defined?(super)
|
|
40
|
+
|
|
41
|
+
define_method :"#{name}_remote_url=" do |url|
|
|
42
|
+
send(:"#{name}_attacher").remote_url = url
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
define_method :"#{name}_remote_url" do
|
|
46
|
+
send(:"#{name}_attacher").remote_url
|
|
47
|
+
end
|
|
30
48
|
end
|
|
31
49
|
end
|
|
32
50
|
|
|
@@ -38,12 +56,22 @@ class Shrine
|
|
|
38
56
|
options = { max_size: opts[:remote_url][:max_size] }.merge(options)
|
|
39
57
|
|
|
40
58
|
instrument_remote_url(url, options) do
|
|
41
|
-
|
|
59
|
+
download_remote_url(url, options)
|
|
42
60
|
end
|
|
43
61
|
end
|
|
44
62
|
|
|
45
63
|
private
|
|
46
64
|
|
|
65
|
+
def download_remote_url(url, options)
|
|
66
|
+
opts[:remote_url][:downloader].call(url, **options)
|
|
67
|
+
rescue Down::TooLarge
|
|
68
|
+
fail DownloadError, "remote file too large"
|
|
69
|
+
rescue Down::Error
|
|
70
|
+
fail DownloadError, "remote file not found"
|
|
71
|
+
rescue DownloadError
|
|
72
|
+
fail # re-raise
|
|
73
|
+
end
|
|
74
|
+
|
|
47
75
|
# Sends a `remote_url.shrine` event for instrumentation plugin.
|
|
48
76
|
def instrument_remote_url(url, options, &block)
|
|
49
77
|
return yield unless respond_to?(:instrument)
|
|
@@ -52,20 +80,6 @@ class Shrine
|
|
|
52
80
|
end
|
|
53
81
|
end
|
|
54
82
|
|
|
55
|
-
module AttachmentMethods
|
|
56
|
-
def initialize(name, **options)
|
|
57
|
-
super
|
|
58
|
-
|
|
59
|
-
define_method :"#{name}_remote_url=" do |url|
|
|
60
|
-
send(:"#{name}_attacher").remote_url = url
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
define_method :"#{name}_remote_url" do
|
|
64
|
-
send(:"#{name}_attacher").remote_url
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
83
|
module AttacherMethods
|
|
70
84
|
# Downloads the remote file and assigns it. If download failed, sets
|
|
71
85
|
# the error message and assigns the url to an instance variable so that
|
|
@@ -73,45 +87,31 @@ class Shrine
|
|
|
73
87
|
def assign_remote_url(url, downloader: {}, **options)
|
|
74
88
|
return if url == "" || url.nil?
|
|
75
89
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if downloaded_file
|
|
83
|
-
assign(downloaded_file, **options)
|
|
84
|
-
else
|
|
85
|
-
message = download_error_message(url, download_error)
|
|
86
|
-
errors.replace [message]
|
|
87
|
-
@remote_url = url
|
|
88
|
-
end
|
|
90
|
+
downloaded_file = shrine_class.remote_url(url, **downloader)
|
|
91
|
+
attach_cached(downloaded_file, **options)
|
|
92
|
+
rescue DownloadError => error
|
|
93
|
+
errors.clear << remote_url_error_message(url, error)
|
|
94
|
+
false
|
|
89
95
|
end
|
|
90
96
|
|
|
91
|
-
#
|
|
97
|
+
# Used by `<name>_data_uri=` attachment method.
|
|
92
98
|
def remote_url=(url)
|
|
93
99
|
assign_remote_url(url)
|
|
100
|
+
@remote_url = url
|
|
94
101
|
end
|
|
95
102
|
|
|
96
|
-
#
|
|
103
|
+
# Used by `<name>_data_uri` attachment method.
|
|
97
104
|
def remote_url
|
|
98
105
|
@remote_url
|
|
99
106
|
end
|
|
100
107
|
|
|
101
108
|
private
|
|
102
109
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
end
|
|
109
|
-
else
|
|
110
|
-
message = "download failed"
|
|
111
|
-
message = "#{message}: #{error.message}" if error
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
message
|
|
110
|
+
# Generates an error message for failed remote URL download.
|
|
111
|
+
def remote_url_error_message(url, error)
|
|
112
|
+
message = shrine_class.opts[:remote_url][:error_message]
|
|
113
|
+
message = message.call *[url, error].take(message.arity.abs) if message.respond_to?(:call)
|
|
114
|
+
message || "download failed: #{error.message}"
|
|
115
115
|
end
|
|
116
116
|
end
|
|
117
117
|
end
|
|
@@ -2,13 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
class Shrine
|
|
4
4
|
module Plugins
|
|
5
|
-
# Documentation
|
|
6
|
-
#
|
|
7
|
-
# [doc/plugins/remove_attachment.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/remove_attachment.md
|
|
5
|
+
# Documentation can be found on https://shrinerb.com/docs/plugins/remove_attachment
|
|
8
6
|
module RemoveAttachment
|
|
9
7
|
module AttachmentMethods
|
|
10
|
-
def
|
|
11
|
-
super
|
|
8
|
+
def define_model_methods(name)
|
|
9
|
+
super if defined?(super)
|
|
12
10
|
|
|
13
11
|
define_method :"remove_#{name}=" do |value|
|
|
14
12
|
send(:"#{name}_attacher").remove = value
|
|
@@ -24,7 +22,8 @@ class Shrine
|
|
|
24
22
|
# We remove the attachment if the value evaluates to true.
|
|
25
23
|
def remove=(value)
|
|
26
24
|
@remove = value
|
|
27
|
-
|
|
25
|
+
|
|
26
|
+
change(nil) if remove?
|
|
28
27
|
end
|
|
29
28
|
|
|
30
29
|
def remove
|
|
@@ -33,8 +32,15 @@ class Shrine
|
|
|
33
32
|
|
|
34
33
|
private
|
|
35
34
|
|
|
35
|
+
# Don't override previously removed attachment that wasn't yet deleted.
|
|
36
|
+
def change?(file)
|
|
37
|
+
super && !(changed? && remove?)
|
|
38
|
+
end
|
|
39
|
+
|
|
36
40
|
# Rails sends "0" or "false" if the checkbox hasn't been ticked.
|
|
37
41
|
def remove?
|
|
42
|
+
return remove if [true, false].include?(remove)
|
|
43
|
+
|
|
38
44
|
remove && remove != "" && remove !~ /\A(0|false)\z/
|
|
39
45
|
end
|
|
40
46
|
end
|
|
@@ -2,18 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
class Shrine
|
|
4
4
|
module Plugins
|
|
5
|
-
# Documentation
|
|
6
|
-
#
|
|
7
|
-
# [doc/plugins/remove_invalid.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/remove_invalid.md
|
|
5
|
+
# Documentation can be found on https://shrinerb.com/docs/plugins/remove_invalid
|
|
8
6
|
module RemoveInvalid
|
|
7
|
+
def self.load_dependencies(uploader)
|
|
8
|
+
uploader.plugin :validation
|
|
9
|
+
end
|
|
10
|
+
|
|
9
11
|
module AttacherMethods
|
|
10
|
-
def validate
|
|
12
|
+
def validate(*)
|
|
11
13
|
super
|
|
12
14
|
ensure
|
|
13
|
-
if errors.any?
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
deassign if errors.any?
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def deassign
|
|
21
|
+
destroy
|
|
22
|
+
|
|
23
|
+
if changed?
|
|
24
|
+
load_data @previous.data
|
|
25
|
+
@previous = nil
|
|
26
|
+
else
|
|
27
|
+
load_data nil
|
|
17
28
|
end
|
|
18
29
|
end
|
|
19
30
|
end
|
|
@@ -2,20 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
class Shrine
|
|
4
4
|
module Plugins
|
|
5
|
-
# Documentation
|
|
6
|
-
#
|
|
7
|
-
# [doc/plugins/restore_cached_data.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/restore_cached_data.md
|
|
5
|
+
# Documentation can be found on https://shrinerb.com/docs/plugins/restore_cached_data
|
|
8
6
|
module RestoreCachedData
|
|
9
|
-
def self.load_dependencies(uploader
|
|
7
|
+
def self.load_dependencies(uploader)
|
|
10
8
|
uploader.plugin :refresh_metadata
|
|
11
9
|
end
|
|
12
10
|
|
|
13
11
|
module AttacherMethods
|
|
14
12
|
private
|
|
15
13
|
|
|
16
|
-
def
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
def cached(value, **options)
|
|
15
|
+
cached_file = super
|
|
16
|
+
|
|
17
|
+
# TODO: Remove this conditional when we remove the versions plugin
|
|
18
|
+
if cached_file.is_a?(Hash) || cached_file.is_a?(Array)
|
|
19
|
+
uploaded_file(cached_file) { |file| file.refresh_metadata!(**context, **options) }
|
|
20
|
+
else
|
|
21
|
+
cached_file.refresh_metadata!(**context, **options)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
cached_file
|
|
19
25
|
end
|
|
20
26
|
end
|
|
21
27
|
end
|
|
@@ -4,13 +4,21 @@ require "sequel"
|
|
|
4
4
|
|
|
5
5
|
class Shrine
|
|
6
6
|
module Plugins
|
|
7
|
-
# Documentation
|
|
8
|
-
#
|
|
9
|
-
# [doc/plugins/sequel.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/sequel.md
|
|
7
|
+
# Documentation can be found on https://shrinerb.com/docs/plugins/sequel
|
|
10
8
|
module Sequel
|
|
11
|
-
def self.
|
|
12
|
-
uploader.
|
|
13
|
-
uploader.
|
|
9
|
+
def self.load_dependencies(uploader, **)
|
|
10
|
+
uploader.plugin :model
|
|
11
|
+
uploader.plugin :_persistence, plugin: self
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.configure(uploader, **opts)
|
|
15
|
+
if opts.key?(:callbacks)
|
|
16
|
+
Shrine.deprecation("The :callbacks option in sequel plugin has been renamed to :hooks. The :callbacks alias will be removed in Shrine 4.")
|
|
17
|
+
opts[:hooks] = opts.delete(:callbacks)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
uploader.opts[:sequel] ||= { hooks: true, validations: true }
|
|
21
|
+
uploader.opts[:sequel].merge!(opts)
|
|
14
22
|
end
|
|
15
23
|
|
|
16
24
|
module AttachmentMethods
|
|
@@ -19,68 +27,110 @@ class Shrine
|
|
|
19
27
|
|
|
20
28
|
return unless model < ::Sequel::Model
|
|
21
29
|
|
|
22
|
-
name =
|
|
30
|
+
name = @name
|
|
23
31
|
|
|
24
|
-
if shrine_class.opts[:
|
|
32
|
+
if shrine_class.opts[:sequel][:validations]
|
|
25
33
|
define_method :validate do
|
|
26
34
|
super()
|
|
27
|
-
send(:"#{name}_attacher").
|
|
28
|
-
errors.add(name, *message)
|
|
29
|
-
end
|
|
35
|
+
send(:"#{name}_attacher").send(:sequel_validate)
|
|
30
36
|
end
|
|
31
37
|
end
|
|
32
38
|
|
|
33
|
-
if shrine_class.opts[:
|
|
39
|
+
if shrine_class.opts[:sequel][:hooks]
|
|
34
40
|
define_method :before_save do
|
|
35
41
|
super()
|
|
36
|
-
|
|
37
|
-
|
|
42
|
+
if send(:"#{name}_attacher").changed?
|
|
43
|
+
send(:"#{name}_attacher").send(:sequel_before_save)
|
|
44
|
+
end
|
|
38
45
|
end
|
|
39
46
|
|
|
40
47
|
define_method :after_save do
|
|
41
48
|
super()
|
|
42
|
-
|
|
43
|
-
|
|
49
|
+
if send(:"#{name}_attacher").changed?
|
|
50
|
+
send(:"#{name}_attacher").send(:sequel_after_save)
|
|
51
|
+
end
|
|
44
52
|
end
|
|
45
53
|
|
|
46
54
|
define_method :after_destroy do
|
|
47
55
|
super()
|
|
48
|
-
|
|
49
|
-
|
|
56
|
+
if send(:"#{name}_attacher").attached?
|
|
57
|
+
send(:"#{name}_attacher").send(:sequel_after_destroy)
|
|
58
|
+
end
|
|
50
59
|
end
|
|
51
60
|
end
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
61
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
62
|
+
# reload the attacher on record reload
|
|
63
|
+
define_method :_refresh do |*args|
|
|
64
|
+
result = super(*args)
|
|
65
|
+
send(:"#{name}_attacher").reload if instance_variable_defined?(:"@#{name}_attacher")
|
|
66
|
+
result
|
|
67
|
+
end
|
|
68
|
+
private :_refresh
|
|
59
69
|
end
|
|
60
70
|
end
|
|
61
71
|
|
|
72
|
+
# The _persistence plugin uses #sequel_persist, #sequel_reload and
|
|
73
|
+
# #sequel? to implement the following methods:
|
|
74
|
+
#
|
|
75
|
+
# * Attacher#persist
|
|
76
|
+
# * Attacher#atomic_persist
|
|
77
|
+
# * Attacher#atomic_promote
|
|
62
78
|
module AttacherMethods
|
|
63
79
|
private
|
|
64
80
|
|
|
65
|
-
#
|
|
66
|
-
def
|
|
67
|
-
|
|
81
|
+
# Adds file validation errors to the model. Called on model validation.
|
|
82
|
+
def sequel_validate
|
|
83
|
+
return unless respond_to?(:errors)
|
|
84
|
+
|
|
85
|
+
errors.each do |message|
|
|
86
|
+
record.errors.add(name, *message)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Calls Attacher#save. Called before model save.
|
|
91
|
+
def sequel_before_save
|
|
92
|
+
save
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Finalizes attachment and persists changes. Called after model save.
|
|
96
|
+
def sequel_after_save
|
|
97
|
+
record.db.after_commit do
|
|
98
|
+
finalize
|
|
99
|
+
persist
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Deletes attached files. Called after model destroy.
|
|
104
|
+
def sequel_after_destroy
|
|
105
|
+
record.db.after_commit do
|
|
106
|
+
destroy_attached
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Saves changes to the model instance, skipping validations. Used by
|
|
111
|
+
# the _persistence plugin.
|
|
112
|
+
def sequel_persist
|
|
68
113
|
record.save_changes(validate: false)
|
|
69
114
|
end
|
|
70
115
|
|
|
71
|
-
#
|
|
72
|
-
#
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
sequel_json_column? ? value.to_hash : super
|
|
116
|
+
# Locks the database row and yields the reloaded record. Used by the
|
|
117
|
+
# _persistence plugin.
|
|
118
|
+
def sequel_reload
|
|
119
|
+
record.db.transaction { yield record.dup.lock! }
|
|
76
120
|
end
|
|
77
121
|
|
|
78
122
|
# Returns true if the data attribute represents a JSON or JSONB column.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
123
|
+
# Used by the _persistence plugin to determine whether serialization
|
|
124
|
+
# should be skipped.
|
|
125
|
+
def sequel_hash_attribute?
|
|
126
|
+
column = record.class.db_schema[attribute]
|
|
127
|
+
column && [:json, :jsonb].include?(column[:type])
|
|
128
|
+
end
|
|
82
129
|
|
|
83
|
-
|
|
130
|
+
# Returns whether the record is a Sequel model. Used by the
|
|
131
|
+
# _persistence plugin.
|
|
132
|
+
def sequel?
|
|
133
|
+
record.is_a?(::Sequel::Model)
|
|
84
134
|
end
|
|
85
135
|
end
|
|
86
136
|
end
|
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
class Shrine
|
|
4
4
|
module Plugins
|
|
5
|
-
# Documentation
|
|
6
|
-
#
|
|
7
|
-
# [doc/plugins/signature.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/signature.md
|
|
5
|
+
# Documentation can be found on https://shrinerb.com/docs/plugins/signature
|
|
8
6
|
module Signature
|
|
9
7
|
LOG_SUBSCRIBER = -> (event) do
|
|
10
8
|
Shrine.logger.info "Signature (#{event.duration}ms) – #{{
|
|
@@ -15,23 +13,21 @@ class Shrine
|
|
|
15
13
|
}.inspect}"
|
|
16
14
|
end
|
|
17
15
|
|
|
18
|
-
def self.configure(uploader,
|
|
19
|
-
uploader.opts[:signature] ||= { log_subscriber: LOG_SUBSCRIBER }
|
|
20
|
-
uploader.opts[:signature].merge!(opts)
|
|
21
|
-
|
|
16
|
+
def self.configure(uploader, log_subscriber: LOG_SUBSCRIBER)
|
|
22
17
|
# instrumentation plugin integration
|
|
23
|
-
if uploader.respond_to?(:subscribe)
|
|
24
|
-
uploader.subscribe(:signature, &uploader.opts[:signature][:log_subscriber])
|
|
25
|
-
end
|
|
18
|
+
uploader.subscribe(:signature, &log_subscriber) if uploader.respond_to?(:subscribe)
|
|
26
19
|
end
|
|
27
20
|
|
|
28
21
|
module ClassMethods
|
|
29
22
|
# Calculates `algorithm` hash of the contents of the IO object, and
|
|
30
23
|
# encodes it into `format`.
|
|
31
|
-
def calculate_signature(io, algorithm, format: :hex)
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
24
|
+
def calculate_signature(io, algorithm, format: :hex, rewind: true)
|
|
25
|
+
calculator = SignatureCalculator.new(algorithm.downcase, format: format)
|
|
26
|
+
|
|
27
|
+
signature = instrument_signature(io, algorithm, format) { calculator.call(io) }
|
|
28
|
+
io.rewind if rewind
|
|
29
|
+
|
|
30
|
+
signature
|
|
35
31
|
end
|
|
36
32
|
alias signature calculate_signature
|
|
37
33
|
|
|
@@ -69,8 +65,6 @@ class Shrine
|
|
|
69
65
|
|
|
70
66
|
def call(io)
|
|
71
67
|
hash = send(:"calculate_#{algorithm}", io)
|
|
72
|
-
io.rewind
|
|
73
|
-
|
|
74
68
|
send(:"encode_#{format}", hash)
|
|
75
69
|
end
|
|
76
70
|
|
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
class Shrine
|
|
4
4
|
module Plugins
|
|
5
|
-
# Documentation
|
|
6
|
-
#
|
|
7
|
-
# [doc/plugins/store_dimensions.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/store_dimensions.md
|
|
5
|
+
# Documentation can be found on https://shrinerb.com/docs/plugins/store_dimensions
|
|
8
6
|
module StoreDimensions
|
|
9
7
|
LOG_SUBSCRIBER = -> (event) do
|
|
10
8
|
Shrine.logger.info "Image Dimensions (#{event.duration}ms) – #{{
|
|
@@ -13,24 +11,22 @@ class Shrine
|
|
|
13
11
|
}.inspect}"
|
|
14
12
|
end
|
|
15
13
|
|
|
16
|
-
def self.configure(uploader,
|
|
17
|
-
uploader.opts[:store_dimensions] ||= { analyzer: :fastimage, on_error: :
|
|
14
|
+
def self.configure(uploader, log_subscriber: LOG_SUBSCRIBER, **opts)
|
|
15
|
+
uploader.opts[:store_dimensions] ||= { analyzer: :fastimage, on_error: :warn, auto_extraction: true }
|
|
18
16
|
uploader.opts[:store_dimensions].merge!(opts)
|
|
19
17
|
|
|
20
18
|
# resolve error strategy
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
19
|
+
uploader.opts[:store_dimensions][:on_error] =
|
|
20
|
+
case uploader.opts[:store_dimensions][:on_error]
|
|
21
|
+
when :fail then -> (error) { fail error }
|
|
22
|
+
when :warn then -> (error) { Shrine.warn "Error occurred when attempting to extract image dimensions: #{error.inspect}" }
|
|
23
|
+
when :ignore then -> (error) { }
|
|
24
|
+
else
|
|
25
|
+
uploader.opts[:store_dimensions][:on_error]
|
|
26
|
+
end
|
|
29
27
|
|
|
30
28
|
# instrumentation plugin integration
|
|
31
|
-
if uploader.respond_to?(:subscribe)
|
|
32
|
-
uploader.subscribe(:image_dimensions, &uploader.opts[:store_dimensions][:log_subscriber])
|
|
33
|
-
end
|
|
29
|
+
uploader.subscribe(:image_dimensions, &log_subscriber) if uploader.respond_to?(:subscribe)
|
|
34
30
|
end
|
|
35
31
|
|
|
36
32
|
module ClassMethods
|
|
@@ -75,23 +71,13 @@ class Shrine
|
|
|
75
71
|
end
|
|
76
72
|
|
|
77
73
|
module InstanceMethods
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
width, height = extract_dimensions(io)
|
|
74
|
+
def extract_metadata(io, **options)
|
|
75
|
+
return super unless opts[:store_dimensions][:auto_extraction]
|
|
81
76
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
private
|
|
86
|
-
|
|
87
|
-
# Extracts dimensions using the specified analyzer.
|
|
88
|
-
def extract_dimensions(io)
|
|
89
|
-
self.class.extract_dimensions(io)
|
|
90
|
-
end
|
|
77
|
+
# We update the metadata with "width" and "height".
|
|
78
|
+
width, height = self.class.extract_dimensions(io)
|
|
91
79
|
|
|
92
|
-
|
|
93
|
-
def dimensions_analyzers
|
|
94
|
-
self.class.dimensions_analyzers
|
|
80
|
+
super.merge!("width" => width, "height" => height)
|
|
95
81
|
end
|
|
96
82
|
end
|
|
97
83
|
|
|
@@ -129,23 +115,32 @@ class Shrine
|
|
|
129
115
|
|
|
130
116
|
def extract_with_fastimage(io)
|
|
131
117
|
require "fastimage"
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
118
|
+
|
|
119
|
+
begin
|
|
120
|
+
FastImage.size(io, raise_on_failure: true)
|
|
121
|
+
rescue FastImage::FastImageException => error
|
|
122
|
+
on_error(error)
|
|
123
|
+
end
|
|
135
124
|
end
|
|
136
125
|
|
|
137
126
|
def extract_with_mini_magick(io)
|
|
138
127
|
require "mini_magick"
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
128
|
+
|
|
129
|
+
begin
|
|
130
|
+
Shrine.with_file(io) { |file| MiniMagick::Image.new(file.path).dimensions }
|
|
131
|
+
rescue MiniMagick::Error => error
|
|
132
|
+
on_error(error)
|
|
133
|
+
end
|
|
142
134
|
end
|
|
143
135
|
|
|
144
136
|
def extract_with_ruby_vips(io)
|
|
145
137
|
require "vips"
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
138
|
+
|
|
139
|
+
begin
|
|
140
|
+
Shrine.with_file(io) { |file| Vips::Image.new_from_file(file.path).size }
|
|
141
|
+
rescue Vips::Error => error
|
|
142
|
+
on_error(error)
|
|
143
|
+
end
|
|
149
144
|
end
|
|
150
145
|
|
|
151
146
|
def on_error(error)
|
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
class Shrine
|
|
4
4
|
module Plugins
|
|
5
|
-
# Documentation
|
|
6
|
-
#
|
|
7
|
-
# [doc/plugins/tempfile.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/tempfile.md
|
|
5
|
+
# Documentation can be found on https://shrinerb.com/docs/plugins/tempfile
|
|
8
6
|
module Tempfile
|
|
9
7
|
module ClassMethods
|
|
10
8
|
def with_file(io)
|