class CloudEvents::ContentType

A parsed content-type header.

This object represents the information contained in a Content-Type, obtained by parsing the header according to RFC 2045.

Case-insensitive fields, such as media_type and subtype, are normalized to lower case.

If parsing fails, this class will try to get as much information as it can, and fill the rest with defaults as recommended in RFC 2045 sec 5.2. In case of a parsing error, the {#error_message} field will be set.

This object is immutable, and Ractor-shareable on Ruby 3.

Attributes

canonical_string[R]

A “canonical” header content string with spacing and capitalization normalized. @return [String]

charset[R]

The charset, defaulting to “us-ascii” if none is explicitly set. @return [String]

error_message[R]

The error message when parsing, or `nil` if there was no error message. @return [String,nil]

media_type[R]

The media type. @return [String]

params[R]

An array of parameters, each element as a two-element array of the parameter name and value. @return [Array<Array(String,String)>]

string[R]

The original header content string. @return [String]

subtype[R]

The entire content subtype (which could include an extension delimited by a plus sign). @return [String]

subtype_base[R]

The portion of the content subtype before any plus sign. @return [String]

subtype_format[R]

The portion of the content subtype after any plus sign, or nil if there is no plus sign in the subtype. @return [String,nil]

to_s[R]

The original header content string. @return [String]

Public Class Methods

new(string, default_charset: nil) click to toggle source

Parse the given header value.

@param string [String] Content-Type header value in RFC 2045 format @param default_charset [String] Optional. The charset to use if none is

specified. Defaults to `us-ascii`.
# File lib/cloud_events/content_type.rb, line 27
def initialize string, default_charset: nil
  @string = string.to_s
  @media_type = "text"
  @subtype_base = @subtype = "plain"
  @subtype_format = nil
  @params = []
  @charset = default_charset || "us-ascii"
  @error_message = nil
  parse consume_comments string.strip
  @canonical_string = "#{@media_type}/#{@subtype}" +
                      @params.map { |k, v| "; #{k}=#{maybe_quote v}" }.join
  full_freeze
end

Public Instance Methods

==(other) click to toggle source

@private

# File lib/cloud_events/content_type.rb, line 111
def == other
  other.is_a?(ContentType) && canonical_string == other.canonical_string
end
Also aliased as: eql?
eql?(other)
Alias for: ==
hash() click to toggle source

@private

# File lib/cloud_events/content_type.rb, line 117
def hash
  canonical_string.hash
end
param_values(key) click to toggle source

An array of values for the given parameter name @param key [String] @return [Array<String>]

# File lib/cloud_events/content_type.rb, line 105
def param_values key
  key = key.downcase
  @params.inject([]) { |a, (k, v)| key == k ? a << v : a }
end

Private Instance Methods

consume_comments(str) click to toggle source
# File lib/cloud_events/content_type.rb, line 184
def consume_comments str
  return str unless str.start_with? "("
  index = 1
  loop do
    char = str[index]
    case char
    when nil
      raise ParseError, "Comment never finished"
    when ")"
      break
    when "\\"
      index += 2
    when "("
      str = consume_comments str[index..-1]
      index = 0
    else
      index += 1
    end
  end
  index += 1
  consume_comments str[index..-1].strip
end
consume_special(str, expected, error_message: nil) click to toggle source
# File lib/cloud_events/content_type.rb, line 153
def consume_special str, expected, error_message: nil
  raise ParseError, error_message || "Expected #{expected.inspect}" unless str.start_with? expected
  consume_comments str[1..-1].strip
end
consume_token(str, downcase: false, error_message: nil) click to toggle source
# File lib/cloud_events/content_type.rb, line 144
def consume_token str, downcase: false, error_message: nil
  match = /^([\w!#$%&'*+.\^`{|}-]+)(.*)$/.match str
  raise ParseError, error_message || "Expected token" unless match
  token = match[1]
  token.downcase! if downcase
  str = consume_comments match[2].strip
  [token, str]
end
consume_token_or_quoted(str, error_message: nil) click to toggle source
# File lib/cloud_events/content_type.rb, line 158
def consume_token_or_quoted str, error_message: nil
  return consume_token str unless str.start_with? '"'
  arr = []
  index = 1
  loop do
    char = str[index]
    case char
    when nil
      raise ParseError, error_message || "Quoted-string never finished"
    when "\""
      break
    when "\\"
      char = str[index + 1]
      raise ParseError, error_message || "Quoted-string never finished" unless char
      arr << char
      index += 2
    else
      arr << char
      index += 1
    end
  end
  index += 1
  str = consume_comments str[index..-1].strip
  [arr.join, str]
end
full_freeze() click to toggle source
# File lib/cloud_events/content_type.rb, line 213
def full_freeze
  instance_variables.each do |iv|
    instance_variable_get(iv).freeze
  end
  @params.each do |name_val|
    name_val[0].freeze
    name_val[1].freeze
    name_val.freeze
  end
  freeze
end
maybe_quote(str) click to toggle source
# File lib/cloud_events/content_type.rb, line 207
def maybe_quote str
  return str if /^[\w!#$%&'*+.\^`{|}-]+$/ =~ str
  str = str.gsub("\\", "\\\\\\\\").gsub("\"", "\\\\\"")
  "\"#{str}\""
end
parse(str) click to toggle source
# File lib/cloud_events/content_type.rb, line 127
def parse str
  @media_type, str = consume_token str, downcase: true, error_message: "Failed to parse media type"
  str = consume_special str, "/"
  @subtype, str = consume_token str, downcase: true, error_message: "Failed to parse subtype"
  @subtype_base, @subtype_format = @subtype.split "+", 2
  until str.empty?
    str = consume_special str, ";"
    name, str = consume_token str, downcase: true, error_message: "Faled to parse attribute name"
    str = consume_special str, "=", error_message: "Failed to find value for attribute #{name}"
    val, str = consume_token_or_quoted str, error_message: "Failed to parse value for attribute #{name}"
    @params << [name, val]
    @charset = val if name == "charset"
  end
rescue ParseError => e
  @error_message = e.message
end