mime-types 2.5 → 2.6

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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Contributing.rdoc +72 -20
  4. data/History-Types.rdoc +11 -0
  5. data/History.rdoc +31 -1
  6. data/Manifest.txt +18 -1
  7. data/README.rdoc +142 -61
  8. data/Rakefile +59 -91
  9. data/data/mime-types.json +1 -1
  10. data/data/mime.content_type.column +1907 -0
  11. data/data/mime.docs.column +1907 -0
  12. data/data/mime.encoding.column +1907 -0
  13. data/data/mime.friendly.column +1907 -0
  14. data/data/mime.obsolete.column +1907 -0
  15. data/data/mime.references.column +1907 -0
  16. data/data/mime.registered.column +1907 -0
  17. data/data/mime.signature.column +1907 -0
  18. data/data/mime.system.column +1907 -0
  19. data/data/mime.use_instead.column +1907 -0
  20. data/data/mime.xrefs.column +1907 -0
  21. data/lib/mime/type.rb +192 -119
  22. data/lib/mime/type/columnar.rb +112 -0
  23. data/lib/mime/types.rb +39 -25
  24. data/lib/mime/types/cache.rb +41 -35
  25. data/lib/mime/types/columnar.rb +160 -0
  26. data/lib/mime/types/deprecations.rb +53 -0
  27. data/lib/mime/types/loader.rb +60 -20
  28. data/lib/mime/types/loader_path.rb +2 -3
  29. data/lib/mime/types/logger.rb +35 -0
  30. data/support/apache_mime_types.rb +8 -1
  31. data/support/benchmarks/load.rb +17 -8
  32. data/support/benchmarks/load_allocations.rb +83 -0
  33. data/support/benchmarks/object_counts.rb +41 -0
  34. data/support/convert.rb +44 -24
  35. data/support/convert/columnar.rb +90 -0
  36. data/support/iana_registry.rb +18 -8
  37. data/test/fixture/json.json +1 -1
  38. data/test/fixture/yaml.yaml +3 -6
  39. data/test/minitest_helper.rb +9 -10
  40. data/test/test_mime_type.rb +126 -62
  41. data/test/test_mime_types.rb +15 -11
  42. data/test/test_mime_types_class.rb +16 -14
  43. data/test/test_mime_types_lazy.rb +7 -1
  44. data/test/test_mime_types_loader.rb +17 -8
  45. metadata +54 -12
  46. data/lib/mime.rb +0 -51
@@ -0,0 +1,53 @@
1
+ # -*- ruby encoding: utf-8 -*-
2
+
3
+ require 'mime/types/logger'
4
+
5
+ # The namespace for MIME applications, tools, and libraries.
6
+ module MIME
7
+ class Types
8
+ # Used to mark a method as deprecated in the mime-types interface.
9
+ def self.deprecated(klass, sym, message = nil, &block) # :nodoc:
10
+ level = case klass
11
+ when Class, Module
12
+ '.'
13
+ else
14
+ klass = klass.class
15
+ '#'
16
+ end
17
+ message = case message
18
+ when :private, :protected
19
+ "and will be #{message}"
20
+ when nil
21
+ 'and will be removed'
22
+ else
23
+ message
24
+ end
25
+ MIME::Types.logger.warn <<-warning.chomp
26
+ #{caller[1]}: #{klass}#{level}#{sym} is deprecated #{message}.
27
+ warning
28
+ block.call if block
29
+ end
30
+ end
31
+
32
+ class << self
33
+ # MIME::InvalidContentType was moved to MIME::Type::InvalidContentType.
34
+ # Provide a single warning about this fact in the interim.
35
+ def const_missing(name) # :nodoc:
36
+ case name.to_s
37
+ when 'InvalidContentType'
38
+ warn_about_moved_constants(name)
39
+ MIME::Type.const_get(name.to_sym)
40
+ else
41
+ super
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def warn_about_moved_constants(name)
48
+ MIME::Types.logger.warn <<-warning.chomp
49
+ #{caller[1]}: MIME::#{name} is deprecated. Use MIME::Type::#{name} instead.
50
+ warning
51
+ end
52
+ end
53
+ end
@@ -1,5 +1,8 @@
1
1
  # -*- ruby encoding: utf-8 -*-
2
2
 
3
+ module MIME; end
4
+ class MIME::Types; end
5
+
3
6
  require 'mime/types/loader_path'
4
7
 
5
8
  # This class is responsible for initializing the MIME::Types registry from
@@ -23,10 +26,13 @@ class MIME::Types::Loader
23
26
  # Creates a Loader object that can be used to load MIME::Types registries
24
27
  # into memory, using YAML, JSON, or v1 registry format loaders.
25
28
  def initialize(path = nil, container = nil)
26
- path = path || ENV['RUBY_MIME_TYPES_DATA'] ||
27
- MIME::Types::Loader::PATH
28
- @path = File.expand_path(File.join(path, '**'))
29
+ path = path || ENV['RUBY_MIME_TYPES_DATA'] || MIME::Types::Loader::PATH
29
30
  @container = container || MIME::Types.new
31
+ @path = File.expand_path(path)
32
+ # begin
33
+ # require 'mime/lazy_types'
34
+ # @container.extend(MIME::LazyTypes)
35
+ # end
30
36
  end
31
37
 
32
38
  # Loads a MIME::Types registry from YAML files (<tt>*.yml</tt> or
@@ -52,8 +58,6 @@ class MIME::Types::Loader
52
58
  # It is expected that the JSON objects will be an array of hash objects.
53
59
  # The JSON format is the registry format for the MIME types registry
54
60
  # shipped with the mime-types library.
55
- #
56
- # This method is aliased to #load.
57
61
  def load_json
58
62
  Dir[json_path].sort.each do |f|
59
63
  types = self.class.load_from_json(f)
@@ -61,29 +65,52 @@ class MIME::Types::Loader
61
65
  end
62
66
  container
63
67
  end
64
- alias_method :load, :load_json
68
+
69
+ # Loads a MIME::Types registry from columnar files recursively found in
70
+ # +path+.
71
+ def load_columnar
72
+ require 'mime/types/columnar'
73
+ container.extend(MIME::Types::Columnar)
74
+ container.load_base_data(path)
75
+
76
+ container
77
+ end
78
+
79
+ # Loads a MIME::Types registry. Loads from JSON files by default
80
+ # (#load_json).
81
+ #
82
+ # This will load from columnar files (#load_columnar) if <tt>columnar:
83
+ # true</tt> is provided in +options+ and there are columnar files in +path+.
84
+ def load(options = { columnar: false })
85
+ if options[:columnar] && !Dir[columnar_path].empty?
86
+ load_columnar
87
+ else
88
+ load_json
89
+ end
90
+ end
65
91
 
66
92
  # Loads a MIME::Types registry from files found in +path+ that are in the
67
93
  # v1 data format. The file search for this will exclude both JSON
68
94
  # (<tt>*.json</tt>) and YAML (<tt>*.yml</tt> or <tt>*.yaml</tt>) files.
69
95
  #
70
- # This method has been deprecated.
96
+ # This method has been deprecated and will be removed from mime-types 3.0.
71
97
  def load_v1
72
- MIME.deprecated(self.class, __method__)
98
+ MIME::Types.deprecated(self.class, __method__)
73
99
  Dir[v1_path].sort.each do |f|
74
- next if f =~ /\.ya?ml$|\.json$/
75
- container.add(self.class.load_from_v1(f), true)
100
+ next if f =~ /\.(?:ya?ml|json|column)$/
101
+ container.add(self.class.load_from_v1(f, true), true)
76
102
  end
77
103
  container
78
104
  end
79
105
 
80
- # Raised when a V1 format file is discovered.
106
+ # Raised when a V1 format file is discovered. This exception will be removed
107
+ # for mime-types 3.0.
81
108
  BadV1Format = Class.new(Exception)
82
109
 
83
110
  class << self
84
111
  # Loads the default MIME::Type registry.
85
- def load
86
- new.load
112
+ def load(options = { columnar: false })
113
+ new.load(options)
87
114
  end
88
115
 
89
116
  # Build the type list from a file in the format:
@@ -122,8 +149,10 @@ class MIME::Types::Loader
122
149
  # That is, everything except the media type and the subtype is optional. The
123
150
  # more information that's available, though, the richer the values that can
124
151
  # be provided.
125
- def load_from_v1(filename)
126
- MIME.deprecated(self.class, __method__)
152
+ #
153
+ # This method has been deprecated and will be removed in mime-types 3.0.
154
+ def load_from_v1(filename, __internal__ = false)
155
+ MIME::Types.deprecated(self.class, __method__) unless __internal__
127
156
  data = read_file(filename).split($/)
128
157
  mime = MIME::Types.new
129
158
  data.each_with_index { |line, index|
@@ -133,11 +162,11 @@ class MIME::Types::Loader
133
162
  m = V1_FORMAT.match(item)
134
163
 
135
164
  unless m
136
- warn <<-EOS
165
+ MIME::Types.logger.warn <<-EOS
137
166
  #{filename}:#{index + 1}: Parsing error in v1 MIME type definition.
138
167
  => #{line}
139
168
  EOS
140
- raise BadV1Format, line
169
+ fail BadV1Format, line
141
170
  end
142
171
 
143
172
  unregistered, obsolete, platform, mediatype, subtype, extensions,
@@ -151,8 +180,8 @@ class MIME::Types::Loader
151
180
  if docs.nil?
152
181
  use_instead = nil
153
182
  else
154
- use_instead = docs.scan(%r{use-instead:(\S+)}).flatten
155
- docs = docs.gsub(%r{use-instead:\S+}, "").squeeze(" \t")
183
+ use_instead = docs.scan(%r{use-instead:(\S+)}).flatten.first
184
+ docs = docs.gsub(%r{use-instead:\S+}, '').squeeze(' \t')
156
185
  end
157
186
 
158
187
  mime_type = MIME::Type.new("#{mediatype}/#{subtype}") do |t|
@@ -163,7 +192,10 @@ class MIME::Types::Loader
163
192
  t.registered = false if unregistered
164
193
  t.use_instead = use_instead
165
194
  t.docs = docs
166
- t.references = urls
195
+
196
+ # This is being removed. Cheat to silence it for now.
197
+ t.instance_variable_set :@references,
198
+ Array(urls).flatten.compact.uniq
167
199
  end
168
200
 
169
201
  mime.add_type(mime_type, true)
@@ -201,12 +233,14 @@ class MIME::Types::Loader
201
233
  end
202
234
 
203
235
  private
236
+
204
237
  def read_file(filename)
205
238
  File.open(filename, 'r:UTF-8:-') { |f| f.read }
206
239
  end
207
240
  end
208
241
 
209
242
  private
243
+
210
244
  def yaml_path
211
245
  File.join(path, '*.y{,a}ml')
212
246
  end
@@ -215,12 +249,18 @@ class MIME::Types::Loader
215
249
  File.join(path, '*.json')
216
250
  end
217
251
 
252
+ def columnar_path
253
+ File.join(path, '*.column')
254
+ end
255
+
218
256
  def v1_path
219
257
  File.join(path, '*')
220
258
  end
221
259
 
222
260
  # The regular expression used to match a v1-format file-based MIME type
223
261
  # definition.
262
+ #
263
+ # This constant has been deprecated and will be removed in mime-types 3.0.
224
264
  V1_FORMAT = # :nodoc:
225
265
  %r{\A\s*
226
266
  ([*])? # 0: Unregistered?
@@ -5,9 +5,8 @@ class MIME::Types::Loader
5
5
  # location is __FILE__/../../../../data, which is where the data lives
6
6
  # in the gem installation of the mime-types library.
7
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.).
8
+ # The MIME::Types::Loader will load all JSON or columnar files contained in
9
+ # this path.
11
10
  #
12
11
  # System repackagers note: this is the constant that you would change if
13
12
  # you repackage mime-types for your system. It is recommended that the
@@ -0,0 +1,35 @@
1
+ # -*- ruby encoding: utf-8 -*-
2
+
3
+ require 'logger'
4
+
5
+ module MIME
6
+ class Types
7
+ class << self
8
+ # Configure the MIME::Types logger. This defaults to an instance of a
9
+ # logger that passes messages (unformatted) through to Kernel#warn.
10
+ attr_accessor :logger
11
+ end
12
+
13
+ class WarnLogger < ::Logger #:nodoc:
14
+ class WarnLogDevice < ::Logger::LogDevice #:nodoc:
15
+ def initialize(*)
16
+ end
17
+
18
+ def write(m)
19
+ Kernel.warn(m)
20
+ end
21
+
22
+ def close
23
+ end
24
+ end
25
+
26
+ def initialize(_1, _2 = nil, _3 = nil)
27
+ super nil
28
+ @logdev = WarnLogDevice.new
29
+ @formatter = ->(_s, _d, _p, m) { m }
30
+ end
31
+ end
32
+
33
+ self.logger = WarnLogger.new(nil)
34
+ end
35
+ end
@@ -15,6 +15,12 @@ class MIME::Type
15
15
  public_constant :UNREGISTERED_RE
16
16
  end
17
17
 
18
+ class MIME::Types
19
+ def self.deprecated(*_args, &_block)
20
+ # We are an internal tool. Silence deprecation warnings.
21
+ end
22
+ end
23
+
18
24
  class ApacheMIMETypes
19
25
  DEFAULTS = {
20
26
  url: %q(http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types),
@@ -25,7 +31,7 @@ class ApacheMIMETypes
25
31
  dest = Pathname(options[:to] || DEFAULTS[:to]).expand_path
26
32
  url = options.fetch(:url, DEFAULTS[:url])
27
33
 
28
- puts "Downloading Apache MIME type list."
34
+ puts 'Downloading Apache MIME type list.'
29
35
  puts "\t#{url}"
30
36
  data = open(url) { |f| f.read }.split($/)
31
37
  data.delete_if { |line| line =~ /\A#/ }
@@ -91,6 +97,7 @@ class ApacheMIMETypes
91
97
  end
92
98
 
93
99
  private
100
+
94
101
  def mime_types_for(file)
95
102
  if file.exist?
96
103
  MIME::Types::Loader.load_from_yaml(file)
@@ -15,11 +15,19 @@ module Benchmarks
15
15
  @load_path = load_path
16
16
  end
17
17
 
18
- def reload_mime_types(repeats = 1, force_load = false)
18
+ def reload_mime_types(repeats = 1, options = {})
19
+ force_load = options.fetch(:force_load, false)
20
+ columnar = options.fetch(:columnar, false)
21
+
19
22
  repeats.times {
20
23
  Object.send(:remove_const, :MIME) if defined? ::MIME
21
24
  $LOADED_FEATURES.delete_if { |n| n =~ /#{@load_path}/ }
22
- require 'mime/types'
25
+
26
+ if columnar
27
+ require 'mime/types/columnar'
28
+ else
29
+ require 'mime/types'
30
+ end
23
31
  ::MIME::Types.send(:__types__) if force_load
24
32
  }
25
33
  end
@@ -28,21 +36,22 @@ module Benchmarks
28
36
  remove_cache
29
37
 
30
38
  Benchmark.bm(17) do |mark|
31
- mark.report("Normal:") { reload_mime_types(@repeats) }
39
+ mark.report('Normal:') { reload_mime_types(@repeats) }
40
+ mark.report('Columnar:') { reload_mime_types(@repeats, columnar: true) }
32
41
 
33
42
  ENV['RUBY_MIME_TYPES_LAZY_LOAD'] = 'yes'
34
- mark.report("Lazy:") { reload_mime_types(@repeats) }
35
- mark.report("Lazy+Load:") { reload_mime_types(@repeats, true) }
43
+ mark.report('Lazy:') { reload_mime_types(@repeats) }
44
+ mark.report('Lazy+Load:') { reload_mime_types(@repeats, force_load: true) }
36
45
 
37
46
  ENV.delete('RUBY_MIME_TYPES_LAZY_LOAD')
38
47
 
39
48
  ENV['RUBY_MIME_TYPES_CACHE'] = @cache_file
40
49
  reload_mime_types
41
50
 
42
- mark.report("Cached:") { reload_mime_types(@repeats) }
51
+ mark.report('Cached:') { reload_mime_types(@repeats) }
43
52
  ENV['RUBY_MIME_TYPES_LAZY_LOAD'] = 'yes'
44
- mark.report("Lazy Cached:") { reload_mime_types(@repeats) }
45
- mark.report("Lazy Cached Load:") { reload_mime_types(@repeats, true) }
53
+ mark.report('Lazy Cached:') { reload_mime_types(@repeats) }
54
+ mark.report('Lazy Cached Load:') { reload_mime_types(@repeats, force_load: true) }
46
55
  end
47
56
  ensure
48
57
  remove_cache
@@ -0,0 +1,83 @@
1
+ # -*- ruby encoding: utf-8 -*-
2
+
3
+ if RUBY_VERSION < '2.1'
4
+ $stderr.puts "Cannot count allocations on #{RUBY_VERSION}."
5
+ exit 1
6
+ end
7
+
8
+ begin
9
+ require 'allocation_tracer'
10
+ rescue LoadError
11
+ $stderr.puts "Allocation tracking requires the gem 'allocation_tracer'."
12
+ exit 1
13
+ end
14
+
15
+ module Benchmarks
16
+ class LoadAllocations
17
+ def self.report(columnar: false, top_x: nil, mime_types_only: false)
18
+ new(columnar: columnar, top_x: top_x, mime_types_only: mime_types_only).
19
+ report
20
+ end
21
+
22
+ def initialize(columnar: false, top_x: nil, mime_types_only: false)
23
+ @columnar = columnar
24
+ @mime_types_only = !!mime_types_only
25
+
26
+ @top_x = top_x
27
+
28
+ return unless @top_x
29
+ @top_x = top_x.to_i
30
+ @top_x = 10 if @top_x <= 0
31
+ end
32
+
33
+ def report
34
+ collect
35
+ report_top_x if @top_x
36
+ puts "TOTAL Allocations: #{@count}"
37
+ end
38
+
39
+ private
40
+
41
+ def report_top_x
42
+ table = @allocations.sort_by { |_, v| v.first }.reverse.first(@top_x)
43
+ table.map! { |(location, allocs)|
44
+ next if @mime_types_only and location.first !~ %r{mime-types/lib}
45
+ [ location.join(':').gsub(%r{^#{Dir.pwd}/}, ''), *allocs ]
46
+ }.compact!
47
+
48
+ head = (ObjectSpace::AllocationTracer.header - [ :line ]).map {|h|
49
+ h.to_s.split(/_/).map(&:capitalize).join(' ')
50
+ }
51
+ table.unshift head
52
+
53
+ max_widths = [].tap do |mw|
54
+ table.map { |row| row.lazy.map(&:to_s).map(&:length).to_a }.tap do |w|
55
+ w.first.each_index do |i|
56
+ mw << w.lazy.map { |r| r[i] }.max
57
+ end
58
+ end
59
+ end
60
+
61
+ pattern = [ '%%-%ds' ]
62
+ pattern << ([ '%% %ds' ] * (max_widths.length - 1))
63
+ pattern = pattern.join("\t") % max_widths
64
+ table.each { |row| puts pattern % row }
65
+ puts
66
+ end
67
+
68
+ def collect
69
+ if @columnar
70
+ @allocations = ObjectSpace::AllocationTracer.trace do
71
+ require 'mime/types/columnar'
72
+ end
73
+ else
74
+ @allocations = ObjectSpace::AllocationTracer.trace do
75
+ require 'mime/types'
76
+ end
77
+ end
78
+
79
+ @count = ObjectSpace::AllocationTracer.allocated_count_table.values.
80
+ inject(:+)
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,41 @@
1
+ # -*- ruby encoding: utf-8 -*-
2
+
3
+ module Benchmarks
4
+ class ObjectCounts
5
+ def self.report(columnar: false)
6
+ new(columnar: columnar).report
7
+ end
8
+
9
+ def initialize(columnar: false)
10
+ @columnar = columnar
11
+ end
12
+
13
+ def report
14
+ collect
15
+ @before.keys.grep(/T_/).map { |key|
16
+ [ key, @after[key] - @before[key] ]
17
+ }.sort_by { |_, delta| -delta }.each { |key, delta|
18
+ puts '%10s +%6d' % [ key, delta ]
19
+ }
20
+ end
21
+
22
+ private
23
+
24
+ def collect
25
+ @before = count_objects
26
+
27
+ if @columnar
28
+ require 'mime/types/columnar'
29
+ else
30
+ require 'mime/types'
31
+ end
32
+
33
+ @after = count_objects
34
+ end
35
+
36
+ def count_objects
37
+ GC.start
38
+ ObjectSpace.count_objects
39
+ end
40
+ end
41
+ end