Project

jsonatra

0.01
No commit activity in last 3 years
No release in over 3 years
JSON API extension for Sinatra
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Runtime

 Project Readme

jsonatra

Build Status

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"
        }
      ]
    }
  }
}