Skip to content

Commit

Permalink
Deprecate array and string type initialization
Browse files Browse the repository at this point in the history
- 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.
  • Loading branch information
halostatue committed Oct 2, 2024
1 parent e8d0b42 commit 1586511
Show file tree
Hide file tree
Showing 14 changed files with 238 additions and 141 deletions.
8 changes: 8 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
version: 2

updates:
- package-ecosystem: github-actions
directory: /

- package-ecosystem: bundler
directory: /
52 changes: 37 additions & 15 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4

- uses: ruby/setup-ruby@v1
- uses: ruby/setup-ruby@c04af2bb7258bb6a03df1d3c1865998ac9390972 # v1
with:
ruby-version: '3.3'
rubygems: latest
Expand All @@ -39,22 +39,13 @@ jobs:
- '3.1'
- '3.2'
- '3.3'
- jruby
- truffleruby
include:
- ruby: jruby
os: ubuntu-22.04
continue-on-error: true
- ruby: truffleruby
os: ubuntu-22.04
continue-on-error: true

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

steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
- uses: ruby/setup-ruby@v1
- uses: ruby/setup-ruby@c04af2bb7258bb6a03df1d3c1865998ac9390972 # v1
with:
ruby-version: ${{ matrix.ruby }}
rubygems: latest
Expand Down Expand Up @@ -84,7 +75,7 @@ jobs:

steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
- uses: ruby/setup-ruby@v1
- uses: ruby/setup-ruby@c04af2bb7258bb6a03df1d3c1865998ac9390972 # v1
with:
ruby-version: ${{ matrix.ruby }}
rubygems: latest
Expand All @@ -104,7 +95,6 @@ jobs:
ruby:
- '2.6'
- '2.7'
- '3.0'
- '3.1'
- '3.2'
- '3.3'
Expand All @@ -113,12 +103,14 @@ jobs:
include:
- ruby: mingw
os: windows-2022
- ruby: '3.0'
os: windows-2022

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

steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
- uses: ruby/setup-ruby@v1
- uses: ruby/setup-ruby@c04af2bb7258bb6a03df1d3c1865998ac9390972 # v1
with:
ruby-version: ${{ matrix.ruby }}
rubygems: latest
Expand All @@ -127,6 +119,36 @@ jobs:

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

# JRuby should NOT be optional, but I have not been able to make it work in quite
# a while.
jruby:
name: Ruby ${{ matrix.ruby }} - ${{ matrix.os }})

strategy:
fail-fast: false

matrix:
ruby:
- jruby
os:
- ubuntu-20.04
- ubuntu-22.04
- ubuntu-24.04

continue-on-error: true
runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
- uses: ruby/setup-ruby@c04af2bb7258bb6a03df1d3c1865998ac9390972 # v1
with:
ruby-version: ${{ matrix.ruby }}
rubygems: latest
bundler: 2
bundler-cache: true

- run: bundle exec jruby -S rake test --trace

ruby-head-optional:
name: Ruby ${{ matrix.ruby }} - ${{ matrix.os }} (optional)

Expand All @@ -146,7 +168,7 @@ jobs:

steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
- uses: ruby/setup-ruby@v1
- uses: ruby/setup-ruby@c04af2bb7258bb6a03df1d3c1865998ac9390972 # v1
with:
ruby-version: ${{ matrix.ruby }}
rubygems: latest
Expand Down
1 change: 1 addition & 0 deletions .hoerc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ exclude: !ruby/regexp '/
fasterer |
pullreview |
rubocop* |
standard* |
travis |
unused
)\.yml$
Expand Down
18 changes: 18 additions & 0 deletions History.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
# Changelog

## 3.6 / 2024-10-02

- 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.

## 3.5.2 / 2024-01-02

There are no primary code changes, but we are releasing this as an update as
Expand Down
1 change: 1 addition & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ spec = Hoe.spec "mime-types" do
spec_extras[:metadata] = ->(val) { val["rubygems_mfa_required"] = "true" }

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

extra_dev_deps << ["hoe", ">= 3.0", "< 5"]
extra_dev_deps << ["hoe-doofus", "~> 1.0"]
Expand Down
111 changes: 68 additions & 43 deletions lib/mime/type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,22 @@
module MIME
end

require "mime/types/deprecations"

# The definition of one MIME content-type.
#
# == Usage
# require 'mime/types'
# require "mime/types"
#
# plaintext = MIME::Types['text/plain'] # => [ text/plain ]
# plaintext = MIME::Types["text/plain"] # => [ text/plain ]
# text = plaintext.first
# puts text.media_type # => 'text'
# puts text.sub_type # => 'plain'
# puts text.media_type # => "text"
# puts text.sub_type # => "plain"
#
# puts text.extensions.join(' ') # => 'txt asc c cc h hh cpp hpp dat hlp'
# puts text.preferred_extension # => 'txt'
# puts text.friendly # => 'Text Document'
# puts text.i18n_key # => 'text.plain'
# puts text.extensions.join(" ") # => "txt asc c cc h hh cpp hpp dat hlp"
# puts text.preferred_extension # => "txt"
# puts text.friendly # => "Text Document"
# puts text.i18n_key # => "text.plain"
#
# puts text.encoding # => quoted-printable
# puts text.default_encoding # => quoted-printable
Expand All @@ -28,45 +30,45 @@ module MIME
# puts text.provisional? # => false
# puts text.complete? # => true
#
# puts text # => 'text/plain'
# puts text # => "text/plain"
#
# puts text == 'text/plain' # => true
# puts 'text/plain' == text # => true
# puts text == 'text/x-plain' # => false
# puts 'text/x-plain' == text # => false
# puts text == "text/plain" # => true
# puts "text/plain" == text # => true
# puts text == "text/x-plain" # => false
# puts "text/x-plain" == text # => false
#
# puts MIME::Type.simplified('x-appl/x-zip') # => 'x-appl/x-zip'
# puts MIME::Type.i18n_key('x-appl/x-zip') # => 'x-appl.x-zip'
# puts MIME::Type.simplified("x-appl/x-zip") # => "x-appl/x-zip"
# puts MIME::Type.i18n_key("x-appl/x-zip") # => "x-appl.x-zip"
#
# puts text.like?('text/x-plain') # => true
# puts text.like?(MIME::Type.new('x-text/x-plain')) # => true
# puts text.like?("text/x-plain") # => true
# puts text.like?(MIME::Type.new("content-type" => "x-text/x-plain")) # => true
#
# puts text.xrefs.inspect # => { "rfc" => [ "rfc2046", "rfc3676", "rfc5147" ] }
# puts text.xref_urls # => [ "http://www.iana.org/go/rfc2046",
# # "http://www.iana.org/go/rfc3676",
# # "http://www.iana.org/go/rfc5147" ]
#
# xtext = MIME::Type.new('x-text/x-plain')
# puts xtext.media_type # => 'text'
# puts xtext.raw_media_type # => 'x-text'
# puts xtext.sub_type # => 'plain'
# puts xtext.raw_sub_type # => 'x-plain'
# xtext = MIME::Type.new("x-text/x-plain")
# puts xtext.media_type # => "text"
# puts xtext.raw_media_type # => "x-text"
# puts xtext.sub_type # => "plain"
# puts xtext.raw_sub_type # => "x-plain"
# puts xtext.complete? # => false
#
# puts MIME::Types.any? { |type| type.content_type == 'text/plain' } # => true
# puts MIME::Types.any? { |type| type.content_type == "text/plain" } # => true
# puts MIME::Types.all?(&:registered?) # => false
#
# # Various string representations of MIME types
# qcelp = MIME::Types['audio/QCELP'].first # => audio/QCELP
# puts qcelp.content_type # => 'audio/QCELP'
# puts qcelp.simplified # => 'audio/qcelp'
# qcelp = MIME::Types["audio/QCELP"].first # => audio/QCELP
# puts qcelp.content_type # => "audio/QCELP"
# puts qcelp.simplified # => "audio/qcelp"
#
# xwingz = MIME::Types['application/x-Wingz'].first # => application/x-Wingz
# puts xwingz.content_type # => 'application/x-Wingz'
# puts xwingz.simplified # => 'application/x-wingz'
# xwingz = MIME::Types["application/x-Wingz"].first # => application/x-Wingz
# puts xwingz.content_type # => "application/x-Wingz"
# puts xwingz.simplified # => "application/x-wingz"
class MIME::Type
# Reflects a MIME content-type specification that is not correctly
# formatted (it isn't +type+/+subtype+).
# formatted (it is not +type+/+subtype+).
class InvalidContentType < ArgumentError
# :stopdoc:
def initialize(type_string)
Expand All @@ -93,14 +95,20 @@ def to_s
end

# The released version of the mime-types library.
VERSION = "3.5.2"
VERSION = "3.6.0"

include Comparable

# :stopdoc:
# TODO verify mime-type character restrictions; I am pretty sure that this is
# too wide open.
MEDIA_TYPE_RE = %r{([-\w.+]+)/([-\w.+]*)}.freeze
# Full conformance with RFC 6838 §4.2 (the recommendation for < 64 characters is not
# enforced or reported because MIME::Types mostly deals with registered data). RFC 4288
# §4.2 does not restrict the first character to alphanumeric, but the total length of
# each part is limited to 127 characters. RFCC 2045 §5.1 does not restrict the character
# composition except for whitespace, but MIME::Type was always more strict than this.
restricted_name_first = "[0-9a-zA-Z]"
restricted_name_chars = "[-!#{$&}^_.+0-9a-zA-Z]{0,126}"
restricted_name = "#{restricted_name_first}#{restricted_name_chars}"
MEDIA_TYPE_RE = %r{(#{restricted_name})/(#{restricted_name})}.freeze
I18N_RE = /[^[:alnum:]]/.freeze
BINARY_ENCODINGS = %w[base64 8bit].freeze
ASCII_ENCODINGS = %w[7bit quoted-printable].freeze
Expand All @@ -110,12 +118,15 @@ def to_s
:ASCII_ENCODINGS

# Builds a MIME::Type object from the +content_type+, a MIME Content Type
# value (e.g., 'text/plain' or 'application/x-eruby'). The constructed object
# value (e.g., "text/plain" or "application/x-eruby"). The constructed object
# is yielded to an optional block for additional configuration, such as
# associating extensions and encoding information.
#
# * When provided a Hash or a MIME::Type, the MIME::Type will be
# constructed with #init_with.
#
# There are two deprecated initialization forms:
#
# * When provided an Array, the MIME::Type will be constructed using
# the first element as the content type and the remaining flattened
# elements as extensions.
Expand All @@ -132,11 +143,23 @@ def initialize(content_type) # :yields: self
when Hash
init_with(content_type)
when Array
MIME::Types.deprecated(
class: MIME::Type,
method: :new,
pre: "when called with an Array",
once: true
)
self.content_type = content_type.shift
self.extensions = content_type.flatten
when MIME::Type
init_with(content_type.to_h)
else
MIME::Types.deprecated(
class: MIME::Type,
method: :new,
pre: "when called with a String",
once: true
)
self.content_type = content_type
end

Expand Down Expand Up @@ -181,7 +204,7 @@ def <=>(other)
# comparisons involved are:
#
# 1. self.simplified <=> other.simplified (ensures that we
# don't try to compare different types)
# do not try to compare different types)
# 2. IANA-registered definitions < other definitions.
# 3. Complete definitions < incomplete definitions.
# 4. Current definitions < obsolete definitions.
Expand Down Expand Up @@ -243,7 +266,7 @@ def eql?(other)
# +a.simplified+.
#
# Presumably, if <code>a.simplified <=> b.simplified</code> is +0+, then
# +a.simplified+ has the same hash as +b.simplified+. So we assume it's
# +a.simplified+ has the same hash as +b.simplified+. So we assume it is
# suitable for #hash to delegate to #simplified in service of the #eql?
# invariant.
def hash
Expand Down Expand Up @@ -319,7 +342,7 @@ def add_extensions(*extensions)
# exceptions defined, the first extension will be used.
#
# When setting #preferred_extensions, if #extensions does not contain this
# extension, this will be added to #xtensions.
# extension, this will be added to #extensions.
#
# :attr_accessor: preferred_extension

Expand All @@ -330,7 +353,9 @@ def preferred_extension

##
def preferred_extension=(value) # :nodoc:
add_extensions(value) if value
if value
add_extensions(value)
end
@preferred_extension = value
end

Expand All @@ -343,7 +368,7 @@ def preferred_extension=(value) # :nodoc:
# provided is invalid.
#
# If the encoding is not provided on construction, this will be either
# 'quoted-printable' (for text/* media types) and 'base64' for eveything
# "quoted-printable" (for text/* media types) and "base64" for eveything
# else.
#
# :attr_accessor: encoding
Expand Down Expand Up @@ -393,7 +418,7 @@ def use_instead
#
# call-seq:
# text_plain.friendly # => "Text File"
# text_plain.friendly('en') # => "Text File"
# text_plain.friendly("en") # => "Text File"
def friendly(lang = "en")
@friendly ||= {}

Expand Down Expand Up @@ -486,7 +511,7 @@ def to_s
# Returns the MIME::Type as a string for implicit conversions. This allows
# MIME::Type objects to appear on either side of a comparison.
#
# 'text/plain' == MIME::Type.new('text/plain')
# "text/plain" == MIME::Type.new("content-type" => "text/plain")
def to_str
content_type
end
Expand Down Expand Up @@ -627,7 +652,7 @@ def intern_string(string)
-string
end
else
# MRI 2.2 and older don't have a method for string interning,
# MRI 2.2 and older do not have a method for string interning,
# so we simply freeze them for keeping a similar interface
def intern_string(string)
string.freeze
Expand Down
Loading

0 comments on commit 1586511

Please sign in to comment.