module Sinatra::RespondWith

Sinatra::RespondWith

These extensions let Sinatra automatically choose what template to render or action to perform depending on the request's Accept header.

Example:

# Without Sinatra::RespondWith
get '/' do
  data = { :name => 'example' }
  request.accept.each do |type|
    case type.to_s
    when 'text/html'
      halt haml(:index, :locals => data)
    when 'text/json'
      halt data.to_json
    when 'application/atom+xml'
      halt nokogiri(:'index.atom', :locals => data)
    when 'application/xml', 'text/xml'
      halt nokogiri(:'index.xml', :locals => data)
    when 'text/plain'
      halt 'just an example'
    end
  end
  error 406
end

# With Sinatra::RespondWith
get '/' do
  respond_with :index, :name => 'example' do |f|
    f.txt { 'just an example' }
  end
end

Both helper methods respond_to and respond_with let you define custom handlers like the one above for text/plain. respond_with additionally takes a template name and/or an object to offer the following default behavior:

Security

Since methods are triggered based on client input, this can lead to security issues (but not as severe as those might appear in the first place: keep in mind that only known file extensions are used). You should limit the possible formats you serve.

This is possible with the provides condition:

get '/', :provides => [:html, :json, :xml, :atom] do
  respond_with :index, :name => 'example'
end

However, since you have to set provides for every route, this extension adds an app global (class method) `respond_to`, that lets you define content types for all routes:

respond_to :html, :json, :xml, :atom
get('/a') { respond_with :index, :name => 'a' }
get('/b') { respond_with :index, :name => 'b' }

Custom Types

Use the on method for defining actions for custom types:

get '/' do
  respond_to do |f|
    f.xml { nokogiri :index }
    f.on('application/custom') { custom_action }
    f.on('text/*') { data.to_s }
    f.on('*/*') { "matches everything" }
  end
end

Definition order does not matter.

Private Class Methods

engines() click to toggle source
# File lib/sinatra/respond_with.rb, line 238
def self.engines
  engines = {
    :css  => [:less,  :sass, :scss],
    :xml  => [:builder, :nokogiri],
    :js   => [:coffee],
    :html => [:erb, :erubi, :erubis, :haml, :halmit, :slim, :liquid, :radius,
      :mab, :markdown, :textile, :rdoc],
    :all =>  (Sinatra::Templates.instance_methods.map(&:to_sym) +
      [:mab] - [:find_template, :markaby]),
    :json => [:yajl],
  }
  engines.default = []
  (defined? JRUBY_VERSION) ? jrubyify(engines) : engines
end
jrubyify(engs) click to toggle source
# File lib/sinatra/respond_with.rb, line 229
def self.jrubyify(engs)
  not_supported = [:markdown]
  engs.keys.each do |key|
    engs[key].collect! { |eng| (eng == :yajl) ? :json_pure : eng }
    engs[key].delete_if { |eng| not_supported.include?(eng) }
  end
  engs
end
registered(base) click to toggle source
# File lib/sinatra/respond_with.rb, line 253
def self.registered(base)
  base.set :ext_map, Hash.new { |h,k| h[k] = [] }
  base.set :template_engines, engines
  base.remap_extensions
  base.helpers Helpers
end

Public Instance Methods

mime_type(*) click to toggle source
Calls superclass method
# File lib/sinatra/respond_with.rb, line 197
def mime_type(*)
  result = super
  remap_extensions
  result
end
remap_extensions() click to toggle source
# File lib/sinatra/respond_with.rb, line 190
def remap_extensions
  ext_map.clear
  Rack::Mime::MIME_TYPES.each { |e,t| ext_map[t] << e[1..-1].to_sym }
  ext_map['text/javascript'] << 'js'
  ext_map['text/xml'] << 'xml'
end
rendering_method(engine) click to toggle source
# File lib/sinatra/respond_with.rb, line 216
def rendering_method(engine)
  return [engine] if Sinatra::Templates.method_defined? engine
  return [:mab] if engine.to_sym == :markaby
  [:render, :engine]
end
respond_to(*formats) click to toggle source
# File lib/sinatra/respond_with.rb, line 203
def respond_to(*formats)
  @respond_to ||= nil

  if formats.any?
    @respond_to ||= []
    @respond_to.concat formats
  elsif @respond_to.nil? and superclass.respond_to? :respond_to
    superclass.respond_to
  else
    @respond_to
  end
end

Private Instance Methods

compile!(verb, path, block, options = {}) click to toggle source
Calls superclass method
# File lib/sinatra/respond_with.rb, line 224
def compile!(verb, path, block, options = {})
  options[:provides] ||= respond_to if respond_to
  super
end