shrine 2.18.1 → 2.19.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.
Potentially problematic release.
This version of shrine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +53 -1
- data/README.md +96 -137
- data/doc/advantages.md +4 -4
- data/doc/attacher.md +1 -2
- data/doc/carrierwave.md +3 -2
- data/doc/creating_storages.md +0 -20
- data/doc/design.md +1 -1
- data/doc/metadata.md +62 -36
- data/doc/paperclip.md +7 -6
- data/doc/plugins/data_uri.md +50 -4
- data/doc/plugins/derivation_endpoint.md +24 -0
- data/doc/plugins/determine_mime_type.md +47 -5
- data/doc/plugins/infer_extension.md +45 -9
- data/doc/plugins/instrumentation.md +170 -0
- data/doc/plugins/presign_endpoint.md +1 -1
- data/doc/plugins/pretty_location.md +23 -0
- data/doc/plugins/remote_url.md +59 -8
- data/doc/plugins/signature.md +54 -7
- data/doc/plugins/store_dimensions.md +69 -4
- data/doc/plugins/upload_endpoint.md +2 -2
- data/doc/plugins/validation_helpers.md +71 -29
- data/doc/refile.md +1 -1
- data/doc/release_notes/2.18.0.md +2 -2
- data/doc/release_notes/2.19.0.md +263 -0
- data/doc/storage/file_system.md +26 -8
- data/doc/testing.md +10 -10
- data/lib/shrine.rb +32 -16
- data/lib/shrine/attacher.rb +3 -0
- data/lib/shrine/attachment.rb +3 -0
- data/lib/shrine/plugins/add_metadata.rb +12 -16
- data/lib/shrine/plugins/backup.rb +2 -0
- data/lib/shrine/plugins/copy.rb +2 -0
- data/lib/shrine/plugins/data_uri.rb +56 -28
- data/lib/shrine/plugins/derivation_endpoint.rb +61 -27
- data/lib/shrine/plugins/determine_mime_type.rb +27 -5
- data/lib/shrine/plugins/infer_extension.rb +26 -5
- data/lib/shrine/plugins/instrumentation.rb +300 -0
- data/lib/shrine/plugins/logging.rb +2 -0
- data/lib/shrine/plugins/moving.rb +2 -0
- data/lib/shrine/plugins/pretty_location.rb +21 -12
- data/lib/shrine/plugins/rack_file.rb +23 -18
- data/lib/shrine/plugins/refresh_metadata.rb +4 -4
- data/lib/shrine/plugins/remote_url.rb +42 -23
- data/lib/shrine/plugins/signature.rb +32 -1
- data/lib/shrine/plugins/store_dimensions.rb +54 -9
- data/lib/shrine/plugins/validation_helpers.rb +148 -47
- data/lib/shrine/storage/file_system.rb +32 -15
- data/lib/shrine/storage/linter.rb +0 -13
- data/lib/shrine/storage/s3.rb +2 -5
- data/lib/shrine/uploaded_file.rb +8 -0
- data/lib/shrine/version.rb +2 -2
- data/shrine.gemspec +18 -3
- metadata +58 -27
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
Shrine.deprecation("The moving plugin has been deprecated in favor of the :move upload option for FileSystem storage. It will no longer be available in Shrine 3.")
|
4
|
+
|
3
5
|
class Shrine
|
4
6
|
module Plugins
|
5
7
|
# Documentation lives in [doc/plugins/moving.md] on GitHub.
|
@@ -7,29 +7,38 @@ class Shrine
|
|
7
7
|
# [doc/plugins/pretty_location.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/pretty_location.md
|
8
8
|
module PrettyLocation
|
9
9
|
def self.configure(uploader, opts = {})
|
10
|
-
uploader.opts[:
|
10
|
+
uploader.opts[:pretty_location] ||= { identifier: :id }
|
11
|
+
uploader.opts[:pretty_location].merge!(opts)
|
11
12
|
end
|
12
13
|
|
13
14
|
module InstanceMethods
|
14
15
|
def generate_location(io, context)
|
15
|
-
|
16
|
-
|
17
|
-
|
16
|
+
pretty_location(io, context)
|
17
|
+
end
|
18
|
+
|
19
|
+
def pretty_location(io, name: nil, record: nil, version: nil, identifier: nil, **)
|
20
|
+
if record
|
21
|
+
namespace = record_namespace(record)
|
22
|
+
identifier ||= record_identifier(record)
|
18
23
|
end
|
19
|
-
name = context[:name]
|
20
24
|
|
21
|
-
|
22
|
-
basename = "#{
|
23
|
-
original = dirname + slash + basename
|
25
|
+
basename = basic_location(io)
|
26
|
+
basename = "#{version}-#{basename}" if version
|
24
27
|
|
25
|
-
[
|
28
|
+
[*namespace, *identifier, *name, basename].join("/")
|
26
29
|
end
|
27
30
|
|
28
31
|
private
|
29
32
|
|
30
|
-
def
|
31
|
-
|
32
|
-
|
33
|
+
def record_identifier(record)
|
34
|
+
record.public_send(opts[:pretty_location][:identifier])
|
35
|
+
end
|
36
|
+
|
37
|
+
def record_namespace(record)
|
38
|
+
class_name = record.class.name or return
|
39
|
+
parts = class_name.downcase.split("::")
|
40
|
+
|
41
|
+
if separator = opts[:pretty_location][:namespace]
|
33
42
|
parts.join(separator)
|
34
43
|
else
|
35
44
|
parts.last
|
@@ -19,7 +19,7 @@ class Shrine
|
|
19
19
|
)
|
20
20
|
end
|
21
21
|
|
22
|
-
|
22
|
+
Shrine::RackFile.new(hash)
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
@@ -77,27 +77,32 @@ class Shrine
|
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
80
|
-
|
81
|
-
# can upload.
|
82
|
-
class UploadedFile
|
83
|
-
attr_reader :tempfile, :original_filename, :content_type
|
84
|
-
alias :to_io :tempfile
|
80
|
+
end
|
85
81
|
|
86
|
-
|
87
|
-
|
88
|
-
@original_filename = hash[:filename]
|
89
|
-
@content_type = hash[:type]
|
90
|
-
end
|
82
|
+
register_plugin(:rack_file, RackFile)
|
83
|
+
end
|
91
84
|
|
92
|
-
|
93
|
-
|
94
|
-
|
85
|
+
# This is used to wrap the Rack hash into an IO-like object which Shrine
|
86
|
+
# can upload.
|
87
|
+
class RackFile
|
88
|
+
attr_reader :tempfile, :original_filename, :content_type
|
89
|
+
alias :to_io :tempfile
|
95
90
|
|
96
|
-
|
97
|
-
|
98
|
-
|
91
|
+
def initialize(hash)
|
92
|
+
@tempfile = hash[:tempfile]
|
93
|
+
@original_filename = hash[:filename]
|
94
|
+
@content_type = hash[:type]
|
99
95
|
end
|
100
96
|
|
101
|
-
|
97
|
+
def path
|
98
|
+
@tempfile.path
|
99
|
+
end
|
100
|
+
|
101
|
+
extend Forwardable
|
102
|
+
delegate [:read, :size, :rewind, :eof?, :close] => :@tempfile
|
102
103
|
end
|
104
|
+
|
105
|
+
# backwards compatibility
|
106
|
+
Plugins::RackFile.const_set(:UploadedFile, RackFile)
|
107
|
+
Plugins::RackFile.deprecate_constant(:UploadedFile)
|
103
108
|
end
|
@@ -7,12 +7,12 @@ class Shrine
|
|
7
7
|
# [doc/plugins/refresh_metadata.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/refresh_metadata.md
|
8
8
|
module RefreshMetadata
|
9
9
|
module FileMethods
|
10
|
-
def refresh_metadata!(context
|
10
|
+
def refresh_metadata!(**context)
|
11
11
|
refreshed_metadata =
|
12
|
-
if
|
13
|
-
uploader.
|
12
|
+
if opened?
|
13
|
+
uploader.send(:get_metadata, self, metadata: true, **context)
|
14
14
|
else
|
15
|
-
open { uploader.
|
15
|
+
open { uploader.send(:get_metadata, self, metadata: true, **context) }
|
16
16
|
end
|
17
17
|
|
18
18
|
@data = @data.merge("metadata" => metadata.merge(refreshed_metadata))
|
@@ -8,12 +8,48 @@ class Shrine
|
|
8
8
|
#
|
9
9
|
# [doc/plugins/remote_url.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/remote_url.md
|
10
10
|
module RemoteUrl
|
11
|
+
LOG_SUBSCRIBER = -> (event) do
|
12
|
+
Shrine.logger.info "Remote URL (#{event.duration}ms) – #{{
|
13
|
+
remote_url: event[:remote_url],
|
14
|
+
download_options: event[:download_options],
|
15
|
+
uploader: event[:uploader],
|
16
|
+
}.inspect}"
|
17
|
+
end
|
18
|
+
|
11
19
|
def self.configure(uploader, opts = {})
|
12
|
-
|
20
|
+
uploader.opts[:remote_url] ||= { downloader: Down.method(:download), log_subscriber: LOG_SUBSCRIBER }
|
21
|
+
uploader.opts[:remote_url].merge!(opts)
|
22
|
+
|
23
|
+
unless uploader.opts[:remote_url].key?(:max_size)
|
24
|
+
fail Error, "The :max_size option is required for remote_url plugin"
|
25
|
+
end
|
26
|
+
|
27
|
+
# instrumentation plugin integration
|
28
|
+
if uploader.respond_to?(:subscribe)
|
29
|
+
uploader.subscribe(:remote_url, &uploader.opts[:remote_url][:log_subscriber])
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
module ClassMethods
|
34
|
+
# Downloads the file using the "down" gem or a custom downloader.
|
35
|
+
# Checks the file size and terminates the download early if the file
|
36
|
+
# is too big.
|
37
|
+
def remote_url(url, **options)
|
38
|
+
options = { max_size: opts[:remote_url][:max_size] }.merge(options)
|
39
|
+
|
40
|
+
instrument_remote_url(url, options) do
|
41
|
+
opts[:remote_url][:downloader].call(url, options)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# Sends a `remote_url.shrine` event for instrumentation plugin.
|
48
|
+
def instrument_remote_url(url, options, &block)
|
49
|
+
return yield unless respond_to?(:instrument)
|
13
50
|
|
14
|
-
|
15
|
-
|
16
|
-
uploader.opts[:remote_url_error_message] = opts.fetch(:error_message, uploader.opts[:remote_url_error_message])
|
51
|
+
instrument(:remote_url, remote_url: url, download_options: options, &block)
|
52
|
+
end
|
17
53
|
end
|
18
54
|
|
19
55
|
module AttachmentMethods
|
@@ -38,7 +74,7 @@ class Shrine
|
|
38
74
|
return if url == "" || url.nil?
|
39
75
|
|
40
76
|
begin
|
41
|
-
downloaded_file =
|
77
|
+
downloaded_file = shrine_class.remote_url(url, downloader)
|
42
78
|
rescue => error
|
43
79
|
download_error = error
|
44
80
|
end
|
@@ -64,25 +100,8 @@ class Shrine
|
|
64
100
|
|
65
101
|
private
|
66
102
|
|
67
|
-
# Downloads the file using the "down" gem or a custom downloader.
|
68
|
-
# Checks the file size and terminates the download early if the file
|
69
|
-
# is too big.
|
70
|
-
def download(url, options)
|
71
|
-
downloader = shrine_class.opts[:remote_url_downloader]
|
72
|
-
downloader = method(:"download_with_#{downloader}") if downloader.is_a?(Symbol)
|
73
|
-
max_size = shrine_class.opts[:remote_url_max_size]
|
74
|
-
|
75
|
-
downloader.call(url, { max_size: max_size }.merge(options))
|
76
|
-
end
|
77
|
-
|
78
|
-
# We silence any download errors, because for the user's point of view
|
79
|
-
# the download simply failed.
|
80
|
-
def download_with_open_uri(url, options)
|
81
|
-
Down.download(url, options)
|
82
|
-
end
|
83
|
-
|
84
103
|
def download_error_message(url, error)
|
85
|
-
if message = shrine_class.opts[:
|
104
|
+
if message = shrine_class.opts[:remote_url][:error_message]
|
86
105
|
if message.respond_to?(:call)
|
87
106
|
args = [url, error].take(message.arity.abs)
|
88
107
|
message = message.call(*args)
|
@@ -6,11 +6,42 @@ class Shrine
|
|
6
6
|
#
|
7
7
|
# [doc/plugins/signature.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/signature.md
|
8
8
|
module Signature
|
9
|
+
LOG_SUBSCRIBER = -> (event) do
|
10
|
+
Shrine.logger.info "Signature (#{event.duration}ms) – #{{
|
11
|
+
io: event[:io].class,
|
12
|
+
algorithm: event[:algorithm],
|
13
|
+
format: event[:format],
|
14
|
+
uploader: event[:uploader],
|
15
|
+
}.inspect}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.configure(uploader, opts = {})
|
19
|
+
uploader.opts[:signature] ||= { log_subscriber: LOG_SUBSCRIBER }
|
20
|
+
uploader.opts[:signature].merge!(opts)
|
21
|
+
|
22
|
+
# instrumentation plugin integration
|
23
|
+
if uploader.respond_to?(:subscribe)
|
24
|
+
uploader.subscribe(:signature, &uploader.opts[:signature][:log_subscriber])
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
9
28
|
module ClassMethods
|
10
29
|
# Calculates `algorithm` hash of the contents of the IO object, and
|
11
30
|
# encodes it into `format`.
|
12
31
|
def calculate_signature(io, algorithm, format: :hex)
|
13
|
-
|
32
|
+
instrument_signature(io, algorithm, format) do
|
33
|
+
SignatureCalculator.new(algorithm.downcase, format: format).call(io)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
alias signature calculate_signature
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
# Sends a `signature.shrine` event for instrumentation plugin.
|
41
|
+
def instrument_signature(io, algorithm, format, &block)
|
42
|
+
return yield unless respond_to?(:instrument)
|
43
|
+
|
44
|
+
instrument(:signature, io: io, algorithm: algorithm, format: format, &block)
|
14
45
|
end
|
15
46
|
end
|
16
47
|
|
@@ -6,23 +6,47 @@ class Shrine
|
|
6
6
|
#
|
7
7
|
# [doc/plugins/store_dimensions.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/store_dimensions.md
|
8
8
|
module StoreDimensions
|
9
|
+
LOG_SUBSCRIBER = -> (event) do
|
10
|
+
Shrine.logger.info "Image Dimensions (#{event.duration}ms) – #{{
|
11
|
+
io: event[:io].class,
|
12
|
+
uploader: event[:uploader],
|
13
|
+
}.inspect}"
|
14
|
+
end
|
15
|
+
|
9
16
|
def self.configure(uploader, opts = {})
|
10
|
-
uploader.opts[:
|
17
|
+
uploader.opts[:store_dimensions] ||= { analyzer: :fastimage, on_error: :warn, log_subscriber: LOG_SUBSCRIBER }
|
18
|
+
uploader.opts[:store_dimensions].merge!(opts)
|
19
|
+
|
20
|
+
# resolve error strategy
|
21
|
+
case uploader.opts[:store_dimensions][:on_error]
|
22
|
+
when :fail
|
23
|
+
uploader.opts[:store_dimensions][:on_error] = -> (error) { fail error }
|
24
|
+
when :warn
|
25
|
+
uploader.opts[:store_dimensions][:on_error] = -> (error) { Shrine.warn "Error occurred when attempting to extract image dimensions: #{error.inspect}" }
|
26
|
+
when :ignore
|
27
|
+
uploader.opts[:store_dimensions][:on_error] = -> (error) { }
|
28
|
+
end
|
29
|
+
|
30
|
+
# instrumentation plugin integration
|
31
|
+
if uploader.respond_to?(:subscribe)
|
32
|
+
uploader.subscribe(:image_dimensions, &uploader.opts[:store_dimensions][:log_subscriber])
|
33
|
+
end
|
11
34
|
end
|
12
35
|
|
13
36
|
module ClassMethods
|
14
37
|
# Determines the dimensions of the IO object by calling the specified
|
15
38
|
# analyzer.
|
16
39
|
def extract_dimensions(io)
|
17
|
-
analyzer = opts[:
|
40
|
+
analyzer = opts[:store_dimensions][:analyzer]
|
18
41
|
analyzer = dimensions_analyzer(analyzer) if analyzer.is_a?(Symbol)
|
19
42
|
args = [io, dimensions_analyzers].take(analyzer.arity.abs)
|
20
43
|
|
21
|
-
dimensions = analyzer.call(*args)
|
44
|
+
dimensions = instrument_dimensions(io) { analyzer.call(*args) }
|
22
45
|
io.rewind
|
23
46
|
|
24
47
|
dimensions
|
25
48
|
end
|
49
|
+
alias dimensions extract_dimensions
|
26
50
|
|
27
51
|
# Returns a hash of built-in dimensions analyzers, where keys are
|
28
52
|
# analyzer names and values are `#call`-able objects which accepts the
|
@@ -35,7 +59,18 @@ class Shrine
|
|
35
59
|
|
36
60
|
# Returns callable dimensions analyzer object.
|
37
61
|
def dimensions_analyzer(name)
|
38
|
-
|
62
|
+
on_error = opts[:store_dimensions][:on_error]
|
63
|
+
|
64
|
+
DimensionsAnalyzer.new(name, on_error: on_error).method(:call)
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
# Sends a `image_dimensions.shrine` event for instrumentation plugin.
|
70
|
+
def instrument_dimensions(io, &block)
|
71
|
+
return yield unless respond_to?(:instrument)
|
72
|
+
|
73
|
+
instrument(:image_dimensions, io: io, &block)
|
39
74
|
end
|
40
75
|
end
|
41
76
|
|
@@ -77,10 +112,11 @@ class Shrine
|
|
77
112
|
class DimensionsAnalyzer
|
78
113
|
SUPPORTED_TOOLS = [:fastimage, :mini_magick, :ruby_vips]
|
79
114
|
|
80
|
-
def initialize(tool)
|
115
|
+
def initialize(tool, on_error: method(:fail))
|
81
116
|
raise Error, "unknown dimensions analyzer #{tool.inspect}, supported analyzers are: #{SUPPORTED_TOOLS.join(",")}" unless SUPPORTED_TOOLS.include?(tool)
|
82
117
|
|
83
|
-
@tool
|
118
|
+
@tool = tool
|
119
|
+
@on_error = on_error
|
84
120
|
end
|
85
121
|
|
86
122
|
def call(io)
|
@@ -93,19 +129,28 @@ class Shrine
|
|
93
129
|
|
94
130
|
def extract_with_fastimage(io)
|
95
131
|
require "fastimage"
|
96
|
-
FastImage.size(io)
|
132
|
+
FastImage.size(io, raise_on_failure: true)
|
133
|
+
rescue FastImage::FastImageException => error
|
134
|
+
on_error(error)
|
97
135
|
end
|
98
136
|
|
99
137
|
def extract_with_mini_magick(io)
|
100
138
|
require "mini_magick"
|
101
139
|
Shrine.with_file(io) { |file| MiniMagick::Image.new(file.path).dimensions }
|
102
|
-
rescue MiniMagick::Error
|
140
|
+
rescue MiniMagick::Error => error
|
141
|
+
on_error(error)
|
103
142
|
end
|
104
143
|
|
105
144
|
def extract_with_ruby_vips(io)
|
106
145
|
require "vips"
|
107
146
|
Shrine.with_file(io) { |file| Vips::Image.new_from_file(file.path).size }
|
108
|
-
rescue Vips::Error
|
147
|
+
rescue Vips::Error => error
|
148
|
+
on_error(error)
|
149
|
+
end
|
150
|
+
|
151
|
+
def on_error(error)
|
152
|
+
@on_error.call(error)
|
153
|
+
nil
|
109
154
|
end
|
110
155
|
end
|
111
156
|
end
|
@@ -12,16 +12,18 @@ class Shrine
|
|
12
12
|
end
|
13
13
|
|
14
14
|
DEFAULT_MESSAGES = {
|
15
|
-
max_size:
|
16
|
-
min_size:
|
17
|
-
max_width:
|
18
|
-
min_width:
|
19
|
-
max_height:
|
20
|
-
min_height:
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
15
|
+
max_size: -> (max) { "size must not be greater than #{PRETTY_FILESIZE.call(max)}" },
|
16
|
+
min_size: -> (min) { "size must not be less than #{PRETTY_FILESIZE.call(min)}" },
|
17
|
+
max_width: -> (max) { "width must not be greater than #{max}px" },
|
18
|
+
min_width: -> (min) { "width must not be less than #{min}px" },
|
19
|
+
max_height: -> (max) { "height must not be greater than #{max}px" },
|
20
|
+
min_height: -> (min) { "height must not be less than #{min}px" },
|
21
|
+
max_dimensions: -> (dims) { "dimensions must not be greater than #{dims.join("x")}" },
|
22
|
+
min_dimensions: -> (dims) { "dimensions must not be less than #{dims.join("x")}" },
|
23
|
+
mime_type_inclusion: -> (list) { "type must be one of: #{list.join(", ")}" },
|
24
|
+
mime_type_exclusion: -> (list) { "type must not be one of: #{list.join(", ")}" },
|
25
|
+
extension_inclusion: -> (list) { "extension must be one of: #{list.join(", ")}" },
|
26
|
+
extension_exclusion: -> (list) { "extension must not be one of: #{list.join(", ")}" },
|
25
27
|
}
|
26
28
|
|
27
29
|
FILESIZE_UNITS = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"].freeze
|
@@ -45,96 +47,195 @@ class Shrine
|
|
45
47
|
end
|
46
48
|
|
47
49
|
module AttacherMethods
|
48
|
-
# Validates that the
|
50
|
+
# Validates that the `size` metadata is not larger than `max`.
|
51
|
+
#
|
52
|
+
# validate_max_size 5*1024*1024
|
49
53
|
def validate_max_size(max, message: nil)
|
50
|
-
get.size <= max
|
54
|
+
validate_result(get.size <= max, :max_size, message, max)
|
51
55
|
end
|
52
56
|
|
53
|
-
# Validates that the
|
57
|
+
# Validates that the `size` metadata is not smaller than `min`.
|
58
|
+
#
|
59
|
+
# validate_min_size 1024
|
54
60
|
def validate_min_size(min, message: nil)
|
55
|
-
get.size >= min
|
61
|
+
validate_result(get.size >= min, :min_size, message, min)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Validates that the `size` metadata is in the given range.
|
65
|
+
#
|
66
|
+
# validate_size 1024..5*1024*1024
|
67
|
+
def validate_size(size_range)
|
68
|
+
min_size, max_size = size_range.begin, size_range.end
|
69
|
+
|
70
|
+
validate_min_size(min_size) && validate_max_size(max_size)
|
56
71
|
end
|
57
72
|
|
58
|
-
|
59
|
-
# `
|
73
|
+
|
74
|
+
# Validates that the `width` metadata is not larger than `max`.
|
75
|
+
# Requires the `store_dimensions` plugin.
|
76
|
+
#
|
77
|
+
# validate_max_width 5000
|
60
78
|
def validate_max_width(max, message: nil)
|
61
|
-
|
79
|
+
fail Error, ":store_dimensions plugin is required" unless get.respond_to?(:width)
|
62
80
|
if get.width
|
63
|
-
get.width <= max
|
81
|
+
validate_result(get.width <= max, :max_width, message, max)
|
64
82
|
else
|
65
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.")
|
66
84
|
end
|
67
85
|
end
|
68
86
|
|
69
|
-
# Validates that the
|
70
|
-
# `store_dimensions` plugin.
|
87
|
+
# Validates that the `width` metadata is not smaller than `min`.
|
88
|
+
# Requires the `store_dimensions` plugin.
|
89
|
+
#
|
90
|
+
# validate_min_width 100
|
71
91
|
def validate_min_width(min, message: nil)
|
72
|
-
|
92
|
+
fail Error, ":store_dimensions plugin is required" unless get.respond_to?(:width)
|
73
93
|
if get.width
|
74
|
-
get.width >= min
|
94
|
+
validate_result(get.width >= min, :min_width, message, min)
|
75
95
|
else
|
76
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.")
|
77
97
|
end
|
78
98
|
end
|
79
99
|
|
80
|
-
# Validates that the
|
81
|
-
#
|
100
|
+
# Validates that the `width` metadata is in the given range.
|
101
|
+
#
|
102
|
+
# validate_width 100..5000
|
103
|
+
def validate_width(width_range)
|
104
|
+
min_width, max_width = width_range.begin, width_range.end
|
105
|
+
|
106
|
+
validate_min_width(min_width) && validate_max_width(max_width)
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
# Validates that the `height` metadata is not larger than `max`.
|
111
|
+
# Requires the `store_dimensions` plugin.
|
112
|
+
#
|
113
|
+
# validate_max_height 5000
|
82
114
|
def validate_max_height(max, message: nil)
|
83
|
-
|
115
|
+
fail Error, ":store_dimensions plugin is required" unless get.respond_to?(:height)
|
84
116
|
if get.height
|
85
|
-
get.height <= max
|
117
|
+
validate_result(get.height <= max, :max_height, message, max)
|
86
118
|
else
|
87
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.")
|
88
120
|
end
|
89
121
|
end
|
90
122
|
|
91
|
-
# Validates that the
|
92
|
-
# `store_dimensions` plugin.
|
123
|
+
# Validates that the `height` metadata is not smaller than `min`.
|
124
|
+
# Requires the `store_dimensions` plugin.
|
125
|
+
#
|
126
|
+
# validate_min_height 100
|
93
127
|
def validate_min_height(min, message: nil)
|
94
|
-
|
128
|
+
fail Error, ":store_dimensions plugin is required" unless get.respond_to?(:height)
|
95
129
|
if get.height
|
96
|
-
get.height >= min
|
130
|
+
validate_result(get.height >= min, :min_height, message, min)
|
97
131
|
else
|
98
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.")
|
99
133
|
end
|
100
134
|
end
|
101
135
|
|
102
|
-
# Validates that the
|
136
|
+
# Validates that the `height` metadata is in the given range.
|
137
|
+
#
|
138
|
+
# validate_height 100..5000
|
139
|
+
def validate_height(height_range)
|
140
|
+
min_height, max_height = height_range.begin, height_range.end
|
141
|
+
|
142
|
+
validate_min_height(min_height) && validate_max_height(max_height)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Validates that the dimensions are not larger than specified.
|
146
|
+
#
|
147
|
+
# validate_max_dimensions [5000, 5000]
|
148
|
+
def validate_max_dimensions((max_width, max_height), message: nil)
|
149
|
+
fail Error, ":store_dimensions plugin is required" unless get.respond_to?(:width) && get.respond_to?(:height)
|
150
|
+
fail Error, "width or height metadata is nil" unless get.width && get.height
|
151
|
+
|
152
|
+
validate_result(
|
153
|
+
get.width <= max_width && get.height <= max_height,
|
154
|
+
:max_dimensions, message, [max_width, max_height]
|
155
|
+
)
|
156
|
+
end
|
157
|
+
|
158
|
+
# Validates that the dimensions are not smaller than specified.
|
159
|
+
#
|
160
|
+
# validate_max_dimensions [100, 100]
|
161
|
+
def validate_min_dimensions((min_width, min_height), message: nil)
|
162
|
+
fail Error, ":store_dimensions plugin is required" unless get.respond_to?(:width) && get.respond_to?(:height)
|
163
|
+
fail Error, "width or height metadata is nil" unless get.width && get.height
|
164
|
+
|
165
|
+
validate_result(
|
166
|
+
get.width >= min_width && get.height >= min_height,
|
167
|
+
:min_dimensions, message, [min_width, min_height]
|
168
|
+
)
|
169
|
+
end
|
170
|
+
|
171
|
+
# Validates that the dimensions are in the given range.
|
172
|
+
#
|
173
|
+
# validate_dimensions [100..5000, 100..5000]
|
174
|
+
def validate_dimensions((width_range, height_range))
|
175
|
+
min_dims = width_range.begin, height_range.begin
|
176
|
+
max_dims = width_range.end, height_range.end
|
177
|
+
|
178
|
+
validate_min_dimensions(min_dims) && validate_max_dimensions(max_dims)
|
179
|
+
end
|
180
|
+
|
181
|
+
# Validates that the `mime_type` metadata is included in the given
|
182
|
+
# list.
|
103
183
|
#
|
104
184
|
# validate_mime_type_inclusion %w[audio/mp3 audio/flac]
|
105
|
-
def validate_mime_type_inclusion(
|
106
|
-
|
107
|
-
|
185
|
+
def validate_mime_type_inclusion(types, message: nil)
|
186
|
+
validate_result(
|
187
|
+
types.any? { |type| regex(type) =~ get.mime_type.to_s },
|
188
|
+
:mime_type_inclusion, message, types
|
189
|
+
)
|
108
190
|
end
|
191
|
+
alias validate_mime_type validate_mime_type_inclusion
|
109
192
|
|
110
|
-
# Validates that the
|
193
|
+
# Validates that the `mime_type` metadata is not included in the given
|
194
|
+
# list.
|
111
195
|
#
|
112
196
|
# validate_mime_type_exclusion %w[text/x-php]
|
113
|
-
def validate_mime_type_exclusion(
|
114
|
-
|
115
|
-
|
197
|
+
def validate_mime_type_exclusion(types, message: nil)
|
198
|
+
validate_result(
|
199
|
+
types.none? { |type| regex(type) =~ get.mime_type.to_s },
|
200
|
+
:mime_type_exclusion, message, types
|
201
|
+
)
|
116
202
|
end
|
117
203
|
|
118
|
-
# Validates that the extension is in the given
|
119
|
-
# is case insensitive.
|
204
|
+
# Validates that the extension is included in the given list.
|
205
|
+
# Comparison is case insensitive.
|
120
206
|
#
|
121
207
|
# validate_extension_inclusion %w[jpg jpeg png gif]
|
122
|
-
def validate_extension_inclusion(
|
123
|
-
|
124
|
-
|
208
|
+
def validate_extension_inclusion(extensions, message: nil)
|
209
|
+
validate_result(
|
210
|
+
extensions.any? { |extension| regex(extension) =~ get.extension.to_s },
|
211
|
+
:extension_inclusion, message, extensions
|
212
|
+
)
|
125
213
|
end
|
214
|
+
alias validate_extension validate_extension_inclusion
|
126
215
|
|
127
|
-
# Validates that the extension is not in the given
|
216
|
+
# Validates that the extension is not included in the given list.
|
128
217
|
# Comparison is case insensitive.
|
129
218
|
#
|
130
219
|
# validate_extension_exclusion %[php jar]
|
131
|
-
def validate_extension_exclusion(
|
132
|
-
|
133
|
-
|
220
|
+
def validate_extension_exclusion(extensions, message: nil)
|
221
|
+
validate_result(
|
222
|
+
extensions.none? { |extension| regex(extension) =~ get.extension.to_s },
|
223
|
+
:extension_exclusion, message, extensions
|
224
|
+
)
|
134
225
|
end
|
135
226
|
|
136
227
|
private
|
137
228
|
|
229
|
+
# Adds an error if result is false and returns the result.
|
230
|
+
def validate_result(result, type, message, *args)
|
231
|
+
if result
|
232
|
+
true
|
233
|
+
else
|
234
|
+
add_error(type, message, *args)
|
235
|
+
false
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
138
239
|
# Converts a string to a regex.
|
139
240
|
def regex(value)
|
140
241
|
if value.is_a?(Regexp)
|