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-wantedSetup
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
endClassic application:
require 'sinatra'
require 'sinatra/helpers/wanted'
helpers Sinatra::Helpers::WantedExamples
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 (aSymbol) to look up in Sinatra'sparams, or a value to process directly. -
type— type checking / coercion, applied through the first available method among[]andcall. -
getter— object retrieval, applied through the first available method among[],get,fetch,call, andnew. -
id— name of the parameter, used in raised exceptions; it defaults toparamwhenparamis aSymbol. -
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:
-
Retrieve the parameter value. If
paramis aSymbol, 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
paramis not aSymbol, it is used directly as the value. -
-
Check for a missing parameter (i.e.
nil). According to themissingkeyword:-
:raise— raiseWantedMissing -
:ignore— use thedefaultvalue and continue processing -
:return— return thedefaultvalue (processing stops here)
-
-
Check for a missing value (i.e.
NO_VALUE).- If the value is missing, use the
no_valuekeyword instead (defaults toNO_VALUE). - If the type checking / coercion is a dry-type,
NO_VALUEis replaced withDry::Types::Undefinedso dry can correctly process the undefined value.
- If the value is missing, use the
-
Perform type checking / coercion. This uses the first available method among
[]andcall. The easiest (but not required) approach is to use the dry-types library. -
Retrieve the real object with the getter (unless the value is already
nil). This uses the first available method among[],get,fetch,call, andnew. Getters are usually a Sequel model, an LDAP object model, a class, ...Retrieval is considered not found if the getter returns
nil(see alsoNotFoundListbelow). According to thenot_foundkeyword:-
:raise— raiseWantedNotFound -
:ignore— keep the value asniland continue processing -
:not_found— trigger Sinatra'snot_found(404) handler -
:pass— trigger Sinatra'spass
-
-
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 isnil -
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
endWhen 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::NoMatchingRowLicense
Released under the MIT License.