mime-types 3.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mime/type'
4
+
5
+ # A version of MIME::Type that works hand-in-hand with a MIME::Types::Columnar
6
+ # container to load data by columns.
7
+ #
8
+ # When a field is has not yet been loaded, that data will be loaded for all
9
+ # types in the container before forwarding the message to MIME::Type.
10
+ #
11
+ # More information can be found in MIME::Types::Columnar.
12
+ #
13
+ # MIME::Type::Columnar is *not* intended to be created except by
14
+ # MIME::Types::Columnar containers.
15
+ class MIME::Type::Columnar < MIME::Type
16
+ def initialize(container, content_type, extensions) # :nodoc:
17
+ @container = container
18
+ self.content_type = content_type
19
+ self.extensions = extensions
20
+ end
21
+
22
+ def self.column(*methods, file: nil) # :nodoc:
23
+ file ||= methods.first
24
+
25
+ file_method = :"load_#{file}"
26
+ methods.each do |m|
27
+ define_method m do |*args|
28
+ @container.send(file_method)
29
+ super(*args)
30
+ end
31
+ end
32
+ end
33
+
34
+ column :friendly
35
+ column :encoding, :encoding=
36
+ column :docs, :docs=
37
+ column :preferred_extension, :preferred_extension=
38
+ column :obsolete, :obsolete=, :obsolete?, :registered, :registered=,
39
+ :registered?, :signature, :signature=, :signature?, file: 'flags'
40
+ column :xrefs, :xrefs=, :xref_urls
41
+ column :use_instead, :use_instead=
42
+
43
+ def encode_with(coder) # :nodoc:
44
+ @container.send(:load_friendly)
45
+ @container.send(:load_encoding)
46
+ @container.send(:load_docs)
47
+ @container.send(:load_flags)
48
+ @container.send(:load_use_instead)
49
+ @container.send(:load_xrefs)
50
+ @container.send(:load_preferred_extension)
51
+ super
52
+ end
53
+
54
+ class << self
55
+ undef column
56
+ end
57
+ end
@@ -0,0 +1,231 @@
1
+ # frozen_string_literal: true
2
+
3
+ ##
4
+ module MIME
5
+ ##
6
+ class Types
7
+ end
8
+ end
9
+
10
+ require 'mime/type'
11
+
12
+ # MIME::Types is a registry of MIME types. It is both a class (created with
13
+ # MIME::Types.new) and a default registry (loaded automatically or through
14
+ # interactions with MIME::Types.[] and MIME::Types.type_for).
15
+ #
16
+ # == The Default mime-types Registry
17
+ #
18
+ # The default mime-types registry is loaded automatically when the library
19
+ # is required (<tt>require 'mime/types'</tt>), but it may be lazily loaded
20
+ # (loaded on first use) with the use of the environment variable
21
+ # +RUBY_MIME_TYPES_LAZY_LOAD+ having any value other than +false+. The
22
+ # initial startup is about 14× faster (~10 ms vs ~140 ms), but the
23
+ # registry will be loaded at some point in the future.
24
+ #
25
+ # The default mime-types registry can also be loaded from a Marshal cache
26
+ # file specific to the version of MIME::Types being loaded. This will be
27
+ # handled automatically with the use of a file referred to in the
28
+ # environment variable +RUBY_MIME_TYPES_CACHE+. MIME::Types will attempt to
29
+ # load the registry from this cache file (MIME::Type::Cache.load); if it
30
+ # cannot be loaded (because the file does not exist, there is an error, or
31
+ # the data is for a different version of mime-types), the default registry
32
+ # will be loaded from the normal JSON version and then the cache file will
33
+ # be *written* to the location indicated by +RUBY_MIME_TYPES_CACHE+. Cache
34
+ # file loads just over 4½× faster (~30 ms vs ~140 ms).
35
+ # loads.
36
+ #
37
+ # Notes:
38
+ # * The loading of the default registry is *not* atomic; when using a
39
+ # multi-threaded environment, it is recommended that lazy loading is not
40
+ # used and mime-types is loaded as early as possible.
41
+ # * Cache files should be specified per application in a multiprocess
42
+ # environment and should be initialized during deployment or before
43
+ # forking to minimize the chance that the multiple processes will be
44
+ # trying to write to the same cache file at the same time, or that two
45
+ # applications that are on different versions of mime-types would be
46
+ # thrashing the cache.
47
+ # * Unless cache files are preinitialized, the application using the
48
+ # mime-types cache file must have read/write permission to the cache file.
49
+ #
50
+ # == Usage
51
+ # require 'mime/types'
52
+ #
53
+ # plaintext = MIME::Types['text/plain']
54
+ # print plaintext.media_type # => 'text'
55
+ # print plaintext.sub_type # => 'plain'
56
+ #
57
+ # puts plaintext.extensions.join(" ") # => 'asc txt c cc h hh cpp'
58
+ #
59
+ # puts plaintext.encoding # => 8bit
60
+ # puts plaintext.binary? # => false
61
+ # puts plaintext.ascii? # => true
62
+ # puts plaintext.obsolete? # => false
63
+ # puts plaintext.registered? # => true
64
+ # puts plaintext == 'text/plain' # => true
65
+ # puts MIME::Type.simplified('x-appl/x-zip') # => 'appl/zip'
66
+ #
67
+ class MIME::Types
68
+ # The release version of Ruby MIME::Types
69
+ VERSION = MIME::Type::VERSION
70
+
71
+ include Enumerable
72
+
73
+ # Creates a new MIME::Types registry.
74
+ def initialize
75
+ @type_variants = Container.new
76
+ @extension_index = Container.new
77
+ end
78
+
79
+ # Returns the number of known type variants.
80
+ def count
81
+ @type_variants.values.inject(0) { |a, e| a + e.size }
82
+ end
83
+
84
+ def inspect # :nodoc:
85
+ "#<#{self.class}: #{count} variants, #{@extension_index.count} extensions>"
86
+ end
87
+
88
+ # Iterates through the type variants.
89
+ def each
90
+ if block_given?
91
+ @type_variants.each_value { |tv| tv.each { |t| yield t } }
92
+ else
93
+ enum_for(:each)
94
+ end
95
+ end
96
+
97
+ @__types__ = nil
98
+
99
+ # Returns a list of MIME::Type objects, which may be empty. The optional
100
+ # flag parameters are <tt>:complete</tt> (finds only complete MIME::Type
101
+ # objects) and <tt>:registered</tt> (finds only MIME::Types that are
102
+ # registered). It is possible for multiple matches to be returned for
103
+ # either type (in the example below, 'text/plain' returns two values --
104
+ # one for the general case, and one for VMS systems).
105
+ #
106
+ # puts "\nMIME::Types['text/plain']"
107
+ # MIME::Types['text/plain'].each { |t| puts t.to_a.join(", ") }
108
+ #
109
+ # puts "\nMIME::Types[/^image/, complete: true]"
110
+ # MIME::Types[/^image/, :complete => true].each do |t|
111
+ # puts t.to_a.join(", ")
112
+ # end
113
+ #
114
+ # If multiple type definitions are returned, returns them sorted as
115
+ # follows:
116
+ # 1. Complete definitions sort before incomplete ones;
117
+ # 2. IANA-registered definitions sort before LTSW-recorded
118
+ # definitions.
119
+ # 3. Current definitions sort before obsolete ones;
120
+ # 4. Obsolete definitions with use-instead clauses sort before those
121
+ # without;
122
+ # 5. Obsolete definitions use-instead clauses are compared.
123
+ # 6. Sort on name.
124
+ def [](type_id, complete: false, registered: false)
125
+ matches = case type_id
126
+ when MIME::Type
127
+ @type_variants[type_id.simplified]
128
+ when Regexp
129
+ match(type_id)
130
+ else
131
+ @type_variants[MIME::Type.simplified(type_id)]
132
+ end
133
+
134
+ prune_matches(matches, complete, registered).sort { |a, b|
135
+ a.priority_compare(b)
136
+ }
137
+ end
138
+
139
+ # Return the list of MIME::Types which belongs to the file based on its
140
+ # filename extension. If there is no extension, the filename will be used
141
+ # as the matching criteria on its own.
142
+ #
143
+ # This will always return a merged, flatten, priority sorted, unique array.
144
+ #
145
+ # puts MIME::Types.type_for('citydesk.xml')
146
+ # => [application/xml, text/xml]
147
+ # puts MIME::Types.type_for('citydesk.gif')
148
+ # => [image/gif]
149
+ # puts MIME::Types.type_for(%w(citydesk.xml citydesk.gif))
150
+ # => [application/xml, image/gif, text/xml]
151
+ def type_for(filename)
152
+ Array(filename).flat_map { |fn|
153
+ @extension_index[fn.chomp.downcase[/\.?([^.]*?)$/, 1]]
154
+ }.compact.inject(Set.new, :+).sort { |a, b|
155
+ a.priority_compare(b)
156
+ }
157
+ end
158
+ alias of type_for
159
+
160
+ # Add one or more MIME::Type objects to the set of known types. If the
161
+ # type is already known, a warning will be displayed.
162
+ #
163
+ # The last parameter may be the value <tt>:silent</tt> or +true+ which
164
+ # will suppress duplicate MIME type warnings.
165
+ def add(*types)
166
+ quiet = ((types.last == :silent) or (types.last == true))
167
+
168
+ types.each do |mime_type|
169
+ case mime_type
170
+ when true, false, nil, Symbol
171
+ nil
172
+ when MIME::Types
173
+ variants = mime_type.instance_variable_get(:@type_variants)
174
+ add(*variants.values.inject(Set.new, :+).to_a, quiet)
175
+ when Array
176
+ add(*mime_type, quiet)
177
+ else
178
+ add_type(mime_type, quiet)
179
+ end
180
+ end
181
+ end
182
+
183
+ # Add a single MIME::Type object to the set of known types. If the +type+ is
184
+ # already known, a warning will be displayed. The +quiet+ parameter may be a
185
+ # truthy value to suppress that warning.
186
+ def add_type(type, quiet = false)
187
+ if !quiet and @type_variants[type.simplified].include?(type)
188
+ MIME::Types.logger.warn <<-WARNING
189
+ Type #{type} is already registered as a variant of #{type.simplified}.
190
+ WARNING
191
+ end
192
+
193
+ add_type_variant!(type)
194
+ index_extensions!(type)
195
+ end
196
+
197
+ private
198
+
199
+ def add_type_variant!(mime_type)
200
+ @type_variants.add(mime_type.simplified, mime_type)
201
+ end
202
+
203
+ def reindex_extensions!(mime_type)
204
+ return unless @type_variants[mime_type.simplified].include?(mime_type)
205
+
206
+ index_extensions!(mime_type)
207
+ end
208
+
209
+ def index_extensions!(mime_type)
210
+ mime_type.extensions.each { |ext| @extension_index.add(ext, mime_type) }
211
+ end
212
+
213
+ def prune_matches(matches, complete, registered)
214
+ matches.delete_if { |e| !e.complete? } if complete
215
+ matches.delete_if { |e| !e.registered? } if registered
216
+ matches
217
+ end
218
+
219
+ def match(pattern)
220
+ @type_variants.select { |k, _|
221
+ k =~ pattern
222
+ }.values.inject(Set.new, :+)
223
+ end
224
+ end
225
+
226
+ require 'mime/types/cache'
227
+ require 'mime/types/container'
228
+ require 'mime/types/loader'
229
+ require 'mime/types/logger'
230
+ require 'mime/types/_columnar'
231
+ require 'mime/types/registry'
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mime/type/columnar'
4
+
5
+ # MIME::Types::Columnar is used to extend a MIME::Types container to load data
6
+ # by columns instead of from JSON or YAML. Column loads of MIME types loaded
7
+ # through the columnar store are synchronized with a Mutex.
8
+ #
9
+ # MIME::Types::Columnar is not intended to be used directly, but will be added
10
+ # to an instance of MIME::Types when it is loaded with
11
+ # MIME::Types::Loader#load_columnar.
12
+ module MIME::Types::Columnar
13
+ LOAD_MUTEX = Mutex.new # :nodoc:
14
+
15
+ def self.extended(obj) # :nodoc:
16
+ super
17
+ obj.instance_variable_set(:@__mime_data__, [])
18
+ obj.instance_variable_set(:@__files__, Set.new)
19
+ end
20
+
21
+ # Load the first column data file (type and extensions).
22
+ def load_base_data(path) #:nodoc:
23
+ @__root__ = path
24
+
25
+ each_file_line('content_type', false) do |line|
26
+ line = line.split
27
+ content_type = line.shift
28
+ extensions = line
29
+ # content_type, *extensions = line.split
30
+
31
+ type = MIME::Type::Columnar.new(self, content_type, extensions)
32
+ @__mime_data__ << type
33
+ add(type)
34
+ end
35
+
36
+ self
37
+ end
38
+
39
+ private
40
+
41
+ def each_file_line(name, lookup = true)
42
+ LOAD_MUTEX.synchronize do
43
+ next if @__files__.include?(name)
44
+
45
+ i = -1
46
+ column = File.join(@__root__, "mime.#{name}.column")
47
+
48
+ IO.readlines(column, encoding: 'UTF-8').each do |line|
49
+ line.chomp!
50
+
51
+ if lookup
52
+ type = @__mime_data__[i += 1] or next
53
+ yield type, line
54
+ else
55
+ yield line
56
+ end
57
+ end
58
+
59
+ @__files__ << name
60
+ end
61
+ end
62
+
63
+ def load_encoding
64
+ each_file_line('encoding') do |type, line|
65
+ pool ||= {}
66
+ type.instance_variable_set(:@encoding, (pool[line] ||= line))
67
+ end
68
+ end
69
+
70
+ def load_docs
71
+ each_file_line('docs') do |type, line|
72
+ type.instance_variable_set(:@docs, opt(line))
73
+ end
74
+ end
75
+
76
+ def load_preferred_extension
77
+ each_file_line('pext') do |type, line|
78
+ type.instance_variable_set(:@preferred_extension, opt(line))
79
+ end
80
+ end
81
+
82
+ def load_flags
83
+ each_file_line('flags') do |type, line|
84
+ line = line.split
85
+ type.instance_variable_set(:@obsolete, flag(line.shift))
86
+ type.instance_variable_set(:@registered, flag(line.shift))
87
+ type.instance_variable_set(:@signature, flag(line.shift))
88
+ end
89
+ end
90
+
91
+ def load_xrefs
92
+ each_file_line('xrefs') { |type, line|
93
+ type.instance_variable_set(:@xrefs, dict(line, array: true))
94
+ }
95
+ end
96
+
97
+ def load_friendly
98
+ each_file_line('friendly') { |type, line|
99
+ type.instance_variable_set(:@friendly, dict(line))
100
+ }
101
+ end
102
+
103
+ def load_use_instead
104
+ each_file_line('use_instead') do |type, line|
105
+ type.instance_variable_set(:@use_instead, opt(line))
106
+ end
107
+ end
108
+
109
+ def dict(line, array: false)
110
+ if line == '-'
111
+ {}
112
+ else
113
+ line.split('|').each_with_object({}) { |l, h|
114
+ k, v = l.split('^')
115
+ v = nil if v.empty?
116
+ h[k] = array ? Array(v) : v
117
+ }
118
+ end
119
+ end
120
+
121
+ def arr(line)
122
+ if line == '-'
123
+ []
124
+ else
125
+ line.split('|').flatten.compact.uniq
126
+ end
127
+ end
128
+
129
+ def opt(line)
130
+ line unless line == '-'
131
+ end
132
+
133
+ def flag(line)
134
+ line == '1'
135
+ end
136
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ MIME::Types::Cache = Struct.new(:version, :data) # :nodoc:
4
+
5
+ # Caching of MIME::Types registries is advisable if you will be loading
6
+ # the default registry relatively frequently. With the class methods on
7
+ # MIME::Types::Cache, any MIME::Types registry can be marshaled quickly
8
+ # and easily.
9
+ #
10
+ # The cache is invalidated on a per-data-version basis; a cache file for
11
+ # version 3.2015.1118 will not be reused with version 3.2015.1201.
12
+ class << MIME::Types::Cache
13
+ # Attempts to load the cache from the file provided as a parameter or in
14
+ # the environment variable +RUBY_MIME_TYPES_CACHE+. Returns +nil+ if the
15
+ # file does not exist, if the file cannot be loaded, or if the data in
16
+ # the cache version is different than this version.
17
+ def load(cache_file = nil)
18
+ cache_file ||= ENV['RUBY_MIME_TYPES_CACHE']
19
+ return nil unless cache_file and File.exist?(cache_file)
20
+
21
+ cache = Marshal.load(File.binread(cache_file))
22
+ if cache.version == MIME::Types::Data::VERSION
23
+ Marshal.load(cache.data)
24
+ else
25
+ MIME::Types.logger.warn <<-WARNING.chomp
26
+ Could not load MIME::Types cache: invalid version
27
+ WARNING
28
+ nil
29
+ end
30
+ rescue => e
31
+ MIME::Types.logger.warn <<-WARNING.chomp
32
+ Could not load MIME::Types cache: #{e}
33
+ WARNING
34
+ nil
35
+ end
36
+
37
+ # Attempts to save the types provided to the cache file provided.
38
+ #
39
+ # If +types+ is not provided or is +nil+, the cache will contain the
40
+ # current MIME::Types default registry.
41
+ #
42
+ # If +cache_file+ is not provided or is +nil+, the cache will be written
43
+ # to the file specified in the environment variable
44
+ # +RUBY_MIME_TYPES_CACHE+. If there is no cache file specified either
45
+ # directly or through the environment, this method will return +nil+
46
+ def save(types = nil, cache_file = nil)
47
+ cache_file ||= ENV['RUBY_MIME_TYPES_CACHE']
48
+ return nil unless cache_file
49
+
50
+ types ||= MIME::Types.send(:__types__)
51
+
52
+ File.open(cache_file, 'wb') do |f|
53
+ f.write(
54
+ Marshal.dump(new(MIME::Types::Data::VERSION, Marshal.dump(types)))
55
+ )
56
+ end
57
+ end
58
+ end