Skip to content


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Latest commit


Git stats


Failed to load latest commit information.


Clear & easy parameter specification for Rails controllers & APIs.

Despecable allows you to easily declare your parameter types, allowed values, default values, and other requirements and automatically get parameter parsing and clean error handling. This is probably most useful for controllers responding to external API calls, but nothing's stopping you from using it to parse params from your own forms or links either.


Do this:

class EvilPlansController < ApplicationController
  include Despecable::ActionController

  def steal_the_moon
    despec! do
      datetime :when,       require: true
      integer  :minions,    in: 0..500, default: 0
      string   :weapons,    in: ["freeze_ray", "squid_launcher", "fart_gun"], array: true
      boolean  :recital     default: false

    # ...

Request this:


Get this:

#> params

{"when"=>#<DateTime: 2019-03-24T21:33:33-04:00 ((2458568j,5613s,921152000n),-14400s,2299161j)>,
 "weapons"=>["freeze_ray", "fart_gun"],


  • Self-document what you expect parameters to be for a given action. This is especially useful for API controllers.
  • Get in front of incorrect parameters with easy to understand messaging before any other code in the action is executed.
  • Convert params into their expected types
  • Specify required params
  • Complain about unrecognized params (optional)
  • Give default values
  • Validate allowable values
  • Easily generate documentation for your API endpoints

What's new (just the highlights)

v 1.0.0

  • Rails 5 support!
  • Custom parsers.
  • Better testing and documentation.

Basic Usage

Add despecable to your Gemfile, or gem install despecable.

The basic anatomy of using Despecable is to call despec (non-destructive) or despec! (destructive / in-place) in your controller action, preferably at or near the top, with a block containing param specs:

despec! do
  [type] [param_name] [options, ...]

For example:

despec! do
  string   :name,       required: true
  string   :gender,     in: ["female", "male", "other", "unspecified"]
  datetime :birthday
  float    :numbers,    array: true
  boolean  :send_email, default: false

(Note: using despec! instead of despec to modify params in place performs only a dup, not a deep_dup, so be careful with in-place modification of param values!)


Declare parameter types to have Despecable validate and convert the parameter values for you. Supported types are:

  • string
  • integer
  • float
  • boolean
  • datetime (RFC3339 format)
  • date (RFC3339 format)
  • file (form upload)
  • any
  • custom


Supported options include:

  • :required (boolean, default false) - If true, raise an error if a parameter was not present in the request.
  • :default (value) - If parameter is not present in the request, use this default value
  • :array (boolean, default false) - Convert the param into an array from either a comma-separated string or Rails parameter array
  • :in (Array or Range) - Raise an error if a value is not in the supplied array or range
  • :case (boolean; default true) - String param type only. Ignore case when matching param value to :in spec.
  • :length (Integer or Array/Range) - String param type only. Raise an error if param value is not within specified length

Syntax examples

integer :id,      required: true
string  :type,    default: "evil"
float   :alpha,   in: 0..1
string  :color,   in: ["red", "green", "blue"]
string  :token,   length: 16
string  :tag,     length: 4..32
string  :drink,   in: ["Coffee", "Tea"], case: false
float   :ratings, in: 0..5, array: true
custom( :birthday, default: ) do

The parentheses in the last example are not strictly necessary but relieve much confusion.


Errors that result from bad parameter values will throw one of the following subtypes of Descpable::DespecableError:

  • Despecable::InvalidParamterError: value could not be converted into specified type
  • Despecable::IncorrectParameterError: value was outside of allowed range
  • Despecable::MissingParameterError: value was required but not provided
  • Despecable::UnrecognizedParameterError: strict mode was enabled and a parameter not in the spec was provided

The error message will be a safe, informative message you can pass directly back to the client along with your favorite 400s status code. The error will also have an extra field, parameters, that contains the parameter names in violation. If you're writing a json API, for example, you can easily standardize your error responses like this, using ActionController's rescue_from method:

class WidgetsController < ActionController::Base
  rescue_from Despecable::DespecableError, with: :parameter_error


  def parameter_error(exception)
    render json: {error: exception.message, items: exception.parameters}, status: 400

An example error response:

  error: "Invalid value for param: 'active'. Require type: boolean (1/0 or true/false)",
  items: ["param1"]

Advanced Usage

Strict Mode

To raise Despecable::UnrecognizedParameterError if any parameters are supplied that are not specified, add strict: true to the despec method call:

despec!(strict: true) do

This is particularly useful for debugging API call issues such as mistyped parameter names.

Despecable supports calling despec as many times as you want. So if you have a parent class controller or before_action that you want to add a parameter validation to, you can! Just save strict mode to the last call, of course.

class ApiController < ActionController::Base
  before_action :get_api_key

  def get_api_key
    # Don't use strict: true here or no other params will be allowed!!
    despec! do
      string :api_keuy
    @api_key = ApiKey.find_by(key: params[:api_key]) or raise ApiError, "Invalid API Key"

class WidgetsController < ApiControler
  def show
    # strict: true is used on the final param-parsing block of any action
    despec!(strict: true) do
      integer :id, required: true

If you have no additional parameters for a specific action other than the ones from the parent controller or before_action, simple pass despec an empty block:

# No additional parameters.
depsec(strict: true) {}

Custom types

If the standard set of type parsers aren't good enough for ya, you can use custom with a provided block. The block should take arguments: name, value, options, where name is the name of the parameter, value is the value straight out of the params object, and options is a Hash that contains the above options such as in:, array:, etc. You can use any of these options, of course, but the standard options are still parsed by Despecable so you don't have to worry about reimplementing any of those features.

custom(:doubled) do |name, value, options|
  value.to_i * 2

Dynamic specs

The code inside a despec block does not have access to your controller methods or variables. What, you think this is Javascript? No, to use additional information, pass it in as an argument to despec that will be passed to the block:

formats = ['mp3', 'ogg', 'm4a']
despec!(formats, strict: true) do |fmt|
  string :format, in: fmt

Monkey Patching

Happy to have you hacking. The root of the magic happens in a Despecable::Spectator. This BasicObject subclass is responsible for evaluating the block you pass to the despec method. It dons a pair of Despecable::Spectacles to help it read and verify the parameters. So if you want to modify the parsing of DateTime from the default rfc3999 parsing to use, for example, the Chronic gem, you can monkey-patch:

class Despecable::Spectacle
  def datetime(value)
    Chronic.parse(value) or raise Despecable::InvalidParameterError, "Required: date string (e.g. 'tomorrow' or '2017-01-01')"

Note that these methods on Spectacle do not get called if the param is not present, so you don't have to worry about handling the nil case.

Human Patching

If you have more legit contributions to make to Despecable, submit a PR! Please keep your commits clean and rebased off of the current master branch, and message your commits with type(concern) [initials] message; e.g. feat(Chronic) [AS] Add Chronic parsing as a datetime option. type can be feat, fix, refactor, doc, or get creative.


Easy self-documenting parameter specifications for Rails API routes






No releases published


No packages published