mime-types 2.99.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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.autotest +35 -0
  3. data/.gemtest +0 -0
  4. data/.gitignore +17 -0
  5. data/.hoerc +20 -0
  6. data/Code-of-Conduct.rdoc +27 -60
  7. data/Contributing.rdoc +0 -1
  8. data/History.rdoc +75 -36
  9. data/Licence.rdoc +2 -16
  10. data/Manifest.txt +10 -18
  11. data/README.rdoc +46 -46
  12. data/Rakefile +112 -58
  13. data/lib/mime-types.rb +0 -2
  14. data/lib/mime/type.rb +183 -415
  15. data/lib/mime/type/columnar.rb +27 -62
  16. data/lib/mime/types.rb +37 -135
  17. data/lib/mime/types/cache.rb +49 -73
  18. data/lib/mime/types/columnar.rb +42 -48
  19. data/lib/mime/types/container.rb +30 -0
  20. data/lib/mime/types/deprecations.rb +1 -22
  21. data/lib/mime/types/full.rb +17 -0
  22. data/lib/mime/types/loader.rb +10 -137
  23. data/lib/mime/types/logger.rb +2 -0
  24. data/lib/mime/types/registry.rb +81 -0
  25. data/support/benchmarks/load.rb +27 -26
  26. data/support/benchmarks/load_allocations.rb +14 -7
  27. data/support/benchmarks/object_counts.rb +6 -4
  28. data/support/profile/columnar.rb +5 -0
  29. data/support/profile/columnar_full.rb +5 -0
  30. data/support/profile/full.rb +5 -0
  31. data/test/minitest_helper.rb +3 -12
  32. data/test/test_mime_type.rb +461 -454
  33. data/test/test_mime_types.rb +126 -86
  34. data/test/test_mime_types_cache.rb +55 -45
  35. data/test/test_mime_types_class.rb +113 -97
  36. data/test/test_mime_types_lazy.rb +19 -23
  37. data/test/test_mime_types_loader.rb +5 -32
  38. metadata +67 -44
  39. data/History-Types.rdoc +0 -454
  40. data/data/mime-types.json +0 -1
  41. data/data/mime.content_type.column +0 -1980
  42. data/data/mime.docs.column +0 -1980
  43. data/data/mime.encoding.column +0 -1980
  44. data/data/mime.friendly.column +0 -1980
  45. data/data/mime.obsolete.column +0 -1980
  46. data/data/mime.registered.column +0 -1980
  47. data/data/mime.signature.column +0 -1980
  48. data/data/mime.use_instead.column +0 -1980
  49. data/data/mime.xrefs.column +0 -1980
  50. data/docs/COPYING.txt +0 -339
  51. data/docs/artistic.txt +0 -127
  52. data/lib/mime/types/loader_path.rb +0 -15
  53. data/support/apache_mime_types.rb +0 -108
  54. data/support/convert.rb +0 -158
  55. data/support/convert/columnar.rb +0 -88
  56. data/support/iana_registry.rb +0 -172
data/Rakefile CHANGED
@@ -1,4 +1,4 @@
1
- # frozen_string_literal: true
1
+ # -*- ruby encoding: utf-8 -*-
2
2
 
3
3
  require 'rubygems'
4
4
  require 'hoe'
@@ -15,13 +15,14 @@ spec = Hoe.spec 'mime-types' do
15
15
  developer('Austin Ziegler', 'halostatue@gmail.com')
16
16
  self.need_tar = true
17
17
 
18
- require_ruby_version '>= 1.9.2'
18
+ require_ruby_version '>= 2.0'
19
19
 
20
20
  self.history_file = 'History.rdoc'
21
21
  self.readme_file = 'README.rdoc'
22
- self.extra_rdoc_files = FileList['*.rdoc'].to_a
23
22
 
24
- self.licenses = ['MIT', 'Artistic-2.0', 'GPL-2.0']
23
+ license 'MIT'
24
+
25
+ extra_deps << ['mime-types-data', '~> 3.2015']
25
26
 
26
27
  extra_dev_deps << ['hoe-doofus', '~> 1.0']
27
28
  extra_dev_deps << ['hoe-gemspec2', '~> 1.1']
@@ -31,28 +32,28 @@ spec = Hoe.spec 'mime-types' do
31
32
  extra_dev_deps << ['minitest', '~> 5.4']
32
33
  extra_dev_deps << ['minitest-autotest', '~> 1.0']
33
34
  extra_dev_deps << ['minitest-focus', '~> 1.0']
35
+ extra_dev_deps << ['minitest-bonus-assertions', '~> 2.0']
34
36
  extra_dev_deps << ['rake', '~> 10.0']
35
37
  extra_dev_deps << ['fivemat', '~> 1.3' ]
36
38
  extra_dev_deps << ['minitest-rg', '~> 5.2']
37
- end
38
39
 
39
- task :support do
40
- %w(lib support).each { |path|
41
- $LOAD_PATH.unshift(File.join(Rake.application.original_dir, path))
42
- }
43
- end
44
-
45
- task 'support:nokogiri' => :support do
46
- begin
47
- gem 'nokogiri'
48
- rescue Gem::LoadError
49
- raise 'Nokogiri is not installed. Please install it.'
40
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.0')
41
+ extra_dev_deps << ['simplecov', '~> 0.7']
42
+ # if ENV['CI'] or ENV['TRAVIS']
43
+ # extra_dev_deps << ['coveralls', '~> 0.8']
44
+ # end
50
45
  end
51
46
  end
52
47
 
53
48
  namespace :benchmark do
49
+ task :support do
50
+ %w(lib support).each { |path|
51
+ $LOAD_PATH.unshift(File.join(Rake.application.original_dir, path))
52
+ }
53
+ end
54
+
54
55
  desc 'Benchmark Load Times'
55
- task :load, [ :repeats ] => :support do |_, args|
56
+ task :load, [ :repeats ] => 'benchmark:support' do |_, args|
56
57
  require 'benchmarks/load'
57
58
  Benchmarks::Load.report(
58
59
  File.join(Rake.application.original_dir, 'lib'),
@@ -61,7 +62,7 @@ namespace :benchmark do
61
62
  end
62
63
 
63
64
  desc 'Allocation counts'
64
- task :allocations, [ :top_x, :mime_types_only ] => :support do |_, args|
65
+ task :allocations, [ :top_x, :mime_types_only ] => 'benchmark:support' do |_, args|
65
66
  require 'benchmarks/load_allocations'
66
67
  Benchmarks::LoadAllocations.report(
67
68
  top_x: args.top_x,
@@ -70,7 +71,7 @@ namespace :benchmark do
70
71
  end
71
72
 
72
73
  desc 'Columnar allocation counts'
73
- task 'allocations:columnar', [ :top_x, :mime_types_only ] => :support do |_, args|
74
+ task 'allocations:columnar', [ :top_x, :mime_types_only ] => 'benchmark:support' do |_, args|
74
75
  require 'benchmarks/load_allocations'
75
76
  Benchmarks::LoadAllocations.report(
76
77
  columnar: true,
@@ -79,30 +80,102 @@ namespace :benchmark do
79
80
  )
80
81
  end
81
82
 
83
+ desc 'Columnar allocation counts (full load)'
84
+ task 'allocations:columnar:full', [ :top_x, :mime_types_only ] => 'benchmark:support' do |_, args|
85
+ require 'benchmarks/load_allocations'
86
+ Benchmarks::LoadAllocations.report(
87
+ columnar: true,
88
+ top_x: args.top_x,
89
+ mime_types_only: args.mime_types_only,
90
+ full: true
91
+ )
92
+ end
93
+
82
94
  desc 'Object counts'
83
- task objects: :support do
95
+ task objects: 'benchmark:support' do
84
96
  require 'benchmarks/object_counts'
85
97
  Benchmarks::ObjectCounts.report
86
98
  end
87
99
 
88
100
  desc 'Columnar object counts'
89
- task 'objects:columnar' => :support do
101
+ task 'objects:columnar' => 'benchmark:support' do
90
102
  require 'benchmarks/object_counts'
91
103
  Benchmarks::ObjectCounts.report(columnar: true)
92
104
  end
105
+
106
+ desc 'Columnar object counts (full load)'
107
+ task 'objects:columnar:full' => 'benchmark:support' do
108
+ require 'benchmarks/object_counts'
109
+ Benchmarks::ObjectCounts.report(columnar: true, full: true)
110
+ end
93
111
  end
94
112
 
95
- namespace :mime do
96
- desc 'Download the current MIME type registrations from IANA.'
97
- task :iana, [ :destination ] => 'support:nokogiri' do |_, args|
98
- require 'iana_registry'
99
- IANARegistry.download(to: args.destination)
113
+ namespace :profile do
114
+ directory 'tmp/profile'
115
+
116
+ CLEAN.add 'tmp'
117
+
118
+ def ruby_prof(script)
119
+ require 'pathname'
120
+ output = Pathname('tmp/profile').join(script)
121
+ output.mkpath
122
+ script = Pathname('support/profile').join("#{script}.rb")
123
+
124
+ args = [
125
+ '-W0',
126
+ '-Ilib',
127
+ '-S', 'ruby-prof',
128
+ '-R', 'mime/types',
129
+ '-s', 'self',
130
+ '-p', 'multi',
131
+ '-f', "#{output}",
132
+ script.to_s
133
+ ]
134
+ ruby args.join(' ')
100
135
  end
101
136
 
102
- desc 'Download the current MIME type configuration from Apache.'
103
- task :apache, [ :destination ] => 'support:nokogiri' do |_, args|
104
- require 'apache_mime_types'
105
- ApacheMIMETypes.download(to: args.destination)
137
+ task full: 'tmp/profile' do
138
+ ruby_prof 'full'
139
+ end
140
+
141
+ task columnar: :support do
142
+ ruby_prof 'columnar'
143
+ end
144
+
145
+ task 'columnar:full' => :support do
146
+ ruby_prof 'columnar_full'
147
+ end
148
+ end
149
+
150
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.0')
151
+ namespace :test do
152
+ # Coveralls needs to be disabled for now because it transitively depends on
153
+ # an earlier version of mime-types.
154
+ # if ENV['CI'] or ENV['TRAVIS']
155
+ # task :coveralls do
156
+ # spec.test_prelude = [
157
+ # 'require "psych"',
158
+ # 'require "simplecov"',
159
+ # 'require "coveralls"',
160
+ # 'SimpleCov.formatter = Coveralls::SimpleCov::Formatter',
161
+ # 'SimpleCov.start("test_frameworks") { command_name "Minitest" }',
162
+ # 'gem "minitest"'
163
+ # ].join('; ')
164
+ # Rake::Task['test'].execute
165
+ # end
166
+
167
+ # Rake::Task['travis'].prerequisites.replace(%w(test:coveralls))
168
+ # end
169
+
170
+ desc 'Run test coverage'
171
+ task :coverage do
172
+ spec.test_prelude = [
173
+ 'require "simplecov"',
174
+ 'SimpleCov.start("test_frameworks") { command_name "Minitest" }',
175
+ 'gem "minitest"'
176
+ ].join('; ')
177
+ Rake::Task['test'].execute
178
+ end
106
179
  end
107
180
  end
108
181
 
@@ -114,7 +187,7 @@ namespace :convert do
114
187
  @doc_converter ||= RDoc::Markup::ToMarkdown.new
115
188
  end
116
189
 
117
- %w(README History History-Types).each do |name|
190
+ FileList['*.rdoc'].each do |name|
118
191
  rdoc = "#{name}.rdoc"
119
192
  mark = "#{name}.md"
120
193
 
@@ -133,35 +206,16 @@ namespace :convert do
133
206
 
134
207
  desc 'Convert documentation from RDoc to Markdown'
135
208
  task docs: 'convert:docs:run'
209
+ end
136
210
 
137
- namespace :yaml do
138
- desc 'Convert from YAML to JSON'
139
- task :json, [ :source, :destination, :multiple_files ] => :support do |_, args|
140
- require 'convert'
141
- Convert.from_yaml_to_json(args)
142
- end
143
-
144
- desc 'Convert from YAML to Columnar'
145
- task :columnar, [ :source, :destination ] => :support do |_, args|
146
- require 'convert/columnar'
147
- Convert::Columnar.from_yaml_to_columnar(args)
148
- end
149
- end
150
-
151
- namespace :json do
152
- desc 'Convert from JSON to YAML'
153
- task :yaml, [ :source, :destination, :multiple_files ] => :support do |_, args|
154
- require 'convert'
155
- Convert.from_json_to_yaml(args)
156
- end
211
+ task :console do
212
+ arguments = %w(pry)
213
+ arguments.push(*spec.spec.require_paths.map { |dir| "-I#{dir}" })
214
+ arguments.push("-r#{spec.spec.name.gsub('-', File::SEPARATOR)}")
215
+ unless system(*arguments)
216
+ error "Command failed: #{show_command}"
217
+ abort
157
218
  end
158
219
  end
159
220
 
160
- desc 'Default conversion from YAML to JSON and Columnar'
161
- task convert: [ 'convert:yaml:json', 'convert:yaml:columnar' ]
162
-
163
- Rake::Task['gem'].prerequisites.unshift('convert')
164
- Rake::Task['gem'].prerequisites.unshift('git:manifest')
165
- Rake::Task['gem'].prerequisites.unshift('gemspec')
166
-
167
221
  # vim: syntax=ruby
@@ -1,3 +1 @@
1
- # -*- ruby encoding: utf-8 -*-
2
-
3
1
  require 'mime/types'
@@ -1,4 +1,6 @@
1
- # -*- ruby encoding: utf-8 -*-
1
+ ##
2
+ module MIME
3
+ end
2
4
 
3
5
  # The definition of one MIME content-type.
4
6
  #
@@ -53,33 +55,21 @@ class MIME::Type
53
55
  end
54
56
 
55
57
  # The released version of the mime-types library.
56
- VERSION = '2.99.3'
58
+ VERSION = '3.0'
57
59
 
58
60
  include Comparable
59
61
 
60
62
  # :stopdoc:
61
- MEDIA_TYPE_RE = %r{([-\w.+]+)/([-\w.+]*)}o
62
- UNREGISTERED_RE = %r{[Xx]-}o
63
- I18N_RE = %r{[^[:alnum:]]}o
64
- PLATFORM_RE = %r{#{RUBY_PLATFORM}}o
65
-
66
- DEFAULT_ENCODINGS = [ nil, :default ]
67
- BINARY_ENCODINGS = %w(base64 8bit)
68
- TEXT_ENCODINGS = %w(7bit quoted-printable)
69
- VALID_ENCODINGS = DEFAULT_ENCODINGS + BINARY_ENCODINGS + TEXT_ENCODINGS
70
-
71
- IANA_URL = 'http://www.iana.org/assignments/media-types/%s/%s'
72
- RFC_URL = 'http://rfc-editor.org/rfc/rfc%s.txt'
73
- DRAFT_URL = 'http://datatracker.ietf.org/public/idindex.cgi?command=id_details&filename=%s' # rubocop:disable Metrics/LineLength
74
- CONTACT_URL = 'http://www.iana.org/assignments/contact-people.htm#%s'
63
+ # TODO verify mime-type character restrictions; I am pretty sure that this is
64
+ # too wide open.
65
+ MEDIA_TYPE_RE = %r{([-\w.+]+)/([-\w.+]*)}
66
+ I18N_RE = %r{[^[:alnum:]]}
67
+ BINARY_ENCODINGS = %w(base64 8bit)
68
+ ASCII_ENCODINGS = %w(7bit quoted-printable)
75
69
  # :startdoc:
76
70
 
77
- if respond_to? :private_constant
78
- private_constant :MEDIA_TYPE_RE, :UNREGISTERED_RE, :I18N_RE, :PLATFORM_RE,
79
- :DEFAULT_ENCODINGS, :BINARY_ENCODINGS, :TEXT_ENCODINGS,
80
- :VALID_ENCODINGS, :IANA_URL, :RFC_URL, :DRAFT_URL,
81
- :CONTACT_URL
82
- end
71
+ private_constant :MEDIA_TYPE_RE, :I18N_RE, :BINARY_ENCODINGS,
72
+ :ASCII_ENCODINGS
83
73
 
84
74
  # Builds a MIME::Type object from the +content_type+, a MIME Content Type
85
75
  # value (e.g., 'text/plain' or 'applicaton/x-eruby'). The constructed object
@@ -88,46 +78,45 @@ class MIME::Type
88
78
  #
89
79
  # * When provided a Hash or a MIME::Type, the MIME::Type will be
90
80
  # constructed with #init_with.
91
- # * When provided an Array, the MIME::Type will be constructed only using
92
- # the first two elements of the array as the content type and
93
- # extensions.
81
+ # * When provided an Array, the MIME::Type will be constructed using
82
+ # the first element as the content type and the remaining flattened
83
+ # elements as extensions.
94
84
  # * Otherwise, the content_type will be used as a string.
95
85
  #
96
86
  # Yields the newly constructed +self+ object.
97
87
  def initialize(content_type) # :yields self:
98
88
  @friendly = {}
99
- self.obsolete = false
100
- self.registered = nil
101
- self.use_instead = nil
102
- self.signature = nil
89
+ @obsolete = @registered = false
90
+ @preferred_extension = @docs = @use_instead = nil
91
+ self.extensions = []
103
92
 
104
93
  case content_type
105
94
  when Hash
106
95
  init_with(content_type)
107
96
  when Array
108
- self.content_type = content_type[0]
109
- self.extensions = content_type[1] || []
97
+ self.content_type = content_type.shift
98
+ self.extensions = content_type.flatten
110
99
  when MIME::Type
111
100
  init_with(content_type.to_h)
112
101
  else
113
102
  self.content_type = content_type
114
103
  end
115
104
 
116
- self.extensions ||= []
117
- self.docs ||= []
118
105
  self.encoding ||= :default
119
106
  self.xrefs ||= {}
120
107
 
121
108
  yield self if block_given?
122
109
  end
123
110
 
124
- # Returns +true+ if the +other+ simplified type matches the current type.
111
+ # Indicates that a MIME type is like another type. This differs from
112
+ # <tt>==</tt> because <tt>x-</tt> prefixes are removed for this comparison.
125
113
  def like?(other)
126
- if other.respond_to?(:simplified)
127
- @simplified == other.simplified
128
- else
129
- @simplified == MIME::Type.simplified(other)
130
- end
114
+ other = if other.respond_to?(:simplified)
115
+ MIME::Type.simplified(other.simplified, remove_x_prefix: true)
116
+ else
117
+ MIME::Type.simplified(other.to_s, remove_x_prefix: true)
118
+ end
119
+ MIME::Type.simplified(simplified, remove_x_prefix: true) == other
131
120
  end
132
121
 
133
122
  # Compares the +other+ MIME::Type against the exact content type or the
@@ -135,10 +124,12 @@ class MIME::Type
135
124
  # something that can be treated as a String with #to_s). In comparisons, this
136
125
  # is done against the lowercase version of the MIME::Type.
137
126
  def <=>(other)
138
- if other.respond_to?(:content_type)
139
- @content_type.downcase <=> other.content_type.downcase
140
- elsif other.respond_to?(:to_s)
141
- @simplified <=> MIME::Type.simplified(other.to_s)
127
+ if other.nil?
128
+ -1
129
+ elsif other.respond_to?(:simplified)
130
+ simplified <=> other.simplified
131
+ else
132
+ simplified <=> MIME::Type.simplified(other.to_s)
142
133
  end
143
134
  end
144
135
 
@@ -204,54 +195,74 @@ class MIME::Type
204
195
  # removed and converted to lowercase.
205
196
  #
206
197
  # text/plain => text/plain
207
- # x-chemical/x-pdb => chemical/pdb
198
+ # x-chemical/x-pdb => x-chemical/x-pdb
208
199
  # audio/QCELP => audio/qcelp
209
200
  attr_reader :simplified
210
201
  # Returns the media type of the simplified MIME::Type.
211
202
  #
212
203
  # text/plain => text
213
- # x-chemical/x-pdb => chemical
204
+ # x-chemical/x-pdb => x-chemical
205
+ # audio/QCELP => audio
214
206
  attr_reader :media_type
215
207
  # Returns the media type of the unmodified MIME::Type.
216
208
  #
217
209
  # text/plain => text
218
210
  # x-chemical/x-pdb => x-chemical
211
+ # audio/QCELP => audio
219
212
  attr_reader :raw_media_type
220
213
  # Returns the sub-type of the simplified MIME::Type.
221
214
  #
222
215
  # text/plain => plain
223
216
  # x-chemical/x-pdb => pdb
217
+ # audio/QCELP => QCELP
224
218
  attr_reader :sub_type
225
219
  # Returns the media type of the unmodified MIME::Type.
226
220
  #
227
221
  # text/plain => plain
228
222
  # x-chemical/x-pdb => x-pdb
223
+ # audio/QCELP => qcelp
229
224
  attr_reader :raw_sub_type
230
225
 
226
+ ##
231
227
  # The list of extensions which are known to be used for this MIME::Type.
232
228
  # Non-array values will be coerced into an array with #to_a. Array values
233
229
  # will be flattened, +nil+ values removed, and made unique.
234
- attr_reader :extensions
235
- def extensions=(ext) # :nodoc:
236
- @extensions = Array(ext).flatten.compact.uniq
237
- # TODO: In mime-types 3.x, we probably want to have a clue about the
238
- # container(s) we belong to so we can trigger reindexing when this is done.
230
+ #
231
+ # :attr_accessor: extensions
232
+ def extensions
233
+ @extensions.to_a
234
+ end
235
+
236
+ ##
237
+ def extensions=(value) # :nodoc:
238
+ @extensions = Set[*Array(value).flatten.compact].freeze
239
+ MIME::Types.send(:reindex_extensions, self)
239
240
  end
240
241
 
241
242
  # Merge the +extensions+ provided into this MIME::Type. The extensions added
242
243
  # will be merged uniquely.
243
244
  def add_extensions(*extensions)
244
- self.extensions = self.extensions + extensions
245
+ self.extensions += extensions
245
246
  end
246
247
 
247
248
  ##
248
- # The preferred extension for this MIME type, if one is set.
249
+ # The preferred extension for this MIME type. If one is not set and there are
250
+ # exceptions defined, the first extension will be used.
251
+ #
252
+ # When setting #preferred_extensions, if #extensions does not contain this
253
+ # extension, this will be added to #xtensions.
249
254
  #
250
- # :attr_reader: preferred_extension
255
+ # :attr_accessor: preferred_extension
251
256
 
252
257
  ##
253
258
  def preferred_extension
254
- extensions.first
259
+ @preferred_extension || extensions.first
260
+ end
261
+
262
+ ##
263
+ def preferred_extension=(value) # :nodoc:
264
+ add_extensions(value) if value
265
+ @preferred_extension = value
255
266
  end
256
267
 
257
268
  ##
@@ -270,60 +281,41 @@ class MIME::Type
270
281
 
271
282
  ##
272
283
  attr_reader :encoding
284
+
285
+ ##
273
286
  def encoding=(enc) # :nodoc:
274
- if DEFAULT_ENCODINGS.include?(enc)
287
+ if enc.nil? or enc == :default
275
288
  @encoding = default_encoding
276
- elsif BINARY_ENCODINGS.include?(enc) or TEXT_ENCODINGS.include?(enc)
289
+ elsif BINARY_ENCODINGS.include?(enc) or ASCII_ENCODINGS.include?(enc)
277
290
  @encoding = enc
278
291
  else
279
292
  fail InvalidEncoding, enc
280
293
  end
281
294
  end
282
295
 
283
- # Returns +nil+ and assignments are ignored. Prior to mime-types 2.99, this
284
- # would return the regular expression for the operating system indicated if
285
- # the MIME::Type is a system-specific MIME::Type,
286
- #
287
- # This information about MIME content types is deprecated and will be removed
288
- # in mime-types 3.
289
- def system
290
- MIME::Types.deprecated(self, __method__)
291
- nil
292
- end
293
-
294
- def system=(_os) # :nodoc:
295
- MIME::Types.deprecated(self, __method__)
296
- end
297
-
298
296
  # Returns the default encoding for the MIME::Type based on the media type.
299
297
  def default_encoding
300
298
  (@media_type == 'text') ? 'quoted-printable' : 'base64'
301
299
  end
302
300
 
303
301
  ##
304
- # Returns the media type or types that should be used instead of this
305
- # media type, if it is obsolete. If there is no replacement media type, or
306
- # it is not obsolete, +nil+ will be returned.
302
+ # Returns the media type or types that should be used instead of this media
303
+ # type, if it is obsolete. If there is no replacement media type, or it is
304
+ # not obsolete, +nil+ will be returned.
307
305
  #
308
306
  # :attr_accessor: use_instead
309
307
 
310
308
  ##
311
309
  def use_instead
312
- return nil unless obsolete?
313
- @use_instead
310
+ obsolete? ? @use_instead : nil
314
311
  end
315
312
 
316
313
  ##
317
314
  attr_writer :use_instead
318
315
 
319
316
  # Returns +true+ if the media type is obsolete.
320
- def obsolete?
321
- !!@obsolete
322
- end
323
-
324
- def obsolete=(v) # :nodoc:
325
- @obsolete = !!v
326
- end
317
+ attr_accessor :obsolete
318
+ alias_method :obsolete?, :obsolete
327
319
 
328
320
  # The documentation for this MIME::Type.
329
321
  attr_accessor :docs
@@ -337,14 +329,15 @@ class MIME::Type
337
329
  @friendly ||= {}
338
330
 
339
331
  case lang
340
- when String
341
- @friendly[lang]
332
+ when String, Symbol
333
+ @friendly[lang.to_s]
342
334
  when Array
343
- @friendly.merge!(Hash[*lang])
335
+ @friendly.update(Hash[*lang])
344
336
  when Hash
345
- @friendly.merge!(lang)
337
+ @friendly.update(lang)
346
338
  else
347
- fail ArgumentError
339
+ fail ArgumentError,
340
+ "Expected a language or translation set, not #{lang.inspect}"
348
341
  end
349
342
  end
350
343
 
@@ -359,50 +352,6 @@ class MIME::Type
359
352
  # # from application/x-msword
360
353
  attr_reader :i18n_key
361
354
 
362
- ##
363
- # Returns an empty array and warns that this method has been deprecated.
364
- # Assignments are ignored. Prior to mime-types 2.99, this was the encoded
365
- # references URL list for this MIME::Type.
366
- #
367
- # This was previously called #url.
368
- #
369
- # #references has been deprecated and both versions (#references and #url)
370
- # will be removed in mime-types 3.
371
- #
372
- # :attr_accessor: references
373
-
374
- ##
375
- def references(*)
376
- MIME::Types.deprecated(self, __method__)
377
- []
378
- end
379
-
380
- ##
381
- def references=(_r) # :nodoc:
382
- MIME::Types.deprecated(self, __method__)
383
- end
384
-
385
- ##
386
- # Returns an empty array and warns that this method has been deprecated.
387
- # Assignments are ignored. Prior to mime-types 2.99, this was the encoded
388
- # references URL list for this MIME::Type. See #urls for more information.
389
- #
390
- # #url has been deprecated and both versions (#references and #url) will be
391
- # removed in mime-types 3.
392
- #
393
- # :attr_accessor: url
394
-
395
- ##
396
- def url
397
- MIME::Types.deprecated(self, __method__)
398
- []
399
- end
400
-
401
- ##
402
- def url=(_r) # :nodoc:
403
- MIME::Types.deprecated(self, __method__)
404
- end
405
-
406
355
  ##
407
356
  # The cross-references list for this MIME::Type.
408
357
  #
@@ -413,133 +362,44 @@ class MIME::Type
413
362
 
414
363
  ##
415
364
  def xrefs=(x) # :nodoc:
416
- @xrefs = MIME::Types::Container.new.merge(x)
417
- @xrefs.each_value(&:sort!)
418
- @xrefs.each_value(&:uniq!)
419
- end
365
+ MIME::Types::Container.new.merge(x).tap do |xr|
366
+ xr.each do |k, v|
367
+ xr[k] = Set[*v] unless v.kind_of? Set
368
+ end
420
369
 
421
- # Returns an empty array. Prior to mime-types 2.99, this returned the decoded
422
- # URL list for this MIME::Type.
423
- #
424
- # The special URL value IANA was translated into:
425
- # http://www.iana.org/assignments/media-types/<mediatype>/<subtype>
426
- #
427
- # The special URL value RFC### was translated into:
428
- # http://www.rfc-editor.org/rfc/rfc###.txt
429
- #
430
- # The special URL value DRAFT:name was translated into:
431
- # https://datatracker.ietf.org/public/idindex.cgi?
432
- # command=id_detail&filename=<name>
433
- #
434
- # The special URL value [token] was translated into:
435
- # http://www.iana.org/assignments/contact-people.htm#<token>
436
- #
437
- # These values were accessible through #urls, which always returns an array.
438
- #
439
- # This method is deprecated and will be removed in mime-types 3.
440
- def urls
441
- MIME::Types.deprecated(self, __method__)
442
- []
370
+ @xrefs = xr
371
+ end
443
372
  end
444
373
 
445
374
  # The decoded cross-reference URL list for this MIME::Type.
446
375
  def xref_urls
447
- xrefs.flat_map { |(type, values)|
448
- case type
449
- when 'rfc'.freeze
450
- values.map { |data| 'http://www.iana.org/go/%s'.freeze % data }
451
- when 'draft'.freeze
452
- values.map { |data|
453
- 'http://www.iana.org/go/%s'.freeze % data.sub(/\ARFC/, 'draft')
454
- }
455
- when 'rfc-errata'.freeze
456
- values.map { |data|
457
- 'http://www.rfc-editor.org/errata_search.php?eid=%s'.freeze % data
458
- }
459
- when 'person'.freeze
460
- values.map { |data|
461
- 'http://www.iana.org/assignments/media-types/media-types.xhtml#%s'.freeze % data # rubocop:disable Metrics/LineLength
462
- }
463
- when 'template'.freeze
464
- values.map { |data|
465
- 'http://www.iana.org/assignments/media-types/%s'.freeze % data
466
- }
467
- else # 'uri', 'text', etc.
468
- values
469
- end
376
+ xrefs.flat_map { |type, values|
377
+ name = :"xref_url_for_#{type.tr('-', '_')}"
378
+ respond_to?(name, true) and xref_map(values, name) or values.to_a
470
379
  }
471
380
  end
472
381
 
473
- ##
474
- # Prior to BCP 178 (RFC 6648), it could be assumed that MIME content types
475
- # that start with <tt>x-</tt> were unregistered MIME. Per this BCP, this
476
- # assumption is no longer being made by default in this library.
477
- #
478
- # There are three possible registration states for a MIME::Type:
479
- # - Explicitly registered, like application/x-www-url-encoded.
480
- # - Explicitly not registered, like image/webp.
481
- # - Unspecified, in which case the media-type and the content-type will be
482
- # scanned to see if they start with <tt>x-</tt>, indicating that they
483
- # are assumed unregistered.
484
- #
485
- # In mime-types 3, only a MIME content type that is explicitly registered
486
- # will be used; there will be assumption that <tt>x-</tt> types are
487
- # unregistered.
488
- def registered?
489
- if @registered.nil?
490
- (@raw_media_type !~ UNREGISTERED_RE) and
491
- (@raw_sub_type !~ UNREGISTERED_RE)
492
- else
493
- !!@registered
494
- end
495
- end
496
-
497
- def registered=(v) # :nodoc:
498
- @registered = v.nil? ? v : !!v
499
- end
382
+ # Indicates whether the MIME type has been registered with IANA.
383
+ attr_accessor :registered
384
+ alias_method :registered?, :registered
500
385
 
501
386
  # MIME types can be specified to be sent across a network in particular
502
387
  # formats. This method returns +true+ when the MIME::Type encoding is set
503
388
  # to <tt>base64</tt>.
504
389
  def binary?
505
- BINARY_ENCODINGS.include?(@encoding)
390
+ BINARY_ENCODINGS.include?(encoding)
506
391
  end
507
392
 
508
393
  # MIME types can be specified to be sent across a network in particular
509
394
  # formats. This method returns +false+ when the MIME::Type encoding is
510
395
  # set to <tt>base64</tt>.
511
396
  def ascii?
512
- !binary?
513
- end
514
-
515
- # Returns +true+ when the simplified MIME::Type is one of the known digital
516
- # signature types.
517
- def signature?
518
- !!@signature
519
- end
520
-
521
- def signature=(v) # :nodoc:
522
- @signature = !!v
397
+ ASCII_ENCODINGS.include?(encoding)
523
398
  end
524
399
 
525
- # Returns +false+. Prior to mime-types 2.99, would return +true+ if the
526
- # MIME::Type is specific to an operating system.
527
- #
528
- # This method is deprecated and will be removed in mime-types 3.
529
- def system?(*)
530
- MIME::Types.deprecated(self, __method__)
531
- false
532
- end
533
-
534
- # Returns +false+. Prior to mime-types 2.99, would return +true+ if the
535
- # MIME::Type is specific to the current operating system as represented by
536
- # RUBY_PLATFORM.
537
- #
538
- # This method is deprecated and will be removed in mime-types 3.
539
- def platform?(*)
540
- MIME::Types.deprecated(self, __method__)
541
- false
542
- end
400
+ # Indicateswhether the MIME type is declared as a signature type.
401
+ attr_accessor :signature
402
+ alias_method :signature?, :signature
543
403
 
544
404
  # Returns +true+ if the MIME::Type specifies an extension list,
545
405
  # indicating that it is a complete MIME::Type.
@@ -560,41 +420,14 @@ class MIME::Type
560
420
  content_type
561
421
  end
562
422
 
563
- # Returns the MIME::Type as an array suitable for use with
564
- # MIME::Type.from_array.
565
- #
566
- # This method is deprecated and will be removed in mime-types 3.
567
- def to_a
568
- MIME::Types.deprecated(self, __method__)
569
- [ @content_type, @extensions, @encoding, nil, obsolete?, @docs, [],
570
- registered? ]
571
- end
572
-
573
- # Returns the MIME::Type as an array suitable for use with
574
- # MIME::Type.from_hash.
575
- #
576
- # This method is deprecated and will be removed in mime-types 3.
577
- def to_hash
578
- MIME::Types.deprecated(self, __method__)
579
- { 'Content-Type' => @content_type,
580
- 'Content-Transfer-Encoding' => @encoding,
581
- 'Extensions' => @extensions,
582
- 'System' => nil,
583
- 'Obsolete' => obsolete?,
584
- 'Docs' => @docs,
585
- 'URL' => [],
586
- 'Registered' => registered?,
587
- }
588
- end
589
-
590
423
  # Converts the MIME::Type to a JSON string.
591
424
  def to_json(*args)
592
425
  require 'json'
593
426
  to_h.to_json(*args)
594
427
  end
595
428
 
596
- # Converts the MIME::Type to a hash suitable for use in JSON. The output
597
- # of this method can also be used to initialize a MIME::Type.
429
+ # Converts the MIME::Type to a hash. The output of this method can also be
430
+ # used to initialize a MIME::Type.
598
431
  def to_h
599
432
  encode_with({})
600
433
  end
@@ -605,18 +438,21 @@ class MIME::Type
605
438
  #
606
439
  # This method should be considered a private implementation detail.
607
440
  def encode_with(coder)
608
- coder['content-type'] = @content_type
609
- coder['docs'] = @docs unless @docs.nil? or @docs.empty?
610
- coder['friendly'] = @friendly unless @friendly.empty?
611
- coder['encoding'] = @encoding
612
- coder['extensions'] = @extensions unless @extensions.empty?
441
+ coder['content-type'] = @content_type
442
+ coder['docs'] = @docs unless @docs.nil? or @docs.empty?
443
+ unless @friendly.nil? or @friendly.empty?
444
+ coder['friendly'] = @friendly
445
+ end
446
+ coder['encoding'] = @encoding
447
+ coder['extensions'] = @extensions.to_a unless @extensions.empty?
448
+ coder['preferred-extension'] = @preferred_extension if @preferred_extension
613
449
  if obsolete?
614
- coder['obsolete'] = obsolete?
615
- coder['use-instead'] = use_instead if use_instead
450
+ coder['obsolete'] = obsolete?
451
+ coder['use-instead'] = use_instead if use_instead
616
452
  end
617
- coder['xrefs'] = xrefs unless xrefs.empty?
618
- coder['registered'] = registered?
619
- coder['signature'] = signature? if signature?
453
+ coder['xrefs'] = xrefs unless xrefs.empty?
454
+ coder['registered'] = registered?
455
+ coder['signature'] = signature? if signature?
620
456
  coder
621
457
  end
622
458
 
@@ -625,161 +461,68 @@ class MIME::Type
625
461
  #
626
462
  # This method should be considered a private implementation detail.
627
463
  def init_with(coder)
628
- self.content_type = coder['content-type']
629
- self.docs = coder['docs'] || []
464
+ self.content_type = coder['content-type']
465
+ self.docs = coder['docs'] || ''
466
+ self.encoding = coder['encoding']
467
+ self.extensions = coder['extensions'] || []
468
+ self.preferred_extension = coder['preferred-extension']
469
+ self.obsolete = coder['obsolete'] || false
470
+ self.registered = coder['registered'] || false
471
+ self.signature = coder['signature']
472
+ self.xrefs = coder['xrefs'] || {}
473
+ self.use_instead = coder['use-instead']
474
+
630
475
  friendly(coder['friendly'] || {})
631
- self.encoding = coder['encoding']
632
- self.extensions = coder['extensions'] || []
633
- self.obsolete = coder['obsolete']
634
- self.registered = coder['registered']
635
- self.signature = coder['signature']
636
- self.xrefs = coder['xrefs'] || {}
637
- self.use_instead = coder['use-instead']
638
476
  end
639
477
 
640
- class << self
641
- # The MIME types main- and sub-label can both start with <tt>x-</tt>,
642
- # which indicates that it is a non-registered name. Of course, after
643
- # registration this flag may disappear, adds to the confusing
644
- # proliferation of MIME types. The simplified +content_type+ string has the
645
- # <tt>x-</tt> removed and is translated to lowercase.
646
- def simplified(content_type)
647
- matchdata = case content_type
648
- when MatchData
649
- content_type
650
- else
651
- MEDIA_TYPE_RE.match(content_type)
652
- end
653
-
654
- return unless matchdata
478
+ def inspect # :nodoc:
479
+ # We are intentionally lying here because MIME::Type::Columnar is an
480
+ # implementation detail.
481
+ "#<MIME::Type: #{self}>"
482
+ end
655
483
 
656
- matchdata.captures.map { |e|
657
- e.downcase!
658
- e.gsub!(UNREGISTERED_RE, ''.freeze)
659
- e
660
- }.join('/'.freeze)
484
+ class << self
485
+ # MIME media types are case-insensitive, but are typically presented in a
486
+ # case-preserving format in the type registry. This method converts
487
+ # +content_type+ to lowercase.
488
+ #
489
+ # In previous versions of mime-types, this would also remove any extension
490
+ # prefix (<tt>x-</tt>). This is no longer default behaviour, but may be
491
+ # provided by providing a truth value to +remove_x_prefix+.
492
+ def simplified(content_type, remove_x_prefix: false)
493
+ simplify_matchdata(match(content_type), remove_x_prefix)
661
494
  end
662
495
 
663
496
  # Converts a provided +content_type+ into a translation key suitable for
664
497
  # use with the I18n library.
665
498
  def i18n_key(content_type)
666
- matchdata = case content_type
667
- when MatchData
668
- content_type
669
- else
670
- MEDIA_TYPE_RE.match(content_type)
671
- end
672
-
673
- return unless matchdata
674
-
675
- matchdata.captures.map { |e|
676
- e.downcase!
677
- e.gsub!(UNREGISTERED_RE, ''.freeze)
499
+ simplify_matchdata(match(content_type), joiner: '.') { |e|
678
500
  e.gsub!(I18N_RE, '-'.freeze)
679
- e
680
- }.join('.'.freeze)
501
+ }
681
502
  end
682
503
 
683
- # Creates a MIME::Type from an +args+ array in the form of:
684
- # [ type-name, [ extensions ], encoding, system ]
685
- #
686
- # +extensions+, and +encoding+ are optional; +system+ is ignored.
687
- #
688
- # MIME::Type.from_array('application/x-ruby', %w(rb), '8bit')
689
- # MIME::Type.from_array([ 'application/x-ruby', [ 'rb' ], '8bit' ])
690
- #
691
- # These are equivalent to:
692
- #
693
- # MIME::Type.new('application/x-ruby') do |t|
694
- # t.extensions = %w(rb)
695
- # t.encoding = '8bit'
696
- # end
697
- #
698
- # It will yield the type (+t+) if a block is given.
699
- #
700
- # This method is deprecated and will be removed in mime-types 3.
701
- def from_array(*args) # :yields t:
702
- MIME::Types.deprecated(self, __method__)
703
-
704
- # Dereferences the array one level, if necessary.
705
- args = args.first if args.first.kind_of? Array
706
-
707
- unless args.size.between?(1, 8)
708
- fail ArgumentError,
709
- 'Array provided must contain between one and eight elements.'
710
- end
711
-
712
- MIME::Type.new(args.shift) do |t|
713
- t.extensions, t.encoding, _system, t.obsolete, t.docs, _references,
714
- t.registered = *args
715
- yield t if block_given?
504
+ # Return a +MatchData+ object of the +content_type+ against pattern of
505
+ # media types.
506
+ def match(content_type)
507
+ case content_type
508
+ when MatchData
509
+ content_type
510
+ else
511
+ MEDIA_TYPE_RE.match(content_type)
716
512
  end
717
513
  end
718
514
 
719
- # Creates a MIME::Type from a +hash+. Keys are case-insensitive, dashes
720
- # may be replaced with underscores, and the internal Symbol of the
721
- # lowercase-underscore version can be used as well. That is,
722
- # Content-Type can be provided as content-type, Content_Type,
723
- # content_type, or :content_type.
724
- #
725
- # Known keys are <tt>Content-Type</tt>,
726
- # <tt>Content-Transfer-Encoding</tt>, <tt>Extensions</tt>, and
727
- # <tt>System</tt>. +System+ is ignored.
728
- #
729
- # MIME::Type.from_hash('Content-Type' => 'text/x-yaml',
730
- # 'Content-Transfer-Encoding' => '8bit',
731
- # 'System' => 'linux',
732
- # 'Extensions' => ['yaml', 'yml'])
733
- #
734
- # This is equivalent to:
735
- #
736
- # MIME::Type.new('text/x-yaml') do |t|
737
- # t.encoding = '8bit'
738
- # t.system = 'linux'
739
- # t.extensions = ['yaml', 'yml']
740
- # end
741
- #
742
- # It will yield the constructed type +t+ if a block has been provided.
743
- #
744
- #
745
- # This method is deprecated and will be removed in mime-types 3.
746
- def from_hash(hash) # :yields t:
747
- MIME::Types.deprecated(self, __method__)
748
- type = {}
749
- hash.each_pair do |k, v|
750
- type[k.to_s.tr('A-Z', 'a-z').gsub(/-/, '_').to_sym] = v
751
- end
515
+ private
752
516
 
753
- MIME::Type.new(type[:content_type]) do |t|
754
- t.extensions = type[:extensions]
755
- t.encoding = type[:content_transfer_encoding]
756
- t.obsolete = type[:obsolete]
757
- t.docs = type[:docs]
758
- t.url = type[:url]
759
- t.registered = type[:registered]
760
-
761
- yield t if block_given?
762
- end
763
- end
517
+ def simplify_matchdata(matchdata, remove_x = false, joiner: '/'.freeze)
518
+ return nil unless matchdata
764
519
 
765
- # Essentially a copy constructor for +mime_type+.
766
- #
767
- # MIME::Type.from_mime_type(plaintext)
768
- #
769
- # is equivalent to:
770
- #
771
- # MIME::Type.new(plaintext.content_type.dup) do |t|
772
- # t.extensions = plaintext.extensions.dup
773
- # t.system = plaintext.system.dup
774
- # t.encoding = plaintext.encoding.dup
775
- # end
776
- #
777
- # It will yield the type (+t+) if a block is given.
778
- #
779
- # This method is deprecated and will be removed in mime-types 3.
780
- def from_mime_type(mime_type) # :yields the new MIME::Type:
781
- MIME::Types.deprecated(self, __method__)
782
- new(mime_type)
520
+ matchdata.captures.map { |e|
521
+ e.downcase!
522
+ e.sub!(%r{^x-}, ''.freeze) if remove_x
523
+ yield e if block_given?
524
+ e
525
+ }.join(joiner)
783
526
  end
784
527
  end
785
528
 
@@ -795,4 +538,29 @@ class MIME::Type
795
538
  @i18n_key = MIME::Type.i18n_key(match)
796
539
  @media_type, @sub_type = MEDIA_TYPE_RE.match(@simplified).captures
797
540
  end
541
+
542
+ def xref_map(values, helper)
543
+ values.map { |value| send(helper, value) }
544
+ end
545
+
546
+ def xref_url_for_rfc(value)
547
+ 'http://www.iana.org/go/%s'.freeze % value
548
+ end
549
+
550
+ def xref_url_for_draft(value)
551
+ 'http://www.iana.org/go/%s'.freeze % value.sub(/\ARFC/, 'draft')
552
+ end
553
+
554
+ def xref_url_for_rfc_errata(value)
555
+ 'http://www.rfc-editor.org/errata_search.php?eid=%s'.freeze % value
556
+ end
557
+
558
+ def xref_url_for_person(value)
559
+ 'http://www.iana.org/assignments/media-types/media-types.xhtml#%s'.freeze %
560
+ value
561
+ end
562
+
563
+ def xref_url_for_template(value)
564
+ 'http://www.iana.org/assignments/media-types/%s'.freeze % value
565
+ end
798
566
  end