module Roda::RodaPlugins::TypeRouting

  1. lib/roda/plugins/type_routing.rb

This plugin makes it easier to to respond to specific request data types. User agents can request specific data types by either supplying an appropriate Accept header or by appending it as file extension to the path.


plugin :type_routing

route do |r|
  r.get 'a' do
    r.html{ "<h1>This is the HTML response</h1>" }
    r.json{ '{"json": "ok"}' }
    r.xml{ "<root>This is the XML response</root>" }
    "Unsupported data type"

This application will handle the following paths:


HTML response


JSON response


XML response


HTML, JSON, or XML response, depending on the Accept header

The response Content-Type header will be set to a suitable value when the r.html, r.json, or r.xml block is matched.

Note that if no match is found, code will continue to execute, which can result in unexpected behaviour. This should only happen if you do not handle all supported/configured types. If you want to simplify handling, you can just place the html handling after the other types, without using a separate block:

route do |r|
  r.get 'a' do
    r.json{ '{"json": "ok"}' }
    r.xml{ "<root>This is the XML response</root>" }

    "<h1>This is the HTML response</h1>"

This works correctly because Roda's default Content-Type is text/html. Note that if you use this approach, the type_routing plugin's :html content type will not be used for html responses, since you aren't using an r.html block. Instead, the Content-Type header will be set to Roda's default (which you can override via the default_headers plugin).

To match custom extensions, use the :types option:

plugin :type_routing, types: {
  yaml: 'application/x-yaml',
  js: 'application/javascript; charset=utf-8'

route do |r|
  r.get 'a' do
    r.yaml{ YAML.dump "YAML data" }
    r.js{ "JavaScript code" }
    # or:
    r.on_type(:js){ "JavaScript code" }
    "Unsupported data type"

Plugin options

The following plugin options are supported:


The default data type to assume if the client did not provide one. Defaults to :html.


Exclude one or more types from the default set (default set is :html, :xml, :json).


Mapping from a data type to its MIME-Type. Used both to match incoming requests and to provide Content-Type values. If the value is nil, no Content-Type will be set. The type may contain media type parameters, which will be sent to the client but ignored for request matching.


Whether to take the path extension into account. Default is true.


Whether to take the Accept header into account. Default is true.


Public Class

  1. configure


CONFIGURATION = { :mimes => { 'text/json' => :json, 'application/json' => :json, 'text/xml' => :xml, 'application/xml' => :xml, 'text/html' => :html, }.freeze, :types => { :json => 'application/json'.freeze, :xml => 'application/xml'.freeze, :html => 'text/html'.freeze, }.freeze, :use_extension => true, :use_header => true, :default_type => :html }.freeze  

Public Class methods

configure (app, opts = {})
[show source]
    # File lib/roda/plugins/type_routing.rb
106 def self.configure(app, opts = {})
107   config = (app.opts[:type_routing] || CONFIGURATION).dup
108   [:use_extension, :use_header, :default_type].each do |key|
109     config[key] = opts[key] if opts.has_key?(key)
110   end
112   types = config[:types] = config[:types].dup
113   mimes = config[:mimes] = config[:mimes].dup
115   Array(opts[:exclude]).each do |type|
116     types.delete(type)
117     mimes.reject!{|_, v| v == type}
118   end
120   if mapping = opts[:types]
121     types.merge!(mapping)
123     mapping.each do |k, v|
124       if v
125         mimes[v.split(';', 2).first] = k
126       end
127     end
128   end
130   types.freeze
131   mimes.freeze
133   type_keys = config[:types].keys
134   config[:extension_regexp] = /(.+?)\.(#{Regexp.union(})\z/
136   type_keys.each do |type|
137     app::RodaRequest.send(:define_method, type) do |&block|
138       on_type(type, &block)
139     end
140   end
142   app.opts[:type_routing] = config.freeze
143 end