Skip to content

Commit 8512c15

Browse files
committed
Deprecate array and string type initialization
- 2 deprecations: - Array-based MIME::Type initialization - String-based MIME::Type initialization Use of these these will result in deprecation warnings. - Added `logger` to the gemspec to suppress a bundled gem warning with Ruby 3.3.5. This warning should not be showing up until Ruby 3.4.0 is released and will be suppressed in Ruby 3.3.6. - Reworked the deprecation message code to be somewhat more flexible and allow for outputting certain warnings once. Because there will be at least one other release after 3.6, we do not need to make the type initialization deprecations frequent with this release.
1 parent e8d0b42 commit 8512c15

14 files changed

+238
-141
lines changed

.github/dependabot.yml

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
version: 2
2+
3+
updates:
4+
- package-ecosystem: github-actions
5+
directory: /
6+
7+
- package-ecosystem: bundler
8+
directory: /

.github/workflows/ci.yml

+37-15
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
steps:
1515
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
1616

17-
- uses: ruby/setup-ruby@v1
17+
- uses: ruby/setup-ruby@c04af2bb7258bb6a03df1d3c1865998ac9390972 # v1
1818
with:
1919
ruby-version: '3.3'
2020
rubygems: latest
@@ -39,22 +39,13 @@ jobs:
3939
- '3.1'
4040
- '3.2'
4141
- '3.3'
42-
- jruby
4342
- truffleruby
44-
include:
45-
- ruby: jruby
46-
os: ubuntu-22.04
47-
continue-on-error: true
48-
- ruby: truffleruby
49-
os: ubuntu-22.04
50-
continue-on-error: true
5143

5244
runs-on: ${{ matrix.os }}
53-
continue-on-error: ${{ matrix.continue-on-error }}
5445

5546
steps:
5647
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
57-
- uses: ruby/setup-ruby@v1
48+
- uses: ruby/setup-ruby@c04af2bb7258bb6a03df1d3c1865998ac9390972 # v1
5849
with:
5950
ruby-version: ${{ matrix.ruby }}
6051
rubygems: latest
@@ -84,7 +75,7 @@ jobs:
8475

8576
steps:
8677
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
87-
- uses: ruby/setup-ruby@v1
78+
- uses: ruby/setup-ruby@c04af2bb7258bb6a03df1d3c1865998ac9390972 # v1
8879
with:
8980
ruby-version: ${{ matrix.ruby }}
9081
rubygems: latest
@@ -104,7 +95,6 @@ jobs:
10495
ruby:
10596
- '2.6'
10697
- '2.7'
107-
- '3.0'
10898
- '3.1'
10999
- '3.2'
110100
- '3.3'
@@ -113,12 +103,14 @@ jobs:
113103
include:
114104
- ruby: mingw
115105
os: windows-2022
106+
- ruby: '3.0'
107+
os: windows-2022
116108

117109
runs-on: ${{ matrix.os }}
118110

119111
steps:
120112
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
121-
- uses: ruby/setup-ruby@v1
113+
- uses: ruby/setup-ruby@c04af2bb7258bb6a03df1d3c1865998ac9390972 # v1
122114
with:
123115
ruby-version: ${{ matrix.ruby }}
124116
rubygems: latest
@@ -127,6 +119,36 @@ jobs:
127119

128120
- run: bundle exec ruby -S rake test --trace
129121

122+
# JRuby should NOT be optional, but I have not been able to make it work in quite
123+
# a while.
124+
jruby:
125+
name: Ruby ${{ matrix.ruby }} - ${{ matrix.os }})
126+
127+
strategy:
128+
fail-fast: false
129+
130+
matrix:
131+
ruby:
132+
- jruby
133+
os:
134+
- ubuntu-20.04
135+
- ubuntu-22.04
136+
- ubuntu-24.04
137+
138+
continue-on-error: true
139+
runs-on: ${{ matrix.os }}
140+
141+
steps:
142+
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
143+
- uses: ruby/setup-ruby@c04af2bb7258bb6a03df1d3c1865998ac9390972 # v1
144+
with:
145+
ruby-version: ${{ matrix.ruby }}
146+
rubygems: latest
147+
bundler: 2
148+
bundler-cache: true
149+
150+
- run: bundle exec jruby -S rake test --trace
151+
130152
ruby-head-optional:
131153
name: Ruby ${{ matrix.ruby }} - ${{ matrix.os }} (optional)
132154

@@ -146,7 +168,7 @@ jobs:
146168

147169
steps:
148170
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
149-
- uses: ruby/setup-ruby@v1
171+
- uses: ruby/setup-ruby@c04af2bb7258bb6a03df1d3c1865998ac9390972 # v1
150172
with:
151173
ruby-version: ${{ matrix.ruby }}
152174
rubygems: latest

.hoerc

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ exclude: !ruby/regexp '/
3030
fasterer |
3131
pullreview |
3232
rubocop* |
33+
standard* |
3334
travis |
3435
unused
3536
)\.yml$

History.md

+18
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
11
# Changelog
22

3+
## 3.6 / 2024-10-02
4+
5+
- 2 deprecations:
6+
7+
- Array-based MIME::Type initialization
8+
- String-based MIME::Type initialization
9+
10+
Use of these these will result in deprecation warnings.
11+
12+
- Added `logger` to the gemspec to suppress a bundled gem warning with Ruby
13+
3.3.5. This warning should not be showing up until Ruby 3.4.0 is released and
14+
will be suppressed in Ruby 3.3.6.
15+
16+
- Reworked the deprecation message code to be somewhat more flexible and allow
17+
for outputting certain warnings once. Because there will be at least one other
18+
release after 3.6, we do not need to make the type initialization deprecations
19+
frequent with this release.
20+
321
## 3.5.2 / 2024-01-02
422

523
There are no primary code changes, but we are releasing this as an update as

Rakefile

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ spec = Hoe.spec "mime-types" do
2222
spec_extras[:metadata] = ->(val) { val["rubygems_mfa_required"] = "true" }
2323

2424
extra_deps << ["mime-types-data", "~> 3.2015"]
25+
extra_deps << ["logger", ">= 0"]
2526

2627
extra_dev_deps << ["hoe", ">= 3.0", "< 5"]
2728
extra_dev_deps << ["hoe-doofus", "~> 1.0"]

lib/mime/type.rb

+68-43
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,22 @@
44
module MIME
55
end
66

7+
require "mime/types/deprecations"
8+
79
# The definition of one MIME content-type.
810
#
911
# == Usage
10-
# require 'mime/types'
12+
# require "mime/types"
1113
#
12-
# plaintext = MIME::Types['text/plain'] # => [ text/plain ]
14+
# plaintext = MIME::Types["text/plain"] # => [ text/plain ]
1315
# text = plaintext.first
14-
# puts text.media_type # => 'text'
15-
# puts text.sub_type # => 'plain'
16+
# puts text.media_type # => "text"
17+
# puts text.sub_type # => "plain"
1618
#
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'
19+
# puts text.extensions.join(" ") # => "txt asc c cc h hh cpp hpp dat hlp"
20+
# puts text.preferred_extension # => "txt"
21+
# puts text.friendly # => "Text Document"
22+
# puts text.i18n_key # => "text.plain"
2123
#
2224
# puts text.encoding # => quoted-printable
2325
# puts text.default_encoding # => quoted-printable
@@ -28,45 +30,45 @@ module MIME
2830
# puts text.provisional? # => false
2931
# puts text.complete? # => true
3032
#
31-
# puts text # => 'text/plain'
33+
# puts text # => "text/plain"
3234
#
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
35+
# puts text == "text/plain" # => true
36+
# puts "text/plain" == text # => true
37+
# puts text == "text/x-plain" # => false
38+
# puts "text/x-plain" == text # => false
3739
#
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+
# puts MIME::Type.simplified("x-appl/x-zip") # => "x-appl/x-zip"
41+
# puts MIME::Type.i18n_key("x-appl/x-zip") # => "x-appl.x-zip"
4042
#
41-
# puts text.like?('text/x-plain') # => true
42-
# puts text.like?(MIME::Type.new('x-text/x-plain')) # => true
43+
# puts text.like?("text/x-plain") # => true
44+
# puts text.like?(MIME::Type.new("content-type" => "x-text/x-plain")) # => true
4345
#
4446
# puts text.xrefs.inspect # => { "rfc" => [ "rfc2046", "rfc3676", "rfc5147" ] }
4547
# puts text.xref_urls # => [ "http://www.iana.org/go/rfc2046",
4648
# # "http://www.iana.org/go/rfc3676",
4749
# # "http://www.iana.org/go/rfc5147" ]
4850
#
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'
51+
# xtext = MIME::Type.new("x-text/x-plain")
52+
# puts xtext.media_type # => "text"
53+
# puts xtext.raw_media_type # => "x-text"
54+
# puts xtext.sub_type # => "plain"
55+
# puts xtext.raw_sub_type # => "x-plain"
5456
# puts xtext.complete? # => false
5557
#
56-
# puts MIME::Types.any? { |type| type.content_type == 'text/plain' } # => true
58+
# puts MIME::Types.any? { |type| type.content_type == "text/plain" } # => true
5759
# puts MIME::Types.all?(&:registered?) # => false
5860
#
5961
# # 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'
62+
# qcelp = MIME::Types["audio/QCELP"].first # => audio/QCELP
63+
# puts qcelp.content_type # => "audio/QCELP"
64+
# puts qcelp.simplified # => "audio/qcelp"
6365
#
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'
66+
# xwingz = MIME::Types["application/x-Wingz"].first # => application/x-Wingz
67+
# puts xwingz.content_type # => "application/x-Wingz"
68+
# puts xwingz.simplified # => "application/x-wingz"
6769
class MIME::Type
6870
# Reflects a MIME content-type specification that is not correctly
69-
# formatted (it isn't +type+/+subtype+).
71+
# formatted (it is not +type+/+subtype+).
7072
class InvalidContentType < ArgumentError
7173
# :stopdoc:
7274
def initialize(type_string)
@@ -93,14 +95,20 @@ def to_s
9395
end
9496

9597
# The released version of the mime-types library.
96-
VERSION = "3.5.2"
98+
VERSION = "3.6.0"
9799

98100
include Comparable
99101

100102
# :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
103+
# Full conformance with RFC 6838 §4.2 (the recommendation for < 64 characters is not
104+
# enforced or reported because MIME::Types mostly deals with registered data). RFC 4288
105+
# §4.2 does not restrict the first character to alphanumeric, but the total length of
106+
# each part is limited to 127 characters. RFCC 2045 §5.1 does not restrict the character
107+
# composition except for whitespace, but MIME::Type was always more strict than this.
108+
restricted_name_first = "[0-9a-zA-Z]"
109+
restricted_name_chars = "[-!#{$&}^_.+0-9a-zA-Z]{0,126}"
110+
restricted_name = "#{restricted_name_first}#{restricted_name_chars}"
111+
MEDIA_TYPE_RE = %r{(#{restricted_name})/(#{restricted_name})}.freeze
104112
I18N_RE = /[^[:alnum:]]/.freeze
105113
BINARY_ENCODINGS = %w[base64 8bit].freeze
106114
ASCII_ENCODINGS = %w[7bit quoted-printable].freeze
@@ -110,12 +118,15 @@ def to_s
110118
:ASCII_ENCODINGS
111119

112120
# 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
121+
# value (e.g., "text/plain" or "application/x-eruby"). The constructed object
114122
# is yielded to an optional block for additional configuration, such as
115123
# associating extensions and encoding information.
116124
#
117125
# * When provided a Hash or a MIME::Type, the MIME::Type will be
118126
# constructed with #init_with.
127+
#
128+
# There are two deprecated initialization forms:
129+
#
119130
# * When provided an Array, the MIME::Type will be constructed using
120131
# the first element as the content type and the remaining flattened
121132
# elements as extensions.
@@ -132,11 +143,23 @@ def initialize(content_type) # :yields: self
132143
when Hash
133144
init_with(content_type)
134145
when Array
146+
MIME::Types.deprecated(
147+
class: MIME::Type,
148+
method: :new,
149+
pre: "when called with an Array",
150+
once: true
151+
)
135152
self.content_type = content_type.shift
136153
self.extensions = content_type.flatten
137154
when MIME::Type
138155
init_with(content_type.to_h)
139156
else
157+
MIME::Types.deprecated(
158+
class: MIME::Type,
159+
method: :new,
160+
pre: "when called with a String",
161+
once: true
162+
)
140163
self.content_type = content_type
141164
end
142165

@@ -181,7 +204,7 @@ def <=>(other)
181204
# comparisons involved are:
182205
#
183206
# 1. self.simplified <=> other.simplified (ensures that we
184-
# don't try to compare different types)
207+
# do not try to compare different types)
185208
# 2. IANA-registered definitions < other definitions.
186209
# 3. Complete definitions < incomplete definitions.
187210
# 4. Current definitions < obsolete definitions.
@@ -243,7 +266,7 @@ def eql?(other)
243266
# +a.simplified+.
244267
#
245268
# 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
269+
# +a.simplified+ has the same hash as +b.simplified+. So we assume it is
247270
# suitable for #hash to delegate to #simplified in service of the #eql?
248271
# invariant.
249272
def hash
@@ -319,7 +342,7 @@ def add_extensions(*extensions)
319342
# exceptions defined, the first extension will be used.
320343
#
321344
# When setting #preferred_extensions, if #extensions does not contain this
322-
# extension, this will be added to #xtensions.
345+
# extension, this will be added to #extensions.
323346
#
324347
# :attr_accessor: preferred_extension
325348

@@ -330,7 +353,9 @@ def preferred_extension
330353

331354
##
332355
def preferred_extension=(value) # :nodoc:
333-
add_extensions(value) if value
356+
if value
357+
add_extensions(value)
358+
end
334359
@preferred_extension = value
335360
end
336361

@@ -343,7 +368,7 @@ def preferred_extension=(value) # :nodoc:
343368
# provided is invalid.
344369
#
345370
# If the encoding is not provided on construction, this will be either
346-
# 'quoted-printable' (for text/* media types) and 'base64' for eveything
371+
# "quoted-printable" (for text/* media types) and "base64" for eveything
347372
# else.
348373
#
349374
# :attr_accessor: encoding
@@ -393,7 +418,7 @@ def use_instead
393418
#
394419
# call-seq:
395420
# text_plain.friendly # => "Text File"
396-
# text_plain.friendly('en') # => "Text File"
421+
# text_plain.friendly("en") # => "Text File"
397422
def friendly(lang = "en")
398423
@friendly ||= {}
399424

@@ -486,7 +511,7 @@ def to_s
486511
# Returns the MIME::Type as a string for implicit conversions. This allows
487512
# MIME::Type objects to appear on either side of a comparison.
488513
#
489-
# 'text/plain' == MIME::Type.new('text/plain')
514+
# "text/plain" == MIME::Type.new("content-type" => "text/plain")
490515
def to_str
491516
content_type
492517
end
@@ -627,7 +652,7 @@ def intern_string(string)
627652
-string
628653
end
629654
else
630-
# MRI 2.2 and older don't have a method for string interning,
655+
# MRI 2.2 and older do not have a method for string interning,
631656
# so we simply freeze them for keeping a similar interface
632657
def intern_string(string)
633658
string.freeze

0 commit comments

Comments
 (0)