mime-types 1.16 → 3.5.2

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