mime-types 1.16 → 3.5.2

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