shrine 2.19.4 → 3.4.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 +485 -43
- data/LICENSE.txt +1 -1
- data/README.md +81 -977
- 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 +102 -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 +1115 -0
- data/doc/metadata.md +190 -109
- data/doc/multiple_files.md +62 -34
- 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 +162 -101
- data/doc/plugins/derivatives.md +829 -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 +14 -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 +185 -167
- 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 +4 -0
- 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/retrieving_uploads.md +4 -1
- 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 +117 -83
- data/doc/testing.md +124 -144
- data/doc/upgrading_to_3.md +710 -0
- data/doc/validation.md +54 -90
- data/lib/shrine/attacher.rb +287 -171
- 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 +89 -134
- data/lib/shrine/plugins/derivatives.rb +637 -0
- data/lib/shrine/plugins/determine_mime_type.rb +9 -21
- data/lib/shrine/plugins/download_endpoint.rb +109 -133
- data/lib/shrine/plugins/dynamic_storage.rb +5 -11
- data/lib/shrine/plugins/entity.rb +152 -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 +13 -20
- data/lib/shrine/plugins/instrumentation.rb +54 -42
- 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 +158 -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 +18 -22
- 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 +15 -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 +10 -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 +25 -23
- 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 +34 -57
- 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 +154 -158
- data/lib/shrine/uploaded_file.rb +28 -30
- data/lib/shrine/version.rb +3 -3
- data/lib/shrine.rb +86 -149
- data/shrine.gemspec +9 -10
- metadata +79 -83
- 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,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Shrine
|
4
|
+
module Plugins
|
5
|
+
# Documentation can be found on https://shrinerb.com/docs/plugins/validation
|
6
|
+
module Validation
|
7
|
+
module AttacherClassMethods
|
8
|
+
# Block that is executed in context of Shrine::Attacher during
|
9
|
+
# validation. Example:
|
10
|
+
#
|
11
|
+
# Shrine::Attacher.validate do
|
12
|
+
# if file.size > 5*1024*1024
|
13
|
+
# errors << "is too big (max is 5 MB)"
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
def validate(&block)
|
17
|
+
private define_method(:validate_block, &block)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module AttacherMethods
|
22
|
+
# Returns an array of validation errors created on file assignment in
|
23
|
+
# the `Attacher.validate` block.
|
24
|
+
attr_reader :errors
|
25
|
+
|
26
|
+
# Initializes validation errors to an empty array.
|
27
|
+
def initialize(**options)
|
28
|
+
super
|
29
|
+
@errors = []
|
30
|
+
end
|
31
|
+
|
32
|
+
# Performs validations after attaching cached file.
|
33
|
+
def attach_cached(value, validate: nil, **options)
|
34
|
+
result = super(value, validate: false, **options)
|
35
|
+
validation(validate)
|
36
|
+
result
|
37
|
+
end
|
38
|
+
|
39
|
+
# Performs validations after attaching file.
|
40
|
+
def attach(io, validate: nil, **options)
|
41
|
+
result = super(io, **options)
|
42
|
+
validation(validate)
|
43
|
+
result
|
44
|
+
end
|
45
|
+
|
46
|
+
# Runs the validation defined by `Attacher.validate`.
|
47
|
+
def validate(**options)
|
48
|
+
errors.clear
|
49
|
+
_validate(**options) if attached?
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
# Calls validation appropriately based on the :validate value.
|
55
|
+
def validation(argument)
|
56
|
+
case argument
|
57
|
+
when Hash then validate(**argument)
|
58
|
+
when false then errors.clear # skip validation
|
59
|
+
else validate
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Calls #validate_block, passing it accepted parameters.
|
64
|
+
def _validate(**options)
|
65
|
+
if method(:validate_block).arity.zero?
|
66
|
+
validate_block
|
67
|
+
else
|
68
|
+
validate_block(**options)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Overridden by the `Attacher.validate` block.
|
73
|
+
def validate_block(**options)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
register_plugin(:validation, Validation)
|
79
|
+
end
|
80
|
+
end
|
@@ -2,15 +2,8 @@
|
|
2
2
|
|
3
3
|
class Shrine
|
4
4
|
module Plugins
|
5
|
-
# Documentation
|
6
|
-
#
|
7
|
-
# [doc/plugins/validation_helpers.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/validation_helpers.md
|
5
|
+
# Documentation can be found on https://shrinerb.com/docs/plugins/validation_helpers
|
8
6
|
module ValidationHelpers
|
9
|
-
def self.configure(uploader, opts = {})
|
10
|
-
uploader.opts[:validation_default_messages] ||= {}
|
11
|
-
uploader.opts[:validation_default_messages].merge!(opts[:default_messages] || {})
|
12
|
-
end
|
13
|
-
|
14
7
|
DEFAULT_MESSAGES = {
|
15
8
|
max_size: -> (max) { "size must not be greater than #{PRETTY_FILESIZE.call(max)}" },
|
16
9
|
min_size: -> (min) { "size must not be less than #{PRETTY_FILESIZE.call(min)}" },
|
@@ -24,7 +17,7 @@ class Shrine
|
|
24
17
|
mime_type_exclusion: -> (list) { "type must not be one of: #{list.join(", ")}" },
|
25
18
|
extension_inclusion: -> (list) { "extension must be one of: #{list.join(", ")}" },
|
26
19
|
extension_exclusion: -> (list) { "extension must not be one of: #{list.join(", ")}" },
|
27
|
-
}
|
20
|
+
}.freeze
|
28
21
|
|
29
22
|
FILESIZE_UNITS = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"].freeze
|
30
23
|
|
@@ -39,10 +32,18 @@ class Shrine
|
|
39
32
|
"%.1f %s" % [bytes.to_f / 1024 ** exp, FILESIZE_UNITS[exp]]
|
40
33
|
end
|
41
34
|
|
35
|
+
def self.load_dependencies(uploader, *)
|
36
|
+
uploader.plugin :validation
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.configure(uploader, default_messages: {}, **opts)
|
40
|
+
uploader.opts[:validation_helpers] ||= { default_messages: DEFAULT_MESSAGES.dup }
|
41
|
+
uploader.opts[:validation_helpers][:default_messages].merge!(default_messages)
|
42
|
+
end
|
43
|
+
|
42
44
|
module AttacherClassMethods
|
43
45
|
def default_validation_messages
|
44
|
-
|
45
|
-
shrine_class.opts[:validation_default_messages])
|
46
|
+
shrine_class.opts[:validation_helpers][:default_messages]
|
46
47
|
end
|
47
48
|
end
|
48
49
|
|
@@ -51,14 +52,14 @@ class Shrine
|
|
51
52
|
#
|
52
53
|
# validate_max_size 5*1024*1024
|
53
54
|
def validate_max_size(max, message: nil)
|
54
|
-
validate_result(
|
55
|
+
validate_result(file.size <= max, :max_size, message, max)
|
55
56
|
end
|
56
57
|
|
57
58
|
# Validates that the `size` metadata is not smaller than `min`.
|
58
59
|
#
|
59
60
|
# validate_min_size 1024
|
60
61
|
def validate_min_size(min, message: nil)
|
61
|
-
validate_result(
|
62
|
+
validate_result(file.size >= min, :min_size, message, min)
|
62
63
|
end
|
63
64
|
|
64
65
|
# Validates that the `size` metadata is in the given range.
|
@@ -76,12 +77,9 @@ class Shrine
|
|
76
77
|
#
|
77
78
|
# validate_max_width 5000
|
78
79
|
def validate_max_width(max, message: nil)
|
79
|
-
fail Error, "
|
80
|
-
|
81
|
-
|
82
|
-
else
|
83
|
-
Shrine.deprecation("Width of the uploaded file is nil, and Shrine skipped the validation. In Shrine 3 the validation will fail if width is nil.")
|
84
|
-
end
|
80
|
+
fail Error, "width metadata is missing" unless file["width"]
|
81
|
+
|
82
|
+
validate_result(file["width"] <= max, :max_width, message, max)
|
85
83
|
end
|
86
84
|
|
87
85
|
# Validates that the `width` metadata is not smaller than `min`.
|
@@ -89,12 +87,9 @@ class Shrine
|
|
89
87
|
#
|
90
88
|
# validate_min_width 100
|
91
89
|
def validate_min_width(min, message: nil)
|
92
|
-
fail Error, "
|
93
|
-
|
94
|
-
|
95
|
-
else
|
96
|
-
Shrine.deprecation("Width of the uploaded file is nil, and Shrine skipped the validation. In Shrine 3 the validation will fail if width is nil.")
|
97
|
-
end
|
90
|
+
fail Error, "width metadata is missing" unless file["width"]
|
91
|
+
|
92
|
+
validate_result(file["width"] >= min, :min_width, message, min)
|
98
93
|
end
|
99
94
|
|
100
95
|
# Validates that the `width` metadata is in the given range.
|
@@ -112,12 +107,9 @@ class Shrine
|
|
112
107
|
#
|
113
108
|
# validate_max_height 5000
|
114
109
|
def validate_max_height(max, message: nil)
|
115
|
-
fail Error, "
|
116
|
-
|
117
|
-
|
118
|
-
else
|
119
|
-
Shrine.deprecation("Height of the uploaded file is nil, and Shrine skipped the validation. In Shrine 3 the validation will fail if height is nil.")
|
120
|
-
end
|
110
|
+
fail Error, "height metadata is missing" unless file["height"]
|
111
|
+
|
112
|
+
validate_result(file["height"] <= max, :max_height, message, max)
|
121
113
|
end
|
122
114
|
|
123
115
|
# Validates that the `height` metadata is not smaller than `min`.
|
@@ -125,12 +117,9 @@ class Shrine
|
|
125
117
|
#
|
126
118
|
# validate_min_height 100
|
127
119
|
def validate_min_height(min, message: nil)
|
128
|
-
fail Error, "
|
129
|
-
|
130
|
-
|
131
|
-
else
|
132
|
-
Shrine.deprecation("Height of the uploaded file is nil, and Shrine skipped the validation. In Shrine 3 the validation will fail if height is nil.")
|
133
|
-
end
|
120
|
+
fail Error, "height metadata is missing" unless file["height"]
|
121
|
+
|
122
|
+
validate_result(file["height"] >= min, :min_height, message, min)
|
134
123
|
end
|
135
124
|
|
136
125
|
# Validates that the `height` metadata is in the given range.
|
@@ -146,11 +135,10 @@ class Shrine
|
|
146
135
|
#
|
147
136
|
# validate_max_dimensions [5000, 5000]
|
148
137
|
def validate_max_dimensions((max_width, max_height), message: nil)
|
149
|
-
fail Error, "
|
150
|
-
fail Error, "width or height metadata is nil" unless get.width && get.height
|
138
|
+
fail Error, "width and/or height metadata is missing" unless file["width"] && file["height"]
|
151
139
|
|
152
140
|
validate_result(
|
153
|
-
|
141
|
+
file["width"] <= max_width && file["height"] <= max_height,
|
154
142
|
:max_dimensions, message, [max_width, max_height]
|
155
143
|
)
|
156
144
|
end
|
@@ -159,11 +147,10 @@ class Shrine
|
|
159
147
|
#
|
160
148
|
# validate_max_dimensions [100, 100]
|
161
149
|
def validate_min_dimensions((min_width, min_height), message: nil)
|
162
|
-
fail Error, "
|
163
|
-
fail Error, "width or height metadata is nil" unless get.width && get.height
|
150
|
+
fail Error, "width and/or height metadata is missing" unless file["width"] && file["height"]
|
164
151
|
|
165
152
|
validate_result(
|
166
|
-
|
153
|
+
file["width"] >= min_width && file["height"] >= min_height,
|
167
154
|
:min_dimensions, message, [min_width, min_height]
|
168
155
|
)
|
169
156
|
end
|
@@ -184,7 +171,7 @@ class Shrine
|
|
184
171
|
# validate_mime_type_inclusion %w[audio/mp3 audio/flac]
|
185
172
|
def validate_mime_type_inclusion(types, message: nil)
|
186
173
|
validate_result(
|
187
|
-
types.
|
174
|
+
types.include?(file.mime_type),
|
188
175
|
:mime_type_inclusion, message, types
|
189
176
|
)
|
190
177
|
end
|
@@ -196,7 +183,7 @@ class Shrine
|
|
196
183
|
# validate_mime_type_exclusion %w[text/x-php]
|
197
184
|
def validate_mime_type_exclusion(types, message: nil)
|
198
185
|
validate_result(
|
199
|
-
types.
|
186
|
+
!types.include?(file.mime_type),
|
200
187
|
:mime_type_exclusion, message, types
|
201
188
|
)
|
202
189
|
end
|
@@ -207,7 +194,7 @@ class Shrine
|
|
207
194
|
# validate_extension_inclusion %w[jpg jpeg png gif]
|
208
195
|
def validate_extension_inclusion(extensions, message: nil)
|
209
196
|
validate_result(
|
210
|
-
extensions.any? { |extension|
|
197
|
+
extensions.any? { |extension| extension.casecmp(file.extension.to_s) == 0 },
|
211
198
|
:extension_inclusion, message, extensions
|
212
199
|
)
|
213
200
|
end
|
@@ -219,7 +206,7 @@ class Shrine
|
|
219
206
|
# validate_extension_exclusion %[php jar]
|
220
207
|
def validate_extension_exclusion(extensions, message: nil)
|
221
208
|
validate_result(
|
222
|
-
extensions.none? { |extension|
|
209
|
+
extensions.none? { |extension| extension.casecmp(file.extension.to_s) == 0 },
|
223
210
|
:extension_exclusion, message, extensions
|
224
211
|
)
|
225
212
|
end
|
@@ -236,16 +223,6 @@ class Shrine
|
|
236
223
|
end
|
237
224
|
end
|
238
225
|
|
239
|
-
# Converts a string to a regex.
|
240
|
-
def regex(value)
|
241
|
-
if value.is_a?(Regexp)
|
242
|
-
Shrine.deprecation("Passing regexes to type/extension whitelists/blacklists in validation_helpers plugin is deprecated and will be removed in Shrine 3. Use strings instead.")
|
243
|
-
value
|
244
|
-
else
|
245
|
-
/\A#{Regexp.escape(value)}\z/i
|
246
|
-
end
|
247
|
-
end
|
248
|
-
|
249
226
|
# Generates an error message and appends it to errors array.
|
250
227
|
def add_error(*args)
|
251
228
|
errors << error_message(*args)
|
@@ -1,113 +1,70 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
Shrine.deprecation("The versions plugin is deprecated and will be removed in Shrine 4. Use the new derivatives plugin instead.")
|
4
|
+
|
3
5
|
class Shrine
|
4
6
|
module Plugins
|
5
|
-
# Documentation
|
6
|
-
#
|
7
|
-
# [doc/plugins/versions.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/versions.md
|
7
|
+
# Documentation can be found on https://shrinerb.com/docs/plugins/versions
|
8
8
|
module Versions
|
9
|
-
def self.load_dependencies(uploader,
|
9
|
+
def self.load_dependencies(uploader, **)
|
10
|
+
uploader.plugin :processing
|
10
11
|
uploader.plugin :default_url
|
11
12
|
end
|
12
13
|
|
13
|
-
def self.configure(uploader, opts
|
14
|
-
|
15
|
-
|
16
|
-
uploader.opts[:version_names] = opts.fetch(:names, uploader.opts[:version_names])
|
17
|
-
uploader.opts[:version_fallbacks] = opts.fetch(:fallbacks, uploader.opts.fetch(:version_fallbacks, {}))
|
18
|
-
uploader.opts[:versions_fallback_to_original] = opts.fetch(:fallback_to_original, uploader.opts.fetch(:versions_fallback_to_original, true))
|
14
|
+
def self.configure(uploader, **opts)
|
15
|
+
uploader.opts[:versions] ||= { fallbacks: {}, fallback_to_original: true }
|
16
|
+
uploader.opts[:versions].merge!(opts)
|
19
17
|
end
|
20
18
|
|
21
19
|
module ClassMethods
|
22
|
-
def version_names
|
23
|
-
Shrine.deprecation("Shrine.version_names is deprecated and will be removed in Shrine 3.")
|
24
|
-
opts[:version_names]
|
25
|
-
end
|
26
|
-
|
27
20
|
def version_fallbacks
|
28
|
-
opts[:
|
29
|
-
end
|
30
|
-
|
31
|
-
# Checks that the identifier is a registered version.
|
32
|
-
def version?(name)
|
33
|
-
Shrine.deprecation("Shrine.version? is deprecated and will be removed in Shrine 3.")
|
34
|
-
version_names.nil? || version_names.map(&:to_s).include?(name.to_s)
|
21
|
+
opts[:versions][:fallbacks]
|
35
22
|
end
|
36
23
|
|
37
24
|
# Converts a hash of data into a hash of versions.
|
38
|
-
def uploaded_file(object
|
39
|
-
|
40
|
-
|
41
|
-
|
25
|
+
def uploaded_file(object)
|
26
|
+
object = JSON.parse(object) if object.is_a?(String)
|
27
|
+
|
28
|
+
Utils.deep_map(object, transform_keys: :to_sym) do |path, value|
|
29
|
+
if value.is_a?(Hash) && (value["id"].is_a?(String) || value[:id].is_a?(String))
|
30
|
+
file = super(value)
|
31
|
+
elsif value.is_a?(UploadedFile)
|
32
|
+
file = value
|
33
|
+
end
|
34
|
+
|
35
|
+
if file
|
36
|
+
yield file if block_given?
|
37
|
+
file
|
42
38
|
end
|
43
|
-
elsif object.is_a?(Array)
|
44
|
-
object.map { |value| uploaded_file(value, &block) }
|
45
|
-
else
|
46
|
-
super
|
47
39
|
end
|
48
40
|
end
|
49
41
|
end
|
50
42
|
|
51
43
|
module InstanceMethods
|
52
|
-
|
53
|
-
|
54
|
-
if object.is_a?(Hash)
|
55
|
-
object.all? { |name, version| uploaded?(version) }
|
56
|
-
elsif object.is_a?(Array)
|
57
|
-
object.all? { |version| uploaded?(version) }
|
58
|
-
else
|
59
|
-
super
|
60
|
-
end
|
61
|
-
end
|
44
|
+
def upload(io, **options)
|
45
|
+
files = process(io, **options) || io
|
62
46
|
|
63
|
-
|
64
|
-
|
65
|
-
# Stores each version individually. It asserts that all versions are
|
66
|
-
# known, because later the versions will be silently filtered, so
|
67
|
-
# we want to let the user know that they forgot to register a new
|
68
|
-
# version.
|
69
|
-
def _store(io, context)
|
70
|
-
if (hash = io).is_a?(Hash)
|
71
|
-
raise Error, ":location is not applicable to versions" if context.key?(:location)
|
72
|
-
raise Error, "detected multiple versions that point to the same IO object: given versions: #{hash.keys}, unique versions: #{hash.invert.invert.keys}" if hash.invert.invert != hash
|
73
|
-
|
74
|
-
hash.inject({}) do |result, (name, value)|
|
75
|
-
result.merge!(name.to_sym => _store(value, context.merge(version: name.to_sym){|_, v1, v2| Array(v1) + Array(v2)}))
|
76
|
-
end
|
77
|
-
elsif (array = io).is_a?(Array)
|
78
|
-
array.map.with_index { |value, idx| _store(value, context.merge(version: idx){|_, v1, v2| Array(v1) + Array(v2)}) }
|
79
|
-
else
|
80
|
-
super
|
81
|
-
end
|
82
|
-
end
|
47
|
+
Utils.map_file(files) do |name, version|
|
48
|
+
options.merge!(version: name.one? ? name.first : name) if name
|
83
49
|
|
84
|
-
|
85
|
-
def _delete(uploaded_file, context)
|
86
|
-
if (hash = uploaded_file).is_a?(Hash)
|
87
|
-
hash.each do |name, value|
|
88
|
-
_delete(value, context)
|
89
|
-
end
|
90
|
-
elsif (array = uploaded_file).is_a?(Array)
|
91
|
-
array.each do |value|
|
92
|
-
_delete(value, context)
|
93
|
-
end
|
94
|
-
else
|
95
|
-
super
|
50
|
+
super(version, **options, process: false)
|
96
51
|
end
|
97
52
|
end
|
98
53
|
end
|
99
54
|
|
100
55
|
module AttacherMethods
|
56
|
+
def destroy(*)
|
57
|
+
Utils.each_file(self.file) { |_, file| file.delete }
|
58
|
+
end
|
59
|
+
|
101
60
|
# Smart versioned URLs, which include the version name in the default
|
102
61
|
# URL, and properly forwards any options to the underlying storage.
|
103
62
|
def url(version = nil, **options)
|
104
|
-
|
105
|
-
|
106
|
-
if attachment.is_a?(Hash)
|
63
|
+
if file.is_a?(Hash)
|
107
64
|
if version
|
108
65
|
version = version.to_sym
|
109
|
-
if
|
110
|
-
|
66
|
+
if file.key?(version)
|
67
|
+
file[version].url(**options)
|
111
68
|
elsif fallback = shrine_class.version_fallbacks[version]
|
112
69
|
url(fallback, **options)
|
113
70
|
else
|
@@ -118,8 +75,8 @@ class Shrine
|
|
118
75
|
end
|
119
76
|
else
|
120
77
|
if version
|
121
|
-
if
|
122
|
-
|
78
|
+
if file && shrine_class.opts[:versions][:fallback_to_original]
|
79
|
+
file.url(**options)
|
123
80
|
else
|
124
81
|
default_url(**options, version: version)
|
125
82
|
end
|
@@ -129,22 +86,85 @@ class Shrine
|
|
129
86
|
end
|
130
87
|
end
|
131
88
|
|
89
|
+
# Converts the Hash/Array of UploadedFile objects into a Hash/Array of data.
|
90
|
+
def data
|
91
|
+
Utils.map_file(file, transform_keys: :to_s) do |_, version|
|
92
|
+
version.data
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def file=(file)
|
97
|
+
if file.is_a?(Hash) || file.is_a?(Array)
|
98
|
+
@file = file
|
99
|
+
else
|
100
|
+
super
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def uploaded_file(value, &block)
|
105
|
+
shrine_class.uploaded_file(value, &block)
|
106
|
+
end
|
107
|
+
|
132
108
|
private
|
133
109
|
|
134
|
-
def
|
135
|
-
|
110
|
+
def uploaded?(file, storage_key)
|
111
|
+
if file.is_a?(Hash) || file.is_a?(Array)
|
112
|
+
Utils.each_file(file).all? { |_, f| f.storage_key == storage_key }
|
113
|
+
else
|
114
|
+
super
|
115
|
+
end
|
136
116
|
end
|
117
|
+
end
|
137
118
|
|
138
|
-
|
139
|
-
|
119
|
+
module Utils
|
120
|
+
module_function
|
121
|
+
|
122
|
+
def each_file(object)
|
123
|
+
return enum_for(__method__, object) unless block_given?
|
124
|
+
|
125
|
+
map_file(object) do |path, file|
|
126
|
+
yield path, file
|
127
|
+
file
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def map_file(object, transform_keys: :to_sym)
|
132
|
+
if object.is_a?(Hash) || object.is_a?(Array)
|
133
|
+
deep_map(object, transform_keys: transform_keys) do |path, value|
|
134
|
+
yield path, value unless value.is_a?(Hash) || value.is_a?(Array)
|
135
|
+
end
|
136
|
+
elsif object
|
137
|
+
yield nil, object
|
138
|
+
else
|
139
|
+
object
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def deep_map(object, path = [], transform_keys:, &block)
|
140
144
|
if object.is_a?(Hash)
|
141
|
-
|
142
|
-
|
145
|
+
result = yield path, object
|
146
|
+
|
147
|
+
return result if result
|
148
|
+
|
149
|
+
object.inject({}) do |hash, (key, value)|
|
150
|
+
key = key.send(transform_keys)
|
151
|
+
result = yield [*path, key], value
|
152
|
+
|
153
|
+
hash.merge! key => (result || deep_map(value, [*path, key], transform_keys: transform_keys, &block))
|
143
154
|
end
|
144
155
|
elsif object.is_a?(Array)
|
145
|
-
|
156
|
+
result = yield path, object
|
157
|
+
|
158
|
+
return result if result
|
159
|
+
|
160
|
+
object.map.with_index do |value, idx|
|
161
|
+
result = yield [*path, idx], value
|
162
|
+
|
163
|
+
result || deep_map(value, [*path, idx], transform_keys: transform_keys, &block)
|
164
|
+
end
|
146
165
|
else
|
147
|
-
|
166
|
+
result = yield path, object
|
167
|
+
result or fail Shrine::Error, "leaf reached"
|
148
168
|
end
|
149
169
|
end
|
150
170
|
end
|
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
|
#
|