Class: Rack::Conneg

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

Constant Summary collapse

VERSION =
'0.1.4'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(app) ⇒ Conneg

Returns a new instance of Conneg.



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
39
40
41
# File 'lib/rack/conneg.rb', line 12

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 negotiated?     ; not @rack_conneg_type.nil? ; 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 Attribute Details

#ignoresObject (readonly)

Returns the value of attribute ignores.



10
11
12
# File 'lib/rack/conneg.rb', line 10

def ignores
  @ignores
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)


95
96
97
# File 'lib/rack/conneg.rb', line 95

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

#call(env) ⇒ Object



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
84
85
86
# File 'lib/rack/conneg.rb', line 43

def call(env)
  extension = nil
  path_info = env['PATH_INFO']
  unless ignored?(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.



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

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.



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

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

#ignore_contents_of(path, prefix = '') ⇒ Object

Specifies a directory whose contents should be considered static and therefore ignored. Use with caution, since it acts recursively and can therefore build a pretty big ignore list, slowing down each request. It’s smart enough not to add anything that’s already been ignored, so if you ignore(‘/javascripts/’) before you ignore_contents_of(‘public’), public/javascripts/* won’t be added. In short, do all of your general ignore()ing before you ignore_contents_of().



118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/rack/conneg.rb', line 118

def ignore_contents_of(path, prefix = '')
  dir = Dir.open(path)
  dir.select { |f| f !~ /^\.\.?$/ }.each { |entry|
    entry_path = "#{prefix}/#{entry}"
    unless ignored?(entry_path)
      if ::File.directory?(::File.join(dir.path,entry))
        ignore_contents_of(::File.join(dir.path,entry),entry_path)
      else
        ignore(%r{^#{Regexp.escape(entry_path)}$})
      end
    end
  }
end

#ignored?(path_info) ⇒ Boolean

Determine if the given path matches any items in the ignore list.

Returns:

  • (Boolean)


89
90
91
# File 'lib/rack/conneg.rb', line 89

def ignored?(path_info)
  @ignores.find { |ignore| ignore.match(path_info) } ? true : false
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).



134
135
136
137
138
139
# File 'lib/rack/conneg.rb', line 134

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



146
147
148
149
150
151
152
# File 'lib/rack/conneg.rb', line 146

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