123456789_123456789_123456789_123456789_123456789_

Module: Sinatra::RespondWith

Relationships & Source Files
Namespace Children
Modules:
Classes:
Defined in: sinatra-contrib/lib/sinatra/respond_with.rb

Overview

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:

  • If a template name is given, search for a template called name.format.engine (+index.xml.nokogiri+ in the above example).
  • If a template name is given, search for a templated called name.engine for engines known to result in the requested format (+index.haml+).
  • If a file extension associated with the mime type is known to ::Sinatra, and the object responds to to_extension, call that method and use the result (+data.to_json+).

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.

Class Method Summary

Instance Method Summary

Class Method Details

.engines (private)

[ GitHub ]

  
# File 'sinatra-contrib/lib/sinatra/respond_with.rb', line 250

def self.engines
  engines = {
    css: %i[sass scss],
    xml: %i[builder nokogiri],
    html: %i[erb erubi haml hamlit slim liquid
             mab markdown rdoc],
    all: (Sinatra::Templates.instance_methods.map(&:to_sym) +
      [:mab] - %i[find_template markaby]),
    json: [:yajl]
  }
  engines.default = []
  defined?(JRUBY_VERSION) ? jrubyify(engines) : engines
end

.jrubyify(engs) (private)

[ GitHub ]

  
# File 'sinatra-contrib/lib/sinatra/respond_with.rb', line 241

def self.jrubyify(engs)
  not_supported = [:markdown]
  engs.each_key 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) (private)

[ GitHub ]

  
# File 'sinatra-contrib/lib/sinatra/respond_with.rb', line 264

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

Instance Method Details

#compile!(verb, path, block, **options) (private)

[ GitHub ]

  
# File 'sinatra-contrib/lib/sinatra/respond_with.rb', line 236

def compile!(verb, path, block, **options)
  options[:provides] ||= respond_to if respond_to
  super
end

#mime_type

[ GitHub ]

  
# File 'sinatra-contrib/lib/sinatra/respond_with.rb', line 208

def mime_type(*)
  result = super
  remap_extensions
  result
end

#remap_extensions

[ GitHub ]

  
# File 'sinatra-contrib/lib/sinatra/respond_with.rb', line 201

def remap_extensions
  ext_map.clear
  Rack::Mime::MIME_TYPES.each { |e, t| ext_map[t] << e[1..].to_sym }
  ext_map['text/javascript'] << 'js'
  ext_map['text/xml'] << 'xml'
end

#rendering_method(engine)

[ GitHub ]

  
# File 'sinatra-contrib/lib/sinatra/respond_with.rb', line 227

def rendering_method(engine)
  return [engine] if Sinatra::Templates.method_defined? engine
  return [:mab] if engine.to_sym == :markaby

  %i[render engine]
end

#respond_to(*formats)

[ GitHub ]

  
# File 'sinatra-contrib/lib/sinatra/respond_with.rb', line 214

def respond_to(*formats)
  @respond_to ||= nil

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