Module: Sinatra::RespondTo

Defined in:
lib/sinatra/respond_to.rb

Defined Under Namespace

Modules: Helpers Classes: MissingTemplate

Constant Summary collapse

SUPPORTED_ACCEPT_HEADERS =

Define all MIME types you want to support here. This conversion table will be used for auto-negotiation with browser in sinatra when no ‘format’ parameter is specified.

{
  :xml => [
    'text/xml',
    'application/xml'
  ],
  :html => [
    'text/html',
    'application/xhtml+xml'
  ],
  :json => [
    'application/json'
  ]
}

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.registered(app) ⇒ Object



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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/sinatra/respond_to.rb', line 59

def self.registered(app)

  app.helpers RespondTo::Helpers

  app.before do

    # Skip development error image and static content
    next if self.class.development? && request.path_info =~ %r{/__sinatra__/.*?.png}
    next if options.static? && options.public? && (request.get? || request.head?) && static_file?(request.path_info)

    # Remove extension from URI
    # Extension will be available as a 'extension' method (extension=='txt')
   
    extension request.path_info.match(/\.([^\.\/]+)$/).to_a.first

    # If ?format= is present, ignore all Accept negotiations because
    # we are not dealing with browser
    if request.params.has_key? 'format'
      format params['format'].to_sym
    end
    
    # Let's make a little exception here to handle
    # /api/instance_states[.gv/.png] calls
    if extension.eql?('gv')
      format :gv
    elsif extension.eql?('png')
      format :png
    end

    # Get Rack::Accept::Response object and find best possible
    # mime type to output.
    # This negotiation works fine with latest rest-client gem:
    #
    # RestClient.get 'http://localhost:3001/api', {:accept => :json } =>
    # 'application/json'
    # RestClient.get 'http://localhost:3001/api', {:accept => :xml } =>
    # 'application/xml'
    #
    # Also browsers like Firefox (3.6.x) and Chromium reporting
    # 'application/xml+xhtml' which is recognized as :html reponse
    # In browser you can force output using ?format=[format] parameter.

    rack_accept = env['rack-accept.request']

    if rack_accept.media_type.to_s.strip.eql?('Accept:')
      format :xml
    elsif is_chrome?
      format :html
    else
      format lookup_format_from_mime(rack_accept.best_media_type(accept_to_array))
    end

  end

  app.class_eval do

    # Simple helper to detect Chrome based browsers
    # which have screwed up they Accept headers.
    # Set HTML as default output format here
    def is_chrome?
      true if env['HTTP_USER_AGENT'] =~ /Chrome/
    end

    # This code was copied from respond_to plugin
    # http://github.com/cehoffman/sinatra-respond_to
    # MIT License
    alias :render_without_format :render
    def render(*args, &block)
      assumed_layout = args[1] == :layout
      args[1] = "#{args[1]}.#{format}".to_sym if args[1].is_a?(::Symbol)
      render_without_format *args, &block
    rescue Errno::ENOENT => e
      raise MissingTemplate, "#{args[1]}.#{args[0]}" unless assumed_layout
      raise e
    end
    private :render
  end

  # This code was copied from respond_to plugin
  # http://github.com/cehoffman/sinatra-respond_to
  app.configure :development do |dev|
    dev.error MissingTemplate do
      content_type :html, :charset => 'utf-8'
      response.status = request.env['sinatra.error'].code

      engine = request.env['sinatra.error'].message.split('.').last
      engine = 'haml' unless ['haml', 'builder', 'erb'].include? engine

      path = File.basename(request.path_info)
      path = "root" if path.nil? || path.empty?

      format = engine == 'builder' ? 'xml' : 'html'

      layout = case engine
               when 'haml' then "!!!\n%html\n  %body= yield"
               when 'erb' then "<html>\n  <body>\n    <%= yield %>\n  </body>\n</html>"
               end

      layout = "<small>app.#{format}.#{engine}</small>\n<pre>#{escape_html(layout)}</pre>"

      (<<-HTML).gsub(/^ {10}/, '')
      <!DOCTYPE html>
      <html>
      <head>
        <style type="text/css">
        body { text-align:center;font-family:helvetica,arial;font-size:22px;
          color:#888;margin:20px}
        #c {margin:0 auto;width:500px;text-align:left;}
        small {float:right;clear:both;}
        pre {clear:both;text-align:left;font-size:70%;width:500px;margin:0 auto;}
        </style>
      </head>
      <body>
        <h2>Sinatra can't find #{request.env['sinatra.error'].message}</h2>
        <img src='/__sinatra__/500.png'>
        <pre>#{request.env['sinatra.error'].backtrace.join("\n")}</pre>
        <div id="c">
          <small>application.rb</small>
          <pre>#{request.request_method.downcase} '#{request.path_info}' do\n  respond_to do |wants|\n    wants.#{format} { #{engine} :#{path} }\n  end\nend</pre>
        </div>
      </body>
      </html>
      HTML
    end

  end
end

Instance Method Details

#accept_to_arrayObject

We need to pass array of available response types to best_media_type method



45
46
47
48
49
# File 'lib/sinatra/respond_to.rb', line 45

def accept_to_array
  SUPPORTED_ACCEPT_HEADERS.keys.collect do |key|
    SUPPORTED_ACCEPT_HEADERS[key]
  end.flatten
end

#lookup_format_from_mime(mime) ⇒ Object

Then, when we get best media type for response, we need to know which format to choose



53
54
55
56
57
# File 'lib/sinatra/respond_to.rb', line 53

def lookup_format_from_mime(mime)
  SUPPORTED_ACCEPT_HEADERS.keys.each do |format|
    return format if SUPPORTED_ACCEPT_HEADERS[format].include?(mime)
  end
end