mime-types 3.3.1
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.
- checksums.yaml +7 -0
- data/Code-of-Conduct.md +73 -0
- data/Contributing.md +143 -0
- data/History.md +240 -0
- data/Licence.md +25 -0
- data/Manifest.txt +31 -0
- data/README.rdoc +193 -0
- data/Rakefile +284 -0
- data/lib/mime-types.rb +3 -0
- data/lib/mime/type.rb +587 -0
- data/lib/mime/type/columnar.rb +57 -0
- data/lib/mime/types.rb +231 -0
- data/lib/mime/types/_columnar.rb +136 -0
- data/lib/mime/types/cache.rb +58 -0
- data/lib/mime/types/columnar.rb +3 -0
- data/lib/mime/types/container.rb +96 -0
- data/lib/mime/types/deprecations.rb +32 -0
- data/lib/mime/types/full.rb +19 -0
- data/lib/mime/types/loader.rb +146 -0
- data/lib/mime/types/logger.rb +39 -0
- data/lib/mime/types/registry.rb +90 -0
- data/test/bad-fixtures/malformed +9 -0
- data/test/fixture/json.json +1 -0
- data/test/fixture/old-data +9 -0
- data/test/fixture/yaml.yaml +55 -0
- data/test/minitest_helper.rb +13 -0
- data/test/test_mime_type.rb +610 -0
- data/test/test_mime_types.rb +169 -0
- data/test/test_mime_types_cache.rb +118 -0
- data/test/test_mime_types_class.rb +159 -0
- data/test/test_mime_types_lazy.rb +49 -0
- data/test/test_mime_types_loader.rb +32 -0
- metadata +349 -0
data/lib/mime-types.rb
ADDED
data/lib/mime/type.rb
ADDED
@@ -0,0 +1,587 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
##
|
4
|
+
module MIME
|
5
|
+
end
|
6
|
+
|
7
|
+
# The definition of one MIME content-type.
|
8
|
+
#
|
9
|
+
# == Usage
|
10
|
+
# require 'mime/types'
|
11
|
+
#
|
12
|
+
# plaintext = MIME::Types['text/plain'].first
|
13
|
+
# # returns [text/plain, text/plain]
|
14
|
+
# text = plaintext.first
|
15
|
+
# print text.media_type # => 'text'
|
16
|
+
# print text.sub_type # => 'plain'
|
17
|
+
#
|
18
|
+
# puts text.extensions.join(" ") # => 'asc txt c cc h hh cpp'
|
19
|
+
#
|
20
|
+
# puts text.encoding # => 8bit
|
21
|
+
# puts text.binary? # => false
|
22
|
+
# puts text.ascii? # => true
|
23
|
+
# puts text == 'text/plain' # => true
|
24
|
+
# puts MIME::Type.simplified('x-appl/x-zip') # => 'appl/zip'
|
25
|
+
#
|
26
|
+
# puts MIME::Types.any? { |type|
|
27
|
+
# type.content_type == 'text/plain'
|
28
|
+
# } # => true
|
29
|
+
# puts MIME::Types.all?(&:registered?)
|
30
|
+
# # => false
|
31
|
+
class MIME::Type
|
32
|
+
# Reflects a MIME content-type specification that is not correctly
|
33
|
+
# formatted (it isn't +type+/+subtype+).
|
34
|
+
class InvalidContentType < ArgumentError
|
35
|
+
# :stopdoc:
|
36
|
+
def initialize(type_string)
|
37
|
+
@type_string = type_string
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_s
|
41
|
+
"Invalid Content-Type #{@type_string.inspect}"
|
42
|
+
end
|
43
|
+
# :startdoc:
|
44
|
+
end
|
45
|
+
|
46
|
+
# Reflects an unsupported MIME encoding.
|
47
|
+
class InvalidEncoding < ArgumentError
|
48
|
+
# :stopdoc:
|
49
|
+
def initialize(encoding)
|
50
|
+
@encoding = encoding
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_s
|
54
|
+
"Invalid Encoding #{@encoding.inspect}"
|
55
|
+
end
|
56
|
+
# :startdoc:
|
57
|
+
end
|
58
|
+
|
59
|
+
# The released version of the mime-types library.
|
60
|
+
VERSION = '3.3.1'
|
61
|
+
|
62
|
+
include Comparable
|
63
|
+
|
64
|
+
# :stopdoc:
|
65
|
+
# TODO verify mime-type character restrictions; I am pretty sure that this is
|
66
|
+
# too wide open.
|
67
|
+
MEDIA_TYPE_RE = %r{([-\w.+]+)/([-\w.+]*)}.freeze
|
68
|
+
I18N_RE = /[^[:alnum:]]/.freeze
|
69
|
+
BINARY_ENCODINGS = %w(base64 8bit).freeze
|
70
|
+
ASCII_ENCODINGS = %w(7bit quoted-printable).freeze
|
71
|
+
# :startdoc:
|
72
|
+
|
73
|
+
private_constant :MEDIA_TYPE_RE, :I18N_RE, :BINARY_ENCODINGS,
|
74
|
+
:ASCII_ENCODINGS
|
75
|
+
|
76
|
+
# Builds a MIME::Type object from the +content_type+, a MIME Content Type
|
77
|
+
# value (e.g., 'text/plain' or 'applicaton/x-eruby'). The constructed object
|
78
|
+
# is yielded to an optional block for additional configuration, such as
|
79
|
+
# associating extensions and encoding information.
|
80
|
+
#
|
81
|
+
# * When provided a Hash or a MIME::Type, the MIME::Type will be
|
82
|
+
# constructed with #init_with.
|
83
|
+
# * When provided an Array, the MIME::Type will be constructed using
|
84
|
+
# the first element as the content type and the remaining flattened
|
85
|
+
# elements as extensions.
|
86
|
+
# * Otherwise, the content_type will be used as a string.
|
87
|
+
#
|
88
|
+
# Yields the newly constructed +self+ object.
|
89
|
+
def initialize(content_type) # :yields self:
|
90
|
+
@friendly = {}
|
91
|
+
@obsolete = @registered = false
|
92
|
+
@preferred_extension = @docs = @use_instead = nil
|
93
|
+
self.extensions = []
|
94
|
+
|
95
|
+
case content_type
|
96
|
+
when Hash
|
97
|
+
init_with(content_type)
|
98
|
+
when Array
|
99
|
+
self.content_type = content_type.shift
|
100
|
+
self.extensions = content_type.flatten
|
101
|
+
when MIME::Type
|
102
|
+
init_with(content_type.to_h)
|
103
|
+
else
|
104
|
+
self.content_type = content_type
|
105
|
+
end
|
106
|
+
|
107
|
+
self.encoding ||= :default
|
108
|
+
self.xrefs ||= {}
|
109
|
+
|
110
|
+
yield self if block_given?
|
111
|
+
end
|
112
|
+
|
113
|
+
# Indicates that a MIME type is like another type. This differs from
|
114
|
+
# <tt>==</tt> because <tt>x-</tt> prefixes are removed for this comparison.
|
115
|
+
def like?(other)
|
116
|
+
other = if other.respond_to?(:simplified)
|
117
|
+
MIME::Type.simplified(other.simplified, remove_x_prefix: true)
|
118
|
+
else
|
119
|
+
MIME::Type.simplified(other.to_s, remove_x_prefix: true)
|
120
|
+
end
|
121
|
+
MIME::Type.simplified(simplified, remove_x_prefix: true) == other
|
122
|
+
end
|
123
|
+
|
124
|
+
# Compares the +other+ MIME::Type against the exact content type or the
|
125
|
+
# simplified type (the simplified type will be used if comparing against
|
126
|
+
# something that can be treated as a String with #to_s). In comparisons, this
|
127
|
+
# is done against the lowercase version of the MIME::Type.
|
128
|
+
def <=>(other)
|
129
|
+
if other.nil?
|
130
|
+
-1
|
131
|
+
elsif other.respond_to?(:simplified)
|
132
|
+
simplified <=> other.simplified
|
133
|
+
else
|
134
|
+
filtered = 'silent' if other == :silent
|
135
|
+
filtered ||= 'true' if other == true
|
136
|
+
filtered ||= other.to_s
|
137
|
+
|
138
|
+
simplified <=> MIME::Type.simplified(filtered)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Compares the +other+ MIME::Type based on how reliable it is before doing a
|
143
|
+
# normal <=> comparison. Used by MIME::Types#[] to sort types. The
|
144
|
+
# comparisons involved are:
|
145
|
+
#
|
146
|
+
# 1. self.simplified <=> other.simplified (ensures that we
|
147
|
+
# don't try to compare different types)
|
148
|
+
# 2. IANA-registered definitions < other definitions.
|
149
|
+
# 3. Complete definitions < incomplete definitions.
|
150
|
+
# 4. Current definitions < obsolete definitions.
|
151
|
+
# 5. Obselete with use-instead names < obsolete without.
|
152
|
+
# 6. Obsolete use-instead definitions are compared.
|
153
|
+
#
|
154
|
+
# While this method is public, its use is strongly discouraged by consumers
|
155
|
+
# of mime-types. In mime-types 3, this method is likely to see substantial
|
156
|
+
# revision and simplification to ensure current registered content types sort
|
157
|
+
# before unregistered or obsolete content types.
|
158
|
+
def priority_compare(other)
|
159
|
+
pc = simplified <=> other.simplified
|
160
|
+
if pc.zero? || !(extensions & other.extensions).empty?
|
161
|
+
pc = if (reg = registered?) != other.registered?
|
162
|
+
reg ? -1 : 1 # registered < unregistered
|
163
|
+
elsif (comp = complete?) != other.complete?
|
164
|
+
comp ? -1 : 1 # complete < incomplete
|
165
|
+
elsif (obs = obsolete?) != other.obsolete?
|
166
|
+
obs ? 1 : -1 # current < obsolete
|
167
|
+
elsif obs and ((ui = use_instead) != (oui = other.use_instead))
|
168
|
+
if ui.nil?
|
169
|
+
1
|
170
|
+
elsif oui.nil?
|
171
|
+
-1
|
172
|
+
else
|
173
|
+
ui <=> oui
|
174
|
+
end
|
175
|
+
else
|
176
|
+
0
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
pc
|
181
|
+
end
|
182
|
+
|
183
|
+
# Returns +true+ if the +other+ object is a MIME::Type and the content types
|
184
|
+
# match.
|
185
|
+
def eql?(other)
|
186
|
+
other.kind_of?(MIME::Type) and self == other
|
187
|
+
end
|
188
|
+
|
189
|
+
# Returns the whole MIME content-type string.
|
190
|
+
#
|
191
|
+
# The content type is a presentation value from the MIME type registry and
|
192
|
+
# should not be used for comparison. The case of the content type is
|
193
|
+
# preserved, and extension markers (<tt>x-</tt>) are kept.
|
194
|
+
#
|
195
|
+
# text/plain => text/plain
|
196
|
+
# x-chemical/x-pdb => x-chemical/x-pdb
|
197
|
+
# audio/QCELP => audio/QCELP
|
198
|
+
attr_reader :content_type
|
199
|
+
# A simplified form of the MIME content-type string, suitable for
|
200
|
+
# case-insensitive comparison, with any extension markers (<tt>x-</tt)
|
201
|
+
# removed and converted to lowercase.
|
202
|
+
#
|
203
|
+
# text/plain => text/plain
|
204
|
+
# x-chemical/x-pdb => x-chemical/x-pdb
|
205
|
+
# audio/QCELP => audio/qcelp
|
206
|
+
attr_reader :simplified
|
207
|
+
# Returns the media type of the simplified MIME::Type.
|
208
|
+
#
|
209
|
+
# text/plain => text
|
210
|
+
# x-chemical/x-pdb => x-chemical
|
211
|
+
# audio/QCELP => audio
|
212
|
+
attr_reader :media_type
|
213
|
+
# Returns the media type of the unmodified MIME::Type.
|
214
|
+
#
|
215
|
+
# text/plain => text
|
216
|
+
# x-chemical/x-pdb => x-chemical
|
217
|
+
# audio/QCELP => audio
|
218
|
+
attr_reader :raw_media_type
|
219
|
+
# Returns the sub-type of the simplified MIME::Type.
|
220
|
+
#
|
221
|
+
# text/plain => plain
|
222
|
+
# x-chemical/x-pdb => pdb
|
223
|
+
# audio/QCELP => QCELP
|
224
|
+
attr_reader :sub_type
|
225
|
+
# Returns the media type of the unmodified MIME::Type.
|
226
|
+
#
|
227
|
+
# text/plain => plain
|
228
|
+
# x-chemical/x-pdb => x-pdb
|
229
|
+
# audio/QCELP => qcelp
|
230
|
+
attr_reader :raw_sub_type
|
231
|
+
|
232
|
+
##
|
233
|
+
# The list of extensions which are known to be used for this MIME::Type.
|
234
|
+
# Non-array values will be coerced into an array with #to_a. Array values
|
235
|
+
# will be flattened, +nil+ values removed, and made unique.
|
236
|
+
#
|
237
|
+
# :attr_accessor: extensions
|
238
|
+
def extensions
|
239
|
+
@extensions.to_a
|
240
|
+
end
|
241
|
+
|
242
|
+
##
|
243
|
+
def extensions=(value) # :nodoc:
|
244
|
+
@extensions = Set[*Array(value).flatten.compact].freeze
|
245
|
+
MIME::Types.send(:reindex_extensions, self)
|
246
|
+
end
|
247
|
+
|
248
|
+
# Merge the +extensions+ provided into this MIME::Type. The extensions added
|
249
|
+
# will be merged uniquely.
|
250
|
+
def add_extensions(*extensions)
|
251
|
+
self.extensions += extensions
|
252
|
+
end
|
253
|
+
|
254
|
+
##
|
255
|
+
# The preferred extension for this MIME type. If one is not set and there are
|
256
|
+
# exceptions defined, the first extension will be used.
|
257
|
+
#
|
258
|
+
# When setting #preferred_extensions, if #extensions does not contain this
|
259
|
+
# extension, this will be added to #xtensions.
|
260
|
+
#
|
261
|
+
# :attr_accessor: preferred_extension
|
262
|
+
|
263
|
+
##
|
264
|
+
def preferred_extension
|
265
|
+
@preferred_extension || extensions.first
|
266
|
+
end
|
267
|
+
|
268
|
+
##
|
269
|
+
def preferred_extension=(value) # :nodoc:
|
270
|
+
add_extensions(value) if value
|
271
|
+
@preferred_extension = value
|
272
|
+
end
|
273
|
+
|
274
|
+
##
|
275
|
+
# The encoding (+7bit+, +8bit+, <tt>quoted-printable</tt>, or +base64+)
|
276
|
+
# required to transport the data of this content type safely across a
|
277
|
+
# network, which roughly corresponds to Content-Transfer-Encoding. A value of
|
278
|
+
# +nil+ or <tt>:default</tt> will reset the #encoding to the
|
279
|
+
# #default_encoding for the MIME::Type. Raises ArgumentError if the encoding
|
280
|
+
# provided is invalid.
|
281
|
+
#
|
282
|
+
# If the encoding is not provided on construction, this will be either
|
283
|
+
# 'quoted-printable' (for text/* media types) and 'base64' for eveything
|
284
|
+
# else.
|
285
|
+
#
|
286
|
+
# :attr_accessor: encoding
|
287
|
+
|
288
|
+
##
|
289
|
+
attr_reader :encoding
|
290
|
+
|
291
|
+
##
|
292
|
+
def encoding=(enc) # :nodoc:
|
293
|
+
if enc.nil? or enc == :default
|
294
|
+
@encoding = default_encoding
|
295
|
+
elsif BINARY_ENCODINGS.include?(enc) or ASCII_ENCODINGS.include?(enc)
|
296
|
+
@encoding = enc
|
297
|
+
else
|
298
|
+
fail InvalidEncoding, enc
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
# Returns the default encoding for the MIME::Type based on the media type.
|
303
|
+
def default_encoding
|
304
|
+
@media_type == 'text' ? 'quoted-printable' : 'base64'
|
305
|
+
end
|
306
|
+
|
307
|
+
##
|
308
|
+
# Returns the media type or types that should be used instead of this media
|
309
|
+
# type, if it is obsolete. If there is no replacement media type, or it is
|
310
|
+
# not obsolete, +nil+ will be returned.
|
311
|
+
#
|
312
|
+
# :attr_accessor: use_instead
|
313
|
+
|
314
|
+
##
|
315
|
+
def use_instead
|
316
|
+
obsolete? ? @use_instead : nil
|
317
|
+
end
|
318
|
+
|
319
|
+
##
|
320
|
+
attr_writer :use_instead
|
321
|
+
|
322
|
+
# Returns +true+ if the media type is obsolete.
|
323
|
+
attr_accessor :obsolete
|
324
|
+
alias obsolete? obsolete
|
325
|
+
|
326
|
+
# The documentation for this MIME::Type.
|
327
|
+
attr_accessor :docs
|
328
|
+
|
329
|
+
# A friendly short description for this MIME::Type.
|
330
|
+
#
|
331
|
+
# call-seq:
|
332
|
+
# text_plain.friendly # => "Text File"
|
333
|
+
# text_plain.friendly('en') # => "Text File"
|
334
|
+
def friendly(lang = 'en')
|
335
|
+
@friendly ||= {}
|
336
|
+
|
337
|
+
case lang
|
338
|
+
when String, Symbol
|
339
|
+
@friendly[lang.to_s]
|
340
|
+
when Array
|
341
|
+
@friendly.update(Hash[*lang])
|
342
|
+
when Hash
|
343
|
+
@friendly.update(lang)
|
344
|
+
else
|
345
|
+
fail ArgumentError,
|
346
|
+
"Expected a language or translation set, not #{lang.inspect}"
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
# A key suitable for use as a lookup key for translations, such as with
|
351
|
+
# the I18n library.
|
352
|
+
#
|
353
|
+
# call-seq:
|
354
|
+
# text_plain.i18n_key # => "text.plain"
|
355
|
+
# 3gpp_xml.i18n_key # => "application.vnd-3gpp-bsf-xml"
|
356
|
+
# # from application/vnd.3gpp.bsf+xml
|
357
|
+
# x_msword.i18n_key # => "application.word"
|
358
|
+
# # from application/x-msword
|
359
|
+
attr_reader :i18n_key
|
360
|
+
|
361
|
+
##
|
362
|
+
# The cross-references list for this MIME::Type.
|
363
|
+
#
|
364
|
+
# :attr_accessor: xrefs
|
365
|
+
|
366
|
+
##
|
367
|
+
attr_reader :xrefs
|
368
|
+
|
369
|
+
##
|
370
|
+
def xrefs=(xrefs) # :nodoc:
|
371
|
+
@xrefs = MIME::Types::Container.new(xrefs)
|
372
|
+
end
|
373
|
+
|
374
|
+
# The decoded cross-reference URL list for this MIME::Type.
|
375
|
+
def xref_urls
|
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
|
379
|
+
}
|
380
|
+
end
|
381
|
+
|
382
|
+
# Indicates whether the MIME type has been registered with IANA.
|
383
|
+
attr_accessor :registered
|
384
|
+
alias registered? registered
|
385
|
+
|
386
|
+
# MIME types can be specified to be sent across a network in particular
|
387
|
+
# formats. This method returns +true+ when the MIME::Type encoding is set
|
388
|
+
# to <tt>base64</tt>.
|
389
|
+
def binary?
|
390
|
+
BINARY_ENCODINGS.include?(encoding)
|
391
|
+
end
|
392
|
+
|
393
|
+
# MIME types can be specified to be sent across a network in particular
|
394
|
+
# formats. This method returns +false+ when the MIME::Type encoding is
|
395
|
+
# set to <tt>base64</tt>.
|
396
|
+
def ascii?
|
397
|
+
ASCII_ENCODINGS.include?(encoding)
|
398
|
+
end
|
399
|
+
|
400
|
+
# Indicateswhether the MIME type is declared as a signature type.
|
401
|
+
attr_accessor :signature
|
402
|
+
alias signature? signature
|
403
|
+
|
404
|
+
# Returns +true+ if the MIME::Type specifies an extension list,
|
405
|
+
# indicating that it is a complete MIME::Type.
|
406
|
+
def complete?
|
407
|
+
!@extensions.empty?
|
408
|
+
end
|
409
|
+
|
410
|
+
# Returns the MIME::Type as a string.
|
411
|
+
def to_s
|
412
|
+
content_type
|
413
|
+
end
|
414
|
+
|
415
|
+
# Returns the MIME::Type as a string for implicit conversions. This allows
|
416
|
+
# MIME::Type objects to appear on either side of a comparison.
|
417
|
+
#
|
418
|
+
# 'text/plain' == MIME::Type.new('text/plain')
|
419
|
+
def to_str
|
420
|
+
content_type
|
421
|
+
end
|
422
|
+
|
423
|
+
# Converts the MIME::Type to a JSON string.
|
424
|
+
def to_json(*args)
|
425
|
+
require 'json'
|
426
|
+
to_h.to_json(*args)
|
427
|
+
end
|
428
|
+
|
429
|
+
# Converts the MIME::Type to a hash. The output of this method can also be
|
430
|
+
# used to initialize a MIME::Type.
|
431
|
+
def to_h
|
432
|
+
encode_with({})
|
433
|
+
end
|
434
|
+
|
435
|
+
# Populates the +coder+ with attributes about this record for
|
436
|
+
# serialization. The structure of +coder+ should match the structure used
|
437
|
+
# with #init_with.
|
438
|
+
#
|
439
|
+
# This method should be considered a private implementation detail.
|
440
|
+
def encode_with(coder)
|
441
|
+
coder['content-type'] = @content_type
|
442
|
+
coder['docs'] = @docs unless @docs.nil? or @docs.empty?
|
443
|
+
coder['friendly'] = @friendly unless @friendly.nil? or @friendly.empty?
|
444
|
+
coder['encoding'] = @encoding
|
445
|
+
coder['extensions'] = @extensions.to_a unless @extensions.empty?
|
446
|
+
coder['preferred-extension'] = @preferred_extension if @preferred_extension
|
447
|
+
if obsolete?
|
448
|
+
coder['obsolete'] = obsolete?
|
449
|
+
coder['use-instead'] = use_instead if use_instead
|
450
|
+
end
|
451
|
+
unless xrefs.empty?
|
452
|
+
{}.tap do |hash|
|
453
|
+
xrefs.each do |k, v|
|
454
|
+
hash[k] = v.to_a.sort
|
455
|
+
end
|
456
|
+
coder['xrefs'] = hash
|
457
|
+
end
|
458
|
+
end
|
459
|
+
coder['registered'] = registered?
|
460
|
+
coder['signature'] = signature? if signature?
|
461
|
+
coder
|
462
|
+
end
|
463
|
+
|
464
|
+
# Initialize an empty object from +coder+, which must contain the
|
465
|
+
# attributes necessary for initializing an empty object.
|
466
|
+
#
|
467
|
+
# This method should be considered a private implementation detail.
|
468
|
+
def init_with(coder)
|
469
|
+
self.content_type = coder['content-type']
|
470
|
+
self.docs = coder['docs'] || ''
|
471
|
+
self.encoding = coder['encoding']
|
472
|
+
self.extensions = coder['extensions'] || []
|
473
|
+
self.preferred_extension = coder['preferred-extension']
|
474
|
+
self.obsolete = coder['obsolete'] || false
|
475
|
+
self.registered = coder['registered'] || false
|
476
|
+
self.signature = coder['signature']
|
477
|
+
self.xrefs = coder['xrefs'] || {}
|
478
|
+
self.use_instead = coder['use-instead']
|
479
|
+
|
480
|
+
friendly(coder['friendly'] || {})
|
481
|
+
end
|
482
|
+
|
483
|
+
def inspect # :nodoc:
|
484
|
+
# We are intentionally lying here because MIME::Type::Columnar is an
|
485
|
+
# implementation detail.
|
486
|
+
"#<MIME::Type: #{self}>"
|
487
|
+
end
|
488
|
+
|
489
|
+
class << self
|
490
|
+
# MIME media types are case-insensitive, but are typically presented in a
|
491
|
+
# case-preserving format in the type registry. This method converts
|
492
|
+
# +content_type+ to lowercase.
|
493
|
+
#
|
494
|
+
# In previous versions of mime-types, this would also remove any extension
|
495
|
+
# prefix (<tt>x-</tt>). This is no longer default behaviour, but may be
|
496
|
+
# provided by providing a truth value to +remove_x_prefix+.
|
497
|
+
def simplified(content_type, remove_x_prefix: false)
|
498
|
+
simplify_matchdata(match(content_type), remove_x_prefix)
|
499
|
+
end
|
500
|
+
|
501
|
+
# Converts a provided +content_type+ into a translation key suitable for
|
502
|
+
# use with the I18n library.
|
503
|
+
def i18n_key(content_type)
|
504
|
+
simplify_matchdata(match(content_type), joiner: '.') { |e|
|
505
|
+
e.gsub!(I18N_RE, '-')
|
506
|
+
}
|
507
|
+
end
|
508
|
+
|
509
|
+
# Return a +MatchData+ object of the +content_type+ against pattern of
|
510
|
+
# media types.
|
511
|
+
def match(content_type)
|
512
|
+
case content_type
|
513
|
+
when MatchData
|
514
|
+
content_type
|
515
|
+
else
|
516
|
+
MEDIA_TYPE_RE.match(content_type)
|
517
|
+
end
|
518
|
+
end
|
519
|
+
|
520
|
+
private
|
521
|
+
|
522
|
+
def simplify_matchdata(matchdata, remove_x = false, joiner: '/')
|
523
|
+
return nil unless matchdata
|
524
|
+
|
525
|
+
matchdata.captures.map { |e|
|
526
|
+
e.downcase!
|
527
|
+
e.sub!(/^x-/, '') if remove_x
|
528
|
+
yield e if block_given?
|
529
|
+
e
|
530
|
+
}.join(joiner)
|
531
|
+
end
|
532
|
+
end
|
533
|
+
|
534
|
+
private
|
535
|
+
|
536
|
+
def content_type=(type_string)
|
537
|
+
match = MEDIA_TYPE_RE.match(type_string)
|
538
|
+
fail InvalidContentType, type_string if match.nil?
|
539
|
+
|
540
|
+
@content_type = intern_string(type_string)
|
541
|
+
@raw_media_type, @raw_sub_type = match.captures
|
542
|
+
@simplified = intern_string(MIME::Type.simplified(match))
|
543
|
+
@i18n_key = intern_string(MIME::Type.i18n_key(match))
|
544
|
+
@media_type, @sub_type = MEDIA_TYPE_RE.match(@simplified).captures
|
545
|
+
|
546
|
+
@raw_media_type = intern_string(@raw_media_type)
|
547
|
+
@raw_sub_type = intern_string(@raw_sub_type)
|
548
|
+
@media_type = intern_string(@media_type)
|
549
|
+
@sub_type = intern_string(@sub_type)
|
550
|
+
end
|
551
|
+
|
552
|
+
if String.method_defined?(:-@)
|
553
|
+
def intern_string(string)
|
554
|
+
-string
|
555
|
+
end
|
556
|
+
else
|
557
|
+
# MRI 2.2 and older don't have a method for string interning,
|
558
|
+
# so we simply freeze them for keeping a similar interface
|
559
|
+
def intern_string(string)
|
560
|
+
string.freeze
|
561
|
+
end
|
562
|
+
end
|
563
|
+
|
564
|
+
def xref_map(values, helper)
|
565
|
+
values.map { |value| send(helper, value) }
|
566
|
+
end
|
567
|
+
|
568
|
+
def xref_url_for_rfc(value)
|
569
|
+
'http://www.iana.org/go/%s' % value
|
570
|
+
end
|
571
|
+
|
572
|
+
def xref_url_for_draft(value)
|
573
|
+
'http://www.iana.org/go/%s' % value.sub(/\ARFC/, 'draft')
|
574
|
+
end
|
575
|
+
|
576
|
+
def xref_url_for_rfc_errata(value)
|
577
|
+
'http://www.rfc-editor.org/errata_search.php?eid=%s' % value
|
578
|
+
end
|
579
|
+
|
580
|
+
def xref_url_for_person(value)
|
581
|
+
'http://www.iana.org/assignments/media-types/media-types.xhtml#%s' % value
|
582
|
+
end
|
583
|
+
|
584
|
+
def xref_url_for_template(value)
|
585
|
+
'http://www.iana.org/assignments/media-types/%s' % value
|
586
|
+
end
|
587
|
+
end
|