Class: Rack::Conneg

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

Constant Summary collapse

VERSION =
'0.1.6'

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)


97
98
99
# File 'lib/rack/conneg.rb', line 97

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
87
88
# 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
      if env['HTTP_ACCEPT']
        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
    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.



103
104
105
# File 'lib/rack/conneg.rb', line 103

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.



109
110
111
112
# File 'lib/rack/conneg.rb', line 109

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().



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

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)


91
92
93
# File 'lib/rack/conneg.rb', line 91

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).



136
137
138
139
140
141
# File 'lib/rack/conneg.rb', line 136

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



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

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