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
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“
43 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 |
# File 'lib/praxis/media_type_identifier.rb', line 43 def self.load(value, context=Attributor::DEFAULT_ROOT_CONTEXT, recurse: false, **) if value.is_a?(String) 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 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.
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
# File 'lib/praxis/media_type_identifier.rb', line 170 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”.
111 112 113 |
# File 'lib/praxis/media_type_identifier.rb', line 111 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.
154 155 156 |
# File 'lib/praxis/media_type_identifier.rb', line 154 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.
88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/praxis/media_type_identifier.rb', line 88 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
Returns canonicalized representation of the media type including all suffixes and parameters.
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/praxis/media_type_identifier.rb', line 116 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.
137 138 139 140 141 142 143 |
# File 'lib/praxis/media_type_identifier.rb', line 137 def without_parameters if self.parameters.empty? self else MediaTypeIdentifier.load(type: self.type, subtype: self.subtype, suffix: self.suffix) end end |