mime-types 2.99.3 → 3.0

Sign up to get free protection for your applications and to get access to all the features.
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