module Webmachine::Decision::Conneg

Contains methods concerned with Content Negotiation, specifically, choosing media types, encodings, character sets and languages.

Constants

CONNEG_REGEX

Matches acceptable items that include ‘q’ values

HAS_ENCODING

Public Instance Methods

choose_charset(provided, header) click to toggle source

Given the ‘Accept-Charset’ header and provided charsets, chooses an appropriate charset. @api private

# File lib/webmachine/decision/conneg.rb, line 44
def choose_charset(provided, header)
  if provided && !provided.empty?
    charsets = provided.map { |c| c.first }
    if charset = do_choose(charsets, header, HAS_ENCODING ? Encoding.default_external.name : kcode_charset)
      metadata[CHARSET] = charset
    end
  else
    true
  end
end
choose_encoding(provided, header) click to toggle source

Given the ‘Accept-Encoding’ header and provided encodings, chooses an appropriate encoding. @api private

# File lib/webmachine/decision/conneg.rb, line 33
def choose_encoding(provided, header)
  encodings = provided.keys
  if encoding = do_choose(encodings, header, IDENTITY)
    response.headers[CONTENT_ENCODING] = encoding unless encoding == IDENTITY
    metadata[CONTENT_ENCODING] = encoding
  end
end
choose_language(provided, header) click to toggle source

Given the ‘Accept-Language’ header and provided languages, chooses an appropriate language. @api private

# File lib/webmachine/decision/conneg.rb, line 58
def choose_language(provided, header)
  if provided && !provided.empty?
    requested = PriorityList.build(header.split(SPLIT_COMMA))
    star_priority = requested.priority_of(STAR)
    any_ok = star_priority && star_priority > 0.0
    accepted = requested.find do |priority, range|
      if priority == 0.0
        provided.delete_if { |tag| language_match(range, tag) }
        false
      else
        provided.any? { |tag| language_match(range, tag) }
      end
    end
    chosen = if accepted
      provided.find { |tag| language_match(accepted.last, tag) }
    elsif any_ok
      provided.first
    end
    if chosen
      metadata['Language'] = chosen
      response.headers['Content-Language'] = chosen
    end
  else
    true
  end
end
choose_media_type(provided, header) click to toggle source

Given the ‘Accept’ header and provided types, chooses an appropriate media type. @api private

# File lib/webmachine/decision/conneg.rb, line 16
def choose_media_type(provided, header)
  types = Array(header).map { |h| h.split(SPLIT_COMMA) }.flatten
  requested = MediaTypeList.build(types)
  provided = provided.map do |p| # normalize_provided
    MediaType.parse(p)
  end
  # choose_media_type1
  chosen = nil
  requested.each do |_, requested_type|
    break if chosen = media_match(requested_type, provided)
  end
  chosen
end
do_choose(choices, header, default) click to toggle source

Makes an conneg choice based what is accepted and what is provided. @api private

# File lib/webmachine/decision/conneg.rb, line 100
def do_choose(choices, header, default)
  choices = choices.dup.map { |s| s.downcase }
  accepted = PriorityList.build(header.split(SPLIT_COMMA))
  default_priority = accepted.priority_of(default)
  star_priority = accepted.priority_of(STAR)
  default_ok = (default_priority.nil? && star_priority != 0.0) || default_priority
  any_ok = star_priority && star_priority > 0.0
  chosen = accepted.find do |priority, acceptable|
    if priority == 0.0
      choices.delete(acceptable.downcase)
      false
    else
      choices.include?(acceptable.downcase)
    end
  end
  chosen&.last ||  # Use the matching one
    (any_ok && choices.first) || # Or first if "*"
    (default_ok && choices.include?(default) && default) # Or default
end
language_match(range, tag) click to toggle source

Implements language-negotation matching as described in RFC2616, section 14.14.

A language-range matches a language-tag if it exactly equals the tag, or if it exactly equals a prefix of the tag such that the first tag character following the prefix is “-”. @api private

# File lib/webmachine/decision/conneg.rb, line 93
def language_match(range, tag)
  range.downcase == tag.downcase || tag =~ /^#{Regexp.escape(range)}-/i
end

Private Instance Methods

kcode_charset() click to toggle source

Translate a KCODE value to a charset name

# File lib/webmachine/decision/conneg.rb, line 139
def kcode_charset
  case $KCODE
  when /^U/i
    'UTF-8'
  when /^S/i
    'Shift-JIS'
  when /^B/i
    'Big5'
  else # when /^A/i, nil
    'ASCII'
  end
end
media_match(requested, provided) click to toggle source

Matches the requested media type (with potential modifiers) against the provided types (with potential modifiers). @param [MediaType] requested the requested media type @param [Array<MediaType>] provided the provided media

types

@return [MediaType] the first media type that matches

# File lib/webmachine/decision/conneg.rb, line 131
def media_match(requested, provided)
  return provided.first if requested.matches_all?
  provided.find do |p|
    p.match?(requested)
  end
end