jsonatra
This is a very opinionated gem. Some of its strongly held views:
- Sinatra is the BEST.
- JSON is awesome!
- HTTP status codes are for transport!
To that end, this gem subclasses Sinatra::Base
and Sinatra::Response
adding
some helpful defaults to achieve the following goals:
- always respond with JSON, route blocks' return hashes are automatically converted
- all GET routes also respond to POST for large query values (
require 'jsonatra/break_rest'
) - accept form encoded OR JSON POST body parameters
- always supply CORS headers
- short-circuit OPTIONS requests
- application errors (i.e. param validation) should still 200, respond with error object
- 404s still 404 with a JSON body
- have handy error helpers
Settings
:arrayified_params
Accepts all the following formats for the given param names, always turning it into an
Array
in the params
hash.
set :arrayified_params, [:foo, :bar]
-
formencoded name
(ex: tags[]=foo&tags[]=bar => ['foo', 'bar'])
-
JSON POST body Array type
(ex: { "tags": ["foo", "bar"] } => ['foo', 'bar'])
-
formencoded comma-separated
(ex: tags=foo%2Cbar => ['foo', 'bar'])
-
JSON POST body comma-separated
(ex: { "tags": "foo,bar"] } => ['foo', 'bar'])
:camelcase_error_types
For whatever reason, you may want to have camelCase goin' on. Here's to working with legacy systems, eh?
enable :camelcase_error_types
Customizing Access Headers
Standard CORS headers not enough? Customize everything about them with:
class Example < Jsonatra::Base
def access_control_headers
if crazy_header_mode?
{
"these" => "headers",
"are" => "CRAZY"
}
else
Jsonatra::ACCESS_CONTROL_HEADERS
end
end
end
Example
example config.ru
:
require 'jsonatra'
class Foo < Jsonatra::Base
configure do
set :arrayified_params, [:foos]
end
get '/hi' do
{ hello: "there", foos: params[:foos] }
end
get '/error' do
param_error :foo, 'type', 'message' unless params[:foo]
{ you: "shouldn't", see: "this", unless: "foo" }
end
get '/halt_on_error' do
param_error :foo, 'type', 'message' unless params[:foo]
param_error :bar, 'type', 'message' unless params[:bar]
# since errors in the response always take precendence,
# halt if you need to just stop now
#
halt if response.error?
{ you: "shouldn't", see: "this", unless: "foo", and: "bar" }
end
end
map '/' do
run Foo
end
The above would respond like this:
< HTTP/1.1 200 OK
< Content-Type: application/json;charset=utf-8
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Methods: GET, POST
< Access-Control-Allow-Headers: Accept, Authorization, Content-Type, Origin
# ...
{"hello":"there","foos":["bars","bats"]}
{
"hello": "there",
"foos": [
"bars",
"bats"
]
}
{
"error": {
"type": "invalidInput",
"message": "invalid parameter or parameter value",
"parameters": {
"foo": [
{
"type": "type",
"message": "message"
}
]
}
}
}
{
"you": "shouldn't",
"see": "this",
"unless": "foo"
}
{
"error": {
"type": "invalidInput",
"message": "invalid parameter or parameter value",
"parameters": {
"foo": [
{
"type": "type",
"message": "message"
}
],
"bar": [
{
"type": "type",
"message": "message"
}
]
}
}
}