Class: Doze::Negotiator

Inherits:
Object
  • Object
show all
Defined in:
lib/doze/negotiator.rb

Overview

A Negotiator handles content negotiation on behalf of a client request. It will choose the entity it prefers from a list of options offered to it. You can ask it to give you a quality value, or to choose from a list of options. It’ll choose from media_types, languages, or combinations of the two.

Instance Method Summary collapse

Constructor Details

#initialize(request, ignore_unacceptable_accepts = false) ⇒ Negotiator

Returns a new instance of Negotiator.



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/doze/negotiator.rb', line 6

def initialize(request, ignore_unacceptable_accepts=false)
  @ignore_unacceptable_accepts = ignore_unacceptable_accepts

  @media_type_criterea = if (ext = request.extension)
    # if the request extension requests a specific media type, this overrides any Accept header and is
    # interpreted as a demand for this media type and this one only.
    media_type = Doze::MediaType::BY_EXTENSION[ext]
    if media_type
      [[media_type.name, 2, 1.0]]
    else
      # if there's a request extension but we can't find a media type for it, we interpret this as an
      # 'impossible demand' that will match nothing
      []
    end

  elsif (accept_header = request.env['HTTP_ACCEPT'])
    parse_accept_header(accept_header) {|range| matcher_from_media_range(range)}.sort_by {|matcher,specificity,q| -specificity}

  else
    # No Accept header - anything will do
    [[Object, 0, 1.0]]
  end

  accept_language_header = request.env['HTTP_ACCEPT_LANGUAGE']
  @language_criterea = if accept_language_header
    parse_accept_header(accept_language_header) {|range| matcher_from_language_range(range)}.sort_by {|matcher,specificity,q| -specificity} + [[nil, 0, 0.001]]
    # When language_criterea are given, we allow a low-specificity tiny-but-nonzero match for a language of 'nil', ie entities with no
    # language, even though the accept header may appear to require a particular language. Because it makes no sense to apply Accept-Language
    # criterea to resources whose representations aren't language-specific.
  else
    [[Object, 0, 1.0]]
  end
end

Instance Method Details

#choose_entity(entities) ⇒ Object

Choose from a list of Doze::Entity



54
55
56
# File 'lib/doze/negotiator.rb', line 54

def choose_entity(entities)
  max_by_non_zero(entities) {|a| quality(a.media_type, a.language)}
end

#language_quality(language) ⇒ Object



44
45
46
# File 'lib/doze/negotiator.rb', line 44

def language_quality(language)
  @language_criterea.each {|matcher,specificity,quality| return quality if matcher === language}; 0
end

#media_type_quality(media_type) ⇒ Object



40
41
42
# File 'lib/doze/negotiator.rb', line 40

def media_type_quality(media_type)
  @media_type_criterea.each {|matcher,specificity,quality| return quality if media_type.matches_names.any? {|name| matcher === name}}; 0
end

#quality(media_type, language) ⇒ Object

Combined quality value for a (media_type, language) pair



49
50
51
# File 'lib/doze/negotiator.rb', line 49

def quality(media_type, language)
  media_type_quality(media_type)*language_quality(language)
end