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 request header or by appending it as file extension to the path.
Example:
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" end end
This application will handle the following paths:
| /a.html |
HTML response |
| /a.json |
JSON response |
| /a.xml |
XML response |
| /a |
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>" end end
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).
If the type routing is based on the Accept request header and not the file extension, then an appropriate Vary header will be set or appended to, so that HTTP caches do not serve the same result for requests with different Accept headers.
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" end end
Plugin options
The following plugin options are supported:
| :default_type |
The default data type to assume if the client did not provide one. Defaults to |
| :exclude |
Exclude one or more types from the default set (default set is :html, :xml, :json). |
| :types |
Mapping from a data type to its MIME-Type. Used both to match incoming requests and to provide |
| :use_extension |
Whether to take the path extension into account. Default is |
| :use_header |
Whether to take the |
Classes and Modules
Constants
| 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 | ||
| REQUEST_INSTANCE_VARIABLES | = | [:@requested_type, :@type_routing_extension].freeze |
Public Class methods
# File lib/roda/plugins/type_routing.rb 112 def self.configure(app, opts = {}) 113 config = (app.opts[:type_routing] || CONFIGURATION).dup 114 [:use_extension, :use_header, :default_type].each do |key| 115 config[key] = opts[key] if opts.has_key?(key) 116 end 117 118 types = config[:types] = config[:types].dup 119 mimes = config[:mimes] = config[:mimes].dup 120 121 Array(opts[:exclude]).each do |type| 122 types.delete(type) 123 mimes.reject!{|_, v| v == type} 124 end 125 126 if mapping = opts[:types] 127 types.merge!(mapping) 128 129 mapping.each do |k, v| 130 if v 131 mimes[v.split(';', 2).first] = k 132 end 133 end 134 end 135 136 types.freeze 137 mimes.freeze 138 139 type_keys = config[:types].keys 140 config[:extension_regexp] = /(.*?)\.(#{Regexp.union(type_keys.map(&:to_s))})\z/ 141 142 type_keys.each do |type| 143 app::RodaRequest.send(:define_method, type) do |&block| 144 on_type(type, &block) 145 end 146 app::RodaRequest.send(:alias_method, type, type) 147 end 148 149 app.opts[:type_routing] = config.freeze 150 end