mime-types 1.25.1 → 2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +8 -8
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.autotest +5 -0
  5. data/.minitest.rb +2 -0
  6. data/.travis.yml +0 -4
  7. data/Contributing.rdoc +13 -14
  8. data/Gemfile +1 -0
  9. data/History.rdoc +100 -7
  10. data/Licence.rdoc +1 -1
  11. data/Manifest.txt +17 -24
  12. data/README.rdoc +26 -47
  13. data/Rakefile +42 -185
  14. data/data/mime-types.json +1 -0
  15. data/docs/COPYING.txt +339 -339
  16. data/docs/artistic.txt +127 -127
  17. data/lib/mime.rb +50 -0
  18. data/lib/mime/type.rb +634 -0
  19. data/lib/mime/types.rb +254 -912
  20. data/lib/mime/types/cache.rb +73 -0
  21. data/lib/mime/types/loader.rb +248 -0
  22. data/lib/mime/types/loader_path.rb +16 -0
  23. data/support/benchmarker.rb +55 -0
  24. data/support/convert.rb +130 -0
  25. data/support/iana_downloader.rb +201 -0
  26. data/test/fixture/json.json +1 -0
  27. data/test/fixture/old-data +9 -0
  28. data/test/fixture/yaml.yaml +75 -0
  29. data/test/minitest_helper.rb +22 -0
  30. data/test/test_mime_type.rb +337 -143
  31. data/test/test_mime_types.rb +75 -84
  32. data/test/test_mime_types_cache.rb +30 -29
  33. data/test/test_mime_types_class.rb +135 -0
  34. data/test/test_mime_types_lazy.rb +3 -2
  35. data/test/test_mime_types_loader.rb +42 -0
  36. metadata +61 -90
  37. metadata.gz.sig +0 -0
  38. data/lib/mime/types/application +0 -1010
  39. data/lib/mime/types/application.mac +0 -3
  40. data/lib/mime/types/application.nonstandard +0 -132
  41. data/lib/mime/types/application.obsolete +0 -41
  42. data/lib/mime/types/audio +0 -138
  43. data/lib/mime/types/audio.nonstandard +0 -11
  44. data/lib/mime/types/audio.obsolete +0 -1
  45. data/lib/mime/types/image +0 -46
  46. data/lib/mime/types/image.nonstandard +0 -20
  47. data/lib/mime/types/image.obsolete +0 -5
  48. data/lib/mime/types/message +0 -18
  49. data/lib/mime/types/message.obsolete +0 -2
  50. data/lib/mime/types/model +0 -15
  51. data/lib/mime/types/multipart +0 -14
  52. data/lib/mime/types/multipart.nonstandard +0 -1
  53. data/lib/mime/types/multipart.obsolete +0 -7
  54. data/lib/mime/types/other.nonstandard +0 -8
  55. data/lib/mime/types/text +0 -61
  56. data/lib/mime/types/text.nonstandard +0 -7
  57. data/lib/mime/types/text.obsolete +0 -8
  58. data/lib/mime/types/text.vms +0 -1
  59. data/lib/mime/types/video +0 -75
  60. data/lib/mime/types/video.nonstandard +0 -16
  61. data/lib/mime/types/video.obsolete +0 -3
@@ -0,0 +1,73 @@
1
+ # -*- ruby encoding: utf-8 -*-
2
+
3
+ class MIME::Types
4
+ # Caching of MIME::Types registries is advisable if you will be loading
5
+ # the default registry relatively frequently. With the class methods on
6
+ # MIME::Types::Cache, any MIME::Types registry can be marshaled quickly
7
+ # and easily.
8
+ #
9
+ # The cache is invalidated on a per-version basis; a cache file for
10
+ # version 2.0 will not be reused with version 2.0.1.
11
+ Cache = Struct.new(:version, :data)
12
+
13
+ class << Cache
14
+ # Attempts to load the cache from the file provided as a parameter or in
15
+ # the environment variable +RUBY_MIME_TYPES_CACHE+. Returns +nil+ if the
16
+ # file does not exist, if the file cannot be loaded, or if the data in
17
+ # the cache version is different than this version.
18
+ def load(cache_file = nil)
19
+ cache_file = cache_file || ENV['RUBY_MIME_TYPES_CACHE']
20
+ return nil unless cache_file and File.exists?(cache_file)
21
+
22
+ cache = Marshal.load(File.binread(cache_file))
23
+ if cache.version == MIME::Types::VERSION
24
+ Marshal.load(cache.data)
25
+ else
26
+ warn "Could not load MIME::Types cache: invalid version"
27
+ nil
28
+ end
29
+ rescue => e
30
+ warn "Could not load MIME::Types cache: #{e}"
31
+ return nil
32
+ end
33
+
34
+ # Attempts to save the types provided to the cache file provided.
35
+ #
36
+ # If +types+ is not provided or is +nil+, the cache will contain the
37
+ # current MIME::Types default registry.
38
+ #
39
+ # If +cache_file+ is not provided or is +nil+, the cache will be written
40
+ # to the file specified in the environment variable
41
+ # +RUBY_MIME_TYPES_CACHE+. If there is no cache file specified either
42
+ # directly or through the environment, this method will return +nil+
43
+ def save(types = nil, cache_file = nil)
44
+ cache_file = cache_file || ENV['RUBY_MIME_TYPES_CACHE']
45
+ return nil unless cache_file
46
+
47
+ types = types || MIME::Types.send(:__types__)
48
+
49
+ File.open(cache_file, 'wb') do |f|
50
+ f.write(Marshal.dump(new(types.data_version, Marshal.dump(types))))
51
+ end
52
+ end
53
+ end
54
+
55
+ # MIME::Types requires a container Hash with a default values for keys
56
+ # resulting in an empty array (<tt>[]</tt>), but this cannot be dumped
57
+ # through Marshal because of the presence of that default Proc. This class
58
+ # exists solely to satisfy that need.
59
+ class Container < Hash # :nodoc:
60
+ def initialize
61
+ super
62
+ self.default_proc = ->(h, k) { h[k] = [] }
63
+ end
64
+
65
+ def marshal_dump
66
+ {}.merge(self)
67
+ end
68
+
69
+ def marshal_load(hash)
70
+ self.merge!(hash)
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,248 @@
1
+ # -*- ruby encoding: utf-8 -*-
2
+
3
+ require 'mime/types/loader_path'
4
+
5
+ # This class is responsible for initializing the MIME::Types registry from
6
+ # the data files supplied with the mime-types library.
7
+ #
8
+ # The Loader will use one of the following paths:
9
+ # 1. The +path+ provided in its constructor argument;
10
+ # 2. The value of ENV['RUBY_MIME_TYPES_DATA']; or
11
+ # 3. The value of MIME::Types::Loader::PATH.
12
+ #
13
+ # When #load is called, the +path+ will be searched recursively for all YAML
14
+ # (.yml or .yaml) files. By convention, there is one file for each media type
15
+ # (application.yml, audio.yml, etc.), but this is not required.
16
+ #
17
+ #
18
+ class MIME::Types::Loader
19
+ # The path that will be read for the MIME::Types files.
20
+ attr_reader :path
21
+ # The MIME::Types container instance that will be loaded. If not provided
22
+ # at initialization, a new MIME::Types instance will be constructed.
23
+ attr_reader :container
24
+
25
+ # Creates a Loader object that can be used to load MIME::Types registries
26
+ # into memory, using YAML, JSON, or v1 registry format loaders.
27
+ def initialize(path = nil, container = nil)
28
+ path = path || ENV['RUBY_MIME_TYPES_DATA'] ||
29
+ MIME::Types::Loader::PATH
30
+ @path = File.expand_path(File.join(path, '**'))
31
+ @container = container || MIME::Types.new
32
+ end
33
+
34
+ # Loads a MIME::Types registry from YAML files (<tt>*.yml</tt> or
35
+ # <tt>*.yaml</tt>) recursively found in +path+.
36
+ #
37
+ # It is expected that the YAML objects contained within the registry array
38
+ # will be tagged as <tt>!ruby/object:MIME::Type</tt>.
39
+ #
40
+ # Note that the YAML format is about 2½ times *slower* than either the v1
41
+ # format or the JSON format.
42
+ #
43
+ # NOTE: The purpose of this format is purely for maintenance reasons.
44
+ def load_yaml
45
+ Dir[yaml_path].sort.each do |f|
46
+ container.add(*self.class.load_from_yaml(f), :silent)
47
+ end
48
+ container
49
+ end
50
+
51
+ # Loads a MIME::Types registry from JSON files (<tt>*.json</tt>)
52
+ # recursively found in +path+.
53
+ #
54
+ # It is expected that the JSON objects will be an array of hash objects.
55
+ # The JSON format is the registry format for the MIME types registry
56
+ # shipped with the mime-types library.
57
+ #
58
+ # This method is aliased to #load.
59
+ def load_json
60
+ Dir[json_path].sort.each do |f|
61
+ types = self.class.load_from_json(f).map { |type|
62
+ MIME::Type.new(type)
63
+ }
64
+ container.add(*types, :silent)
65
+ end
66
+ container
67
+ end
68
+ alias_method :load, :load_json
69
+
70
+ # Loads a MIME::Types registry from files found in +path+ that are in the
71
+ # v1 data format. The file search for this will exclude both JSON
72
+ # (<tt>*.json</tt>) and YAML (<tt>*.yml</tt> or <tt>*.yaml</tt>) files.
73
+ #
74
+ # This method has been deprecated.
75
+ def load_v1
76
+ MIME.deprecated(self.class, __method__)
77
+ Dir[v1_path].sort.each do |f|
78
+ next if f =~ /\.ya?ml$|\.json$/
79
+ container.add(self.class.load_from_v1(f), true)
80
+ end
81
+ container
82
+ end
83
+
84
+ class << self
85
+ # Loads the default MIME::Type registry.
86
+ def load
87
+ new.load
88
+ end
89
+
90
+ # Build the type list from a file in the format:
91
+ #
92
+ # [*][!][os:]mt/st[<ws>@ext][<ws>:enc][<ws>'url-list][<ws>=docs]
93
+ #
94
+ # == *
95
+ # An unofficial MIME type. This should be used if and only if the MIME type
96
+ # is not properly specified (that is, not under either x-type or
97
+ # vnd.name.type).
98
+ #
99
+ # == !
100
+ # An obsolete MIME type. May be used with an unofficial MIME type.
101
+ #
102
+ # == os:
103
+ # Platform-specific MIME type definition.
104
+ #
105
+ # == mt
106
+ # The media type.
107
+ #
108
+ # == st
109
+ # The media subtype.
110
+ #
111
+ # == <ws>@ext
112
+ # The list of comma-separated extensions.
113
+ #
114
+ # == <ws>:enc
115
+ # The encoding.
116
+ #
117
+ # == <ws>'url-list
118
+ # The list of comma-separated URLs.
119
+ #
120
+ # == <ws>=docs
121
+ # The documentation string.
122
+ #
123
+ # That is, everything except the media type and the subtype is optional. The
124
+ # more information that's available, though, the richer the values that can
125
+ # be provided.
126
+ def load_from_v1(filename)
127
+ data = read_file(filename).split($/)
128
+ mime = MIME::Types.new
129
+ data.each_with_index { |line, index|
130
+ item = line.chomp.strip
131
+ next if item.empty?
132
+
133
+ begin
134
+ m = MIME::Types::Loader::V1_FORMAT.match(item).captures
135
+ rescue Exception
136
+ warn <<-EOS
137
+ #{filename}:#{index}: Parsing error in v1 MIME type definition.
138
+ => #{line}
139
+ EOS
140
+ raise
141
+ end
142
+
143
+ unregistered, obsolete, platform, mediatype, subtype, extensions,
144
+ encoding, urls, docs, comment = *m
145
+
146
+ if mediatype.nil?
147
+ if comment.nil?
148
+ warn <<-EOS
149
+ #{filename}:#{index}: Parsing error in v1 MIME type definition (no media type).
150
+ => #{line}
151
+ EOS
152
+ raise
153
+ end
154
+
155
+ next
156
+ end
157
+
158
+ extensions &&= extensions.split(/,/)
159
+ urls &&= urls.split(/,/)
160
+
161
+ if docs.nil?
162
+ use_instead = nil
163
+ else
164
+ use_instead = docs.scan(%r{use-instead:(\S+)}).flatten
165
+ docs = docs.gsub(%r{use-instead:\S+}, "").squeeze(" \t")
166
+ end
167
+
168
+ mime_type = MIME::Type.new("#{mediatype}/#{subtype}") do |t|
169
+ t.extensions = extensions
170
+ t.encoding = encoding
171
+ t.system = platform
172
+ t.obsolete = obsolete
173
+ t.registered = false if unregistered
174
+ t.use_instead = use_instead
175
+ t.docs = docs
176
+ t.references = urls
177
+ end
178
+
179
+ mime.add_type(mime_type, true)
180
+ }
181
+ mime
182
+ end
183
+
184
+ # Loads MIME::Types from a single YAML file.
185
+ #
186
+ # It is expected that the YAML objects contained within the registry
187
+ # array will be tagged as <tt>!ruby/object:MIME::Type</tt>.
188
+ #
189
+ # Note that the YAML format is about 2½ times *slower* than either the v1
190
+ # format or the JSON format.
191
+ #
192
+ # NOTE: The purpose of this format is purely for maintenance reasons.
193
+ def load_from_yaml(filename)
194
+ begin
195
+ require 'psych'
196
+ rescue LoadError
197
+ nil
198
+ end
199
+ require 'yaml'
200
+ YAML.load(read_file(filename))
201
+ end
202
+
203
+ # Loads MIME::Types from a single JSON file.
204
+ #
205
+ # It is expected that the JSON objects will be an array of hash objects.
206
+ # The JSON format is the registry format for the MIME types registry
207
+ # shipped with the mime-types library.
208
+ def load_from_json(filename)
209
+ require 'json'
210
+ JSON.load(read_file(filename)).map { |type| MIME::Type.new(type) }
211
+ end
212
+
213
+ private
214
+ def read_file(filename)
215
+ File.open(filename, 'r:UTF-8:-') { |f| f.read }
216
+ end
217
+ end
218
+
219
+ private
220
+ def yaml_path
221
+ File.join(path, '*.y{,a}ml')
222
+ end
223
+
224
+ def json_path
225
+ File.join(path, '*.json')
226
+ end
227
+
228
+ def v1_path
229
+ File.join(path, '*')
230
+ end
231
+
232
+ # The regular expression used to match a v1-format file-based MIME type
233
+ # definition.
234
+ MIME::Types::Loader::V1_FORMAT = # :nodoc:
235
+ %r{\A\s*
236
+ ([*])? # 0: Unregistered?
237
+ (!)? # 1: Obsolete?
238
+ (?:(\w+):)? # 2: Platform marker
239
+ #{MIME::Type::MEDIA_TYPE_RE}? # 3,4: Media type
240
+ (?:\s+@(\S+))? # 5: Extensions
241
+ (?:\s+:(base64|7bit|8bit|quoted\-printable))? # 6: Encoding
242
+ (?:\s+'(\S+))? # 7: URL list
243
+ (?:\s+=(.+))? # 8: Documentation
244
+ (?:\s*([#].*)?)?
245
+ \s*
246
+ \z
247
+ }x
248
+ end
@@ -0,0 +1,16 @@
1
+ # -*- ruby encoding: utf-8 -*-
2
+
3
+ class MIME::Types::Loader
4
+ # The path that will be used for loading the MIME::Types data. The default
5
+ # location is __FILE__/../../../../data, which is where the data lives
6
+ # in the gem installation of the mime-types library.
7
+ #
8
+ # The MIME::Types::Loader will load all YAML files contained in this path.
9
+ # By convention, there is one file for each media type (e.g.,
10
+ # application.yml, audio.yml, etc.).
11
+ #
12
+ # System repackagers note: this is the constant that you would change if
13
+ # you repackage mime-types for your system. It is recommended that the
14
+ # path be something like /usr/share/ruby/mime-types/.
15
+ PATH = File.expand_path('../../../../data', __FILE__)
16
+ end
@@ -0,0 +1,55 @@
1
+ # -*- ruby encoding: utf-8 -*-
2
+
3
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
4
+ require 'benchmark'
5
+
6
+ class Benchmarker
7
+ def self.benchmark(repeats)
8
+ new(repeats.to_i).benchmark
9
+ end
10
+
11
+ def initialize(repeats = nil)
12
+ @cache_file = File.expand_path('../cache.mtc', __FILE__)
13
+ @repeats = repeats.to_i
14
+ @repeats = 50 if repeats.zero?
15
+ end
16
+
17
+ def reload_mime_types(repeats = 1, force_load = false)
18
+ path = File.expand_path('../../lib', __FILE__)
19
+
20
+ repeats.times {
21
+ Object.send(:remove_const, :MIME) if defined? MIME
22
+ $LOADED_FEATURES.delete_if { |n| n =~ /#{path}/ }
23
+ require 'mime/types'
24
+ MIME::Types.send(:__types__) if force_load
25
+ }
26
+ end
27
+
28
+ def benchmark
29
+ remove_cache
30
+
31
+ Benchmark.bm(17) do |mark|
32
+ mark.report("Normal:") { reload_mime_types(@repeats) }
33
+
34
+ ENV['RUBY_MIME_TYPES_LAZY_LOAD'] = 'yes'
35
+ mark.report("Lazy:") { reload_mime_types(@repeats) }
36
+ mark.report("Lazy+Load:") { reload_mime_types(@repeats, true) }
37
+
38
+ ENV.delete('RUBY_MIME_TYPES_LAZY_LOAD')
39
+
40
+ ENV['RUBY_MIME_TYPES_CACHE'] = @cache_file
41
+ reload_mime_types
42
+
43
+ mark.report("Cached:") { reload_mime_types(@repeats) }
44
+ ENV['RUBY_MIME_TYPES_LAZY_LOAD'] = 'yes'
45
+ mark.report("Lazy Cached:") { reload_mime_types(@repeats) }
46
+ mark.report("Lazy Cached Load:") { reload_mime_types(@repeats, true) }
47
+ end
48
+ ensure
49
+ remove_cache
50
+ end
51
+
52
+ def remove_cache
53
+ File.unlink(@cache_file) if File.exist?(@cache_file)
54
+ end
55
+ end
@@ -0,0 +1,130 @@
1
+ # -*- ruby encoding: utf-8 -*-
2
+
3
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
4
+ require 'mime/types'
5
+ require 'fileutils'
6
+
7
+ class Convert
8
+ class << self
9
+ def from_yaml(path = nil)
10
+ new(path: path, from: :yaml)
11
+ end
12
+
13
+ def from_json(path = nil)
14
+ new(path: path, from: :json)
15
+ end
16
+
17
+ def from_v1(path = nil)
18
+ new(path: path, from: :v1)
19
+ end
20
+
21
+ def from_yaml_to_json(args)
22
+ from_yaml(yaml_path(args.source)).
23
+ to_json(destination: json_path(args.destination),
24
+ multiple_files: multiple_files(args.multiple_files))
25
+ end
26
+
27
+ def from_json_to_yaml(args)
28
+ from_json(json_path(args.source)).
29
+ to_yaml(destination: yaml_path(args.destination),
30
+ multiple_files: multiple_files(args.multiple_files))
31
+ end
32
+
33
+ private :new
34
+
35
+ private
36
+ def yaml_path(path)
37
+ if path.nil? or path.empty?
38
+ 'type-lists'
39
+ else
40
+ path
41
+ end
42
+ end
43
+
44
+ def json_path(path)
45
+ if path.nil? or path.empty?
46
+ 'data'
47
+ else
48
+ path
49
+ end
50
+ end
51
+
52
+ def multiple_files(flag)
53
+ case flag
54
+ when "true", "yes"
55
+ true
56
+ else
57
+ false
58
+ end
59
+ end
60
+ end
61
+
62
+ def initialize(options = {})
63
+ if options[:path].nil? or options[:path].empty?
64
+ raise ArgumentError, ':path is required'
65
+ end
66
+ if options[:from].nil? or options[:from].empty?
67
+ raise ArgumentError, ':from is required'
68
+ end
69
+
70
+ path = options[:path]
71
+
72
+ @loader = MIME::Types::Loader.new(options[:path])
73
+ load_from(options[:from])
74
+ end
75
+
76
+ def to_json(options = {})
77
+ raise ArgumentError, 'destination is required' unless options[:destination]
78
+ write_types(options.merge(format: :json))
79
+ end
80
+
81
+ def to_yaml(options = {})
82
+ raise ArgumentError, 'destination is required' unless options[:destination]
83
+ write_types(options.merge(format: :yaml))
84
+ end
85
+
86
+ private
87
+ def load_from(source_type)
88
+ method = :"load_#{source_type}"
89
+ @loader.send(method)
90
+ end
91
+
92
+ def write_types(options)
93
+ if options[:multiple_files]
94
+ write_multiple_files(options)
95
+ else
96
+ write_one_file(options)
97
+ end
98
+ end
99
+
100
+ def write_one_file(options)
101
+ d = options[:destination]
102
+ d = File.join(d, "mime-types.#{options[:format]}") if File.directory?(d)
103
+
104
+ File.open(d, 'wb') { |f|
105
+ f.puts convert(@loader.container.map.sort, options[:format])
106
+ }
107
+ end
108
+
109
+ def write_multiple_files(options)
110
+ d = options[:destination]
111
+ if File.exist?(d) and not File.directory?(d)
112
+ raise ArgumentError, 'Cannot write multiple files to a file.'
113
+ end
114
+
115
+ FileUtils.mkdir_p d unless File.exist?(d)
116
+
117
+ media_types = MIME::Types.map(&:media_type).uniq
118
+ media_types.each { |media_type|
119
+ n = File.join(d, "#{media_type}.#{options[:format]}")
120
+ t = @loader.container.select { |e| e.media_type == media_type }
121
+ File.open(n, 'wb') { |f|
122
+ f.puts convert(t.sort, options[:format])
123
+ }
124
+ }
125
+ end
126
+
127
+ def convert(data, format)
128
+ data.send(:"to_#{format}")
129
+ end
130
+ end