Class: Praxis::MediaTypeIdentifier
- Inherits:
-
Attributor::Model
- Object
- Attributor::Model
- Praxis::MediaTypeIdentifier
- Defined in:
- lib/praxis/media_type_identifier.rb
Overview
Ruby object representation of an Internet Media Type Identifier as defined by RFC6838; also known as MIME types due to their origin in RFC2046 (the MIME specification).
Constant Summary collapse
- VALID_TYPE =
Postel’s principle encourages us to accept anything that MIGHT be an identifier, although the syntax for type identifiers is substantially narrower than what we accept there.
Note that this ONLY matches type, subtype and suffix; we handle options differently.
/^\s*(?<type>[^\/]+)\/(?<subtype>[^\+]+)(\+(?<suffix>[^; ]+))?\s*$/x.freeze
- PARAMETER_SEPARATOR =
Pattern that separates parameters of a media type from each other, and from the base identifier.
/\s*;\s*/x.freeze
- WORD_SEPARATOR =
Pattern used to identify the first “word” when we encounter a malformed type identifier, so we can apply a heuristic and assume the user meant “application/XYZ”.
/[^a-z0-9-]/i.freeze
- QUOTED_STRING =
Pattern that lets us strip quotes from parameter values.
/^".*"$/.freeze
- WILDCARD =
Token that indicates a media-type component that matches anything.
'*'.freeze
- Parameters =
Inner type representing semicolon-delimited parameters.
Attributor::Hash.of(key: String)
Class Method Summary collapse
-
.load(value, context = Attributor::DEFAULT_ROOT_CONTEXT, recurse: false, **options) ⇒ MediaTypeIdentifier
Parse a media type identifier from a String, or load it from a Hash or Model.
Instance Method Summary collapse
-
#+(extension) ⇒ MediaTypeIdentifier
Extend this type identifier by adding a suffix or parameter(s); return a new type identifier.
-
#=~(other) ⇒ Boolean
Determine whether this type is compatible with (i.e. is a subset or specialization of) another identifier.
-
#handler_name ⇒ String
Make an educated guess about the structured-syntax encoding implied by this media type, which in turn governs which handler should be used to parse and generate media of this type.
-
#match(other) ⇒ Boolean
Determine whether another identifier is compatible with (i.e. is a subset or specialization of) this identifier.
-
#to_s ⇒ String
(also: #to_str)
Canonicalized representation of the media type including all suffixes and parameters.
-
#without_parameters ⇒ MediaTypeIdentifier
If parameters are empty, return self; otherwise return a new object consisting of this type minus parameters.
Class Method Details
.load(value, context = Attributor::DEFAULT_ROOT_CONTEXT, recurse: false, **options) ⇒ MediaTypeIdentifier
Parse a media type identifier from a String, or load it from a Hash or Model. Assume malformed types represent a subtype, e.g. “nachos” => application/nachos“
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/praxis/media_type_identifier.rb', line 44 def self.load(value, context=Attributor::DEFAULT_ROOT_CONTEXT, recurse: false, **) case value when String return nil if value.blank? base, *parameters = value.split(PARAMETER_SEPARATOR) match = VALID_TYPE.match(base) obj = new if match parameters = parameters.each_with_object({}) do |e, h| k, v = e.split('=', 2) v = v[1...-1] if v =~ QUOTED_STRING h[k] = v end obj.type, obj.subtype, obj.suffix, obj.parameters = match[:type], match[:subtype], match[:suffix], parameters else obj.type = 'application' obj.subtype = base.split(WORD_SEPARATOR, 2).first obj.suffix = '' obj.parameters = {} end obj when nil return nil else super end end |
Instance Method Details
#+(extension) ⇒ MediaTypeIdentifier
Extend this type identifier by adding a suffix or parameter(s); return a new type identifier.
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
# File 'lib/praxis/media_type_identifier.rb', line 178 def +(extension) parameters = extension.split(PARAMETER_SEPARATOR) # remove useless initial '; ' parameters.shift if parameters.first && parameters.first.empty? raise ArgumentError, "Must pass a type identifier suffix and/or parameters" if parameters.empty? suffix = parameters.shift unless parameters.first.include?('=') # remove redundant '+' suffix = suffix[1..-1] if suffix && suffix[0] == '+' parameters = parameters.each_with_object({}) do |e, h| k, v = e.split('=', 2) v = v[1...-1] if v =~ /^".*"$/ h[k] = v h end parameters = Parameters.load(parameters) obj = self.class.new obj.type = self.type obj.subtype = self.subtype obj.suffix = suffix || self.suffix || '' obj.parameters = self.parameters.merge(parameters) obj end |
#=~(other) ⇒ Boolean
Determine whether this type is compatible with (i.e. is a subset or specialization of) another identifier. This is the same operation as #match, but with the position of the operands switched – analogous to “Regexp#match(String)” vs “String =~ Regexp”.
116 117 118 |
# File 'lib/praxis/media_type_identifier.rb', line 116 def =~(other) other.match(self) end |
#handler_name ⇒ String
Make an educated guess about the structured-syntax encoding implied by this media type, which in turn governs which handler should be used to parse and generate media of this type.
If a suffix e.g. “+json” is present, return it. Otherwise, return the subtype.
162 163 164 |
# File 'lib/praxis/media_type_identifier.rb', line 162 def handler_name suffix.empty? ? subtype : suffix end |
#match(other) ⇒ Boolean
Determine whether another identifier is compatible with (i.e. is a subset or specialization of) this identifier.
93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/praxis/media_type_identifier.rb', line 93 def match(other) other = MediaTypeIdentifier.load(other) return false if other.nil? return false unless type == other.type || type == WILDCARD return false unless subtype == other.subtype || subtype == WILDCARD return false unless suffix.empty? || suffix == other.suffix parameters.each_pair do |k, v| return false unless other.parameters[k] == v end true end |
#to_s ⇒ String Also known as: to_str
Returns canonicalized representation of the media type including all suffixes and parameters.
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/praxis/media_type_identifier.rb', line 121 def to_s # Our handcrafted media types consist of a rich chocolatey center s = "#{type}/#{subtype}" # coated in a hard candy shell s << '+' << suffix unless suffix.empty? # and encrusted with lexically-ordered sprinkles unless parameters.empty? s << '; ' s << parameters.keys.sort.map { |k| "#{k}=#{parameters[k]}" }.join('; ') end # May contain peanuts, tree nuts, soy, dairy, sawdust or glue s end |
#without_parameters ⇒ MediaTypeIdentifier
If parameters are empty, return self; otherwise return a new object consisting of this type minus parameters.
145 146 147 148 149 150 151 |
# File 'lib/praxis/media_type_identifier.rb', line 145 def without_parameters if self.parameters.empty? self else MediaTypeIdentifier.load(type: self.type, subtype: self.subtype, suffix: self.suffix) end end |