0.0
No release in over 3 years
Low commit activity in last 3 years
Ease processing of parameters in Sinatra framework. Integrates well with dry-types, sequel, ...
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Development

~> 13
~> 0

Runtime

 Project Readme

sinatra-wanted

Parameter processing for the Sinatra framework.

It lets you express, in a single declarative call, the common chain of operations applied to a request parameter:

  • required / optional handling (with default values)
  • type checking / coercion (best done with dry-types)
  • object retrieval (using Sequel, an LDAP object model, plain class instantiation, ...)
  • further processing (such as conversion) through a block

It integrates well with dry-types, sequel, and similar libraries, but depends on none of them.

Installation

Add it to your Gemfile:

gem 'sinatra-wanted'

or install it directly:

gem install sinatra-wanted

Setup

want, want!, and want? are defined as Sinatra helpers. Register them with helpers so they become available inside your routes.

Modular application:

require 'sinatra/base'
require 'sinatra/helpers/wanted'

class App < Sinatra::Base
    helpers Sinatra::Helpers::Wanted

    get '/vm/:vm' do
        vm = want! :vm, Types::VM::Name, VM
        # ...
    end
end

Classic application:

require 'sinatra'
require 'sinatra/helpers/wanted'

helpers Sinatra::Helpers::Wanted

Examples

Note that in a URL query, ?param has no value whereas ?param= has the empty string ("") as value.

# Retrieve a required VM model from Sequel
vm       = want! :vm,       Types::VM::Name, VM

# Retrieve a required public key (in rfc4716 or openssh format)
# and ensure the final result is in openssh format:
pubkey   = want! :pubkey,   Types::SSHPublicKey do |k|
                                SSHPublicKey.to_openssh(k)
                            end

# Get an optional parameter called locked which is a boolean,
# if the parameter is present but has no value use true
locked   = want? :locked,   Types::Params::Bool.default(true)

# Parameter is optional but in this case we require a default value
# which will be processed
type     = want  :type,     Types::VM::Stop, default: 'savestate'

The three methods

want, want!, and want? are the same method; they only differ in their default missing and not_found behaviour:

method missing not_found typical use
want :ignore :ignore optional parameter
want! :raise :raise required parameter / required object
want? :return :ignore optional, return default if absent

Any default can be overridden explicitly, e.g. want :id, missing: :raise, not_found: :pass.

want(param, type = nil, getter = nil,
     id:        nil,
     default:   nil,
     no_value:  NO_VALUE,
     missing:   :ignore,
     not_found: :ignore) { |value| ... }
  • param — the parameter key (a Symbol) to look up in Sinatra's params, or a value to process directly.
  • type — type checking / coercion, applied through the first available method among [] and call.
  • getter — object retrieval, applied through the first available method among [], get, fetch, call, and new.
  • id — name of the parameter, used in raised exceptions; it defaults to param when param is a Symbol.
  • default — value used when the parameter is missing.
  • no_value — value used when the parameter is present but has no associated value.

Parameter processing

Parameter retrieval is processed as follows; please read carefully:

  1. Retrieve the parameter value. If param is a Symbol, its key is looked up in Sinatra's parameter list and assigned a special value:

    • nil — key not found
    • NO_VALUE — key found but with no value (i.e. nil) associated

    If param is not a Symbol, it is used directly as the value.

  2. Check for a missing parameter (i.e. nil). According to the missing keyword:

    • :raise — raise WantedMissing
    • :ignore — use the default value and continue processing
    • :return — return the default value (processing stops here)
  3. Check for a missing value (i.e. NO_VALUE).

    • If the value is missing, use the no_value keyword instead (defaults to NO_VALUE).
    • If the type checking / coercion is a dry-type, NO_VALUE is replaced with Dry::Types::Undefined so dry can correctly process the undefined value.
  4. Perform type checking / coercion. This uses the first available method among [] and call. The easiest (but not required) approach is to use the dry-types library.

  5. Retrieve the real object with the getter (unless the value is already nil). This uses the first available method among [], get, fetch, call, and new. Getters are usually a Sequel model, an LDAP object model, a class, ...

    Retrieval is considered not found if the getter returns nil (see also NotFoundList below). According to the not_found keyword:

    • :raise — raise WantedNotFound
    • :ignore — keep the value as nil and continue processing
    • :not_found — trigger Sinatra's not_found (404) handler
    • :pass — trigger Sinatra's pass
  6. Apply block processing, if a block was given; its return value becomes the result.

Return values

  • an Object — the parameter value / retrieved object
  • nil — the parameter is missing, or its value is nil
  • NO_VALUE — the parameter is present but has no associated value

Exceptions

All exceptions inherit from WantedError (itself a StandardError), so a single rescue can catch them all. Each carries the parameter id and, where relevant, the offending value.

exception raised when
WantedMissing a required parameter is missing
WantedNotFound the getter did not find the object
WantedSyntaxError type checking / coercion failed (dry-types)
rescue Sinatra::Helpers::Wanted::WantedError => e
    halt 422, { error: e.message, parameter: e.id }.to_json
end

When dry-types is loaded, Dry::Types::CoercionError and Dry::Types::ConstraintError raised during coercion are automatically re-raised as WantedSyntaxError, preserving the original message.

Treating getter errors as "not found"

Some getters raise an exception instead of returning nil when nothing matches (for example a strict Sequel lookup). Register such exception classes in NotFoundList and they will be treated as "not found" (i.e. as a nil result) rather than propagating:

Sinatra::Helpers::Wanted::NotFoundList << Sequel::NoMatchingRow

License

Released under the MIT License.