Class: Rack::Conneg

Inherits:
Object
  • Object
show all
Defined in:
lib/rack/conneg.rb

Constant Summary collapse

VERSION =
'0.1.3'

Instance Method Summary collapse

Constructor Details

#initialize(app) ⇒ Conneg

Returns a new instance of Conneg.



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/rack/conneg.rb', line 10

def initialize(app)
  @app = app
  @ignores = []
  @opts = { 
    :accept_all_extensions => false,
    :fallback => 'text/html'
  }
  @types = []

  @app.class.module_eval {
    def negotiated_ext  ; @rack_conneg_ext  ; end #:nodoc:#
    def negotiated_type ; @rack_conneg_type ; end #:nodoc:#
    def respond_to
      wants = { '*/*' => Proc.new { raise TypeError, "No handler for #{@rack_conneg_type}" } }
      def wants.method_missing(ext, *args, &handler)
        type = ext == :other ? '*/*' : Rack::Mime::MIME_TYPES[".#{ext.to_s}"]
        self[type] = handler
      end

      yield wants

      (wants[@rack_conneg_type] || wants['*/*']).call
    end
  }
  
  if block_given?
    yield self
  end
end

Instance Method Details

#accept_all_extensions?Boolean

Should content negotiation accept any file extention passed as part of the URI path, even if it’s not one of the registered provided types?

Returns:

  • (Boolean)


87
88
89
# File 'lib/rack/conneg.rb', line 87

def accept_all_extensions?
  @opts[:accept_all_extensions] ? true : false
end

#call(env) ⇒ Object



40
41
42
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/rack/conneg.rb', line 40

def call(env)
  extension = nil
  path_info = env['PATH_INFO']
  unless @ignores.find { |ignore| ignore.match(path_info) }
    # First, check to see if there's an explicit type requested
    # via the file extension
    mime_type = Rack::Mime.mime_type(::File.extname(path_info),nil)
    if mime_type
      env['PATH_INFO'] = path_info.sub!(/(\..+?)$/,'')
      extension = $1
      if !(accept_all_extensions? || @types.include?(mime_type))
        mime_type = nil
      end
    else
      # Create an array of types out of the HTTP_ACCEPT header, sorted
      # by q value and original order
      accept_types = env['HTTP_ACCEPT'].split(/,/)
      accept_types.each_with_index { |t,i|
        (accept_type,weight) = t.split(/;/)
        weight = weight.nil? ? 1.0 : weight.split(/\=/).last.to_f
        accept_types[i] = { :type => accept_type, :weight => weight, :order => i }
      }
      accept_types.sort! { |a,b| 
        ord = b[:weight] <=> a[:weight] 
        if ord == 0
          ord = a[:order] <=> b[:order]
        end
        ord
      }
      
      # Find the first item in accept_types that matches a registered
      # content type
      accept_types.find { |t|
        re = %r{^#{Regexp.escape(t[:type].gsub(/\*/,'.+'))}$}
        @types.find { |type| re.match(type) ? mime_type = type : nil }
      }
    end
    
    mime_type ||= fallback
    @app.instance_variable_set('@rack_conneg_ext',env['rack.conneg.ext'] = extension)
    @app.instance_variable_set('@rack_conneg_type',env['rack.conneg.type'] = mime_type)
  end
  @app.call(env) unless @app.nil?
end

#fallbackObject

What MIME type should be used as a fallback if negotiation fails? Defaults to ‘text/html’ since that’s what’s used to deliver most error message content.



93
94
95
# File 'lib/rack/conneg.rb', line 93

def fallback
  find_mime_type(@opts[:fallback])
end

#ignore(route) ⇒ Object

Specifies a route prefix or Regexp that should be ignored by the content negotiator. Use for static files or any other route that should be passed through unaltered.



99
100
101
102
# File 'lib/rack/conneg.rb', line 99

def ignore(route)
  route_re = route.kind_of?(Regexp) ? route : %r{^#{route}}
  @ignores << route_re
end

#provide(*args) ⇒ Object

Register one or more content types that the application offers. Can be a content type string, a file extension, or a symbol (e.g., ‘application/xml’, ‘.xml’, and :xml are all equivalent).



106
107
108
109
110
111
# File 'lib/rack/conneg.rb', line 106

def provide(*args)
  args.flatten.each { |type|
    mime_type = find_mime_type(type)
    @types << mime_type
  }
end

#set(key, value) ⇒ Object

Set a content negotiation option. Valid options are:

  • :accept_all_extensions - true if all file extensions should be mapped to MIME types whether or not their associated types are specifically provided

  • :fallback - a content type string, file extention, or symbol representing the MIME type to fall back on if negotiation fails



118
119
120
121
122
123
124
# File 'lib/rack/conneg.rb', line 118

def set(key, value)
  opt_key = key.to_sym
  if !@opts.include?(opt_key)
    raise IndexError, "Unknown option: #{key.to_s}"
  end
  @opts[opt_key] = value
end