shrine 3.0.1 → 3.3.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 +82 -0
- data/LICENSE.txt +1 -1
- data/README.md +15 -5
- data/doc/advantages.md +33 -16
- data/doc/attacher.md +2 -2
- data/doc/carrierwave.md +78 -34
- data/doc/changing_derivatives.md +39 -39
- data/doc/design.md +134 -85
- data/doc/direct_s3.md +1 -0
- data/doc/external/articles.md +57 -45
- data/doc/external/extensions.md +41 -35
- data/doc/external/misc.md +23 -8
- data/doc/getting_started.md +177 -112
- data/doc/metadata.md +79 -43
- data/doc/multiple_files.md +6 -4
- data/doc/paperclip.md +119 -42
- data/doc/plugins/activerecord.md +1 -1
- data/doc/plugins/add_metadata.md +112 -35
- data/doc/plugins/atomic_helpers.md +41 -3
- data/doc/plugins/backgrounding.md +12 -2
- data/doc/plugins/column.md +36 -7
- data/doc/plugins/data_uri.md +2 -2
- data/doc/plugins/default_url.md +6 -3
- data/doc/plugins/derivation_endpoint.md +26 -28
- data/doc/plugins/derivatives.md +238 -171
- data/doc/plugins/determine_mime_type.md +2 -2
- data/doc/plugins/download_endpoint.md +5 -5
- data/doc/plugins/dynamic_storage.md +1 -1
- data/doc/plugins/form_assign.md +5 -5
- data/doc/plugins/included.md +25 -5
- data/doc/plugins/infer_extension.md +11 -2
- data/doc/plugins/instrumentation.md +1 -1
- data/doc/plugins/metadata_attributes.md +22 -10
- data/doc/plugins/mirroring.md +1 -1
- data/doc/plugins/persistence.md +11 -1
- data/doc/plugins/refresh_metadata.md +5 -4
- data/doc/plugins/remote_url.md +8 -3
- data/doc/plugins/remove_invalid.md +9 -1
- data/doc/plugins/signature.md +11 -2
- data/doc/plugins/store_dimensions.md +12 -2
- data/doc/plugins/type_predicates.md +96 -0
- data/doc/plugins/upload_endpoint.md +7 -11
- data/doc/plugins/upload_options.md +1 -1
- data/doc/plugins/url_options.md +4 -4
- data/doc/plugins/validation.md +14 -4
- data/doc/plugins/validation_helpers.md +3 -3
- data/doc/plugins/versions.md +7 -7
- data/doc/processing.md +290 -127
- data/doc/refile.md +39 -18
- data/doc/release_notes/2.19.0.md +1 -1
- data/doc/release_notes/2.8.0.md +1 -1
- data/doc/release_notes/3.0.0.md +1 -1
- data/doc/release_notes/3.0.1.md +4 -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/securing_uploads.md +3 -3
- data/doc/storage/file_system.md +1 -1
- data/doc/storage/memory.md +19 -0
- data/doc/storage/s3.md +105 -82
- data/doc/testing.md +2 -2
- data/doc/upgrading_to_3.md +97 -49
- data/doc/validation.md +3 -2
- data/lib/shrine.rb +8 -8
- data/lib/shrine/attacher.rb +24 -14
- data/lib/shrine/attachment.rb +5 -5
- data/lib/shrine/plugins.rb +22 -0
- data/lib/shrine/plugins/activerecord.rb +1 -1
- data/lib/shrine/plugins/add_metadata.rb +18 -7
- data/lib/shrine/plugins/backgrounding.rb +2 -2
- data/lib/shrine/plugins/default_storage.rb +6 -6
- data/lib/shrine/plugins/default_url.rb +1 -1
- data/lib/shrine/plugins/derivation_endpoint.rb +12 -7
- data/lib/shrine/plugins/derivatives.rb +61 -29
- data/lib/shrine/plugins/determine_mime_type.rb +3 -3
- data/lib/shrine/plugins/entity.rb +6 -6
- data/lib/shrine/plugins/mirroring.rb +8 -8
- data/lib/shrine/plugins/model.rb +3 -3
- data/lib/shrine/plugins/presign_endpoint.rb +16 -4
- data/lib/shrine/plugins/pretty_location.rb +1 -1
- data/lib/shrine/plugins/processing.rb +1 -1
- data/lib/shrine/plugins/refresh_metadata.rb +2 -2
- data/lib/shrine/plugins/remote_url.rb +3 -3
- data/lib/shrine/plugins/remove_attachment.rb +5 -0
- data/lib/shrine/plugins/remove_invalid.rb +10 -5
- data/lib/shrine/plugins/sequel.rb +1 -1
- data/lib/shrine/plugins/signature.rb +7 -6
- data/lib/shrine/plugins/store_dimensions.rb +22 -11
- data/lib/shrine/plugins/type_predicates.rb +113 -0
- data/lib/shrine/plugins/upload_endpoint.rb +10 -5
- data/lib/shrine/plugins/upload_options.rb +2 -2
- data/lib/shrine/plugins/url_options.rb +2 -2
- data/lib/shrine/plugins/validation.rb +9 -7
- data/lib/shrine/storage/linter.rb +4 -4
- data/lib/shrine/storage/memory.rb +5 -3
- data/lib/shrine/storage/s3.rb +117 -38
- data/lib/shrine/uploaded_file.rb +0 -1
- data/lib/shrine/version.rb +2 -2
- data/shrine.gemspec +7 -8
- metadata +25 -31
@@ -141,7 +141,7 @@ class Shrine
|
|
141
141
|
require "mimemagic"
|
142
142
|
|
143
143
|
mime = MimeMagic.by_magic(io)
|
144
|
-
mime
|
144
|
+
mime&.type
|
145
145
|
end
|
146
146
|
|
147
147
|
def extract_with_marcel(io, options)
|
@@ -158,7 +158,7 @@ class Shrine
|
|
158
158
|
|
159
159
|
if filename = extract_filename(io)
|
160
160
|
mime_type = MIME::Types.of(filename).first
|
161
|
-
mime_type
|
161
|
+
mime_type&.content_type
|
162
162
|
end
|
163
163
|
end
|
164
164
|
|
@@ -167,7 +167,7 @@ class Shrine
|
|
167
167
|
|
168
168
|
if filename = extract_filename(io)
|
169
169
|
info = MiniMime.lookup_by_filename(filename)
|
170
|
-
info
|
170
|
+
info&.content_type
|
171
171
|
end
|
172
172
|
end
|
173
173
|
|
@@ -41,27 +41,27 @@ class Shrine
|
|
41
41
|
end
|
42
42
|
|
43
43
|
# Returns the URL to the attached file.
|
44
|
-
define_method :"#{name}_url" do |*args|
|
45
|
-
send(:"#{name}_attacher").url(*args)
|
44
|
+
define_method :"#{name}_url" do |*args, **options|
|
45
|
+
send(:"#{name}_attacher").url(*args, **options)
|
46
46
|
end
|
47
47
|
|
48
48
|
# Returns an attacher instance.
|
49
49
|
define_method :"#{name}_attacher" do |**options|
|
50
|
-
attachment.send(:attacher, self, options)
|
50
|
+
attachment.send(:attacher, self, **options)
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
54
|
# Returns the class attacher instance with loaded entity. It's not
|
55
55
|
# memoized because the entity object could be frozen.
|
56
|
-
def attacher(record, options)
|
57
|
-
attacher = class_attacher(options)
|
56
|
+
def attacher(record, **options)
|
57
|
+
attacher = class_attacher(**options)
|
58
58
|
attacher.load_entity(record, @name)
|
59
59
|
attacher
|
60
60
|
end
|
61
61
|
|
62
62
|
# Creates an instance of the corresponding attacher class with set
|
63
63
|
# name.
|
64
|
-
def class_attacher(options)
|
64
|
+
def class_attacher(**options)
|
65
65
|
attacher = shrine_class::Attacher.new(**@options, **options)
|
66
66
|
attacher.instance_variable_set(:@name, @name)
|
67
67
|
attacher
|
@@ -53,7 +53,7 @@ class Shrine
|
|
53
53
|
# Mirrors upload to other mirror storages.
|
54
54
|
def upload(io, mirror: true, **options)
|
55
55
|
file = super(io, **options)
|
56
|
-
file.trigger_mirror_upload if mirror
|
56
|
+
file.trigger_mirror_upload(**options) if mirror
|
57
57
|
file
|
58
58
|
end
|
59
59
|
end
|
@@ -61,31 +61,31 @@ class Shrine
|
|
61
61
|
module FileMethods
|
62
62
|
# Mirrors upload if mirrors are defined. Calls mirror block if
|
63
63
|
# registered, otherwise mirrors synchronously.
|
64
|
-
def trigger_mirror_upload
|
64
|
+
def trigger_mirror_upload(**options)
|
65
65
|
return unless shrine_class.mirrors[storage_key] && shrine_class.mirror_upload?
|
66
66
|
|
67
67
|
if shrine_class.mirror_upload_block
|
68
|
-
mirror_upload_background
|
68
|
+
mirror_upload_background(**options)
|
69
69
|
else
|
70
|
-
mirror_upload
|
70
|
+
mirror_upload(**options)
|
71
71
|
end
|
72
72
|
end
|
73
73
|
|
74
74
|
# Calls mirror upload block.
|
75
|
-
def mirror_upload_background
|
75
|
+
def mirror_upload_background(**options)
|
76
76
|
fail Error, "mirror upload block is not registered" unless shrine_class.mirror_upload_block
|
77
77
|
|
78
|
-
shrine_class.mirror_upload_block.call(self)
|
78
|
+
shrine_class.mirror_upload_block.call(self, **options)
|
79
79
|
end
|
80
80
|
|
81
81
|
# Uploads the file to each mirror storage.
|
82
|
-
def mirror_upload
|
82
|
+
def mirror_upload(**options)
|
83
83
|
previously_opened = opened?
|
84
84
|
|
85
85
|
each_mirror do |mirror|
|
86
86
|
rewind if opened?
|
87
87
|
|
88
|
-
shrine_class.upload(self, mirror, location: id, close: false, action: :mirror)
|
88
|
+
shrine_class.upload(self, mirror, **options, location: id, close: false, action: :mirror)
|
89
89
|
end
|
90
90
|
ensure
|
91
91
|
if opened? && !previously_opened
|
data/lib/shrine/plugins/model.rb
CHANGED
@@ -55,11 +55,11 @@ class Shrine
|
|
55
55
|
end
|
56
56
|
|
57
57
|
# Memoizes the attacher instance into an instance variable.
|
58
|
-
def attacher(record, options)
|
58
|
+
def attacher(record, **options)
|
59
59
|
return super unless model?
|
60
60
|
|
61
61
|
if !record.instance_variable_get(:"@#{@name}_attacher") || options.any?
|
62
|
-
attacher = class_attacher(options)
|
62
|
+
attacher = class_attacher(**options)
|
63
63
|
attacher.load_model(record, @name)
|
64
64
|
|
65
65
|
record.instance_variable_set(:"@#{@name}_attacher", attacher)
|
@@ -118,7 +118,7 @@ class Shrine
|
|
118
118
|
end
|
119
119
|
|
120
120
|
# Writes uploaded file data into the model.
|
121
|
-
def set(*
|
121
|
+
def set(*)
|
122
122
|
result = super
|
123
123
|
write if model?
|
124
124
|
result
|
@@ -8,7 +8,7 @@ class Shrine
|
|
8
8
|
module Plugins
|
9
9
|
# Documentation can be found on https://shrinerb.com/docs/plugins/presign_endpoint
|
10
10
|
module PresignEndpoint
|
11
|
-
def self.configure(uploader, opts
|
11
|
+
def self.configure(uploader, **opts)
|
12
12
|
uploader.opts[:presign_endpoint] ||= {}
|
13
13
|
uploader.opts[:presign_endpoint].merge!(opts)
|
14
14
|
end
|
@@ -81,9 +81,14 @@ class Shrine
|
|
81
81
|
|
82
82
|
status, headers, body = catch(:halt) do
|
83
83
|
error!(404, "Not Found") unless ["", "/"].include?(request.path_info)
|
84
|
-
error!(405, "Method Not Allowed") unless request.get?
|
85
84
|
|
86
|
-
|
85
|
+
if request.get?
|
86
|
+
handle_request(request)
|
87
|
+
elsif request.options?
|
88
|
+
handle_options_request(request)
|
89
|
+
else
|
90
|
+
error!(405, "Method Not Allowed")
|
91
|
+
end
|
87
92
|
end
|
88
93
|
|
89
94
|
headers["Content-Length"] ||= body.map(&:bytesize).inject(0, :+).to_s
|
@@ -109,6 +114,13 @@ class Shrine
|
|
109
114
|
make_response(presign, request)
|
110
115
|
end
|
111
116
|
|
117
|
+
# Uppy client sends an OPTIONS request to fetch information about the
|
118
|
+
# Uppy Companion. Since our Rack app is only acting as Uppy Companion, we
|
119
|
+
# just return a successful response.
|
120
|
+
def handle_options_request(request)
|
121
|
+
[200, {}, []]
|
122
|
+
end
|
123
|
+
|
112
124
|
# Generates the location using `Shrine#generate_uid`, and extracts the
|
113
125
|
# extension from the `filename` query parameter. If `:presign_location`
|
114
126
|
# option is given, calls that instead.
|
@@ -135,7 +147,7 @@ class Shrine
|
|
135
147
|
if @presign
|
136
148
|
data = @presign.call(location, options, request)
|
137
149
|
else
|
138
|
-
data = storage.presign(location, options)
|
150
|
+
data = storage.presign(location, **options)
|
139
151
|
end
|
140
152
|
|
141
153
|
{ fields: {}, headers: {} }.merge(data.to_h)
|
@@ -11,7 +11,7 @@ class Shrine
|
|
11
11
|
|
12
12
|
module InstanceMethods
|
13
13
|
def generate_location(io, **options)
|
14
|
-
pretty_location(io, options)
|
14
|
+
pretty_location(io, **options)
|
15
15
|
end
|
16
16
|
|
17
17
|
def pretty_location(io, name: nil, record: nil, version: nil, derivative: nil, identifier: nil, metadata: {}, **)
|
@@ -33,7 +33,7 @@ class Shrine
|
|
33
33
|
def process(io, **options)
|
34
34
|
pipeline = processing_pipeline(options[:action])
|
35
35
|
pipeline.inject(io) do |input, processor|
|
36
|
-
instance_exec(input, options, &processor) || input
|
36
|
+
instance_exec(input, **options, &processor) || input
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
@@ -16,7 +16,7 @@ class Shrine
|
|
16
16
|
}.inspect}"
|
17
17
|
end
|
18
18
|
|
19
|
-
DOWNLOADER = -> (url, options) { Down.download(url, options) }
|
19
|
+
DOWNLOADER = -> (url, **options) { Down.download(url, **options) }
|
20
20
|
|
21
21
|
def self.load_dependencies(uploader, *)
|
22
22
|
uploader.plugin :validation
|
@@ -63,7 +63,7 @@ class Shrine
|
|
63
63
|
private
|
64
64
|
|
65
65
|
def download_remote_url(url, options)
|
66
|
-
opts[:remote_url][:downloader].call(url, options)
|
66
|
+
opts[:remote_url][:downloader].call(url, **options)
|
67
67
|
rescue Down::TooLarge
|
68
68
|
fail DownloadError, "remote file too large"
|
69
69
|
rescue Down::Error
|
@@ -87,7 +87,7 @@ class Shrine
|
|
87
87
|
def assign_remote_url(url, downloader: {}, **options)
|
88
88
|
return if url == "" || url.nil?
|
89
89
|
|
90
|
-
downloaded_file = shrine_class.remote_url(url, downloader)
|
90
|
+
downloaded_file = shrine_class.remote_url(url, **downloader)
|
91
91
|
attach_cached(downloaded_file, **options)
|
92
92
|
rescue DownloadError => error
|
93
93
|
errors.clear << remote_url_error_message(url, error)
|
@@ -32,6 +32,11 @@ class Shrine
|
|
32
32
|
|
33
33
|
private
|
34
34
|
|
35
|
+
# Don't override previously removed attachment that wasn't yet deleted.
|
36
|
+
def change?(file)
|
37
|
+
super && !(changed? && remove?)
|
38
|
+
end
|
39
|
+
|
35
40
|
# Rails sends "0" or "false" if the checkbox hasn't been ticked.
|
36
41
|
def remove?
|
37
42
|
remove && remove != "" && remove !~ /\A(0|false)\z/
|
@@ -9,18 +9,23 @@ class Shrine
|
|
9
9
|
end
|
10
10
|
|
11
11
|
module AttacherMethods
|
12
|
-
def
|
12
|
+
def validate(*)
|
13
13
|
super
|
14
14
|
ensure
|
15
|
-
|
15
|
+
deassign if errors.any?
|
16
16
|
end
|
17
17
|
|
18
18
|
private
|
19
19
|
|
20
|
-
def
|
20
|
+
def deassign
|
21
21
|
destroy
|
22
|
-
|
23
|
-
|
22
|
+
|
23
|
+
if changed?
|
24
|
+
load_data @previous.data
|
25
|
+
@previous = nil
|
26
|
+
else
|
27
|
+
load_data nil
|
28
|
+
end
|
24
29
|
end
|
25
30
|
end
|
26
31
|
end
|
@@ -21,10 +21,13 @@ class Shrine
|
|
21
21
|
module ClassMethods
|
22
22
|
# Calculates `algorithm` hash of the contents of the IO object, and
|
23
23
|
# encodes it into `format`.
|
24
|
-
def calculate_signature(io, algorithm, format: :hex)
|
25
|
-
|
26
|
-
|
27
|
-
|
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
|
28
31
|
end
|
29
32
|
alias signature calculate_signature
|
30
33
|
|
@@ -62,8 +65,6 @@ class Shrine
|
|
62
65
|
|
63
66
|
def call(io)
|
64
67
|
hash = send(:"calculate_#{algorithm}", io)
|
65
|
-
io.rewind
|
66
|
-
|
67
68
|
send(:"encode_#{format}", hash)
|
68
69
|
end
|
69
70
|
|
@@ -12,7 +12,7 @@ class Shrine
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def self.configure(uploader, log_subscriber: LOG_SUBSCRIBER, **opts)
|
15
|
-
uploader.opts[:store_dimensions] ||= { analyzer: :fastimage, on_error: :warn }
|
15
|
+
uploader.opts[:store_dimensions] ||= { analyzer: :fastimage, on_error: :warn, auto_extraction: true }
|
16
16
|
uploader.opts[:store_dimensions].merge!(opts)
|
17
17
|
|
18
18
|
# resolve error strategy
|
@@ -71,8 +71,10 @@ class Shrine
|
|
71
71
|
end
|
72
72
|
|
73
73
|
module InstanceMethods
|
74
|
-
# We update the metadata with "width" and "height".
|
75
74
|
def extract_metadata(io, **options)
|
75
|
+
return super unless opts[:store_dimensions][:auto_extraction]
|
76
|
+
|
77
|
+
# We update the metadata with "width" and "height".
|
76
78
|
width, height = self.class.extract_dimensions(io)
|
77
79
|
|
78
80
|
super.merge!("width" => width, "height" => height)
|
@@ -113,23 +115,32 @@ class Shrine
|
|
113
115
|
|
114
116
|
def extract_with_fastimage(io)
|
115
117
|
require "fastimage"
|
116
|
-
|
117
|
-
|
118
|
-
|
118
|
+
|
119
|
+
begin
|
120
|
+
FastImage.size(io, raise_on_failure: true)
|
121
|
+
rescue FastImage::FastImageException => error
|
122
|
+
on_error(error)
|
123
|
+
end
|
119
124
|
end
|
120
125
|
|
121
126
|
def extract_with_mini_magick(io)
|
122
127
|
require "mini_magick"
|
123
|
-
|
124
|
-
|
125
|
-
|
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
|
126
134
|
end
|
127
135
|
|
128
136
|
def extract_with_ruby_vips(io)
|
129
137
|
require "vips"
|
130
|
-
|
131
|
-
|
132
|
-
|
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
|
133
144
|
end
|
134
145
|
|
135
146
|
def on_error(error)
|
@@ -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
|