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,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Shrine
|
|
4
|
+
module Plugins
|
|
5
|
+
# Documentation can be found on https://shrinerb.com/docs/plugins/type_predicates
|
|
6
|
+
module TypePredicates
|
|
7
|
+
def self.configure(uploader, methods: [], **opts)
|
|
8
|
+
uploader.opts[:type_predicates] ||= { mime: :mini_mime }
|
|
9
|
+
uploader.opts[:type_predicates].merge!(opts)
|
|
10
|
+
|
|
11
|
+
methods.each do |name|
|
|
12
|
+
uploader::UploadedFile.send(:define_method, "#{name}?") { type?(name) }
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
module ClassMethods
|
|
17
|
+
def type_lookup(extension, database = nil)
|
|
18
|
+
database ||= opts[:type_predicates][:mime]
|
|
19
|
+
database = MimeDatabase.new(database) if database.is_a?(Symbol)
|
|
20
|
+
database.call(extension.to_s)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
module FileMethods
|
|
25
|
+
def image?
|
|
26
|
+
general_type?("image")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def video?
|
|
30
|
+
general_type?("video")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def audio?
|
|
34
|
+
general_type?("audio")
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def text?
|
|
38
|
+
general_type?("text")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def type?(type)
|
|
42
|
+
matching_mime_type = shrine_class.type_lookup(type)
|
|
43
|
+
|
|
44
|
+
fail Error, "type #{type.inspect} is not recognized by the MIME library" unless matching_mime_type
|
|
45
|
+
|
|
46
|
+
mime_type! == matching_mime_type
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def general_type?(type)
|
|
52
|
+
mime_type!.start_with?(type)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def mime_type!
|
|
56
|
+
mime_type or fail Error, "mime_type metadata value is missing"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
class MimeDatabase
|
|
61
|
+
SUPPORTED_TOOLS = %i[mini_mime mime_types mimemagic marcel rack_mime]
|
|
62
|
+
|
|
63
|
+
def initialize(tool)
|
|
64
|
+
raise Error, "unknown type database #{tool.inspect}, supported databases are: #{SUPPORTED_TOOLS.join(",")}" unless SUPPORTED_TOOLS.include?(tool)
|
|
65
|
+
|
|
66
|
+
@tool = tool
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def call(extension)
|
|
70
|
+
send(:"lookup_with_#{@tool}", extension)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
def lookup_with_mini_mime(extension)
|
|
76
|
+
require "mini_mime"
|
|
77
|
+
|
|
78
|
+
info = MiniMime.lookup_by_extension(extension)
|
|
79
|
+
info&.content_type
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def lookup_with_mime_types(extension)
|
|
83
|
+
require "mime/types"
|
|
84
|
+
|
|
85
|
+
mime_type = MIME::Types.of(".#{extension}").first
|
|
86
|
+
mime_type&.content_type
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def lookup_with_mimemagic(extension)
|
|
90
|
+
require "mimemagic"
|
|
91
|
+
|
|
92
|
+
magic = MimeMagic.by_extension(".#{extension}")
|
|
93
|
+
magic&.type
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def lookup_with_marcel(extension)
|
|
97
|
+
require "marcel"
|
|
98
|
+
|
|
99
|
+
type = Marcel::MimeType.for(extension: ".#{extension}")
|
|
100
|
+
type unless type == "application/octet-stream"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def lookup_with_rack_mime(extension)
|
|
104
|
+
require "rack/mime"
|
|
105
|
+
|
|
106
|
+
Rack::Mime.mime_type(".#{extension}", nil)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
register_plugin(:type_predicates, TypePredicates)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -7,15 +7,13 @@ require "digest"
|
|
|
7
7
|
|
|
8
8
|
class Shrine
|
|
9
9
|
module Plugins
|
|
10
|
-
# Documentation
|
|
11
|
-
#
|
|
12
|
-
# [doc/plugins/upload_endpoint.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/upload_endpoint.md
|
|
10
|
+
# Documentation can be found on https://shrinerb.com/docs/plugins/upload_endpoint
|
|
13
11
|
module UploadEndpoint
|
|
14
|
-
def self.load_dependencies(uploader,
|
|
12
|
+
def self.load_dependencies(uploader, **)
|
|
15
13
|
uploader.plugin :rack_file
|
|
16
14
|
end
|
|
17
15
|
|
|
18
|
-
def self.configure(uploader, opts
|
|
16
|
+
def self.configure(uploader, **opts)
|
|
19
17
|
uploader.opts[:upload_endpoint] ||= {}
|
|
20
18
|
uploader.opts[:upload_endpoint].merge!(opts)
|
|
21
19
|
end
|
|
@@ -93,7 +91,9 @@ class Shrine
|
|
|
93
91
|
handle_request(request)
|
|
94
92
|
end
|
|
95
93
|
|
|
96
|
-
headers[
|
|
94
|
+
headers = Rack::Headers[headers] if Rack.release >= "3"
|
|
95
|
+
headers["Content-Length"] ||= body.respond_to?(:bytesize) ? body.bytesize.to_s :
|
|
96
|
+
body.map(&:bytesize).inject(0, :+).to_s
|
|
97
97
|
|
|
98
98
|
[status, headers, body]
|
|
99
99
|
end
|
|
@@ -137,16 +137,21 @@ class Shrine
|
|
|
137
137
|
end
|
|
138
138
|
|
|
139
139
|
error!(400, "Upload Not Found") if value.nil?
|
|
140
|
-
error!(400, "Upload Not Valid") unless value.is_a?(Hash) && value[:tempfile]
|
|
141
140
|
|
|
142
|
-
|
|
141
|
+
if value.is_a?(Hash) && value[:tempfile]
|
|
142
|
+
@shrine_class.rack_file(value)
|
|
143
|
+
elsif %i[read rewind eof? close].all? { |m| value.respond_to?(m) }
|
|
144
|
+
value
|
|
145
|
+
else
|
|
146
|
+
error!(400, "Upload Not Valid")
|
|
147
|
+
end
|
|
143
148
|
end
|
|
144
149
|
|
|
145
150
|
# Returns a hash of information containing `:action` and `:request`
|
|
146
151
|
# keys, which is to be passed to `Shrine#upload`. Calls
|
|
147
152
|
# `:upload_context` option if given.
|
|
148
153
|
def get_context(request)
|
|
149
|
-
context = { action: :upload
|
|
154
|
+
context = { action: :upload }
|
|
150
155
|
context.merge! @upload_context.call(request) if @upload_context
|
|
151
156
|
context
|
|
152
157
|
end
|
|
@@ -158,7 +163,7 @@ class Shrine
|
|
|
158
163
|
if @upload
|
|
159
164
|
@upload.call(io, context, request)
|
|
160
165
|
else
|
|
161
|
-
uploader.upload(io, context)
|
|
166
|
+
uploader.upload(io, **context)
|
|
162
167
|
end
|
|
163
168
|
end
|
|
164
169
|
|
|
@@ -166,26 +171,29 @@ class Shrine
|
|
|
166
171
|
# a Rack response triple - an array consisting of a status number, hash
|
|
167
172
|
# of headers, and a body enumerable. If a `:rack_response` option is
|
|
168
173
|
# given, calls that instead.
|
|
169
|
-
def make_response(
|
|
174
|
+
def make_response(uploaded_file, request)
|
|
170
175
|
if @rack_response
|
|
171
|
-
@rack_response.call(
|
|
176
|
+
@rack_response.call(uploaded_file, request)
|
|
172
177
|
else
|
|
173
178
|
if @url
|
|
174
|
-
url
|
|
175
|
-
|
|
176
|
-
when Hash then object.url(**@url)
|
|
177
|
-
else @url.call(object, request)
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
body = { data: object, url: url }.to_json
|
|
179
|
+
url = resolve_url(uploaded_file, request)
|
|
180
|
+
body = { data: uploaded_file, url: url }.to_json
|
|
181
181
|
else
|
|
182
|
-
body =
|
|
182
|
+
body = uploaded_file.to_json
|
|
183
183
|
end
|
|
184
184
|
|
|
185
185
|
[200, { "Content-Type" => CONTENT_TYPE_JSON }, [body]]
|
|
186
186
|
end
|
|
187
187
|
end
|
|
188
188
|
|
|
189
|
+
def resolve_url(uploaded_file, request)
|
|
190
|
+
case @url
|
|
191
|
+
when true then uploaded_file.url
|
|
192
|
+
when Hash then uploaded_file.url(**@url)
|
|
193
|
+
else @url.call(uploaded_file, request)
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
189
197
|
def verify_size!(file, request)
|
|
190
198
|
error!(413, "Upload Too Large") if @max_size && file.size > @max_size
|
|
191
199
|
end
|
|
@@ -212,8 +220,4 @@ class Shrine
|
|
|
212
220
|
@shrine_class.new(@storage_key)
|
|
213
221
|
end
|
|
214
222
|
end
|
|
215
|
-
|
|
216
|
-
# backwards compatibility
|
|
217
|
-
Plugins::UploadEndpoint.const_set(:App, UploadEndpoint)
|
|
218
|
-
Plugins::UploadEndpoint.deprecate_constant(:App)
|
|
219
223
|
end
|
|
@@ -2,28 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
class Shrine
|
|
4
4
|
module Plugins
|
|
5
|
-
# Documentation
|
|
6
|
-
#
|
|
7
|
-
# [doc/plugins/upload_options.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/upload_options.md
|
|
5
|
+
# Documentation can be found on https://shrinerb.com/docs/plugins/upload_options
|
|
8
6
|
module UploadOptions
|
|
9
|
-
def self.configure(uploader,
|
|
7
|
+
def self.configure(uploader, **opts)
|
|
10
8
|
uploader.opts[:upload_options] ||= {}
|
|
11
|
-
uploader.opts[:upload_options].merge!(
|
|
9
|
+
uploader.opts[:upload_options].merge!(opts)
|
|
12
10
|
end
|
|
13
11
|
|
|
14
12
|
module InstanceMethods
|
|
15
|
-
def put(io, context)
|
|
16
|
-
upload_options = get_upload_options(io, context)
|
|
17
|
-
context = { upload_options: upload_options }.merge(context)
|
|
18
|
-
super
|
|
19
|
-
end
|
|
20
|
-
|
|
21
13
|
private
|
|
22
14
|
|
|
23
|
-
def
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
options
|
|
15
|
+
def _upload(io, **options)
|
|
16
|
+
upload_options = get_upload_options(io, options)
|
|
17
|
+
|
|
18
|
+
super(io, **options, upload_options: upload_options)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def get_upload_options(io, options)
|
|
22
|
+
upload_options = opts[:upload_options][storage_key] || {}
|
|
23
|
+
upload_options = upload_options.call(io, options) if upload_options.respond_to?(:call)
|
|
24
|
+
upload_options = upload_options.merge(options[:upload_options]) if options[:upload_options]
|
|
25
|
+
upload_options
|
|
27
26
|
end
|
|
28
27
|
end
|
|
29
28
|
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Shrine
|
|
4
|
+
module Plugins
|
|
5
|
+
# Documentation can be found on https://shrinerb.com/docs/plugins/url_options
|
|
6
|
+
module UrlOptions
|
|
7
|
+
def self.configure(uploader, **opts)
|
|
8
|
+
uploader.opts[:url_options] ||= {}
|
|
9
|
+
uploader.opts[:url_options].merge!(opts)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
module FileMethods
|
|
13
|
+
def url(**options)
|
|
14
|
+
default_options = url_options(options)
|
|
15
|
+
|
|
16
|
+
super(**default_options, **options)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def url_options(options)
|
|
22
|
+
default_options = shrine_class.opts[:url_options][storage_key]
|
|
23
|
+
default_options = default_options.call(self, options) if default_options.respond_to?(:call)
|
|
24
|
+
default_options || {}
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
register_plugin(:url_options, UrlOptions)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -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,24 +135,22 @@ 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
|
|
157
145
|
|
|
158
146
|
# Validates that the dimensions are not smaller than specified.
|
|
159
147
|
#
|
|
160
|
-
#
|
|
148
|
+
# validate_min_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)
|