[DRE-commits] [ruby-grape] 01/05: Imported Upstream version 0.12.0

Hleb Valoshka tsfgnu-guest at moszumanska.debian.org
Tue Jul 7 12:46:55 UTC 2015


This is an automated email from the git hooks/post-receive script.

tsfgnu-guest pushed a commit to branch master
in repository ruby-grape.

commit de8bc6c0ab93f4d5b3cbe01a552bd47aa319cf95
Author: Hleb Valoshka <375gnu at gmail.com>
Date:   Thu Jul 2 18:39:16 2015 +0300

    Imported Upstream version 0.12.0
---
 .gitignore                                         |   46 +
 .rspec                                             |    2 +
 .rubocop.yml                                       |    7 +
 .rubocop_todo.yml                                  |   84 +
 .travis.yml                                        |   20 +
 .yardopts                                          |    2 +
 Appraisals                                         |    7 +
 CHANGELOG.md                                       |  408 +++
 CONTRIBUTING.md                                    |  125 +
 Gemfile                                            |   10 +
 Guardfile                                          |   10 +
 LICENSE                                            |   20 +
 README.md                                          | 2561 +++++++++++++++++
 RELEASING.md                                       |  105 +
 Rakefile                                           |   68 +
 UPGRADING.md                                       |  550 ++++
 gemfiles/rails_3.gemfile                           |   14 +
 gemfiles/rails_4.gemfile                           |   14 +
 grape.gemspec                                      |   43 +
 grape.png                                          |  Bin 0 -> 4272 bytes
 lib/backports/active_support/deep_dup.rb           |   49 +
 lib/backports/active_support/duplicable.rb         |   88 +
 lib/grape.rb                                       |  176 ++
 lib/grape/api.rb                                   |  194 ++
 lib/grape/api/helpers.rb                           |    7 +
 lib/grape/cookies.rb                               |   39 +
 lib/grape/dsl/api.rb                               |   19 +
 lib/grape/dsl/callbacks.rb                         |   29 +
 lib/grape/dsl/configuration.rb                     |   71 +
 lib/grape/dsl/helpers.rb                           |   85 +
 lib/grape/dsl/inside_route.rb                      |  269 ++
 lib/grape/dsl/middleware.rb                        |   33 +
 lib/grape/dsl/parameters.rb                        |  113 +
 lib/grape/dsl/request_response.rb                  |  155 ++
 lib/grape/dsl/routing.rb                           |  186 ++
 lib/grape/dsl/settings.rb                          |  110 +
 lib/grape/dsl/validations.rb                       |   37 +
 lib/grape/endpoint.rb                              |  330 +++
 lib/grape/error_formatter/base.rb                  |   59 +
 lib/grape/error_formatter/json.rb                  |   17 +
 lib/grape/error_formatter/txt.rb                   |   18 +
 lib/grape/error_formatter/xml.rb                   |   17 +
 lib/grape/exceptions/base.rb                       |   72 +
 lib/grape/exceptions/incompatible_option_values.rb |   10 +
 lib/grape/exceptions/invalid_accept_header.rb      |   10 +
 lib/grape/exceptions/invalid_formatter.rb          |   10 +
 lib/grape/exceptions/invalid_message_body.rb       |   10 +
 lib/grape/exceptions/invalid_versioner_option.rb   |   10 +
 .../invalid_with_option_for_represent.rb           |   10 +
 lib/grape/exceptions/missing_group_type.rb         |   10 +
 lib/grape/exceptions/missing_mime_type.rb          |   10 +
 lib/grape/exceptions/missing_option.rb             |   10 +
 lib/grape/exceptions/missing_vendor_option.rb      |   10 +
 lib/grape/exceptions/unknown_options.rb            |   10 +
 lib/grape/exceptions/unknown_validator.rb          |   10 +
 lib/grape/exceptions/unsupported_group_type.rb     |   10 +
 lib/grape/exceptions/validation.rb                 |   26 +
 lib/grape/exceptions/validation_errors.rb          |   56 +
 lib/grape/formatter/base.rb                        |   31 +
 lib/grape/formatter/json.rb                        |   12 +
 lib/grape/formatter/serializable_hash.rb           |   35 +
 lib/grape/formatter/txt.rb                         |   11 +
 lib/grape/formatter/xml.rb                         |   12 +
 lib/grape/http/headers.rb                          |   27 +
 lib/grape/http/request.rb                          |   27 +
 lib/grape/locale/en.yml                            |   47 +
 lib/grape/middleware/auth/base.rb                  |   46 +
 lib/grape/middleware/auth/dsl.rb                   |   40 +
 lib/grape/middleware/auth/strategies.rb            |   24 +
 lib/grape/middleware/auth/strategy_info.rb         |   13 +
 lib/grape/middleware/base.rb                       |   62 +
 lib/grape/middleware/error.rb                      |   92 +
 lib/grape/middleware/filter.rb                     |   17 +
 lib/grape/middleware/formatter.rb                  |  156 ++
 lib/grape/middleware/globals.rb                    |   14 +
 lib/grape/middleware/versioner.rb                  |   32 +
 .../middleware/versioner/accept_version_header.rb  |   67 +
 lib/grape/middleware/versioner/header.rb           |  135 +
 lib/grape/middleware/versioner/param.rb            |   42 +
 lib/grape/middleware/versioner/path.rb             |   52 +
 lib/grape/namespace.rb                             |   24 +
 lib/grape/parser/base.rb                           |   29 +
 lib/grape/parser/json.rb                           |   14 +
 lib/grape/parser/xml.rb                            |   14 +
 lib/grape/path.rb                                  |   76 +
 lib/grape/presenters/presenter.rb                  |    9 +
 lib/grape/route.rb                                 |   27 +
 lib/grape/util/content_types.rb                    |   26 +
 lib/grape/util/inheritable_setting.rb              |   74 +
 lib/grape/util/inheritable_values.rb               |   49 +
 lib/grape/util/stackable_values.rb                 |   52 +
 lib/grape/util/strict_hash_configuration.rb        |  106 +
 lib/grape/validations.rb                           |   13 +
 lib/grape/validations/attributes_iterator.rb       |   21 +
 lib/grape/validations/params_scope.rb              |  192 ++
 lib/grape/validations/validators/all_or_none.rb    |   20 +
 lib/grape/validations/validators/allow_blank.rb    |   30 +
 .../validations/validators/at_least_one_of.rb      |   20 +
 lib/grape/validations/validators/base.rb           |   37 +
 lib/grape/validations/validators/coerce.rb         |   67 +
 lib/grape/validations/validators/default.rb        |   25 +
 lib/grape/validations/validators/exactly_one_of.rb |   20 +
 .../validations/validators/multiple_params_base.rb |   27 +
 .../validations/validators/mutual_exclusion.rb     |   25 +
 lib/grape/validations/validators/presence.rb       |   16 +
 lib/grape/validations/validators/regexp.rb         |   12 +
 lib/grape/validations/validators/values.rb         |   26 +
 lib/grape/version.rb                               |    3 +
 metadata.yml                                       |  569 ++++
 spec/grape/api/custom_validations_spec.rb          |   47 +
 spec/grape/api/deeply_included_options_spec.rb     |   56 +
 spec/grape/api/nested_helpers_spec.rb              |   48 +
 spec/grape/api/shared_helpers_spec.rb              |   36 +
 spec/grape/api_spec.rb                             | 2907 ++++++++++++++++++++
 spec/grape/dsl/callbacks_spec.rb                   |   44 +
 spec/grape/dsl/configuration_spec.rb               |   76 +
 spec/grape/dsl/helpers_spec.rb                     |   47 +
 spec/grape/dsl/inside_route_spec.rb                |  317 +++
 spec/grape/dsl/middleware_spec.rb                  |   31 +
 spec/grape/dsl/parameters_spec.rb                  |  164 ++
 spec/grape/dsl/request_response_spec.rb            |  177 ++
 spec/grape/dsl/routing_spec.rb                     |  255 ++
 spec/grape/dsl/settings_spec.rb                    |  219 ++
 spec/grape/dsl/validations_spec.rb                 |   52 +
 spec/grape/endpoint_spec.rb                        |  944 +++++++
 spec/grape/entity_spec.rb                          |  329 +++
 spec/grape/exceptions/body_parse_errors_spec.rb    |  105 +
 .../grape/exceptions/invalid_accept_header_spec.rb |  330 +++
 spec/grape/exceptions/invalid_formatter_spec.rb    |   16 +
 .../exceptions/invalid_versioner_option_spec.rb    |   16 +
 spec/grape/exceptions/missing_mime_type_spec.rb    |   17 +
 spec/grape/exceptions/missing_option_spec.rb       |   16 +
 spec/grape/exceptions/unknown_options_spec.rb      |   16 +
 spec/grape/exceptions/unknown_validator_spec.rb    |   16 +
 spec/grape/exceptions/validation_errors_spec.rb    |   49 +
 spec/grape/integration/rack_spec.rb                |   33 +
 spec/grape/loading_spec.rb                         |   44 +
 spec/grape/middleware/auth/base_spec.rb            |   30 +
 spec/grape/middleware/auth/dsl_spec.rb             |   51 +
 spec/grape/middleware/auth/strategies_spec.rb      |   80 +
 spec/grape/middleware/base_spec.rb                 |   78 +
 spec/grape/middleware/error_spec.rb                |   77 +
 spec/grape/middleware/exception_spec.rb            |  195 ++
 spec/grape/middleware/formatter_spec.rb            |  256 ++
 spec/grape/middleware/globals_spec.rb              |   27 +
 .../versioner/accept_version_header_spec.rb        |  121 +
 spec/grape/middleware/versioner/header_spec.rb     |  280 ++
 spec/grape/middleware/versioner/param_spec.rb      |   56 +
 spec/grape/middleware/versioner/path_spec.rb       |   43 +
 spec/grape/middleware/versioner_spec.rb            |   21 +
 spec/grape/path_spec.rb                            |  252 ++
 spec/grape/presenters/presenter_spec.rb            |   70 +
 spec/grape/util/inheritable_setting_spec.rb        |  217 ++
 spec/grape/util/inheritable_values_spec.rb         |   63 +
 spec/grape/util/stackable_values_spec.rb           |  115 +
 spec/grape/util/strict_hash_configuration_spec.rb  |   38 +
 spec/grape/validations/attributes_iterator_spec.rb |    4 +
 spec/grape/validations/params_scope_spec.rb        |  245 ++
 .../validations/validators/all_or_none_spec.rb     |   60 +
 .../validations/validators/allow_blank_spec.rb     |  282 ++
 .../validations/validators/at_least_one_of_spec.rb |   67 +
 spec/grape/validations/validators/coerce_spec.rb   |  235 ++
 spec/grape/validations/validators/default_spec.rb  |  211 ++
 .../validations/validators/exactly_one_of_spec.rb  |   75 +
 .../validators/mutual_exclusion_spec.rb            |   63 +
 spec/grape/validations/validators/presence_spec.rb |  217 ++
 spec/grape/validations/validators/regexp_spec.rb   |   43 +
 spec/grape/validations/validators/values_spec.rb   |  268 ++
 spec/grape/validations/validators/zh-CN.yml        |   10 +
 spec/grape/validations_spec.rb                     | 1378 ++++++++++
 spec/shared/versioning_examples.rb                 |  152 +
 spec/spec_helper.rb                                |   29 +
 spec/support/basic_auth_encode_helpers.rb          |    3 +
 spec/support/content_type_helpers.rb               |   11 +
 spec/support/endpoint_faker.rb                     |   23 +
 spec/support/file_streamer.rb                      |   11 +
 spec/support/versioned_helpers.rb                  |   50 +
 177 files changed, 21012 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..af485e0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,46 @@
+## MAC OS
+.DS_Store
+.com.apple.timemachine.supported
+
+## TEXTMATE
+*.tmproj
+tmtags
+
+## EMACS
+*~
+\#*
+.\#*
+
+## REDCAR
+.redcar
+
+## VIM
+*.swp
+*.swo
+
+## RUBYMINE
+.idea
+
+## PROJECT::GENERAL
+coverage
+doc
+pkg
+.rvmrc
+.bundle
+.yardoc/*
+dist
+Gemfile.lock
+gemfiles/*.lock
+tmp
+
+## Rubinius
+.rbx
+
+## Bundler binstubs
+bin
+
+## ripper-tags and gem-ctags
+tags
+
+## PROJECT::SPECIFIC
+.project
diff --git a/.rspec b/.rspec
new file mode 100644
index 0000000..d3ad5a9
--- /dev/null
+++ b/.rspec
@@ -0,0 +1,2 @@
+--color
+--format=documentation
diff --git a/.rubocop.yml b/.rubocop.yml
new file mode 100644
index 0000000..ba51557
--- /dev/null
+++ b/.rubocop.yml
@@ -0,0 +1,7 @@
+AllCops:
+  Exclude:
+    - vendor/**/*
+    - bin/**/*
+    - gemfiles/**/*
+
+inherit_from: .rubocop_todo.yml
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
new file mode 100644
index 0000000..6bc31a3
--- /dev/null
+++ b/.rubocop_todo.yml
@@ -0,0 +1,84 @@
+# This configuration was generated by `rubocop --auto-gen-config`
+# on 2015-06-04 09:15:17 -0400 using RuboCop version 0.31.0.
+# The point is for the user to remove these configuration records
+# one by one as the offenses are removed from the code base.
+# Note that changes in the inspected code, or installation of new
+# versions of RuboCop, may require this file to be generated again.
+
+# Offense count: 37
+Metrics/AbcSize:
+  Max: 48
+
+# Offense count: 2
+Metrics/BlockNesting:
+  Max: 4
+
+# Offense count: 4
+# Configuration parameters: CountComments.
+Metrics/ClassLength:
+  Max: 246
+
+# Offense count: 23
+Metrics/CyclomaticComplexity:
+  Max: 20
+
+# Offense count: 675
+# Configuration parameters: AllowURI, URISchemes.
+Metrics/LineLength:
+  Max: 198
+
+# Offense count: 44
+# Configuration parameters: CountComments.
+Metrics/MethodLength:
+  Max: 35
+
+# Offense count: 8
+# Configuration parameters: CountComments.
+Metrics/ModuleLength:
+  Max: 243
+
+# Offense count: 17
+Metrics/PerceivedComplexity:
+  Max: 22
+
+# Offense count: 26
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles, ProceduralMethods, FunctionalMethods, IgnoredMethods.
+Style/BlockDelimiters:
+  Enabled: false
+
+# Offense count: 174
+Style/Documentation:
+  Enabled: false
+
+# Offense count: 7
+Style/DoubleNegation:
+  Enabled: false
+
+# Offense count: 5
+Style/EachWithObject:
+  Enabled: false
+
+# Offense count: 15
+# Configuration parameters: MinBodyLength.
+Style/GuardClause:
+  Enabled: false
+
+# Offense count: 4
+# Cop supports --auto-correct.
+Style/Lambda:
+  Enabled: false
+
+# Offense count: 3
+Style/MultilineTernaryOperator:
+  Enabled: false
+
+# Offense count: 3
+# Configuration parameters: NamePrefix, NamePrefixBlacklist.
+Style/PredicateName:
+  Enabled: false
+
+# Offense count: 13
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+Style/RaiseArgs:
+  Enabled: false
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..91d7f31
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,20 @@
+language: ruby
+
+rvm:
+  - 2.2
+  - 2.1
+  - 2.0.0
+  - rbx-2.2.10
+  - jruby-19mode
+  - ruby-head
+  - jruby-head
+
+matrix:
+  allow_failures:
+    - rvm: ruby-head
+    - rvm: jruby-head
+
+gemfile:
+  - Gemfile
+  - gemfiles/rails_3.gemfile
+  - gemfiles/rails_4.gemfile
diff --git a/.yardopts b/.yardopts
new file mode 100644
index 0000000..98b4316
--- /dev/null
+++ b/.yardopts
@@ -0,0 +1,2 @@
+--markup-provider=redcarpet
+--markup=markdown
diff --git a/Appraisals b/Appraisals
new file mode 100644
index 0000000..099c460
--- /dev/null
+++ b/Appraisals
@@ -0,0 +1,7 @@
+appraise "rails-3" do
+  gem "rails", "3.2.19"
+end
+
+appraise "rails-4" do
+  gem "rails", "4.1.6"
+end
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..cd97c38
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,408 @@
+0.12.0 (6/18/2015)
+==================
+
+#### Features
+
+* [#995](https://github.com/intridea/grape/issues/995): Added support for coercion to Set or Set[Other] - [@jordansexton](https://github.com/jordansexton) [@u2](https://github.com/u2).
+* [#980](https://github.com/intridea/grape/issues/980): Grape is now eager-loaded - [@u2](https://github.com/u2).
+* [#956](https://github.com/intridea/grape/issues/956): Support `present` with `Grape::Presenters::Presenter`  - [@u2](https://github.com/u2).
+* [#974](https://github.com/intridea/grape/pull/974): Added `error!` to `rescue_from` blocks - [@whatasunnyday](https://github.com/whatasunnyday).
+* [#950](https://github.com/intridea/grape/pull/950): Status method can now accept one of Rack::Utils status code symbols (:ok, :found, :bad_request, etc.) - [@dabrorius](https://github.com/dabrorius).
+* [#952](https://github.com/intridea/grape/pull/952): Status method now raises error when called with invalid status code - [@dabrorius](https://github.com/dabrorius).
+* [#957](https://github.com/intridea/grape/pull/957): Regexp validator now supports `allow_blank`, `nil` value behavior changed - [@calfzhou](https://giihub.com/calfzhou).
+* [#962](https://github.com/intridea/grape/pull/962): The `default` attribute with `false` value is documented now - [@ajvondrak](https://github.com/ajvondrak).
+* [#1026](https://github.com/intridea/grape/pull/1026): Added `file` method, explicitly setting a file-like response object - [@dblock](https://github.com/dblock).
+
+#### Fixes
+
+* [#994](https://github.com/intridea/grape/pull/994): Fixed optional Array params default to Hash - [@u2](https://github.com/u2).
+* [#988](https://github.com/intridea/grape/pull/988): Fixed duplicate identical endpoints - [@u2](https://github.com/u2).
+* [#936](https://github.com/intridea/grape/pull/936): Fixed default params processing for optional groups - [@dm1try](https://github.com/dm1try).
+* [#942](https://github.com/intridea/grape/pull/942): Fixed forced presence for optional params when based on a reused entity that was also required in another context - [@croeck](https://github.com/croeck).
+* [#1001](https://github.com/intridea/grape/pull/1001): Fixed calling endpoint with specified format with format in its path - [@hodak](https://github.com/hodak).
+* [#1005](https://github.com/intridea/grape/pull/1005): Fixed the Grape::Middleware::Globals - [@urkle](https://github.com/urkle).
+* [#1012](https://github.com/intridea/grape/pull/1012): Fixed `allow_blank: false` with a Boolean value of `false` - [@mfunaro](https://github.com/mfunaro).
+* [#1023](https://github.com/intridea/grape/issues/1023): Fixes unexpected beahvior with `present` and an object that responds to `merge` but isn't a Hash - [@dblock](https://github.com/dblock).
+* [#1017](https://github.com/intridea/grape/pull/1017): Fixed `undefined method stringify_keys` with nested mutual exclusive params - [@quickpay](https://github.com/quickpay).
+
+0.11.0 (2/23/2015)
+==================
+
+* [#925](https://github.com/intridea/grape/pull/925): Fixed `toplevel constant DateTime referenced by Virtus::Attribute::DateTime` - [@u2](https://github.com/u2).
+* [#916](https://github.com/intridea/grape/pull/916): Added `DateTime/Date/Numeric/Boolean` type support `allow_blank` - [@u2](https://github.com/u2).
+* [#871](https://github.com/intridea/grape/pull/871): Fixed `Grape::Middleware::Base#response` - [@galathius](https://github.com/galathius).
+* [#559](https://github.com/intridea/grape/issues/559): Added support for Rack 1.6.0, which parses requests larger than 128KB - [@myitcv](https://github.com/myitcv).
+* [#876](https://github.com/intridea/grape/pull/876): Call to `declared(params)` now returns a `Hashie::Mash` - [@rodzyn](https://github.com/rodzyn).
+* [#879](https://github.com/intridea/grape/pull/879): The `route_info` value is no longer included in `params` Hash - [@rodzyn](https://github.com/rodzyn).
+* [#881](https://github.com/intridea/grape/issues/881): Fixed `Grape::Validations::ValuesValidator` support for `Range` type - [@ajvondrak](https://github.com/ajvondrak).
+* [#901](https://github.com/intridea/grape/pull/901): Fix: callbacks defined in a version block are only called for the routes defined in that block - [@kushkella](https://github.com/kushkella).
+* [#886](https://github.com/intridea/grape/pull/886): Group of parameters made to require an explicit type of Hash or Array - [@jrichter1](https://github.com/jrichter1).
+* [#912](https://github.com/intridea/grape/pull/912): Extended the `:using` feature for param documentation to `optional` fields - [@croeck](https://github.com/croeck).
+* [#906](https://github.com/intridea/grape/pull/906): Fix: invalid body parse errors are not rescued by handlers - [@croeck](https://github.com/croeck).
+* [#913](https://github.com/intridea/grape/pull/913): Fix: Invalid accept headers are not processed by rescue handlers - [@croeck](https://github.com/croeck).
+* [#913](https://github.com/intridea/grape/pull/913): Fix: Invalid accept headers cause internal processing errors (500) when http_codes are defined - [@croeck](https://github.com/croeck).
+* [#917](https://github.com/intridea/grape/pull/917): Use HTTPS for rubygems.org - [@O-I](https://github.com/O-I).
+
+0.10.1 (12/28/2014)
+===================
+
+* [#868](https://github.com/intridea/grape/pull/868), [#862](https://github.com/intridea/grape/pull/862), [#861](https://github.com/intridea/grape/pull/861): Fixed `version`, `prefix`, and other settings being overridden or changing scope when mounting API - [@yesmeck](https://github.com/yesmeck).
+* [#864](https://github.com/intridea/grape/pull/864): Fixed `declared(params, include_missing: false)` now returning attributes with `nil` and `false` values - [@ppadron](https://github.com/ppadron).
+
+0.10.0 (12/19/2014)
+===================
+
+* [#803](https://github.com/intridea/grape/pull/803), [#820](https://github.com/intridea/grape/pull/820): Added `all_or_none_of` parameter validator - [@loveltyoic](https://github.com/loveltyoic), [@natecj](https://github.com/natecj).
+* [#774](https://github.com/intridea/grape/pull/774): Extended `mutually_exclusive`, `exactly_one_of`, `at_least_one_of` to work inside any kind of group: `requires` or `optional`, `Hash` or `Array` - [@ShPakvel](https://github.com/ShPakvel).
+* [#743](https://github.com/intridea/grape/pull/743): Added `allow_blank` parameter validator to validate non-empty strings - [@elado](https://github.com/elado).
+* [#745](https://github.com/intridea/grape/pull/745): Removed `atom+xml`, `rss+xml`, and `jsonapi` content-types - [@akabraham](https://github.com/akabraham).
+* [#745](https://github.com/intridea/grape/pull/745): Added `:binary, application/octet-stream` content-type - [@akabraham](https://github.com/akabraham).
+* [#757](https://github.com/intridea/grape/pull/757): Changed `desc` can now be used with a block syntax - [@dspaeth-faber](https://github.com/dspaeth-faber).
+* [#779](https://github.com/intridea/grape/pull/779): Fixed using `values` with a `default` proc - [@ShPakvel](https://github.com/ShPakvel).
+* [#799](https://github.com/intridea/grape/pull/799): Fixed custom validators with required `Hash`, `Array` types - [@bwalex](https://github.com/bwalex).
+* [#784](https://github.com/intridea/grape/pull/784): Fixed `present` to not overwrite the previously added contents of the response body whebn called more than once - [@mfunaro](https://github.com/mfunaro).
+* [#809](https://github.com/intridea/grape/pull/809): Removed automatic `(.:format)` suffix on paths if you're using only one format (e.g., with `format :json`, `/path` will respond with JSON but `/path.xml` will be a 404) - [@ajvondrak](https://github.com/ajvondrak).
+* [#816](https://github.com/intridea/grape/pull/816): Added ability to filter out missing params if params is a nested hash with `declared(params, include_missing: false)` - [@georgimitev](https://github.com/georgimitev).
+* [#819](https://github.com/intridea/grape/pull/819): Allowed both `desc` and `description` in the params DSL - [@mzikherman](https://github.com/mzikherman).
+* [#821](https://github.com/intridea/grape/pull/821): Fixed passing string value when hash is expected in params - [@rebelact](https://github.com/rebelact).
+* [#824](https://github.com/intridea/grape/pull/824): Validate array params against list of acceptable values - [@dnd](https://github.com/dnd).
+* [#813](https://github.com/intridea/grape/pull/813): Routing methods dsl refactored to get rid of explicit `paths` parameter - [@AlexYankee](https://github.com/AlexYankee).
+* [#826](https://github.com/intridea/grape/pull/826): Find `coerce_type` for `Array` when not specified - [@manovotn](https://github.com/manovotn).
+* [#645](https://github.com/intridea/grape/issues/645): Invoking `body false` will return `204 No Content` - [@dblock](https://github.com/dblock).
+* [#801](https://github.com/intridea/grape/issues/801): Only evaluate permitted parameter `values` and `default` lazily on each request when declared as a proc - [@dblock](https://github.com/dblock).
+* [#679](https://github.com/intridea/grape/issues/679): Fixed `OPTIONS` method returning 404 when combined with `prefix`- [@dblock](https://github.com/dblock).
+* [#679](https://github.com/intridea/grape/issues/679): Fixed unsupported methods returning 404 instead of 405 when combined with `prefix`- [@dblock](https://github.com/dblock).
+
+0.9.0 (8/27/2014)
+=================
+
+#### Features
+
+* [#691](https://github.com/intridea/grape/issues/691): Added `at_least_one_of` parameter validator - [@dblock](https://github.com/dblock).
+* [#698](https://github.com/intridea/grape/pull/698): `error!` sets `status` for `Endpoint` too - [@dspaeth-faber](https://github.com/dspaeth-faber).
+* [#703](https://github.com/intridea/grape/pull/703): Added support for Auth-Middleware extension - [@dspaeth-faber](https://github.com/dspaeth-faber).
+* [#703](https://github.com/intridea/grape/pull/703): Removed `Grape::Middleware::Auth::Basic` - [@dspaeth-faber](https://github.com/dspaeth-faber).
+* [#703](https://github.com/intridea/grape/pull/703): Removed `Grape::Middleware::Auth::Digest` - [@dspaeth-faber](https://github.com/dspaeth-faber).
+* [#703](https://github.com/intridea/grape/pull/703): Removed `Grape::Middleware::Auth::OAuth2` - [@dspaeth-faber](https://github.com/dspaeth-faber).
+* [#719](https://github.com/intridea/grape/pull/719): Allow passing options hash to a custom validator - [@elado](https://github.com/elado).
+* [#716](https://github.com/intridea/grape/pull/716): Calling `content-type` will now return the current content-type - [@dblock](https://github.com/dblock).
+* [#705](https://github.com/intridea/grape/pull/705): Errors can now be presented with a `Grape::Entity` class - [@dspaeth-faber](https://github.com/dspaeth-faber).
+
+#### Fixes
+
+* [#687](https://github.com/intridea/grape/pull/687): Fix: `mutually_exclusive` and `exactly_one_of` validation error messages now label parameters as strings, consistently with `requires` and `optional` - [@dblock](https://github.com/dblock).
+
+0.8.0 (7/10/2014)
+=================
+
+#### Features
+
+* [#639](https://github.com/intridea/grape/pull/639): Added support for blocks with reusable params - [@mibon](https://github.com/mibon).
+* [#637](https://github.com/intridea/grape/pull/637): Added support for `exactly_one_of` parameter validation - [@Morred](https://github.com/Morred).
+* [#626](https://github.com/intridea/grape/pull/626): Added support for `mutually_exclusive` parameters - [@oliverbarnes](https://github.com/oliverbarnes).
+* [#617](https://github.com/intridea/grape/pull/617): Running tests on Ruby 2.1.1, Rubinius 2.1 and 2.2, Ruby and JRuby HEAD - [@dblock](https://github.com/dblock).
+* [#397](https://github.com/intridea/grape/pull/397): Adds `Grape::Endpoint.before_each` to allow easy helper stubbing - [@mbleigh](https://github.com/mbleigh).
+* [#673](https://github.com/intridea/grape/pull/673): Avoid requiring non-existent fields when using Grape::Entity documentation - [@qqshfox](https://github.com/qqshfox).
+
+#### Fixes
+
+* [#671](https://github.com/intridea/grape/pull/671): Allow required param with predefined set of values to be nil inside optional group - [@dm1try](https://github.com/dm1try).
+* [#651](https://github.com/intridea/grape/pull/651): The `rescue_from` keyword now properly defaults to rescuing subclasses of exceptions - [@xevix](https://github.com/xevix).
+* [#614](https://github.com/intridea/grape/pull/614): Params with `nil` value are now refused by `RegexpValidator` - [@dm1try](https://github.com/dm1try).
+* [#494](https://github.com/intridea/grape/issues/494): Fixed performance issue with requests carrying a large payload - [@dblock](https://github.com/dblock).
+* [#619](https://github.com/intridea/grape/pull/619): Convert specs to RSpec 3 syntax with Transpec - [@danielspector](https://github.com/danielspector).
+* [#632](https://github.com/intridea/grape/pull/632): `Grape::Endpoint#present` causes ActiveRecord to make an extra query during entity's detection - [@fixme](https://github.com/fixme).
+
+0.7.0 (4/2/2014)
+=================
+
+#### Features
+
+* [#558](https://github.com/intridea/grape/pull/558): Support lambda-based values for params - [@wpschallenger](https://github.com/wpschallenger).
+* [#510](https://github.com/intridea/grape/pull/510): Support lambda-based default values for params - [@myitcv](https://github.com/myitcv).
+* [#511](https://github.com/intridea/grape/pull/511): Added `required` option for OAuth2 middleware - [@bcm](https://github.com/bcm).
+* [#520](https://github.com/intridea/grape/pull/520): Use `default_error_status` to specify the default status code returned from `error!` - [@salimane](https://github.com/salimane).
+* [#525](https://github.com/intridea/grape/pull/525): The default status code returned from `error!` has been changed from 403 to 500 - [@dblock](https://github.com/dblock).
+* [#526](https://github.com/intridea/grape/pull/526): Allowed specifying headers in `error!` - [@dblock](https://github.com/dblock).
+* [#527](https://github.com/intridea/grape/pull/527): The `before_validation` callback is now a distinct one - [@myitcv](https://github.com/myitcv).
+* [#530](https://github.com/intridea/grape/pull/530): Added ability to restrict `declared(params)` to the local endpoint with `include_parent_namespaces: false` - [@myitcv](https://github.com/myitcv).
+* [#531](https://github.com/intridea/grape/pull/531): Helpers are now available to auth middleware, executing in the context of the endpoint - [@joelvh](https://github.com/joelvh).
+* [#540](https://github.com/intridea/grape/pull/540): Ruby 2.1.0 is now supported - [@salimane](https://github.com/salimane).
+* [#544](https://github.com/intridea/grape/pull/544): The `rescue_from` keyword now handles subclasses of exceptions by default - [@xevix](https://github.com/xevix).
+* [#545](https://github.com/intridea/grape/pull/545): Added `type` (`Array` or `Hash`) support to `requires`, `optional` and `group` - [@bwalex](https://github.com/bwalex).
+* [#550](https://github.com/intridea/grape/pull/550): Added possibility to define reusable params - [@dm1try](https://github.com/dm1try).
+* [#560](https://github.com/intridea/grape/pull/560): Use `Grape::Entity` documentation to define required and optional parameters with `requires using:` - [@reynardmh](https://github.com/reynardmh).
+* [#572](https://github.com/intridea/grape/pull/572): Added `documentation` support to `requires`, `optional` and `group` parameters - [@johnallen3d](https://github.com/johnallen3d).
+
+#### Fixes
+
+* [#600](https://github.com/intridea/grape/pull/600): Don't use an `Entity` constant that is available in the namespace as presenter - [@fuksito](https://github.com/fuksito).
+* [#590](https://github.com/intridea/grape/pull/590): Fix issue where endpoint param of type `Integer` cannot set values array - [@xevix](https://github.com/xevix).
+* [#586](https://github.com/intridea/grape/pull/586): Do not repeat the same validation error messages - [@kiela](https://github.com/kiela).
+* [#508](https://github.com/intridea/grape/pull/508): Allow parameters, such as content encoding, in `content_type` - [@dm1try](https://github.com/dm1try).
+* [#492](https://github.com/intridea/grape/pull/492): Don't allow to have nil value when a param is required and has a list of allowed values - [@Antti](https://github.com/Antti).
+* [#495](https://github.com/intridea/grape/pull/495): Fixed `ParamsScope#params` for parameters nested inside arrays - [@asross](https://github.com/asross).
+* [#498](https://github.com/intridea/grape/pull/498): Dry'ed up options and headers logic, allow headers to be passed to OPTIONS requests - [@karlfreeman](https://github.com/karlfreeman).
+* [#500](https://github.com/intridea/grape/pull/500): Skip entity auto-detection when explicitely passed - [@yaneq](https://github.com/yaneq).
+* [#503](https://github.com/intridea/grape/pull/503): Calling declared(params) from child namespace fails to include parent namespace defined params - [@myitcv](https://github.com/myitcv).
+* [#512](https://github.com/intridea/grape/pull/512): Don't create `Grape::Request` multiple times - [@dblock](https://github.com/dblock).
+* [#538](https://github.com/intridea/grape/pull/538): Fixed default values for grouped params - [@dm1try](https://github.com/dm1try).
+* [#549](https://github.com/intridea/grape/pull/549): Fixed handling of invalid version headers to return 406 if a header cannot be parsed - [@bwalex](https://github.com/bwalex).
+* [#557](https://github.com/intridea/grape/pull/557): Pass `content_types` option to `Grape::Middleware::Error` to fix the content-type header for custom formats. - [@bernd](https://github.com/bernd).
+* [#585](https://github.com/intridea/grape/pull/585): Fix after boot thread-safety issue - [@etehtsea](https://github.com/etehtsea).
+* [#587](https://github.com/intridea/grape/pull/587): Fix oauth2 middleware compatibility with [draft-ietf-oauth-v2-31](http://tools.ietf.org/html/draft-ietf-oauth-v2-31) spec - [@etehtsea](https://github.com/etehtsea).
+* [#610](https://github.com/intridea/grape/pull/610): Fixed group keyword was not working with type parameter - [@klausmeyer](https://github.com/klausmeyer/).
+
+0.6.1 (10/19/2013)
+==================
+
+#### Features
+
+* [#475](https://github.com/intridea/grape/pull/475): Added support for the `:jsonapi`, `application/vnd.api+json` media type registered at http://jsonapi.org - [@bcm](https://github.com/bcm).
+* [#471](https://github.com/intridea/grape/issues/471): Added parameter validator for a list of allowed values - [@vickychijwani](https://github.com/vickychijwani).
+* [#488](https://github.com/intridea/grape/issues/488): Upgraded to Virtus 1.0 - [@dblock](https://github.com/dblock).
+
+#### Fixes
+
+* [#477](https://github.com/intridea/grape/pull/477): Fixed `default_error_formatter` which takes a format symbol - [@vad4msiu](https://github.com/vad4msiu).
+
+#### Development
+
+* Implemented Rubocop, a Ruby code static code analyzer - [@dblock](https://github.com/dblock).
+
+0.6.0 (9/16/2013)
+=================
+
+#### Features
+
+* Grape is no longer tested against Ruby 1.8.7.
+* [#442](https://github.com/intridea/grape/issues/442): Enable incrementally building on top of a previous API version - [@dblock](https://github.com/dblock).
+* [#442](https://github.com/intridea/grape/issues/442): API `version` can now take an array of multiple versions - [@dblock](https://github.com/dblock).
+* [#444](https://github.com/intridea/grape/issues/444): Added `:en` as fallback locale for I18n - [@aew](https://github.com/aew).
+* [#448](https://github.com/intridea/grape/pull/448): Adding POST style parameters for DELETE requests - [@dquimper](https://github.com/dquimper).
+* [#450](https://github.com/intridea/grape/pull/450): Added option to pass an exception handler lambda as an argument to `rescue_from` - [@robertopedroso](https://github.com/robertopedroso).
+* [#443](https://github.com/intridea/grape/pull/443): Let `requires` and `optional` take blocks that initialize new scopes - [@asross](https://github.com/asross).
+* [#452](https://github.com/intridea/grape/pull/452): Added `with` as a hash option to specify handlers for `rescue_from` and `error_formatter` [@robertopedroso](https://github.com/robertopedroso).
+* [#433](https://github.com/intridea/grape/issues/433), [#462](https://github.com/intridea/grape/issues/462): Validation errors are now collected and `Grape::Exceptions::ValidationErrors` is raised - [@stevschmid](https://github.com/stevschmid).
+
+#### Fixes
+
+* [#428](https://github.com/intridea/grape/issues/428): Removes memoization from `Grape::Request` params to prevent middleware from freezing parameter values before `Formatter` can get them - [@mbleigh](https://github.com/mbleigh).
+
+0.5.0 (6/14/2013)
+=================
+
+#### Features
+
+* [#344](https://github.com/intridea/grape/pull/344): Added `parser :type, nil` which disables input parsing for a given content-type - [@dblock](https://github.com/dblock).
+* [#381](https://github.com/intridea/grape/issues/381): Added `cascade false` option at API level to remove the `X-Cascade: true` header from the API response - [@dblock](https://github.com/dblock).
+* [#392](https://github.com/intridea/grape/pull/392): Extracted headers and params from `Endpoint` to `Grape::Request` - [@niedhui](https://github.com/niedhui).
+* [#376](https://github.com/intridea/grape/pull/376): Added `route_param`, syntax sugar for quick declaration of route parameters - [@mbleigh](https://github.com/mbleigh).
+* [#390](https://github.com/intridea/grape/pull/390): Added default value for an `optional` parameter - [@oivoodoo](https://github.com/oivoodoo).
+* [#403](https://github.com/intridea/grape/pull/403): Added support for versioning using the `Accept-Version` header - [@politician](https://github.com/politician).
+* [#407](https://github.com/intridea/grape/issues/407): Specifying `default_format` will also set the default POST/PUT data parser to the given format - [@dblock](https://github.com/dblock).
+* [#241](https://github.com/intridea/grape/issues/241): Present with multiple entities using an optional Symbol - [@niedhui](https://github.com/niedhui).
+
+#### Fixes
+
+* [#378](https://github.com/intridea/grape/pull/378): Fix: stop rescuing all exceptions during formatting - [@kbarrette](https://github.com/kbarrette).
+* [#380](https://github.com/intridea/grape/pull/380): Fix: `Formatter#read_body_input` when transfer encoding is chunked - [@paulnicholon](https://github.com/paulnicholson).
+* [#347](https://github.com/intridea/grape/issues/347): Fix: handling non-hash body params - [@paulnicholon](https://github.com/paulnicholson).
+* [#394](https://github.com/intridea/grape/pull/394): Fix: path version no longer overwrites a `version` parameter - [@tmornini](https://github.com/tmornini).
+* [#412](https://github.com/intridea/grape/issues/412): Fix: specifying `content_type` will also override the selection of the data formatter - [@dblock](https://github.com/dblock).
+* [#383](https://github.com/intridea/grape/issues/383): Fix: Mounted APIs aren't inheriting settings (including `before` and `after` filters) - [@seanmoon](https://github.com/seanmoon).
+* [#408](https://github.com/intridea/grape/pull/408): Fix: Goliath passes request header keys as symbols not strings - [@bobek](https://github.com/bobek).
+* [#417](https://github.com/intridea/grape/issues/417): Fix: Rails 4 does not rewind input, causes POSTed data to be empty - [@dblock](https://github.com/dblock).
+* [#423](https://github.com/intridea/grape/pull/423): Fix: `Grape::Endpoint#declared` now correctly handles nested params (ie. declared with `group`) - [@jbarreneche](https://github.com/jbarreneche).
+* [#427](https://github.com/intridea/grape/issues/427): Fix: `declared(params)` breaks when `params` contains array - [@timhabermaas](https://github.com/timhabermaas)
+
+0.4.1 (4/1/2013)
+================
+
+* [#375](https://github.com/intridea/grape/pull/375): Fix: throwing an `:error` inside a middleware doesn't respect the `format` settings - [@dblock](https://github.com/dblock).
+
+0.4.0 (3/17/2013)
+=================
+
+* [#356](https://github.com/intridea/grape/pull/356): Fix: presenting collections other than `Array` (eg. `ActiveRecord::Relation`) - [@zimbatm](https://github.com/zimbatm).
+* [#352](https://github.com/intridea/grape/pull/352): Fix: using `Rack::JSONP` with `Grape::Entity` responses - [@deckchair](https://github.com/deckchair).
+* [#347](https://github.com/intridea/grape/issues/347): Grape will accept any valid JSON as PUT or POST, including strings, symbols and arrays - [@qqshfox](https://github.com/qqshfox), [@dblock](https://github.com/dblock).
+* [#347](https://github.com/intridea/grape/issues/347): JSON format APIs always return valid JSON, eg. strings are now returned as `"string"` and no longer `string` - [@dblock](https://github.com/dblock).
+* Raw body input from POST and PUT requests (`env['rack.input'].read`) is now available in `api.request.input` - [@dblock](https://github.com/dblock).
+* Parsed body input from POST and PUT requests is now available in `api.request.body` - [@dblock](https://github.com/dblock).
+* [#343](https://github.com/intridea/grape/pull/343): Fix: return `Content-Type: text/plain` with error 405 - [@gustavosaume](https://github.com/gustavosaume), [@wyattisimo](https://github.com/wyattisimo).
+* [#357](https://github.com/intridea/grape/pull/357): Grape now requires Rack 1.3.0 or newer - [@jhecking](https://github.com/jhecking).
+* [#320](https://github.com/intridea/grape/issues/320): API `namespace` now supports `requirements` - [@niedhui](https://github.com/niedhui).
+* [#353](https://github.com/intridea/grape/issues/353): Revert to standard Ruby logger formatter, `require active_support/all` if you want old behavior - [@rhunter](https://github.com/rhunter), [@dblock](https://github.com/dblock).
+* Fix: `undefined method 'call' for nil:NilClass` for an API method implementation without a block, now returns an empty string - [@dblock](https://github.com/dblock).
+
+0.3.2 (2/28/2013)
+=================
+
+* [#355](https://github.com/intridea/grape/issues/355): Relax dependency constraint on Hashie - [@reset](https://github.com/reset).
+
+0.3.1 (2/25/2013)
+=================
+
+* [#351](https://github.com/intridea/grape/issues/351): Compatibility with Ruby 2.0 - [@mbleigh](https://github.com/mbleigh).
+
+0.3.0 (02/21/2013)
+==================
+
+* [#294](https://github.com/intridea/grape/issues/294): Extracted `Grape::Entity` into a [grape-entity](https://github.com/agileanimal/grape-entity) gem - [@agileanimal](https://github.com/agileanimal).
+* [#340](https://github.com/intridea/grape/pull/339), [#342](https://github.com/intridea/grape/pull/342): Added `:cascade` option to `version` to allow disabling of rack/mount cascade behavior - [@dieb](https://github.com/dieb).
+* [#333](https://github.com/intridea/grape/pull/333): Added support for validation of arrays in `params` - [@flyerhzm](https://github.com/flyerhzm).
+* [#306](https://github.com/intridea/grape/issues/306): Added I18n support for all Grape exceptions - [@niedhui](https://github.com/niedhui).
+* [#309](https://github.com/intridea/grape/pull/309): Added XML support to the entity presenter - [@johnnyiller](https://github.com/johnnyiller), [@dblock](http://github.com/dblock).
+* [#131](https://github.com/intridea/grape/issues/131): Added instructions for Grape API reloading in Rails - [@jyn](http://github.com/jyn), [@dblock](http://github.com/dblock).
+* [#317](https://github.com/intridea/grape/issues/317): Added `headers` that returns a hash of parsed HTTP request headers - [@dblock](http://github.com/dblock).
+* [#332](https://github.com/intridea/grape/pull/332): `Grape::Exceptions::Validation` now contains full nested parameter names - [@alovak](https://github.com/alovak).
+* [#328](https://github.com/intridea/grape/issues/328): API version can now be specified as both String and Symbol - [@dblock](http://github.com/dblock).
+* [#190](https://github.com/intridea/grape/issues/190): When you add a `GET` route for a resource, a route for the `HEAD` method will also be added automatically. You can disable this behavior with `do_not_route_head!` - [@dblock](http://github.com/dblock).
+* Added `do_not_route_options!`, which disables the automatic creation of the `OPTIONS` route - [@dblock](http://github.com/dblock).
+* [#309](https://github.com/intridea/grape/pull/309): An XML format API will return an error instead of returning a string representation of the response if the latter cannot be converted to XML - [@dblock](http://github.com/dblock).
+* A formatter that raises an exception will cause the API to return a 500 error - [@dblock](http://github.com/dblock).
+* [#322](https://github.com/intridea/grape/issues/322): When returning a 406 status, Grape will include the requested format or content-type in the response body - [@dblock](http://github.com/dblock).
+* [#60](https://github.com/intridea/grape/issues/60): Fix: mounting of a Grape API onto a path - [@dblock](http://github.com/dblock).
+* [#335](https://github.com/intridea/grape/pull/335): Fix: request body parameters from a `PATCH` request not available in `params` - [@FreakenK](http://github.com/FreakenK).
+
+0.2.6 (01/11/2013)
+==================
+
+* Fix: support content-type with character set when parsing POST and PUT input - [@dblock](http://github.com/dblock).
+* Fix: CVE-2013-0175, multi_xml parse vulnerability, require multi_xml 0.5.2 - [@dblock](http://github.com/dblock).
+
+0.2.5 (01/10/2013)
+==================
+
+* Added support for custom parsers via `parser`, in addition to built-in multipart, JSON and XML parsers - [@dblock](http://github.com/dblock).
+* Removed `body_params`, data sent via a POST or PUT with a supported content-type is merged into `params` - [@dblock](http://github.com/dblock).
+* Setting `format` will automatically remove other content-types by calling `content_type` - [@dblock](http://github.com/dblock).
+* Setting `content_type` will prevent any input data other than the matching content-type or any Rack-supported form and parseable media types (`application/x-www-form-urlencoded`, `multipart/form-data`, `multipart/related` and `multipart/mixed`) from being parsed - [@dblock](http://github.com/dblock).
+* [#305](https://github.com/intridea/grape/issues/305): Fix: presenting arrays of objects via `represent` or when auto-detecting an `Entity` constant in the objects being presented - [@brandonweiss](https://github.com/brandonweiss).
+* [#306](https://github.com/intridea/grape/issues/306): Added i18n support for validation error messages - [@niedhui](https://github.com/niedhui).
+
+0.2.4 (01/06/2013)
+==================
+
+* [#297](https://github.com/intridea/grape/issues/297): Added `default_error_formatter` - [@dblock](https://github.com/dblock).
+* [#297](https://github.com/intridea/grape/issues/297): Setting `format` will automatically set `default_error_formatter` - [@dblock](https://github.com/dblock).
+* [#295](https://github.com/intridea/grape/issues/295): Storing original API source block in endpoint's `source` attribute - [@dblock](https://github.com/dblock).
+* [#293](https://github.com/intridea/grape/pull/293): Added options to `cookies.delete`, enables passing a path - [@inst](https://github.com/inst).
+* [#174](https://github.com/intridea/grape/issues/174): The value of `env['PATH_INFO']` is no longer altered with `path` versioning - [@dblock](https://github.com/dblock).
+* [#296](https://github.com/intridea/grape/issues/296): Fix: ArgumentError with default error formatter - [@dblock](https://github.com/dblock).
+* [#298](https://github.com/intridea/grape/pull/298): Fix: subsequent calls to `body_params` would fail due to IO read - [@justinmcp](https://github.com/justinmcp).
+* [#301](https://github.com/intridea/grape/issues/301): Fix: symbol memory leak in cookie and formatter middleware - [@dblock](https://github.com/dblock).
+* [#300](https://github.com/intridea/grape/issues/300): Fix `Grape::API.routes` to include mounted api routes - [@aiwilliams](https://github.com/aiwilliams).
+* [#302](https://github.com/intridea/grape/pull/302): Fix: removed redundant `autoload` entries - [@ugisozols](https://github.com/ugisozols).
+* [#172](https://github.com/intridea/grape/issues/172): Fix: MultiJson deprecated methods warnings - [@dblock](https://github.com/dblock).
+* [#133](https://github.com/intridea/grape/issues/133): Fix: header-based versioning with use of `prefix` - [@seanmoon](https://github.com/seanmoon), [@dblock](https://github.com/dblock).
+* [#280](https://github.com/intridea/grape/issues/280): Fix: grouped parameters mangled in `route_params` hash - [@marcusg](https://github.com/marcusg), [@dblock](https://github.com/dblock).
+* [#304](https://github.com/intridea/grape/issues/304): Fix: `present x, :with => Entity` returns class references with `format :json` - [@dblock](https://github.com/dblock).
+* [#196](https://github.com/intridea/grape/issues/196): Fix: root requests don't work with `prefix` - [@dblock](https://github.com/dblock).
+
+0.2.3 (24/12/2012)
+==================
+
+* [#179](https://github.com/intridea/grape/issues/178): Using `content_type` will remove all default content-types - [@dblock](https://github.com/dblock).
+* [#265](https://github.com/intridea/grape/issues/264): Fix: Moved `ValidationError` into `Grape::Exceptions` - [@thepumpkin1979](https://github.com/thepumpkin1979).
+* [#269](https://github.com/intridea/grape/pull/269): Fix: `LocalJumpError` will not be raised when using explict return in API methods - [@simulacre](https://github.com/simulacre).
+* [#86](https://github.com/intridea/grape/issues/275): Fix Path-based versioning not recognizing `/` route - [@walski](https://github.com/walski).
+* [#273](https://github.com/intridea/grape/pull/273): Disabled formatting via `serializable_hash` and added support for `format :serializable_hash` - [@dblock](https://github.com/dblock).
+* [#277](https://github.com/intridea/grape/pull/277): Added a DSL to declare `formatter` in API settings - [@tim-vandecasteele](https://github.com/tim-vandecasteele).
+* [#284](https://github.com/intridea/grape/pull/284): Added a DSL to declare `error_formatter` in API settings - [@dblock](https://github.com/dblock).
+* [#285](https://github.com/intridea/grape/pull/285): Removed `error_format` from API settings, now matches request format - [@dblock](https://github.com/dblock).
+* [#290](https://github.com/intridea/grape/pull/290): The default error format for XML is now `error/message` instead of `hash/error` - [@dpsk](https://github.com/dpsk).
+* [#44](https://github.com/intridea/grape/issues/44): Pass `env` into formatters to enable templating - [@dblock](https://github.com/dblock).
+
+0.2.2
+=====
+
+#### Features
+
+* [#201](https://github.com/intridea/grape/pull/201), [#236](https://github.com/intridea/grape/pull/236), [#221](https://github.com/intridea/grape/pull/221): Added coercion and validations support to `params` DSL - [@schmurfy](https://github.com/schmurfy), [@tim-vandecasteele](https://github.com/tim-vandecasteele), [@adamgotterer](https://github.com/adamgotterer).
+* [#204](https://github.com/intridea/grape/pull/204): Added ability to declare shared `params` at `namespace` level - [@tim-vandecasteele](https://github.com/tim-vandecasteele).
+* [#234](https://github.com/intridea/grape/pull/234): Added a DSL for creating entities via mixin - [@mbleigh](https://github.com/mbleigh).
+* [#240](https://github.com/intridea/grape/pull/240): Define API response format from a query string `format` parameter, if specified - [@neetiraj](https://github.com/neetiraj).
+* Adds Endpoint#declared to easily filter out unexpected params. - [@mbleigh](https://github.com/mbleigh)
+
+#### Fixes
+
+* [#248](https://github.com/intridea/grape/pull/248): Fix: API `version` returns last version set - [@narkoz](https://github.com/narkoz).
+* [#242](https://github.com/intridea/grape/issues/242): Fix: permanent redirect status should be `301`, was `304` - [@adamgotterer](https://github.com/adamgotterer).
+* [#211](https://github.com/intridea/grape/pull/211): Fix: custom validations are no longer triggered when optional and parameter is not present - [@adamgotterer](https://github.com/adamgotterer).
+* [#210](https://github.com/intridea/grape/pull/210): Fix: `Endpoint#body_params` causing undefined method 'size' - [@adamgotterer](https://github.com/adamgotterer).
+* [#205](https://github.com/intridea/grape/pull/205): Fix: Corrected parsing of empty JSON body on POST/PUT - [@tim-vandecasteele](https://github.com/tim-vandecasteele).
+* [#181](https://github.com/intridea/grape/pull/181): Fix: Corrected JSON serialization of nested hashes containing `Grape::Entity` instances - [@benrosenblum](https://github.com/benrosenblum).
+* [#203](https://github.com/intridea/grape/pull/203): Added a check to `Entity#serializable_hash` that verifies an entity exists on an object - [@adamgotterer](https://github.com/adamgotterer).
+* [#208](https://github.com/intridea/grape/pull/208): `Entity#serializable_hash` must also check if attribute is generated by a user supplied block - [@ppadron](https://github.com/ppadron).
+* [#252](https://github.com/intridea/grape/pull/252): Resources that don't respond to a requested HTTP method return 405 (Method Not Allowed) instead of 404 (Not Found) - [@simulacre](https://github.com/simulacre)
+
+0.2.1 (7/11/2012)
+=================
+
+* [#186](https://github.com/intridea/grape/issues/186): Fix: helpers allow multiple calls with modules and blocks - [@ppadron](https://github.com/ppadron).
+* [#188](https://github.com/intridea/grape/pull/188): Fix: multi-method routes append '(.:format)' only once - [@kainosnoema](https://github.com/kainosnoema).
+* [#64](https://github.com/intridea/grape/issues/64), [#180](https://github.com/intridea/grape/pull/180): Added support to `GET` request bodies as parameters - [@bobbytables](https://github.com/bobbytables).
+* [#175](https://github.com/intridea/grape/pull/175): Added support for API versioning based on a request parameter - [@jackcasey](https://github.com/jackcasey).
+* [#168](https://github.com/intridea/grape/pull/168): Fix: Formatter can parse symbol keys in the headers hash - [@netmask](https://github.com/netmask).
+* [#169](https://github.com/intridea/grape/pull/169): Silence multi_json deprecation warnings - [@whiteley](https://github.com/whiteley).
+* [#166](https://github.com/intridea/grape/pull/166): Added support for `redirect`, including permanent and temporary - [@allenwei](https://github.com/allenwei).
+* [#159](https://github.com/intridea/grape/pull/159): Added `:requirements` to routes, allowing to use reserved characters in paths - [@gaiottino](https://github.com/gaiottino).
+* [#156](https://github.com/intridea/grape/pull/156): Added support for adding formatters to entities - [@bobbytables](https://github.com/bobbytables).
+* [#183](https://github.com/intridea/grape/pull/183): Added ability to include documentation in entities - [@flah00](https://github.com/flah00).
+* [#189](https://github.com/intridea/grape/pull/189): `HEAD` requests no longer return a body - [@stephencelis](https://github.com/stephencelis).
+* [#97](https://github.com/intridea/grape/issues/97): Allow overriding `Content-Type` - [@dblock](https://github.com/dblock).
+
+0.2.0 (3/28/2012)
+=================
+
+* Added support for inheriting exposures from entities - [@bobbytables](https://github.com/bobbytables).
+* Extended formatting with `default_format` - [@dblock](https://github.com/dblock).
+* Added support for cookies - [@lukaszsliwa](https://github.com/lukaszsliwa).
+* Added support for declaring additional content-types - [@joeyAghion](https://github.com/joeyAghion).
+* Added support for HTTP PATCH - [@LTe](https://github.com/LTe).
+* Added support for describing, documenting and reflecting APIs - [@dblock](https://github.com/dblock).
+* Added support for anchoring and vendoring - [@jwkoelewijn](https://github.com/jwkoelewijn).
+* Added support for HTTP OPTIONS - [@grimen](https://github.com/grimen).
+* Added support for silencing logger - [@evansj](https://github.com/evansj).
+* Added support for helper modules - [@freelancing-god](https://github.com/freelancing-god).
+* Added support for Accept header-based versioning - [@jch](https://github.com/jch), [@rodzyn](https://github.com/rodzyn).
+* Added support for mounting APIs and other Rack applications within APIs - [@mbleigh](https://github.com/mbleigh).
+* Added entities, multiple object representations - [@mbleigh](https://github.com/mbleigh).
+* Added ability to handle XML in the incoming request body - [@jwillis](https://github.com/jwillis).
+* Added support for a configurable logger - [@mbleigh](https://github.com/mbleigh).
+* Added support for before and after filters - [@mbleigh](https://github.com/mbleigh).
+* Extended `rescue_from`, which can now take a block - [@dblock](https://github.com/dblock).
+
+
+0.1.5 (6/14/2011)
+==================
+
+* Extended exception handling to all exceptions - [@dblock](https://github.com/dblock).
+* Added support for returning JSON objects from within error blocks - [@dblock](https://github.com/dblock).
+* Added support for handling incoming JSON in body - [@tedkulp](https://github.com/tedkulp).
+* Added support for HTTP digest authentication - [@daddz](https://github.com/daddz).
+
+0.1.4 (4/8/2011)
+==================
+
+* Allow multiple definitions of the same endpoint under multiple versions - [@chrisrhoden](https://github.com/chrisrhoden).
+* Added support for multipart URL parameters - [@mcastilho](https://github.com/mcastilho).
+* Added support for custom formatters - [@spraints](https://github.com/spraints).
+
+0.1.3 (1/10/2011)
+==================
+
+* Added support for JSON format in route matching - [@aiwilliams](https://github.com/aiwilliams).
+* Added suport for custom middleware - [@mbleigh](https://github.com/mbleigh).
+
+0.1.1 (11/14/2010)
+==================
+
+* Endpoints properly reset between each request - [@mbleigh](https://github.com/mbleigh).
+
+0.1.0 (11/13/2010)
+==================
+
+* Initial public release - [@mbleigh](https://github.com/mbleigh).
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..f74c34c
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,125 @@
+Contributing to Grape
+=====================
+
+Grape is work of [hundreds of contributors](https://github.com/intridea/grape/graphs/contributors). You're encouraged to submit [pull requests](https://github.com/intridea/grape/pulls), [propose features and discuss issues](https://github.com/intridea/grape/issues). When in doubt, ask a question in the [Grape Google Group](http://groups.google.com/group/ruby-grape).
+
+#### Fork the Project
+
+Fork the [project on Github](https://github.com/intridea/grape) and check out your copy.
+
+```
+git clone https://github.com/contributor/grape.git
+cd grape
+git remote add upstream https://github.com/intridea/grape.git
+```
+
+#### Create a Topic Branch
+
+Make sure your fork is up-to-date and create a topic branch for your feature or bug fix.
+
+```
+git checkout master
+git pull upstream master
+git checkout -b my-feature-branch
+```
+
+#### Bundle Install and Test
+
+Ensure that you can build the project and run tests.
+
+```
+bundle install
+bundle exec rake
+```
+
+Run tests against all supported versions of Rails.
+
+```
+appraisal install
+appraisal rake spec
+```
+
+#### Write Tests
+
+Try to write a test that reproduces the problem you're trying to fix or describes a feature that you want to build. Add to [spec/grape](spec/grape).
+
+We definitely appreciate pull requests that highlight or reproduce a problem, even without a fix.
+
+#### Write Code
+
+Implement your feature or bug fix.
+
+Ruby style is enforced with [Rubocop](https://github.com/bbatsov/rubocop), run `bundle exec rubocop` and fix any style issues highlighted.
+
+Make sure that `bundle exec rake` completes without errors.
+
+#### Write Documentation
+
+Document any external behavior in the [README](README.md).
+
+#### Update Changelog
+
+Add a line to [CHANGELOG](CHANGELOG.md) under *Next Release*. Make it look like every other line, including your name and link to your Github account.
+
+#### Commit Changes
+
+Make sure git knows your name and email address:
+
+```
+git config --global user.name "Your Name"
+git config --global user.email "contributor at example.com"
+```
+
+Writing good commit logs is important. A commit log should describe what changed and why.
+
+```
+git add ...
+git commit
+```
+
+#### Push
+
+```
+git push origin my-feature-branch
+```
+
+#### Make a Pull Request
+
+Go to https://github.com/contributor/grape and select your feature branch. Click the 'Pull Request' button and fill out the form. Pull requests are usually reviewed within a few days.
+
+#### Rebase
+
+If you've been working on a change for a while, rebase with upstream/master.
+
+```
+git fetch upstream
+git rebase upstream/master
+git push origin my-feature-branch -f
+```
+
+#### Update CHANGELOG Again
+
+Update the [CHANGELOG](CHANGELOG.md) with the pull request number. A typical entry looks as follows.
+
+```
+* [#123](https://github.com/intridea/grape/pull/123): Reticulated splines - [@contributor](https://github.com/contributor).
+```
+
+Amend your previous commit and force push the changes.
+
+```
+git commit --amend
+git push origin my-feature-branch -f
+```
+
+#### Check on Your Pull Request
+
+Go back to your pull request after a few minutes and see whether it passed muster with Travis-CI. Everything should look green, otherwise fix issues and amend your commit as described above.
+
+#### Be Patient
+
+It's likely that your change will not be merged and that the nitpicky maintainers will ask you to do more, or fix seemingly benign problems. Hang on there!
+
+#### Thank You
+
+Please do know that we really appreciate and value your time and work. We love you, really.
diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000..7d4786e
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,10 @@
+source 'https://rubygems.org'
+
+gemspec
+
+group :development, :test do
+  gem 'rubocop', '~> 0.31.0'
+  gem 'guard'
+  gem 'guard-rspec'
+  gem 'guard-rubocop'
+end
diff --git a/Guardfile b/Guardfile
new file mode 100644
index 0000000..ad1b725
--- /dev/null
+++ b/Guardfile
@@ -0,0 +1,10 @@
+guard :rspec, all_on_start: true, cmd: 'bundle exec rspec' do
+  watch(%r{^spec/.+_spec\.rb$})
+  watch(%r{^lib/(.+)\.rb$})     { |m| "spec/#{m[1]}_spec.rb" }
+  watch('spec/spec_helper.rb')  { 'spec' }
+end
+
+guard :rubocop do
+  watch(/.+\.rb$/)
+  watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) }
+end
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..1c73c52
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2010-2015 Michael Bleigh and Intridea, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..8246eb1
--- /dev/null
+++ b/README.md
@@ -0,0 +1,2561 @@
+![grape logo](grape.png)
+
+[![Gem Version](http://img.shields.io/gem/v/grape.svg)](http://badge.fury.io/rb/grape)
+[![Build Status](http://img.shields.io/travis/intridea/grape.svg)](https://travis-ci.org/intridea/grape)
+[![Dependency Status](https://gemnasium.com/intridea/grape.svg)](https://gemnasium.com/intridea/grape)
+[![Code Climate](https://codeclimate.com/github/intridea/grape.svg)](https://codeclimate.com/github/intridea/grape)
+[![Inline docs](http://inch-ci.org/github/intridea/grape.svg)](http://inch-ci.org/github/intridea/grape)
+
+## Table of Contents
+
+- [What is Grape?](#what-is-grape)
+- [Stable Release](#stable-release)
+- [Project Resources](#project-resources)
+- [Installation](#installation)
+- [Basic Usage](#basic-usage)
+- [Mounting](#mounting)
+  - [Rack](#rack)
+  - [ActiveRecord without Rails](#activerecord-without-rails)
+  - [Alongside Sinatra (or other frameworks)](#alongside-sinatra-or-other-frameworks)
+  - [Rails](#rails)
+  - [Modules](#modules)
+- [Versioning](#versioning)
+  - [Path](#path)
+  - [Header](#header)
+  - [Accept-Version Header](#accept-version-header)
+  - [Param](#param)
+- [Describing Methods](#describing-methods)
+- [Parameters](#parameters)
+  - [Declared](#declared)
+  - [Include Missing](#include-missing)
+- [Parameter Validation and Coercion](#parameter-validation-and-coercion)
+  - [Built-in Validators](#built-in-validators)
+  - [Namespace Validation and Coercion](#namespace-validation-and-coercion)
+  - [Custom Validators](#custom-validators)
+  - [Validation Errors](#validation-errors)
+  - [I18n](#i18n)
+- [Headers](#headers)
+- [Routes](#routes)
+- [Helpers](#helpers)
+- [Parameter Documentation](#parameter-documentation)
+- [Cookies](#cookies)
+- [HTTP Status Code](#http-status-code)
+- [Redirecting](#redirecting)
+- [Allowed Methods](#allowed-methods)
+- [Raising Exceptions](#raising-exceptions)
+  - [Default Error HTTP Status Code](#default-error-http-status-code)
+  - [Handling 404](#handling-404)
+- [Exception Handling](#exception-handling)
+  - [Rails 3.x](#rails-3x)
+- [Logging](#logging)
+- [API Formats](#api-formats)
+  - [JSONP](#jsonp)
+  - [CORS](#cors)
+- [Content-type](#content-type)
+- [API Data Formats](#api-data-formats)
+- [RESTful Model Representations](#restful-model-representations)
+  - [Grape Entities](#grape-entities)
+  - [Hypermedia](#hypermedia)
+  - [Rabl](#rabl)
+  - [Active Model Serializers](#active-model-serializers)
+- [Sending Raw or No Data](#sending-raw-or-no-data)
+- [Authentication](#authentication)
+- [Describing and Inspecting an API](#describing-and-inspecting-an-api)
+- [Current Route and Endpoint](#current-route-and-endpoint)
+- [Before and After](#before-and-after)
+- [Anchoring](#anchoring)
+- [Using Custom Middleware](#using-custom-middleware)
+  - [Rails Middleware](#rails-middleware)
+    - [Remote IP](#remote-ip)
+- [Writing Tests](#writing-tests)
+  - [Writing Tests with Rack](#writing-tests-with-rack)
+  - [Writing Tests with Rails](#writing-tests-with-rails)
+  - [Stubbing Helpers](#stubbing-helpers)
+- [Reloading API Changes in Development](#reloading-api-changes-in-development)
+  - [Reloading in Rack Applications](#reloading-in-rack-applications)
+  - [Reloading in Rails Applications](#reloading-in-rails-applications)
+- [Performance Monitoring](#performance-monitoring)
+- [Contributing to Grape](#contributing-to-grape)
+- [Hacking on Grape](#hacking-on-grape)
+- [License](#license)
+- [Copyright](#copyright)
+
+## What is Grape?
+
+Grape is a REST-like API micro-framework for Ruby. It's designed to run on Rack
+or complement existing web application frameworks such as Rails and Sinatra by
+providing a simple DSL to easily develop RESTful APIs. It has built-in support
+for common conventions, including multiple formats, subdomain/prefix restriction,
+content negotiation, versioning and much more.
+
+## Stable Release
+
+You're reading the documentation for the stable release of Grape 0.12.0.
+Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
+
+## Project Resources
+
+* Need help? [Grape Google Group](http://groups.google.com/group/ruby-grape)
+* [Grape Wiki](https://github.com/intridea/grape/wiki)
+
+## Installation
+
+Grape is available as a gem, to install it just install the gem:
+
+    gem install grape
+
+If you're using Bundler, add the gem to Gemfile.
+
+    gem 'grape'
+
+Run `bundle install`.
+
+## Basic Usage
+
+Grape APIs are Rack applications that are created by subclassing `Grape::API`.
+Below is a simple example showing some of the more common features of Grape in
+the context of recreating parts of the Twitter API.
+
+```ruby
+module Twitter
+  class API < Grape::API
+    version 'v1', using: :header, vendor: 'twitter'
+    format :json
+    prefix :api
+
+    helpers do
+      def current_user
+        @current_user ||= User.authorize!(env)
+      end
+
+      def authenticate!
+        error!('401 Unauthorized', 401) unless current_user
+      end
+    end
+
+    resource :statuses do
+      desc "Return a public timeline."
+      get :public_timeline do
+        Status.limit(20)
+      end
+
+      desc "Return a personal timeline."
+      get :home_timeline do
+        authenticate!
+        current_user.statuses.limit(20)
+      end
+
+      desc "Return a status."
+      params do
+        requires :id, type: Integer, desc: "Status id."
+      end
+      route_param :id do
+        get do
+          Status.find(params[:id])
+        end
+      end
+
+      desc "Create a status."
+      params do
+        requires :status, type: String, desc: "Your status."
+      end
+      post do
+        authenticate!
+        Status.create!({
+          user: current_user,
+          text: params[:status]
+        })
+      end
+
+      desc "Update a status."
+      params do
+        requires :id, type: String, desc: "Status ID."
+        requires :status, type: String, desc: "Your status."
+      end
+      put ':id' do
+        authenticate!
+        current_user.statuses.find(params[:id]).update({
+          user: current_user,
+          text: params[:status]
+        })
+      end
+
+      desc "Delete a status."
+      params do
+        requires :id, type: String, desc: "Status ID."
+      end
+      delete ':id' do
+        authenticate!
+        current_user.statuses.find(params[:id]).destroy
+      end
+    end
+  end
+end
+```
+
+## Mounting
+
+### Rack
+
+The above sample creates a Rack application that can be run from a rackup `config.ru` file
+with `rackup`:
+
+```ruby
+run Twitter::API
+```
+
+And would respond to the following routes:
+
+    GET /api/statuses/public_timeline
+    GET /api/statuses/home_timeline
+    GET /api/statuses/:id
+    POST /api/statuses
+    PUT /api/statuses/:id
+    DELETE /api/statuses/:id
+
+Grape will also automatically respond to HEAD and OPTIONS for all GET, and just OPTIONS for all other routes.
+
+### ActiveRecord without Rails
+
+If you want to use ActiveRecord within Grape, you will need to make sure that ActiveRecord's connection pool
+is handled correctly.
+
+The easiest way to achieve that is by using ActiveRecord's `ConnectionManagement` middleware in your
+`config.ru` before mounting Grape, e.g.:
+
+```ruby
+use ActiveRecord::ConnectionAdapters::ConnectionManagement
+
+run Twitter::API
+```
+
+### Alongside Sinatra (or other frameworks)
+
+If you wish to mount Grape alongside another Rack framework such as Sinatra, you can do so easily using
+`Rack::Cascade`:
+
+```ruby
+# Example config.ru
+
+require 'sinatra'
+require 'grape'
+
+class API < Grape::API
+  get :hello do
+    { hello: "world" }
+  end
+end
+
+class Web < Sinatra::Base
+  get '/' do
+    "Hello world."
+  end
+end
+
+use Rack::Session::Cookie
+run Rack::Cascade.new [API, Web]
+```
+
+### Rails
+
+Place API files into `app/api`. Rails expects a subdirectory that matches the name of the Ruby module and a file name that matches the name of the class. In our example, the file name location and directory for `Twitter::API` should be `app/api/twitter/api.rb`.
+
+Modify `application.rb`:
+
+```ruby
+config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb')
+config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')]
+```
+
+Modify `config/routes`:
+
+```ruby
+mount Twitter::API => '/'
+```
+
+Additionally, if the version of your Rails is 4.0+ and the application uses the default model layer of ActiveRecord, you will want to use the [hashie-forbidden_attributes gem](https://github.com/Maxim-Filimonov/hashie-forbidden_attributes). This gem disables the security feature of `strong_params` at the model layer, allowing you the use of Grape's own params validation instead.
+
+```ruby
+# Gemfile
+gem "hashie-forbidden_attributes"
+```
+
+See [below](#reloading-api-changes-in-development) for additional code that enables reloading of API changes in development.
+
+### Modules
+
+You can mount multiple API implementations inside another one. These don't have to be
+different versions, but may be components of the same API.
+
+```ruby
+class Twitter::API < Grape::API
+  mount Twitter::APIv1
+  mount Twitter::APIv2
+end
+```
+
+You can also mount on a path, which is similar to using `prefix` inside the mounted API itself.
+
+```ruby
+class Twitter::API < Grape::API
+  mount Twitter::APIv1 => '/v1'
+end
+```
+
+## Versioning
+
+There are four strategies in which clients can reach your API's endpoints: `:path`,
+`:header`, `:accept_version_header` and `:param`. The default strategy is `:path`.
+
+### Path
+
+```ruby
+version 'v1', using: :path
+```
+
+Using this versioning strategy, clients should pass the desired version in the URL.
+
+    curl http://localhost:9292/v1/statuses/public_timeline
+
+### Header
+
+```ruby
+version 'v1', using: :header, vendor: 'twitter'
+```
+
+Using this versioning strategy, clients should pass the desired version in the HTTP `Accept` head.
+
+    curl -H Accept:application/vnd.twitter-v1+json http://localhost:9292/statuses/public_timeline
+
+By default, the first matching version is used when no `Accept` header is
+supplied. This behavior is similar to routing in Rails. To circumvent this default behavior,
+one could use the `:strict` option. When this option is set to `true`, a `406 Not Acceptable` error
+is returned when no correct `Accept` header is supplied.
+
+When an invalid `Accept` header is supplied, a `406 Not Acceptable` error is returned if the `:cascade`
+option is set to `false`. Otherwise a `404 Not Found` error is returned by Rack if no other route
+matches.
+
+### HTTP Status Code
+
+By default Grape returns a 200 status code for `GET`-Requests and 201 for `POST`-Requests.
+You can use `status` to query and set the actual HTTP Status Code
+
+```ruby
+post do
+  status 202
+
+  if status == 200
+     # do some thing
+  end
+end
+```
+
+You can also use one of status codes symbols that are provided by [Rack utils](http://www.rubydoc.info/github/rack/rack/Rack/Utils#HTTP_STATUS_CODES-constant)
+
+```ruby
+post do
+  status :no_content
+end
+```
+
+### Accept-Version Header
+
+```ruby
+version 'v1', using: :accept_version_header
+```
+
+Using this versioning strategy, clients should pass the desired version in the HTTP `Accept-Version` header.
+
+    curl -H "Accept-Version:v1" http://localhost:9292/statuses/public_timeline
+
+By default, the first matching version is used when no `Accept-Version` header is
+supplied. This behavior is similar to routing in Rails. To circumvent this default behavior,
+one could use the `:strict` option. When this option is set to `true`, a `406 Not Acceptable` error
+is returned when no correct `Accept` header is supplied.
+
+### Param
+
+```ruby
+version 'v1', using: :param
+```
+
+Using this versioning strategy, clients should pass the desired version as a request parameter,
+either in the URL query string or in the request body.
+
+    curl http://localhost:9292/statuses/public_timeline?apiver=v1
+
+The default name for the query parameter is 'apiver' but can be specified using the `:parameter` option.
+
+```ruby
+version 'v1', using: :param, parameter: "v"
+```
+
+    curl http://localhost:9292/statuses/public_timeline?v=v1
+
+
+## Describing Methods
+
+You can add a description to API methods and namespaces.
+
+```ruby
+desc "Returns your public timeline." do
+  detail 'more details'
+  params  API::Entities::Status.documentation
+  success API::Entities::Entity
+  failure [[401, 'Unauthorized', "Entities::Error"]]
+  named 'My named route'
+  headers [XAuthToken: {
+             description: 'Valdates your identity',
+             required: true
+           },
+           XOptionalHeader: {
+             description: 'Not really needed',
+            required: false
+           }
+          ]
+end
+get :public_timeline do
+  Status.limit(20)
+end
+```
+
+* `detail`: A more enhanced description
+* `params`: Define parameters directly from an `Entity`
+* `success`: (former entity) The `Entity` to be used to present by default this route
+* `failure`: (former http_codes) A definition of the used failure HTTP Codes and Entities
+* `named`: A helper to give a route a name and find it with this name in the documentation Hash
+* `headers`: A definition of the used Headers
+
+## Parameters
+
+Request parameters are available through the `params` hash object. This includes `GET`, `POST`
+and `PUT` parameters, along with any named parameters you specify in your route strings.
+
+```ruby
+get :public_timeline do
+  Status.order(params[:sort_by])
+end
+```
+
+Parameters are automatically populated from the request body on `POST` and `PUT` for form input, JSON and
+XML content-types.
+
+The request:
+
+```
+curl -d '{"text": "140 characters"}' 'http://localhost:9292/statuses' -H Content-Type:application/json -v
+```
+
+The Grape endpoint:
+
+```ruby
+post '/statuses' do
+  Status.create!(text: params[:text])
+end
+```
+
+Multipart POSTs and PUTs are supported as well.
+
+The request:
+
+```
+curl --form image_file='@image.jpg;type=image/jpg' http://localhost:9292/upload
+```
+
+The Grape endpoint:
+
+```ruby
+post "upload" do
+  # file in params[:image_file]
+end
+```
+
+In the case of conflict between either of:
+
+* route string parameters
+* `GET`, `POST` and `PUT` parameters
+* the contents of the request body on `POST` and `PUT`
+
+route string parameters will have precedence.
+
+#### Declared
+
+Grape allows you to access only the parameters that have been declared by your `params` block. It filters out the params that have been passed, but are not allowed. Let's have the following api:
+
+````ruby
+format :json
+
+post 'users/signup' do
+  { "declared_params" => declared(params) }
+end
+````
+
+If we do not specify any params, declared will return an empty Hashie::Mash instance.
+
+**Request**
+
+````bash
+curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d '{"user": {"first_name":"first name", "last_name": "last name"}}'
+````
+
+**Response**
+
+````json
+{
+  "declared_params": {}
+}
+
+````
+
+Once we add parameters requirements, grape will start returning only the declared params.
+
+````ruby
+format :json
+
+params do
+  requires :user, type: Hash do
+    requires :first_name, type: String
+    requires :last_name, type: String
+  end
+end
+
+post 'users/signup' do
+  { "declared_params" => declared(params) }
+end
+````
+
+**Request**
+
+````bash
+curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d '{"user": {"first_name":"first name", "last_name": "last name", "random": "never shown"}}'
+````
+
+**Response**
+
+````json
+{
+  "declared_params": {
+    "user": {
+      "first_name": "first name",
+      "last_name": "last name"
+    }
+  }
+}
+````
+
+Returned hash is a Hashie::Mash instance so you can access parameters via dot notation:
+
+```ruby
+  declared(params).user == declared(params)["user"]
+```
+
+#### Include missing
+
+By default `declared(params)` returns parameters that has `nil` value. If you want to return only the parameters that have any value, you can use the `include_missing` option. By default it is `true`. Let's have the following api:
+
+````ruby
+format :json
+
+params do
+  requires :first_name, type: String
+  optional :last_name, type: String
+end
+
+post 'users/signup' do
+  { "declared_params" => declared(params, include_missing: false) }
+end
+````
+
+**Request**
+
+````bash
+curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d '{"user": {"first_name":"first name", "random": "never shown"}}'
+````
+
+**Response with include_missing:false**
+
+````json
+{
+  "declared_params": {
+    "user": {
+      "first_name": "first name"
+    }
+  }
+}
+````
+
+**Response with include_missing:true**
+
+````json
+{
+  "declared_params": {
+    "first_name": "first name",
+    "last_name": null
+  }
+}
+````
+
+It also works on nested hashes:
+
+````ruby
+format :json
+
+params do
+  requires :user, :type => Hash do
+    requires :first_name, type: String
+    optional :last_name, type: String
+    requires :address, :type => Hash do
+      requires :city, type: String
+      optional :region, type: String
+    end
+  end
+end
+
+post 'users/signup' do
+  { "declared_params" => declared(params, include_missing: false) }
+end
+````
+
+**Request**
+
+````bash
+curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d '{"user": {"first_name":"first name", "random": "never shown", "address": { "city": "SF"}}}'
+````
+
+**Response with include_missing:false**
+
+````json
+{
+  "declared_params": {
+    "user": {
+      "first_name": "first name",
+      "address": {
+        "city": "SF"
+      }
+    }
+  }
+}
+````
+
+**Response with include_missing:true**
+
+````json
+{
+  "declared_params": {
+    "user": {
+      "first_name": "first name",
+      "last_name": null,
+      "address": {
+        "city": "Zurich",
+        "region": null
+      }
+    }
+  }
+}
+````
+
+Note that an attribute with a `nil` value is not considered *missing* and will also be returned
+when `include_missing` is set to `false`:
+
+**Request**
+
+````bash
+curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d '{"user": {"first_name":"first name", "last_name": null, "address": { "city": "SF"}}}'
+````
+
+**Response with include_missing:false**
+
+````json
+{
+  "declared_params": {
+    "user": {
+      "first_name": "first name",
+      "last_name": null,
+      "address": { "city": "SF"}
+    }
+  }
+}
+````
+
+## Parameter Validation and Coercion
+
+You can define validations and coercion options for your parameters using a `params` block.
+
+```ruby
+params do
+  requires :id, type: Integer
+  optional :text, type: String, regexp: /^[a-z]+$/
+  group :media do
+    requires :url
+  end
+  optional :audio do
+    requires :format, type: Symbol, values: [:mp3, :wav, :aac, :ogg], default: :mp3
+  end
+  mutually_exclusive :media, :audio
+end
+put ':id' do
+  # params[:id] is an Integer
+end
+```
+
+When a type is specified an implicit validation is done after the coercion to ensure
+the output type is the one declared.
+
+Optional parameters can have a default value.
+
+```ruby
+params do
+  optional :color, type: String, default: 'blue'
+  optional :random_number, type: Integer, default: -> { Random.rand(1..100) }
+  optional :non_random_number, type: Integer, default:  Random.rand(1..100)
+end
+```
+
+Note that default values will be passed through to any validation options specified.
+The following example will always fail if `:color` is not explicitly provided.
+
+```ruby
+params do
+  optional :color, type: String, default: 'blue', values: ['red', 'green']
+end
+```
+
+The correct implementation is to ensure the default value passes all validations.
+
+```ruby
+params do
+  optional :color, type: String, default: 'blue', values: ['blue', 'red', 'green']
+end
+```
+
+#### Validation of Nested Parameters
+
+Parameters can be nested using `group` or by calling `requires` or `optional` with a block.
+In the above example, this means `params[:media][:url]` is required along with `params[:id]`,
+and `params[:audio][:format]` is required only if `params[:audio]` is present.
+With a block, `group`, `requires` and `optional` accept an additional option `type` which can
+be either `Array` or `Hash`, and defaults to `Array`. Depending on the value, the nested
+parameters will be treated either as values of a hash or as values of hashes in an array.
+
+```ruby
+params do
+  optional :preferences, type: Array do
+    requires :key
+    requires :value
+  end
+
+  requires :name, type: Hash do
+    requires :first_name
+    requires :last_name
+  end
+end
+```
+
+### Built-in Validators
+
+#### `allow_blank`
+
+Parameters can be defined as `allow_blank`, ensuring that they contain a value. By default, `requires`
+only validates that a parameter was sent in the request, regardless its value. With `allow_blank: false`,
+empty values or whitespace only values are invalid.
+
+`allow_blank` can be combined with both `requires` and `optional`. If the parameter is required, it has to contain
+a value. If it's optional, it's possible to not send it in the request, but if it's being sent, it has to have
+some value, and not an empty string/only whitespaces.
+
+
+```ruby
+params do
+  requires :username, allow_blank: false
+  optional :first_name, allow_blank: false
+end
+```
+
+#### `values`
+
+Parameters can be restricted to a specific set of values with the `:values` option.
+
+Default values are eagerly evaluated. Above `:non_random_number` will evaluate to the same
+number for each call to the endpoint of this `params` block. To have the default evaluate
+lazily with each request use a lambda, like `:random_number` above.
+
+```ruby
+params do
+  requires :status, type: Symbol, values: [:not_started, :processing, :done]
+  optional :numbers, type: Array[Integer], default: 1, values: [1, 2, 3, 5, 8]
+end
+```
+
+Supplying a range to the `:values` option ensures that the parameter is (or parameters are) included in that range (using `Range#include?`).
+
+```ruby
+params do
+  requires :latitude, type: Float, values: -90.0..+90.0
+  requires :longitude, type: Float, values: -180.0..+180.0
+  optional :letters, type: Array[String], values: 'a'..'z'
+end
+```
+
+Note that *both* range endpoints have to be a `#kind_of?` your `:type` option (if you don't supplied the `:type` option, it will be guessed to be equal to the class of the range's first endpoint). So the following is invalid:
+
+```ruby
+params do
+  requires :invalid1, type: Float, values: 0..10 # 0.kind_of?(Float) => false
+  optional :invalid2, values: 0..10.0 # 10.0.kind_of?(0.class) => false
+end
+```
+
+The `:values` option can also be supplied with a `Proc`, evaluated lazily with each request.
+For example, given a status model you may want to restrict by hashtags that you have
+previously defined in the `HashTag` model.
+
+```ruby
+params do
+  requires :hashtag, type: String, values: -> { Hashtag.all.map(&:tag) }
+end
+```
+
+#### `regexp`
+
+Parameters can be restricted to match a specific regular expression with the `:regexp` option. If the value
+does not match the regular expression an error will be returned. Note that this is true for both `requires`
+and `optional` parameters.
+
+```ruby
+params do
+  requires :email, regexp: /.+ at .+/
+end
+```
+
+The validator will pass if the parameter was sent without value. To ensure that the parameter contains a value, use `allow_blank: false`.
+
+```ruby
+params do
+  requires :email, allow_blank: false, regexp: /.+ at .+/
+end
+```
+
+#### `mutually_exclusive`
+
+Parameters can be defined as `mutually_exclusive`, ensuring that they aren't present at the same time in a request.
+
+```ruby
+params do
+  optional :beer
+  optional :wine
+  mutually_exclusive :beer, :wine
+end
+```
+
+Multiple sets can be defined:
+
+```ruby
+params do
+  optional :beer
+  optional :wine
+  mutually_exclusive :beer, :wine
+  optional :scotch
+  optional :aquavit
+  mutually_exclusive :scotch, :aquavit
+end
+```
+
+**Warning**: Never define mutually exclusive sets with any required params. Two mutually exclusive required params will mean params are never valid, thus making the endpoint useless. One required param mutually exclusive with an optional param will mean the latter is never valid.
+
+#### `exactly_one_of`
+
+Parameters can be defined as 'exactly_one_of', ensuring that exactly one parameter gets selected.
+
+```ruby
+params do
+  optional :beer
+  optional :wine
+  exactly_one_of :beer, :wine
+end
+```
+
+#### `at_least_one_of`
+
+Parameters can be defined as 'at_least_one_of', ensuring that at least one parameter gets selected.
+
+```ruby
+params do
+  optional :beer
+  optional :wine
+  optional :juice
+  at_least_one_of :beer, :wine, :juice
+end
+```
+
+#### `all_or_none_of`
+
+Parameters can be defined as 'all_or_none_of', ensuring that all or none of parameters gets selected.
+
+```ruby
+params do
+  optional :beer
+  optional :wine
+  optional :juice
+  all_or_none_of :beer, :wine, :juice
+end
+```
+
+#### Nested `mutually_exclusive`, `exactly_one_of`, `at_least_one_of`, `all_or_none_of`
+
+All of these methods can be used at any nested level.
+
+```ruby
+params do
+  requires :food do
+    optional :meat
+    optional :fish
+    optional :rice
+    at_least_one_of :meat, :fish, :rice
+  end
+  group :drink do
+    optional :beer
+    optional :wine
+    optional :juice
+    exactly_one_of :beer, :wine, :juice
+  end
+  optional :dessert do
+    optional :cake
+    optional :icecream
+    mutually_exclusive :cake, :icecream
+  end
+  optional :recipe do
+    optional :oil
+    optional :meat
+    all_or_none_of :oil, :meat
+  end
+end
+```
+
+### Namespace Validation and Coercion
+
+Namespaces allow parameter definitions and apply to every method within the namespace.
+
+```ruby
+namespace :statuses do
+  params do
+    requires :user_id, type: Integer, desc: "A user ID."
+  end
+  namespace ":user_id" do
+    desc "Retrieve a user's status."
+    params do
+      requires :status_id, type: Integer, desc: "A status ID."
+    end
+    get ":status_id" do
+      User.find(params[:user_id]).statuses.find(params[:status_id])
+    end
+  end
+end
+```
+
+The `namespace` method has a number of aliases, including: `group`, `resource`,
+`resources`, and `segment`. Use whichever reads the best for your API.
+
+You can conveniently define a route parameter as a namespace using `route_param`.
+
+```ruby
+namespace :statuses do
+  route_param :id do
+    desc "Returns all replies for a status."
+    get 'replies' do
+      Status.find(params[:id]).replies
+    end
+    desc "Returns a status."
+    get do
+      Status.find(params[:id])
+    end
+  end
+end
+```
+
+### Custom Validators
+
+```ruby
+class AlphaNumeric < Grape::Validations::Base
+  def validate_param!(attr_name, params)
+    unless params[attr_name] =~ /^[[:alnum:]]+$/
+      fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: "must consist of alpha-numeric characters"
+    end
+  end
+end
+```
+
+```ruby
+params do
+  requires :text, alpha_numeric: true
+end
+```
+
+You can also create custom classes that take parameters.
+
+```ruby
+class Length < Grape::Validations::Base
+  def validate_param!(attr_name, params)
+    unless params[attr_name].length <= @option
+      fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: "must be at the most #{@option} characters long"
+    end
+  end
+end
+```
+
+```ruby
+params do
+  requires :text, length: 140
+end
+```
+
+### Validation Errors
+
+Validation and coercion errors are collected and an exception of type `Grape::Exceptions::ValidationErrors` is raised. If the exception goes uncaught it will respond with a status of 400 and an error message. The validation errors are grouped by parameter name and can be accessed via `Grape::Exceptions::ValidationErrors#errors`.
+
+
+The default response from a `Grape::Exceptions::ValidationErrors` is a humanly readable string, such as "beer, wine are mutually exclusive", in the following example.
+
+```ruby
+params do
+  optional :beer
+  optional :wine
+  optional :juice
+  exactly_one_of :beer, :wine, :juice
+end
+```
+
+You can rescue a `Grape::Exceptions::ValidationErrors` and respond with a custom response or turn the response into well-formatted JSON for a JSON API that separates individual parameters and the corresponding error messages. The following `rescue_from` example produces `[{"params":["beer","wine"],"messages":["are mutually exclusive"]}]`.
+
+```ruby
+format :json
+subject.rescue_from Grape::Exceptions::ValidationErrors do |e|
+  error! e, 400
+end
+```
+
+### I18n
+
+Grape supports I18n for parameter-related error messages, but will fallback to English if
+translations for the default locale have not been provided. See [en.yml](lib/grape/locale/en.yml) for message keys.
+
+## Headers
+
+Request headers are available through the `headers` helper or from `env` in their original form.
+
+```ruby
+get do
+  error!('Unauthorized', 401) unless headers['Secret-Password'] == 'swordfish'
+end
+```
+
+```ruby
+get do
+  error!('Unauthorized', 401) unless env['HTTP_SECRET_PASSWORD'] == 'swordfish'
+end
+```
+
+You can set a response header with `header` inside an API.
+
+```ruby
+header 'X-Robots-Tag', 'noindex'
+```
+
+When raising `error!`, pass additional headers as arguments.
+
+```ruby
+error! 'Unauthorized', 401, 'X-Error-Detail' => 'Invalid token.'
+```
+
+## Routes
+
+Optionally, you can define requirements for your named route parameters using regular
+expressions on namespace or endpoint. The route will match only if all requirements are met.
+
+```ruby
+get ':id', requirements: { id: /[0-9]*/ } do
+  Status.find(params[:id])
+end
+
+namespace :outer, requirements: { id: /[0-9]*/ } do
+  get :id do
+  end
+
+  get ":id/edit" do
+  end
+end
+```
+
+## Helpers
+
+You can define helper methods that your endpoints can use with the `helpers`
+macro by either giving a block or a module.
+
+```ruby
+module StatusHelpers
+  def user_info(user)
+    "#{user} has statused #{user.statuses} status(s)"
+  end
+end
+
+class API < Grape::API
+  # define helpers with a block
+  helpers do
+    def current_user
+      User.find(params[:user_id])
+    end
+  end
+
+  # or mix in a module
+  helpers StatusHelpers
+
+  get 'info' do
+    # helpers available in your endpoint and filters
+    user_info(current_user)
+  end
+end
+```
+
+You can define reusable `params` using `helpers`.
+
+```ruby
+class API < Grape::API
+  helpers do
+    params :pagination do
+      optional :page, type: Integer
+      optional :per_page, type: Integer
+    end
+  end
+
+  desc "Get collection"
+  params do
+    use :pagination # aliases: includes, use_scope
+  end
+  get do
+    Collection.page(params[:page]).per(params[:per_page])
+  end
+end
+```
+
+You can also define reusable `params` using shared helpers.
+
+```ruby
+module SharedParams
+  extend Grape::API::Helpers
+
+  params :period do
+    optional :start_date
+    optional :end_date
+  end
+
+  params :pagination do
+    optional :page, type: Integer
+    optional :per_page, type: Integer
+  end
+end
+
+class API < Grape::API
+  helpers SharedParams
+
+  desc "Get collection."
+  params do
+    use :period, :pagination
+  end
+
+  get do
+    Collection
+      .from(params[:start_date])
+      .to(params[:end_date])
+      .page(params[:page])
+      .per(params[:per_page])
+  end
+end
+```
+
+Helpers support blocks that can help set default values. The following API can return a collection sorted by `id` or `created_at` in `asc` or `desc` order.
+
+```ruby
+module SharedParams
+  extend Grape::API::Helpers
+
+  params :order do |options|
+    optional :order_by, type:Symbol, values:options[:order_by], default:options[:default_order_by]
+    optional :order, type:Symbol, values:%i(asc desc), default:options[:default_order]
+  end
+end
+
+class API < Grape::API
+  helpers SharedParams
+
+  desc "Get a sorted collection."
+  params do
+    use :order, order_by:%i(id created_at), default_order_by: :created_at, default_order: :asc
+  end
+
+  get do
+    Collection.send(params[:order], params[:order_by])
+  end
+end
+```
+
+## Parameter Documentation
+
+You can attach additional documentation to `params` using a `documentation` hash.
+
+```ruby
+params do
+  optional :first_name, type: String, documentation: { example: 'Jim' }
+  requires :last_name, type: String, documentation: { example: 'Smith' }
+end
+```
+
+## Cookies
+
+You can set, get and delete your cookies very simply using `cookies` method.
+
+```ruby
+class API < Grape::API
+  get 'status_count' do
+    cookies[:status_count] ||= 0
+    cookies[:status_count] += 1
+    { status_count: cookies[:status_count] }
+  end
+
+  delete 'status_count' do
+    { status_count: cookies.delete(:status_count) }
+  end
+end
+```
+
+Use a hash-based syntax to set more than one value.
+
+```ruby
+cookies[:status_count] = {
+  value: 0,
+  expires: Time.tomorrow,
+  domain: '.twitter.com',
+  path: '/'
+}
+
+cookies[:status_count][:value] +=1
+```
+
+Delete a cookie with `delete`.
+
+```ruby
+cookies.delete :status_count
+```
+
+Specify an optional path.
+
+```ruby
+cookies.delete :status_count, path: '/'
+```
+
+## Redirecting
+
+You can redirect to a new url temporarily (302) or permanently (301).
+
+```ruby
+redirect '/statuses'
+```
+
+```ruby
+redirect '/statuses', permanent: true
+```
+
+## Allowed Methods
+
+When you add a `GET` route for a resource, a route for the `HEAD`
+method will also be added automatically. You can disable this
+behavior with `do_not_route_head!`.
+
+``` ruby
+class API < Grape::API
+  do_not_route_head!
+
+  get '/example' do
+    # only responds to GET
+  end
+end
+```
+
+When you add a route for a resource, a route for the `OPTIONS`
+method will also be added. The response to an OPTIONS request will
+include an "Allow" header listing the supported methods.
+
+```ruby
+class API < Grape::API
+  get '/rt_count' do
+    { rt_count: current_user.rt_count }
+  end
+
+  params do
+    requires :value, type: Integer, desc: 'Value to add to the rt count.'
+  end
+  put '/rt_count' do
+    current_user.rt_count += params[:value].to_i
+    { rt_count: current_user.rt_count }
+  end
+end
+```
+
+``` shell
+curl -v -X OPTIONS http://localhost:3000/rt_count
+
+> OPTIONS /rt_count HTTP/1.1
+>
+< HTTP/1.1 204 No Content
+< Allow: OPTIONS, GET, PUT
+```
+
+You can disable this behavior with `do_not_route_options!`.
+
+If a request for a resource is made with an unsupported HTTP method, an
+HTTP 405 (Method Not Allowed) response will be returned.
+
+``` shell
+curl -X DELETE -v http://localhost:3000/rt_count/
+
+> DELETE /rt_count/ HTTP/1.1
+> Host: localhost:3000
+>
+< HTTP/1.1 405 Method Not Allowed
+< Allow: OPTIONS, GET, PUT
+```
+
+## Raising Exceptions
+
+You can abort the execution of an API method by raising errors with `error!`.
+
+```ruby
+error! 'Access Denied', 401
+```
+
+You can also return JSON formatted objects by raising error! and passing a hash
+instead of a message.
+
+```ruby
+error!({ error: "unexpected error", detail: "missing widget" }, 500)
+```
+
+You can present documented errors with a Grape entity using the the [grape-entity](https://github.com/intridea/grape-entity) gem.
+
+```ruby
+module API
+  class Error < Grape::Entity
+    expose :code
+    expose :message
+  end
+end
+```
+
+The following example specifies the entity to use in the `http_codes` definition.
+
+```
+desc 'My Route' do
+ failure [[408, 'Unauthorized', API::Error]]
+end
+error!({ message: 'Unauthorized' }, 408)
+```
+
+The following example specifies the presented entity explicitly in the error message.
+
+```ruby
+desc 'My Route' do
+ failure [[408, 'Unauthorized']]
+end
+error!({ message: 'Unauthorized', with: API::Error }, 408)
+```
+
+### Default Error HTTP Status Code
+
+By default Grape returns a 500 status code from `error!`. You can change this with `default_error_status`.
+
+``` ruby
+class API < Grape::API
+  default_error_status 400
+  get '/example' do
+    error! "This should have http status code 400"
+  end
+end
+```
+
+### Handling 404
+
+For Grape to handle all the 404s for your API, it can be useful to use a catch-all.
+In its simplest form, it can be like:
+
+```ruby
+route :any, '*path' do
+  error! # or something else
+end
+```
+
+It is very crucial to __define this endpoint at the very end of your API__, as it
+literally accepts every request.
+
+## Exception Handling
+
+Grape can be told to rescue all exceptions and return them in the API format.
+
+```ruby
+class Twitter::API < Grape::API
+  rescue_from :all
+end
+```
+
+You can also rescue specific exceptions.
+
+```ruby
+class Twitter::API < Grape::API
+  rescue_from ArgumentError, UserDefinedError
+end
+```
+
+In this case ```UserDefinedError``` must be inherited from ```StandardError```.
+
+The error format will match the request format. See "Content-Types" below.
+
+Custom error formatters for existing and additional types can be defined with a proc.
+
+```ruby
+class Twitter::API < Grape::API
+  error_formatter :txt, lambda { |message, backtrace, options, env|
+    "error: #{message} from #{backtrace}"
+  }
+end
+```
+
+You can also use a module or class.
+
+```ruby
+module CustomFormatter
+  def self.call(message, backtrace, options, env)
+    { message: message, backtrace: backtrace }
+  end
+end
+
+class Twitter::API < Grape::API
+  error_formatter :custom, CustomFormatter
+end
+```
+
+You can rescue all exceptions with a code block. The `error!` wrapper
+automatically sets the default error code and content-type.
+
+```ruby
+class Twitter::API < Grape::API
+  rescue_from :all do |e|
+    error!("rescued from #{e.class.name}")
+  end
+end
+```
+
+Optionally, you can set the format, status code and headers.
+
+```ruby
+class Twitter::API < Grape::API
+  format :json
+  rescue_from :all do |e|
+    error!({ error: "Server error.", 500, { 'Content-Type' => 'text/error' } })
+  end
+end
+```
+
+
+You can also rescue specific exceptions with a code block and handle the Rack
+response at the lowest level.
+
+```ruby
+class Twitter::API < Grape::API
+  rescue_from :all do |e|
+    Rack::Response.new([ e.message ], 500, { "Content-type" => "text/error" }).finish
+  end
+end
+```
+
+Or rescue specific exceptions.
+
+```ruby
+class Twitter::API < Grape::API
+  rescue_from ArgumentError do |e|
+    error!("ArgumentError: #{e.message}")
+  end
+
+  rescue_from NotImplementedError do |e|
+    error!("NotImplementedError: #{e.message}")
+  end
+end
+```
+
+By default, `rescue_from` will rescue the exceptions listed and all their subclasses.
+
+Assume you have the following exception classes defined.
+
+```ruby
+module APIErrors
+  class ParentError < StandardError; end
+  class ChildError < ParentError; end
+end
+```
+
+Then the following `rescue_from` clause will rescue exceptions of type `APIErrors::ParentError` and its subclasses (in this case `APIErrors::ChildError`).
+
+```ruby
+rescue_from APIErrors::ParentError do |e|
+    error!({
+      error: "#{e.class} error",
+      message: e.message
+    }, e.status)
+end
+```
+
+To only rescue the base exception class, set `rescue_subclasses: false`.
+The code below will rescue exceptions of type `RuntimeError` but _not_ its subclasses.
+
+```ruby
+rescue_from RuntimeError, rescue_subclasses: false do |e|
+    error!({
+      status: e.status,
+      message: e.message,
+      errors: e.errors
+    }, e.status)
+end
+```
+
+#### Rails 3.x
+
+When mounted inside containers, such as Rails 3.x, errors like "404 Not Found" or
+"406 Not Acceptable" will likely be handled and rendered by Rails handlers. For instance,
+accessing a nonexistent route "/api/foo" raises a 404, which inside rails will ultimately
+be translated to an `ActionController::RoutingError`, which most likely will get rendered
+to a HTML error page.
+
+Most APIs will enjoy preventing downstream handlers from handling errors. You may set the
+`:cascade` option to `false` for the entire API or separately on specific `version` definitions,
+which will remove the `X-Cascade: true` header from API responses.
+
+```ruby
+cascade false
+```
+
+```ruby
+version 'v1', using: :header, vendor: 'twitter', cascade: false
+```
+
+## Logging
+
+`Grape::API` provides a `logger` method which by default will return an instance of the `Logger`
+class from Ruby's standard library.
+
+To log messages from within an endpoint, you need to define a helper to make the logger
+available in the endpoint context.
+
+```ruby
+class API < Grape::API
+  helpers do
+    def logger
+      API.logger
+    end
+  end
+  post '/statuses' do
+    # ...
+    logger.info "#{current_user} has statused"
+  end
+end
+```
+
+You can also set your own logger.
+
+```ruby
+class MyLogger
+  def warning(message)
+    puts "this is a warning: #{message}"
+  end
+end
+
+class API < Grape::API
+  logger MyLogger.new
+  helpers do
+    def logger
+      API.logger
+    end
+  end
+  get '/statuses' do
+    logger.warning "#{current_user} has statused"
+  end
+end
+```
+
+For similar to Rails request logging try the [grape_logging](https://github.com/aserafin/grape_logging) gem.
+
+## API Formats
+
+Your API can declare which content-types to support by using `content_type`. If you do not specify any, Grape will support
+_XML_, _JSON_, _BINARY_, and _TXT_ content-types. The default format is `:txt`; you can change this with `default_format`.
+Essentially, the two APIs below are equivalent.
+
+```ruby
+class Twitter::API < Grape::API
+  # no content_type declarations, so Grape uses the defaults
+end
+
+class Twitter::API < Grape::API
+  # the following declarations are equivalent to the defaults
+
+  content_type :xml, 'application/xml'
+  content_type :json, 'application/json'
+  content_type :binary, 'application/octet-stream'
+  content_type :txt, 'text/plain'
+
+  default_format :txt
+end
+```
+
+If you declare any `content_type` whatsoever, the Grape defaults will be overridden. For example, the following API will only
+support the `:xml` and `:rss` content-types, but not `:txt`, `:json`, or `:binary`. Importantly, this means the `:txt`
+default format is not supported! So, make sure to set a new `default_format`.
+
+```ruby
+class Twitter::API < Grape::API
+  content_type :xml, 'application/xml'
+  content_type :rss, 'application/xml+rss'
+
+  default_format :xml
+end
+```
+
+Serialization takes place automatically. For example, you do not have to call `to_json` in each JSON API endpoint
+implementation. The response format (and thus the automatic serialization) is determined in the following order:
+* Use the file extension, if specified. If the file is .json, choose the JSON format.
+* Use the value of the `format` parameter in the query string, if specified.
+* Use the format set by the `format` option, if specified.
+* Attempt to find an acceptable format from the `Accept` header.
+* Use the default format, if specified by the `default_format` option.
+* Default to `:txt`.
+
+For example, consider the following API.
+
+```ruby
+class MultipleFormatAPI < Grape::API
+  content_type :xml, 'application/xml'
+  content_type :json, 'application/json'
+
+  default_format :json
+
+  get :hello do
+    { hello: 'world' }
+  end
+end
+```
+
+* `GET /hello` (with an `Accept: */*` header) does not have an extension or a `format` parameter, so it will respond with
+  JSON (the default format).
+* `GET /hello.xml` has a recognized extension, so it will respond with XML.
+* `GET /hello?format=xml` has a recognized `format` parameter, so it will respond with XML.
+* `GET /hello.xml?format=json` has a recognized extension (which takes precedence over the `format` parameter), so it will
+  respond with XML.
+* `GET /hello.xls` (with an `Accept: */*` header) has an extension, but that extension is not recognized, so it will respond
+  with JSON (the default format).
+* `GET /hello.xls` with an `Accept: application/xml` header has an unrecognized extension, but the `Accept` header
+  corresponds to a recognized format, so it will respond with XML.
+* `GET /hello.xls` with an `Accept: text/plain` header has an unrecognized extension *and* an unrecognized `Accept` header,
+  so it will respond with JSON (the default format).
+
+You can override this process explicitly by specifying `env['api.format']` in the API itself.
+For example, the following API will let you upload arbitrary files and return their contents as an attachment with the correct MIME type.
+
+```ruby
+class Twitter::API < Grape::API
+  post "attachment" do
+    filename = params[:file][:filename]
+    content_type MIME::Types.type_for(filename)[0].to_s
+    env['api.format'] = :binary # there's no formatter for :binary, data will be returned "as is"
+    header "Content-Disposition", "attachment; filename*=UTF-8''#{URI.escape(filename)}"
+    params[:file][:tempfile].read
+  end
+end
+```
+
+You can have your API only respond to a single format with `format`. If you use this, the API will **not** respond to file
+extensions other than specified in `format`. For example, consider the following API.
+
+```ruby
+class SingleFormatAPI < Grape::API
+  format :json
+
+  get :hello do
+    { hello: 'world' }
+  end
+end
+```
+
+* `GET /hello` will respond with JSON.
+* `GET /hello.json` will respond with JSON.
+* `GET /hello.xml`, `GET /hello.foobar`, or *any* other extension will respond with an HTTP 404 error code.
+* `GET /hello?format=xml` will respond with an HTTP 406 error code, because the XML format specified by the request parameter
+  is not supported.
+* `GET /hello` with an `Accept: application/xml` header will still respond with JSON, since it could not negotiate a
+  recognized content-type from the headers and JSON is the effective default.
+
+The formats apply to parsing, too. The following API will only respond to the JSON content-type and will not parse any other
+input than `application/json`, `application/x-www-form-urlencoded`, `multipart/form-data`, `multipart/related` and
+`multipart/mixed`. All other requests will fail with an HTTP 406 error code.
+
+```ruby
+class Twitter::API < Grape::API
+  format :json
+end
+```
+
+When the content-type is omitted, Grape will return a 406 error code unless `default_format` is specified.
+The following API will try to parse any data without a content-type using a JSON parser.
+
+```ruby
+class Twitter::API < Grape::API
+  format :json
+  default_format :json
+end
+```
+
+If you combine `format` with `rescue_from :all`, errors will be rendered using the same format.
+If you do not want this behavior, set the default error formatter with `default_error_formatter`.
+
+```ruby
+class Twitter::API < Grape::API
+  format :json
+  content_type :txt, "text/plain"
+  default_error_formatter :txt
+end
+```
+
+Custom formatters for existing and additional types can be defined with a proc.
+
+```ruby
+class Twitter::API < Grape::API
+  content_type :xls, "application/vnd.ms-excel"
+  formatter :xls, lambda { |object, env| object.to_xls }
+end
+```
+
+You can also use a module or class.
+
+```ruby
+module XlsFormatter
+  def self.call(object, env)
+    object.to_xls
+  end
+end
+
+class Twitter::API < Grape::API
+  content_type :xls, "application/vnd.ms-excel"
+  formatter :xls, XlsFormatter
+end
+```
+
+Built-in formatters are the following.
+
+* `:json`: use object's `to_json` when available, otherwise call `MultiJson.dump`
+* `:xml`: use object's `to_xml` when available, usually via `MultiXml`, otherwise call `to_s`
+* `:txt`: use object's `to_txt` when available, otherwise `to_s`
+* `:serializable_hash`: use object's `serializable_hash` when available, otherwise fallback to `:json`
+* `:binary`: data will be returned "as is"
+
+### JSONP
+
+Grape supports JSONP via [Rack::JSONP](https://github.com/rack/rack-contrib), part of the
+[rack-contrib](https://github.com/rack/rack-contrib) gem. Add `rack-contrib` to your `Gemfile`.
+
+```ruby
+require 'rack/contrib'
+
+class API < Grape::API
+  use Rack::JSONP
+  format :json
+  get '/' do
+    'Hello World'
+  end
+end
+```
+
+### CORS
+
+Grape supports CORS via [Rack::CORS](https://github.com/cyu/rack-cors), part of the
+[rack-cors](https://github.com/cyu/rack-cors) gem. Add `rack-cors` to your `Gemfile`,
+then use the middleware in your config.ru file.
+
+```ruby
+require 'rack/cors'
+
+use Rack::Cors do
+  allow do
+    origins '*'
+    resource '*', headers: :any, methods: :get
+  end
+end
+
+run Twitter::API
+
+```
+
+## Content-type
+
+Content-type is set by the formatter. You can override the content-type of the response at runtime
+by setting the `Content-Type` header.
+
+```ruby
+class API < Grape::API
+  get '/home_timeline_js' do
+    content_type "application/javascript"
+    "var statuses = ...;"
+  end
+end
+```
+
+## API Data Formats
+
+Grape accepts and parses input data sent with the POST and PUT methods as described in the Parameters
+section above. It also supports custom data formats. You must declare additional content-types via
+`content_type` and optionally supply a parser via `parser` unless a parser is already available within
+Grape to enable a custom format. Such a parser can be a function or a class.
+
+With a parser, parsed data is available "as-is" in `env['api.request.body']`.
+Without a parser, data is available "as-is" and in `env['api.request.input']`.
+
+The following example is a trivial parser that will assign any input with the "text/custom" content-type
+to `:value`. The parameter will be available via `params[:value]` inside the API call.
+
+```ruby
+module CustomParser
+  def self.call(object, env)
+    { value: object.to_s }
+  end
+end
+```
+
+```ruby
+content_type :txt, "text/plain"
+content_type :custom, "text/custom"
+parser :custom, CustomParser
+
+put "value" do
+  params[:value]
+end
+```
+
+You can invoke the above API as follows.
+
+```
+curl -X PUT -d 'data' 'http://localhost:9292/value' -H Content-Type:text/custom -v
+```
+
+You can disable parsing for a content-type with `nil`. For example, `parser :json, nil` will disable JSON parsing altogether. The request data is then available as-is in `env['api.request.body']`.
+
+## RESTful Model Representations
+
+Grape supports a range of ways to present your data with some help from a generic `present` method,
+which accepts two arguments: the object to be presented and the options associated with it. The options
+hash may include `:with`, which defines the entity to expose.
+
+### Grape Entities
+
+Add the [grape-entity](https://github.com/intridea/grape-entity) gem to your Gemfile.
+Please refer to the [grape-entity documentation](https://github.com/intridea/grape-entity/blob/master/README.md)
+for more details.
+
+The following example exposes statuses.
+
+```ruby
+module API
+  module Entities
+    class Status < Grape::Entity
+      expose :user_name
+      expose :text, documentation: { type: "string", desc: "Status update text." }
+      expose :ip, if: { type: :full }
+      expose :user_type, :user_id, if: lambda { |status, options| status.user.public? }
+      expose :digest { |status, options| Digest::MD5.hexdigest(status.txt) }
+      expose :replies, using: API::Status, as: :replies
+    end
+  end
+
+  class Statuses < Grape::API
+    version 'v1'
+
+    desc 'Statuses index' do
+      params: API::Entities::Status.documentation
+    end
+    get '/statuses' do
+      statuses = Status.all
+      type = current_user.admin? ? :full : :default
+      present statuses, with: API::Entities::Status, type: type
+    end
+  end
+end
+```
+
+You can use entity documentation directly in the params block with `using: Entity.documentation`.
+
+```ruby
+module API
+  class Statuses < Grape::API
+    version 'v1'
+
+    desc 'Create a status'
+    params do
+      requires :all, except: [:ip], using: API::Entities::Status.documentation.except(:id)
+    end
+    post '/status' do
+      Status.create! params
+    end
+  end
+end
+```
+
+You can present with multiple entities using an optional Symbol argument.
+
+```ruby
+  get '/statuses' do
+    statuses = Status.all.page(1).per(20)
+    present :total_page, 10
+    present :per_page, 20
+    present :statuses, statuses, with: API::Entities::Status
+  end
+```
+
+The response will be
+
+```
+  {
+    total_page: 10,
+    per_page: 20,
+    statuses: []
+  }
+```
+
+In addition to separately organizing entities, it may be useful to put them as namespaced
+classes underneath the model they represent.
+
+```ruby
+class Status
+  def entity
+    Entity.new(self)
+  end
+
+  class Entity < Grape::Entity
+    expose :text, :user_id
+  end
+end
+```
+
+If you organize your entities this way, Grape will automatically detect the `Entity` class and
+use it to present your models. In this example, if you added `present Status.new` to your endpoint,
+Grape will automatically detect that there is a `Status::Entity` class and use that as the
+representative entity. This can still be overridden by using the `:with` option or an explicit
+`represents` call.
+
+You can present `hash` with `Grape::Presenters::Presenter` to keep things consistent.
+
+```ruby
+get '/users' do
+  present { id: 10, name: :dgz }, with: Grape::Presenters::Presenter
+end
+````
+The response will be
+
+```ruby
+{
+  id:   10,
+  name: 'dgz'
+}
+```
+
+It has the same result with
+
+```ruby
+get '/users' do
+  present :id, 10
+  present :name, :dgz
+end
+```
+
+### Hypermedia and Roar
+
+You can use [Roar](https://github.com/apotonick/roar) to render HAL or Collection+JSON with the help of [grape-roar](https://github.com/dblock/grape-roar), which defines a custom JSON formatter and enables presenting entities with Grape's `present` keyword.
+
+### Rabl
+
+You can use [Rabl](https://github.com/nesquena/rabl) templates with the help of the
+[grape-rabl](https://github.com/LTe/grape-rabl) gem, which defines a custom Grape Rabl
+formatter.
+
+### Active Model Serializers
+
+You can use [Active Model Serializers](https://github.com/rails-api/active_model_serializers) serializers with the help of the
+[grape-active_model_serializers](https://github.com/jrhe/grape-active_model_serializers) gem, which defines a custom Grape AMS
+formatter.
+
+## Sending Raw or No Data
+
+In general, use the binary format to send raw data.
+
+```ruby
+class API < Grape::API
+  get '/file' do
+    content_type 'application/octet-stream'
+    File.binread 'file.bin'
+  end
+end
+```
+
+You can set the response body explicitly with `body`.
+
+```ruby
+class API < Grape::API
+  get '/' do
+    content_type 'text/plain'
+    body 'Hello World'
+    # return value ignored
+  end
+end
+```
+
+Use `body false` to return `204 No Content` without any data or content-type.
+
+You can also set the response to a file-like object with `file`.
+
+```ruby
+class FileStreamer
+  def initialize(file_path)
+    @file_path = file_path
+  end
+
+  def each(&blk)
+    File.open(@file_path, 'rb') do |file|
+      file.each(10, &blk)
+    end
+  end
+end
+
+class API < Grape::API
+  get '/' do
+    file FileStreamer.new('file.bin')
+  end
+end
+```
+
+## Authentication
+
+### Basic and Digest Auth
+
+Grape has built-in Basic and Digest authentication (the given `block`
+is executed in the context of the current `Endpoint`).  Authentication
+applies to the current namespace and any children, but not parents.
+
+```ruby
+http_basic do |username, password|
+  # verify user's password here
+  { 'test' => 'password1' }[username] == password
+end
+```
+
+```ruby
+http_digest({ realm: 'Test Api', opaque: 'app secret' }) do |username|
+  # lookup the user's password here
+  { 'user1' => 'password1' }[username]
+end
+```
+
+### Register custom middleware for authentication
+
+Grape can use custom Middleware for authentication. How to implement these
+Middleware have a look at `Rack::Auth::Basic` or similar implementations.
+
+
+For registering a Middleware you need the following options:
+
+* `label` - the name for your authenticator to use it later
+* `MiddlewareClass` - the MiddlewareClass to use for authentication
+* `option_lookup_proc` - A Proc with one Argument to lookup the options at
+runtime (return value is an `Array` as Paramter for the Middleware).
+
+Example:
+
+```ruby
+
+Grape::Middleware::Auth::Strategies.add(:my_auth, AuthMiddleware, ->(options) { [options[:realm]] } )
+
+
+auth :my_auth, { realm: 'Test Api'} do |credentials|
+  # lookup the user's password here
+  { 'user1' => 'password1' }[username]
+end
+
+```
+
+Use [warden-oauth2](https://github.com/opperator/warden-oauth2) or [rack-oauth2](https://github.com/nov/rack-oauth2) for OAuth2 support.
+
+## Describing and Inspecting an API
+
+Grape routes can be reflected at runtime. This can notably be useful for generating documentation.
+
+Grape exposes arrays of API versions and compiled routes. Each route contains a `route_prefix`, `route_version`, `route_namespace`, `route_method`, `route_path` and `route_params`. You can add custom route settings to the route metadata with `route_setting`.
+
+```ruby
+class TwitterAPI < Grape::API
+  version 'v1'
+  desc "Includes custom settings."
+  route_setting :custom, key: 'value'
+  get do
+
+  end
+end
+```
+
+Examine the routes at runtime.
+
+```ruby
+TwitterAPI::versions # yields [ 'v1', 'v2' ]
+TwitterAPI::routes # yields an array of Grape::Route objects
+TwitterAPI::routes[0].route_version # => 'v1'
+TwitterAPI::routes[0].route_description # => 'Includes custom settings.'
+TwitterAPI::routes[0].route_settings[:custom] # => { key: 'value' }
+```
+
+## Current Route and Endpoint
+
+It's possible to retrieve the information about the current route from within an API call with `route`.
+
+```ruby
+class MyAPI < Grape::API
+  desc "Returns a description of a parameter."
+  params do
+    requires :id, type: Integer, desc: "Identity."
+  end
+  get "params/:id" do
+    route.route_params[params[:id]] # yields the parameter description
+  end
+end
+```
+
+The current endpoint responding to the request is `self` within the API block
+or `env['api.endpoint']` elsewhere. The endpoint has some interesting properties,
+such as `source` which gives you access to the original code block of the API
+implementation. This can be particularly useful for building a logger middleware.
+
+```ruby
+class ApiLogger < Grape::Middleware::Base
+  def before
+    file = env['api.endpoint'].source.source_location[0]
+    line = env['api.endpoint'].source.source_location[1]
+    logger.debug "[api] #{file}:#{line}"
+  end
+end
+```
+
+## Before and After
+
+Blocks can be executed before or after every API call, using `before`, `after`,
+`before_validation` and `after_validation`.
+
+Before and after callbacks execute in the following order:
+
+1. `before`
+2. `before_validation`
+3. _validations_
+4. `after_validation`
+5. _the API call_
+6. `after`
+
+Steps 4, 5 and 6 only happen if validation succeeds.
+
+E.g. using `before`:
+
+```ruby
+before do
+  header "X-Robots-Tag", "noindex"
+end
+```
+
+The block applies to every API call within and below the current namespace:
+
+```ruby
+class MyAPI < Grape::API
+  get '/' do
+    "root - #{@blah}"
+  end
+
+  namespace :foo do
+    before do
+      @blah = 'blah'
+    end
+
+    get '/' do
+      "root - foo - #{@blah}"
+    end
+
+    namespace :bar do
+      get '/' do
+        "root - foo - bar - #{@blah}"
+      end
+    end
+  end
+end
+```
+
+The behaviour is then:
+
+```bash
+GET /           # 'root - '
+GET /foo        # 'root - foo - blah'
+GET /foo/bar    # 'root - foo - bar - blah'
+```
+
+Params on a `namespace` (or whatever alias you are using) also work when using
+`before_validation` or `after_validation`:
+
+```ruby
+class MyAPI < Grape::API
+  params do
+    requires :blah, type: Integer
+  end
+  resource ':blah' do
+    after_validation do
+      # if we reach this point validations will have passed
+      @blah = declared(params, include_missing: false)[:blah]
+    end
+
+    get '/' do
+      @blah.class
+    end
+  end
+end
+```
+
+The behaviour is then:
+
+```bash
+GET /123        # 'Fixnum'
+GET /foo        # 400 error - 'blah is invalid'
+```
+
+When a callback is defined within a version block, it's only called for the routes defined in that block.
+
+```ruby
+class Test < Grape::API
+  resource :foo do
+    version 'v1', :using => :path do
+      before do
+        @output ||= 'v1-'
+      end
+      get '/' do
+        @output += 'hello'
+      end
+    end
+
+    version 'v2', :using => :path do
+      before do
+        @output ||= 'v2-'
+      end
+      get '/' do
+        @output += 'hello'
+      end
+    end
+  end
+end
+```
+
+The behaviour is then:
+
+```bash
+GET /foo/v1       # 'v1-hello'
+GET /foo/v2       # 'v2-hello'
+```
+
+## Anchoring
+
+Grape by default anchors all request paths, which means that the request URL
+should match from start to end to match, otherwise a `404 Not Found` is
+returned. However, this is sometimes not what you want, because it is not always
+known upfront what can be expected from the call. This is because Rack-mount by
+default anchors requests to match from the start to the end, or not at all.
+Rails solves this problem by using a `anchor: false` option in your routes.
+In Grape this option can be used as well when a method is defined.
+
+For instance when your API needs to get part of an URL, for instance:
+
+```ruby
+class TwitterAPI < Grape::API
+  namespace :statuses do
+    get '/(*:status)', anchor: false do
+
+    end
+  end
+end
+```
+
+This will match all paths starting with '/statuses/'. There is one caveat though:
+the `params[:status]` parameter only holds the first part of the request url.
+Luckily this can be circumvented by using the described above syntax for path
+specification and using the `PATH_INFO` Rack environment variable, using
+`env["PATH_INFO"]`. This will hold everything that comes after the '/statuses/'
+part.
+
+# Using Custom Middleware
+
+## Rails Middleware
+
+Note that when you're using Grape mounted on Rails you don't have to use Rails middleware because it's already included into your middleware stack.
+You only have to implement the helpers to access the specific `env` variable.
+
+### Remote IP
+
+By default you can access remote IP with `request.ip`. This is the remote IP address implemented by Rack. Sometimes it is desirable to get the remote IP [Rails-style](http://stackoverflow.com/questions/10997005/whats-the-difference-between-request-remote-ip-and-request-ip-in-rails) with `ActionDispatch::RemoteIp`.
+
+Add `gem 'actionpack'` to your Gemfile and `require 'action_dispatch/middleware/remote_ip.rb'`. Use the middleware in your API and expose a `client_ip` helper. See [this documentation](http://api.rubyonrails.org/classes/ActionDispatch/RemoteIp.html) for additional options.
+
+```ruby
+class API < Grape::API
+  use ActionDispatch::RemoteIp
+
+  helpers do
+    def client_ip
+      env["action_dispatch.remote_ip"].to_s
+    end
+  end
+
+  get :remote_ip do
+    { ip: client_ip }
+  end
+end
+```
+
+## Writing Tests
+
+### Writing Tests with Rack
+
+Use `rack-test` and define your API as `app`.
+
+#### RSpec
+
+You can test a Grape API with RSpec by making HTTP requests and examining the response.
+
+```ruby
+require 'spec_helper'
+
+describe Twitter::API do
+  include Rack::Test::Methods
+
+  def app
+    Twitter::API
+  end
+
+  describe Twitter::API do
+    describe "GET /api/statuses/public_timeline" do
+      it "returns an empty array of statuses" do
+        get "/api/statuses/public_timeline"
+        expect(last_response.status).to eq(200)
+        expect(JSON.parse(last_response.body)).to eq []
+      end
+    end
+    describe "GET /api/statuses/:id" do
+      it "returns a status by id" do
+        status = Status.create!
+        get "/api/statuses/#{status.id}"
+        expect(last_response.body).to eq status.to_json
+      end
+    end
+  end
+end
+```
+
+#### Airborne
+
+You can test with other RSpec-based frameworks, including [Airborne](https://github.com/brooklynDev/airborne), which uses `rack-test` to make requests.
+
+```ruby
+require 'airborne'
+
+Airborne.configure do |config|
+  config.rack_app = Twitter::API
+end
+
+describe Twitter::API do
+  describe "GET /api/statuses/:id" do
+    it "returns a status by id" do
+      status = Status.create!
+      get "/api/statuses/#{status.id}"
+      expect_json(status.as_json)
+    end
+  end
+end
+```
+
+#### MiniTest
+
+```ruby
+require "test_helper"
+
+class Twitter::APITest < MiniTest::Test
+  include Rack::Test::Methods
+
+  def app
+    Twitter::API
+  end
+
+  def test_get_api_statuses_public_timeline_returns_an_empty_array_of_statuses
+    get "/api/statuses/public_timeline"
+    assert last_response.ok?
+    assert_equal [], JSON.parse(last_response.body)
+  end
+
+  def test_get_api_statuses_id_returns_a_status_by_id
+    status = Status.create!
+    get "/api/statuses/#{status.id}"
+    assert_equal status.to_json, last_response.body
+  end
+end
+```
+
+### Writing Tests with Rails
+
+#### RSpec
+
+```ruby
+describe Twitter::API do
+  describe "GET /api/statuses/public_timeline" do
+    it "returns an empty array of statuses" do
+      get "/api/statuses/public_timeline"
+      expect(response.status).to eq(200)
+      expect(JSON.parse(response.body)).to eq []
+    end
+  end
+  describe "GET /api/statuses/:id" do
+    it "returns a status by id" do
+      status = Status.create!
+      get "/api/statuses/#{status.id}"
+      expect(response.body).to eq status.to_json
+    end
+  end
+end
+```
+
+In Rails, HTTP request tests would go into the `spec/requests` group. You may want your API code to go into
+`app/api` - you can match that layout under `spec` by adding the following in `spec/spec_helper.rb`.
+
+```ruby
+RSpec.configure do |config|
+  config.include RSpec::Rails::RequestExampleGroup, type: :request, file_path: /spec\/api/
+end
+```
+
+#### MiniTest
+
+```ruby
+class Twitter::APITest < ActiveSupport::TestCase
+  include Rack::Test::Methods
+
+  def app
+    Rails.application
+  end
+
+  test "GET /api/statuses/public_timeline returns an empty array of statuses" do
+    get "/api/statuses/public_timeline"
+    assert last_response.ok?
+    assert_equal [], JSON.parse(last_response.body)
+  end
+
+  test "GET /api/statuses/:id returns a status by id" do
+    status = Status.create!
+    get "/api/statuses/#{status.id}"
+    assert_equal status.to_json, last_response.body
+  end
+end
+```
+
+### Stubbing Helpers
+
+Because helpers are mixed in based on the context when an endpoint is defined, it can
+be difficult to stub or mock them for testing. The `Grape::Endpoint.before_each` method
+can help by allowing you to define behavior on the endpoint that will run before every
+request.
+
+```ruby
+describe 'an endpoint that needs helpers stubbed' do
+  before do
+    Grape::Endpoint.before_each do |endpoint|
+      allow(endpoint).to receive(:helper_name).and_return('desired_value')
+    end
+  end
+
+  after do
+    Grape::Endpoint.before_each nil
+  end
+
+  it 'should properly stub the helper' do
+    # ...
+  end
+end
+```
+
+## Reloading API Changes in Development
+
+### Reloading in Rack Applications
+
+Use [grape-reload](https://github.com/AlexYankee/grape-reload).
+
+### Reloading in Rails Applications
+
+Add API paths to `config/application.rb`.
+
+```ruby
+# Auto-load API and its subdirectories
+config.paths.add File.join("app", "api"), glob: File.join("**", "*.rb")
+config.autoload_paths += Dir[Rails.root.join("app", "api", "*")]
+```
+
+Create `config/initializers/reload_api.rb`.
+
+```ruby
+if Rails.env.development?
+  ActiveSupport::Dependencies.explicitly_unloadable_constants << "Twitter::API"
+
+  api_files = Dir[Rails.root.join('app', 'api', '**', '*.rb')]
+  api_reloader = ActiveSupport::FileUpdateChecker.new(api_files) do
+    Rails.application.reload_routes!
+  end
+  ActionDispatch::Callbacks.to_prepare do
+    api_reloader.execute_if_updated
+  end
+end
+```
+
+See [StackOverflow #3282655](http://stackoverflow.com/questions/3282655/ruby-on-rails-3-reload-lib-directory-for-each-request/4368838#4368838) for more information.
+
+## Performance Monitoring
+
+Grape integrates with NewRelic via the
+[newrelic-grape](https://github.com/flyerhzm/newrelic-grape) gem, and
+with Librato Metrics with the [grape-librato](https://github.com/seanmoon/grape-librato) gem.
+
+## Contributing to Grape
+
+Grape is work of hundreds of contributors. You're encouraged to submit pull requests, propose
+features and discuss issues.
+
+See [CONTRIBUTING](CONTRIBUTING.md).
+
+## Hacking on Grape
+
+You can start hacking on Grape on
+[Nitrous.IO](https://www.nitrous.io/?utm_source=github.com&utm_campaign=grape&utm_medium=hackonnitrous) in a matter of seconds:
+
+[![Hack intridea/grape on Nitrous.IO](https://d3o0mnbgv6k92a.cloudfront.net/assets/hack-l-v1-3cc067e71372f6045e1949af9d96095b.png)](https://www.nitrous.io/hack_button?source=embed&runtime=rails&repo=intridea%2Fgrape&file_to_open=README.md)
+
+## License
+
+MIT License. See LICENSE for details.
+
+## Copyright
+
+Copyright (c) 2010-2015 Michael Bleigh, and Intridea, Inc.
diff --git a/RELEASING.md b/RELEASING.md
new file mode 100644
index 0000000..97ec24b
--- /dev/null
+++ b/RELEASING.md
@@ -0,0 +1,105 @@
+Releasing Grape
+===============
+
+There're no particular rules about when to release Grape. Release bug fixes frequently, features not so frequently and breaking API changes rarely.
+
+### Release
+
+Run tests, check that all tests succeed locally.
+
+```
+bundle install
+rake
+```
+
+Check that the last build succeeded in [Travis CI](https://travis-ci.org/intridea/grape) for all supported platforms.
+
+Those with r/w permissions to the [master Intridea repository](https://github.com/intridea/grape) generally have large Grape-based projects. Point one to Grape HEAD and run all your API tests to catch any obvious regressions.
+
+```
+gem grape, github: 'intridea/grape'
+```
+
+Increment the version, modify [lib/grape/version.rb](lib/grape/version.rb).
+
+*  Increment the third number if the release has bug fixes and/or very minor features, only (eg. change `0.5.1` to `0.5.2`).
+*  Increment the second number if the release contains major features or breaking API changes (eg. change `0.5.1` to `0.6.0`).
+
+Modify the "Stable Release" section in [README.md](README.md). Change the text to reflect that this is going to be the documentation for a stable release. Remove references to the previous release of Grape. Keep the file open, you'll have to undo this change after the release.
+
+```
+## Stable Release
+
+You're reading the documentation for the stable release of Grape, 0.6.0.
+```
+
+Change "Next Release" in [CHANGELOG.md](CHANGELOG.md) to the new version.
+
+```
+0.6.0 (9/16/2013)
+=================
+```
+
+Remove the line with "Your contribution here.", since there will be no more contributions to this release.
+
+Commit your changes.
+
+```
+git add README.md CHANGELOG.md lib/grape/version.rb
+git commit -m "Preparing for release, 0.6.0."
+git push origin master
+```
+
+Release.
+
+```
+$ rake release
+
+grape 0.6.0 built to pkg/grape-0.6.0.gem.
+Tagged v0.6.0.
+Pushed git commits and tags.
+Pushed grape 0.6.0 to rubygems.org.
+```
+
+### Prepare for the Next Version
+
+Modify the "Stable Release" section in [README.md](README.md). Change the text to reflect that this is going to be the next release.
+
+```
+## Stable Release
+
+You're reading the documentation for the next release of Grape, which should be 0.6.1.
+The current stable release is [0.6.0](https://github.com/intridea/grape/blob/v0.6.0/README.md).
+```
+
+Add the next release to [CHANGELOG.md](CHANGELOG.md).
+
+```
+Next Release
+============
+
+* Your contribution here.
+```
+
+Comit your changes.
+
+```
+git add CHANGELOG.md README.md
+git commit -m "Preparing for next release."
+git push origin master
+```
+
+### Make an Announcement
+
+Make an announcement on the [ruby-grape at googlegroups.com](mailto:ruby-grape at googlegroups.com) mailing list. The general format is as follows.
+
+```
+Grape 0.6.0 has been released.
+
+There were 8 contributors to this release, not counting documentation.
+
+Please note the breaking API change in ...
+
+[copy/paste CHANGELOG here]
+
+```
diff --git a/Rakefile b/Rakefile
new file mode 100644
index 0000000..8129c39
--- /dev/null
+++ b/Rakefile
@@ -0,0 +1,68 @@
+require 'rubygems'
+require 'bundler'
+Bundler.setup :default, :test, :development
+
+Bundler::GemHelper.install_tasks
+
+require 'rspec/core/rake_task'
+RSpec::Core::RakeTask.new(:spec) do |spec|
+  spec.pattern = 'spec/**/*_spec.rb'
+end
+
+RSpec::Core::RakeTask.new(:rcov) do |spec|
+  spec.pattern = 'spec/**/*_spec.rb'
+  spec.rcov = true
+end
+
+task :spec
+
+require 'rainbow/ext/string' unless String.respond_to?(:color)
+require 'rubocop/rake_task'
+RuboCop::RakeTask.new
+
+task default: [:rubocop, :spec]
+
+begin
+  require 'yard'
+  DOC_FILES = ['lib/**/*.rb', 'README.md']
+
+  YARD::Rake::YardocTask.new(:doc) do |t|
+    t.files   = DOC_FILES
+  end
+
+  namespace :doc do
+    YARD::Rake::YardocTask.new(:pages) do |t|
+      t.files   = DOC_FILES
+      t.options = ['-o', '../grape.doc/docs']
+    end
+
+    namespace :pages do
+      desc 'Check out gh-pages.'
+      task :checkout do
+        dir = File.dirname(__FILE__) + '/../grape.doc'
+        unless Dir.exist?(dir)
+          Dir.mkdir(dir)
+          Dir.chdir(dir) do
+            system('git init')
+            system('git remote add origin git at github.com:intridea/grape.git')
+            system('git pull')
+            system('git checkout gh-pages')
+          end
+        end
+      end
+
+      desc 'Generate and publish YARD docs to GitHub pages.'
+      task publish: ['doc:pages:checkout', 'doc:pages'] do
+        Dir.chdir(File.dirname(__FILE__) + '/../grape.doc') do
+          system('git checkout gh-pages')
+          system('git add .')
+          system('git add -u')
+          system("git commit -m 'Generating docs for version #{Grape::VERSION}.'")
+          system('git push origin gh-pages')
+        end
+      end
+    end
+  end
+rescue LoadError
+  puts 'You need to install YARD.'
+end
diff --git a/UPGRADING.md b/UPGRADING.md
new file mode 100644
index 0000000..88bc7b8
--- /dev/null
+++ b/UPGRADING.md
@@ -0,0 +1,550 @@
+Upgrading Grape
+===============
+
+### Upgrading to >= 0.12.0
+
+#### Changes in middleware
+
+The Rack response object is no longer converted to an array by the formatter, enabling streaming. If your custom middleware is accessing `@app_response`, update it to expect a `Rack::Response` instance instead of an array.
+
+For example,
+
+```ruby
+class CacheBusterMiddleware < Grape::Middleware::Base
+  def after
+    @app_response[1]['Expires'] = Time.at(0).utc.to_s
+    @app_response
+  end
+end
+```
+
+becomes
+
+```ruby
+class CacheBusterMiddleware < Grape::Middleware::Base
+  def after
+    @app_response.headers['Expires'] = Time.at(0).utc.to_s
+    @app_response
+  end
+end
+```
+
+See [#1029](https://github.com/intridea/grape/pull/1029) for more information.
+
+#### Changes in present
+
+Using `present` with objects that responded to `merge` would cause early evaluation of the represented object, with unexpected side-effects, such as missing parameters or environment within rendering code. Grape now only merges represented objects with a previously rendered body, usually when multiple `present` calls are made in the same route.
+
+See [grape-with-roar#5](https://github.com/dblock/grape-with-roar/issues/5) and [#1023](https://github.com/intridea/grape/issues/1023).
+
+#### Changes to regexp validator
+
+Parameters with `nil` value will now pass `regexp` validation. To disallow `nil` value for an endpoint, add `allow_blank: false`.
+
+```ruby
+params do
+  requires :email, allow_blank: false, regexp: /.+ at .+/
+end
+```
+
+See [#957](https://github.com/intridea/grape/pull/957) for more information.
+
+#### Replace error_response with error! in rescue_from blocks
+
+Note: `error_response` is being deprecated, not removed.
+
+```ruby
+def error!(message, status = options[:default_status], headers = {}, backtrace = [])
+  headers = { 'Content-Type' => content_type }.merge(headers)
+  rack_response(format_message(message, backtrace), status, headers)
+end
+```
+
+For example,
+
+```
+error_response({ message: { message: 'No such page.', id: 'missing_page' }, status: 404, headers: { 'Content-Type' => 'api/error' })
+```
+
+becomes
+
+```
+error!({ message: 'No such page.', id: 'missing_page' }, 404, { 'Content-Type' => 'api/error' })
+```
+
+`error!` also supports just passing a message. `error!('Server error.')` and `format: :json` returns the following JSON response
+
+```
+{ 'error': 'Server error. }
+```
+
+with a status code of 500 and a Content Type of text/error.
+
+Optionally, also replace `Rack::Response.new` with `error!.`
+The following are equivalent:
+
+```
+Rack::Response.new([ e.message ], 500, { "Content-type" => "text/error" }).finish
+error!(e)
+```
+
+See [#889](https://github.com/intridea/grape/issues/889) for more information.
+
+#### Changes to routes when using `format`
+
+Version 0.10.0 has introduced a change via [#809](https://github.com/intridea/grape/pull/809) whereas routes no longer got file-type suffixes added if you declared a single API `format`. This has been reverted, it's now again possible to call API with proper suffix when single `format` is defined:
+
+```ruby
+class API < Grape::API
+  format :json
+
+  get :hello do
+    { hello: 'world' }
+  end
+end
+```
+
+Will respond with JSON to `/hello` **and** `/hello.json`.
+
+Will respond with 404 to `/hello.xml`, `/hello.txt` etc.
+
+See the [#1001](https://github.com/intridea/grape/pull/1001) and [#914](https://github.com/intridea/grape/issues/914) for more info.
+
+### Upgrading to >= 0.11.0
+
+#### Added Rack 1.6.0 support
+
+Grape now supports, but doesn't require Rack 1.6.0. If you encounter an issue with parsing requests larger than 128KB, explictly require Rack 1.6.0 in your Gemfile.
+
+```ruby
+gem 'rack', '~> 1.6.0'
+```
+
+See [#559](https://github.com/intridea/grape/issues/559) for more information.
+
+#### Removed route_info
+
+Key route_info is excluded from params.
+
+See [#879](https://github.com/intridea/grape/pull/879) for more information.
+
+
+#### Fix callbacks within a version block
+
+Callbacks defined in a version block are only called for the routes defined in that block. This was a regression introduced in Grape 0.10.0, and is fixed in this version.
+
+See [#901](https://github.com/intridea/grape/pull/901) for more information.
+
+
+#### Make type of group of parameters required
+
+Groups of parameters now require their type to be set explicitly as Array or Hash.
+Not setting the type now results in MissingGroupTypeError, unsupported type will raise UnsupportedTypeError.
+
+See [#886](https://github.com/intridea/grape/pull/886) for more information.
+
+### Upgrading to >= 0.10.1
+
+#### Changes to `declared(params, include_missing: false)`
+
+Attributes with `nil` values or with values that evaluate to `false` are no longer considered *missing* and will be returned when `include_missing` is set to `false`.
+
+See [#864](https://github.com/intridea/grape/pull/864) for more information.
+
+### Upgrading to >= 0.10.0
+
+#### Changes to content-types
+
+The following content-types have been removed:
+
+* atom (application/atom+xml)
+* rss (application/rss+xml)
+* jsonapi (application/jsonapi)
+
+This is because they have never been properly supported.
+
+#### Changes to desc
+
+New block syntax:
+
+Former:
+
+```ruby
+  desc "some descs",
+    detail: 'more details',
+    entity: API::Entities::Entity,
+    params: API::Entities::Status.documentation,
+    named: 'a name',
+    headers: [XAuthToken: {
+      description: 'Valdates your identity',
+      required: true
+    }
+  get nil, http_codes: [
+    [401, 'Unauthorized', API::Entities::BaseError],
+    [404, 'not found', API::Entities::Error]
+  ] do
+```
+
+Now:
+
+```ruby
+desc "some descs" do
+  detail 'more details'
+  params API::Entities::Status.documentation
+  success API::Entities::Entity
+  failure [
+    [401, 'Unauthorized', API::Entities::BaseError],
+    [404, 'not found', API::Entities::Error]
+  ]
+  named 'a name'
+  headers [
+    XAuthToken: {
+      description: 'Valdates your identity',
+      required: true
+    },
+    XOptionalHeader: {
+      description: 'Not really needed',
+      required: false
+    }
+  ]
+end
+```
+
+#### Changes to Route Options and Descriptions
+
+A common hack to extend Grape with custom DSL methods was manipulating `@last_description`.
+
+``` ruby
+module Grape
+  module Extensions
+    module SortExtension
+      def sort(value)
+        @last_description ||= {}
+        @last_description[:sort] ||= {}
+        @last_description[:sort].merge! value
+        value
+      end
+    end
+
+    Grape::API.extend self
+  end
+end
+```
+
+You could access this value from within the API with `route.route_sort` or, more generally, via `env['api.endpoint'].options[:route_options][:sort]`.
+
+This will no longer work, use the documented and supported `route_setting`.
+
+``` ruby
+module Grape
+  module Extensions
+    module SortExtension
+      def sort(value)
+        route_setting :sort, sort: value
+        value
+      end
+    end
+
+    Grape::API.extend self
+  end
+end
+```
+
+To retrieve this value at runtime from within an API, use `env['api.endpoint'].route_setting(:sort)` and when introspecting a mounted API, use `route.route_settings[:sort]`.
+
+#### Accessing Class Variables from Helpers
+
+It used to be possible to fetch an API class variable from a helper function. For example:
+
+```ruby
+@@static_variable = 42
+
+helpers do
+  def get_static_variable
+    @@static_variable
+  end
+end
+
+get do
+  get_static_variable
+end
+```
+
+This will no longer work. Use a class method instead of a helper.
+
+```ruby
+@@static_variable = 42
+
+def self.get_static_variable
+  @@static_variable
+end
+
+get do
+  get_static_variable
+end
+```
+
+For more information see [#836](https://github.com/intridea/grape/issues/836).
+
+#### Changes to Custom Validators
+
+To implement a custom validator, you need to inherit from `Grape::Validations::Base` instead of `Grape::Validations::Validator`.
+
+For more information see [Custom Validators](https://github.com/intridea/grape#custom-validators) in the documentation.
+
+#### Changes to Raising Grape::Exceptions::Validation
+
+In previous versions raising `Grape::Exceptions::Validation` required a single `param`.
+
+```ruby
+raise Grape::Exceptions::Validation, param: :id, message_key: :presence
+```
+
+The `param` argument has been deprecated and is now an array of `params`, accepting multiple values.
+
+```ruby
+raise Grape::Exceptions::Validation, params: [:id], message_key: :presence
+```
+
+#### Changes to routes when using `format`
+
+Routes will no longer get file-type suffixes added if you declare a single API `format`. For example,
+
+```ruby
+class API < Grape::API
+  format :json
+
+  get :hello do
+    { hello: 'world' }
+  end
+end
+```
+
+Pre-0.10.0, this would respond with JSON to `/hello`, `/hello.json`, `/hello.xml`, `/hello.txt`, etc.
+
+Now, this will only respond with JSON to `/hello`, but will be a 404 when trying to access `/hello.json`, `/hello.xml`, `/hello.txt`, etc.
+
+If you declare further `content_type`s, this behavior will be circumvented. For example, the following API will respond with JSON to `/hello`, `/hello.json`, `/hello.xml`, `/hello.txt`, etc.
+
+```ruby
+class API < Grape::API
+  format :json
+  content_type :json, 'application/json'
+
+  get :hello do
+    { hello: 'world' }
+  end
+end
+```
+
+See the [the updated API Formats documentation](https://github.com/intridea/grape#api-formats) and [#809](https://github.com/intridea/grape/pull/809) for more info.
+
+#### Changes to Evaluation of Permitted Parameter Values
+
+Permitted and default parameter values are now only evaluated lazily for each request when declared as a proc. The following code would raise an error at startup time.
+
+```ruby
+params do
+  optional :v, values: -> { [:x, :y] }, default: -> { :z } }
+end
+```
+
+Remove the proc to get the previous behavior.
+
+```ruby
+params do
+  optional :v, values: [:x, :y], default: :z }
+end
+```
+
+See [#801](https://github.com/intridea/grape/issues/801) for more information.
+
+#### Changes to version
+
+If version is used with a block, the callbacks defined within that version block are not scoped to that individual block. In other words, the callback would be inherited by all versions blocks that follow the first one e.g
+
+```ruby
+class API < Grape::API
+  resource :foo do
+    version 'v1', :using => :path do
+      before do
+        @output ||= 'hello1'
+      end
+      get '/' do
+        @output += '-v1'
+      end
+    end
+
+    version 'v2', :using => :path do
+      before do
+        @output ||= 'hello2'
+      end
+      get '/:id' do
+        @output += '-v2'
+      end
+    end
+  end
+end
+```
+
+when making a API call `GET /foo/v2/1`, the API would set instance variable `@output` to `hello1-v2`
+
+See [#898](https://github.com/intridea/grape/issues/898) for more information.
+
+
+### Upgrading to >= 0.9.0
+
+#### Changes in Authentication
+
+The following middleware classes have been removed:
+
+* `Grape::Middleware::Auth::Basic`
+* `Grape::Middleware::Auth::Digest`
+* `Grape::Middleware::Auth::OAuth2`
+
+When you use theses classes directly like:
+
+```ruby
+ module API
+   class Root < Grape::API
+     class Protected < Grape::API
+       use Grape::Middleware::Auth::OAuth2,
+           token_class: 'AccessToken',
+           parameter: %w(access_token api_key)
+
+```
+
+you have to replace these classes.
+
+As replacement can be used
+
+* `Grape::Middleware::Auth::Basic`  => [`Rack::Auth::Basic`](https://github.com/rack/rack/blob/master/lib/rack/auth/basic.rb)
+* `Grape::Middleware::Auth::Digest` => [`Rack::Auth::Digest::MD5`](https://github.com/rack/rack/blob/master/lib/rack/auth/digest/md5.rb)
+* `Grape::Middleware::Auth::OAuth2` => [warden-oauth2](https://github.com/opperator/warden-oauth2) or [rack-oauth2](https://github.com/nov/rack-oauth2)
+
+If this is not possible you can extract the middleware files from [grape v0.7.0](https://github.com/intridea/grape/tree/v0.7.0/lib/grape/middleware/auth)
+and host these files within your application
+
+See [#703](https://github.com/intridea/Grape/pull/703) for more information.
+
+### Upgrading to >= 0.7.0
+
+#### Changes in Exception Handling
+
+Assume you have the following exception classes defined.
+
+```ruby
+class ParentError < StandardError; end
+class ChildError < ParentError; end
+```
+
+In Grape <= 0.6.1, the `rescue_from` keyword only handled the exact exception being raised. The following code would rescue `ParentError`, but not `ChildError`.
+
+```ruby
+rescue_from ParentError do |e|
+  # only rescue ParentError
+end
+```
+
+This made it impossible to rescue an exception hieararchy, which is a more sensible default. In Grape 0.7.0 or newer, both `ParentError` and `ChildError` are rescued.
+
+```ruby
+rescue_from ParentError do |e|
+  # rescue both ParentError and ChildError
+end
+```
+
+To only rescue the base exception class, set `rescue_subclasses: false`.
+
+```ruby
+rescue_from ParentError, rescue_subclasses: false do |e|
+  # only rescue ParentError
+end
+```
+
+See [#544](https://github.com/intridea/grape/pull/544) for more information.
+
+
+#### Changes in the Default HTTP Status Code
+
+In Grape <= 0.6.1, the default status code returned from `error!` was 403.
+
+```ruby
+error! "You may not reticulate this spline!" # yields HTTP error 403
+```
+
+This was a bad default value, since 403 means "Forbidden". Change any call to `error!` that does not specify a status code to specify one. The new default value is a more sensible default of 500, which is "Internal Server Error".
+
+```ruby
+error! "You may not reticulate this spline!", 403 # yields HTTP error 403
+```
+
+You may also use `default_error_status` to change the global default.
+
+```ruby
+default_error_status 400
+```
+
+See [#525](https://github.com/intridea/Grape/pull/525) for more information.
+
+
+#### Changes in Parameter Declaration and Validation
+
+In Grape <= 0.6.1, `group`, `optional` and `requires` keywords with a block accepted either an `Array` or a `Hash`.
+
+```ruby
+params do
+  requires :id, type: Integer
+  group :name do
+    requires :first_name
+    requires :last_name
+  end
+end
+```
+
+This caused the ambiguity and unexpected errors described in [#543](https://github.com/intridea/Grape/issues/543).
+
+In Grape 0.7.0, the `group`, `optional` and `requires` keywords take an additional `type` attribute which defaults to `Array`. This means that without a `type` attribute, these nested parameters will no longer accept a single hash, only an array (of hashes).
+
+Whereas in 0.6.1 the API above accepted the following json, it no longer does in 0.7.0.
+
+```json
+{
+  "id": 1,
+  "name": {
+    "first_name": "John",
+    "last_name" : "Doe"
+  }
+}
+```
+
+The `params` block should now read as follows.
+
+```ruby
+params do
+  requires :id, type: Integer
+  requires :name, type: Hash do
+    requires :first_name
+    requires :last_name
+  end
+end
+```
+
+See [#545](https://github.com/intridea/Grape/pull/545) for more information.
+
+
+### Upgrading to 0.6.0
+
+In Grape <= 0.5.0, only the first validation error was raised and processing aborted. Validation errors are now collected and a single `Grape::Exceptions::ValidationErrors` exception is raised. You can access the collection of validation errors as `.errors`.
+
+```ruby
+rescue_from Grape::Exceptions::Validations do |e|
+  Rack::Response.new({
+    status: 422,
+    message: e.message,
+    errors: e.errors
+  }.to_json, 422)
+end
+```
+
+For more information see [#462](https://github.com/intridea/grape/issues/462).
diff --git a/gemfiles/rails_3.gemfile b/gemfiles/rails_3.gemfile
new file mode 100644
index 0000000..63d48aa
--- /dev/null
+++ b/gemfiles/rails_3.gemfile
@@ -0,0 +1,14 @@
+# This file was generated by Appraisal
+
+source 'https://rubygems.org'
+
+gem 'rails', '3.2.19'
+
+group :development, :test do
+  gem 'rubocop', '~> 0.31.0'
+  gem 'guard'
+  gem 'guard-rspec'
+  gem 'guard-rubocop'
+end
+
+gemspec :path => '../'
diff --git a/gemfiles/rails_4.gemfile b/gemfiles/rails_4.gemfile
new file mode 100644
index 0000000..69e4d15
--- /dev/null
+++ b/gemfiles/rails_4.gemfile
@@ -0,0 +1,14 @@
+# This file was generated by Appraisal
+
+source 'https://rubygems.org'
+
+gem 'rails', '4.1.6'
+
+group :development, :test do
+  gem 'rubocop', '~> 0.31.0'
+  gem 'guard'
+  gem 'guard-rspec'
+  gem 'guard-rubocop'
+end
+
+gemspec :path => '../'
diff --git a/grape.gemspec b/grape.gemspec
new file mode 100644
index 0000000..245c143
--- /dev/null
+++ b/grape.gemspec
@@ -0,0 +1,43 @@
+$LOAD_PATH.push File.expand_path('../lib', __FILE__)
+require 'grape/version'
+
+Gem::Specification.new do |s|
+  s.name        = 'grape'
+  s.version     = Grape::VERSION
+  s.platform    = Gem::Platform::RUBY
+  s.authors     = ['Michael Bleigh']
+  s.email       = ['michael at intridea.com']
+  s.homepage    = 'https://github.com/intridea/grape'
+  s.summary     = 'A simple Ruby framework for building REST-like APIs.'
+  s.description = 'A Ruby framework for rapid API development with great conventions.'
+  s.license     = 'MIT'
+
+  s.rubyforge_project = 'grape'
+
+  s.add_runtime_dependency 'rack', '>= 1.3.0'
+  s.add_runtime_dependency 'rack-mount'
+  s.add_runtime_dependency 'rack-accept'
+  s.add_runtime_dependency 'activesupport'
+  s.add_runtime_dependency 'multi_json', '>= 1.3.2'
+  s.add_runtime_dependency 'multi_xml', '>= 0.5.2'
+  s.add_runtime_dependency 'hashie', '>= 2.1.0'
+  s.add_runtime_dependency 'virtus', '>= 1.0.0'
+  s.add_runtime_dependency 'builder'
+
+  s.add_development_dependency 'grape-entity', '>= 0.4.4'
+  s.add_development_dependency 'rake'
+  s.add_development_dependency 'maruku'
+  s.add_development_dependency 'yard'
+  s.add_development_dependency 'rack-test'
+  s.add_development_dependency 'rspec', '~> 3.0'
+  s.add_development_dependency 'bundler'
+  s.add_development_dependency 'cookiejar'
+  s.add_development_dependency 'rack-contrib'
+  s.add_development_dependency 'mime-types'
+  s.add_development_dependency 'appraisal'
+
+  s.files         = `git ls-files`.split("\n")
+  s.test_files    = `git ls-files -- {test,spec,features}/*`.split("\n")
+  s.executables   = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
+  s.require_paths = ['lib']
+end
diff --git a/grape.png b/grape.png
new file mode 100644
index 0000000..97a0e1f
Binary files /dev/null and b/grape.png differ
diff --git a/lib/backports/active_support/deep_dup.rb b/lib/backports/active_support/deep_dup.rb
new file mode 100644
index 0000000..2e4a7de
--- /dev/null
+++ b/lib/backports/active_support/deep_dup.rb
@@ -0,0 +1,49 @@
+# Backport from Rails 4.x
+# https://github.com/rails/rails/blob/4-0-stable/activesupport/lib/active_support/core_ext/object/deep_dup.rb
+
+require_relative 'duplicable'
+
+class Object
+  # Returns a deep copy of object if it's duplicable. If it's
+  # not duplicable, returns +self+.
+  #
+  #   object = Object.new
+  #   dup    = object.deep_dup
+  #   dup.instance_variable_set(:@a, 1)
+  #
+  #   object.instance_variable_defined?(:@a) #=> false
+  #   dup.instance_variable_defined?(:@a)    #=> true
+  def deep_dup
+    duplicable? ? dup : self
+  end
+end
+
+class Array
+  # Returns a deep copy of array.
+  #
+  #   array = [1, [2, 3]]
+  #   dup   = array.deep_dup
+  #   dup[1][2] = 4
+  #
+  #   array[1][2] #=> nil
+  #   dup[1][2]   #=> 4
+  def deep_dup
+    map(&:deep_dup)
+  end
+end
+
+class Hash
+  # Returns a deep copy of hash.
+  #
+  #   hash = { a: { b: 'b' } }
+  #   dup  = hash.deep_dup
+  #   dup[:a][:c] = 'c'
+  #
+  #   hash[:a][:c] #=> nil
+  #   dup[:a][:c]  #=> "c"
+  def deep_dup
+    each_with_object(dup) do |(key, value), hash|
+      hash[key.deep_dup] = value.deep_dup
+    end
+  end
+end
diff --git a/lib/backports/active_support/duplicable.rb b/lib/backports/active_support/duplicable.rb
new file mode 100644
index 0000000..518f166
--- /dev/null
+++ b/lib/backports/active_support/duplicable.rb
@@ -0,0 +1,88 @@
+# Backport from Rails 4.x
+# https://github.com/rails/rails/blob/4-0-stable/activesupport/lib/active_support/core_ext/object/deep_dup.rb
+
+#--
+# Most objects are cloneable, but not all. For example you can't dup +nil+:
+#
+# nil.dup # => TypeError: can't dup NilClass
+#
+# Classes may signal their instances are not duplicable removing +dup+/+clone+
+# or raising exceptions from them. So, to dup an arbitrary object you normally
+# use an optimistic approach and are ready to catch an exception, say:
+#
+# arbitrary_object.dup rescue object
+#
+# Rails dups objects in a few critical spots where they are not that arbitrary.
+# That rescue is very expensive (like 40 times slower than a predicate), and it
+# is often triggered.
+#
+# That's why we hardcode the following cases and check duplicable? instead of
+# using that rescue idiom.
+#++
+class Object
+  # Can you safely dup this object?
+  #
+  # False for +nil+, +false+, +true+, symbol, and number objects;
+  # true otherwise.
+  def duplicable?
+    true
+  end
+end
+class NilClass
+  # +nil+ is not duplicable:
+  #
+  # nil.duplicable? # => false
+  # nil.dup # => TypeError: can't dup NilClass
+  def duplicable?
+    false
+  end
+end
+class FalseClass
+  # +false+ is not duplicable:
+  #
+  # false.duplicable? # => false
+  # false.dup # => TypeError: can't dup FalseClass
+  def duplicable?
+    false
+  end
+end
+class TrueClass
+  # +true+ is not duplicable:
+  #
+  # true.duplicable? # => false
+  # true.dup # => TypeError: can't dup TrueClass
+  def duplicable?
+    false
+  end
+end
+class Symbol
+  # Symbols are not duplicable:
+  #
+  # :my_symbol.duplicable? # => false
+  # :my_symbol.dup # => TypeError: can't dup Symbol
+  def duplicable?
+    false
+  end
+end
+class Numeric
+  # Numbers are not duplicable:
+  #
+  # 3.duplicable? # => false
+  # 3.dup # => TypeError: can't dup Fixnum
+  def duplicable?
+    false
+  end
+end
+require 'bigdecimal'
+# rubocop:disable Lint/HandleExceptions
+class BigDecimal
+  begin
+    BigDecimal.new('4.56').dup
+    def duplicable?
+      true
+    end
+  rescue TypeError
+    # can't dup, so use superclass implementation
+  end
+end
+# rubocop:enable Lint/HandleExceptions
diff --git a/lib/grape.rb b/lib/grape.rb
new file mode 100644
index 0000000..85ac50f
--- /dev/null
+++ b/lib/grape.rb
@@ -0,0 +1,176 @@
+require 'logger'
+require 'rack'
+require 'rack/mount'
+require 'rack/builder'
+require 'rack/accept'
+require 'rack/auth/basic'
+require 'rack/auth/digest/md5'
+require 'hashie'
+require 'set'
+require 'active_support/version'
+require 'active_support/core_ext/hash/indifferent_access'
+
+if ActiveSupport::VERSION::MAJOR >= 4
+  require 'active_support/core_ext/object/deep_dup'
+else
+  require_relative 'backports/active_support/deep_dup'
+end
+
+require 'active_support/ordered_hash'
+require 'active_support/core_ext/object/conversions'
+require 'active_support/core_ext/array/extract_options'
+require 'active_support/core_ext/hash/deep_merge'
+require 'active_support/dependencies/autoload'
+require 'grape/util/content_types'
+require 'multi_json'
+require 'multi_xml'
+require 'virtus'
+require 'i18n'
+require 'thread'
+
+I18n.load_path << File.expand_path('../grape/locale/en.yml', __FILE__)
+
+module Grape
+  extend ActiveSupport::Autoload
+
+  eager_autoload do
+    autoload :API
+    autoload :Endpoint
+
+    autoload :Route
+    autoload :Namespace
+
+    autoload :Path
+
+    autoload :Cookies
+    autoload :Validations
+    autoload :Request, 'grape/http/request'
+  end
+
+  module Http
+    extend ActiveSupport::Autoload
+    eager_autoload do
+      autoload :Headers
+    end
+  end
+
+  module Exceptions
+    extend ActiveSupport::Autoload
+    autoload :Base
+    autoload :Validation
+    autoload :ValidationErrors
+    autoload :MissingVendorOption
+    autoload :MissingMimeType
+    autoload :MissingOption
+    autoload :InvalidFormatter
+    autoload :InvalidVersionerOption
+    autoload :UnknownValidator
+    autoload :UnknownOptions
+    autoload :InvalidWithOptionForRepresent
+    autoload :IncompatibleOptionValues
+    autoload :MissingGroupTypeError,          'grape/exceptions/missing_group_type'
+    autoload :UnsupportedGroupTypeError,      'grape/exceptions/unsupported_group_type'
+    autoload :InvalidMessageBody
+    autoload :InvalidAcceptHeader
+  end
+
+  module ErrorFormatter
+    extend ActiveSupport::Autoload
+    autoload :Base
+    autoload :Json
+    autoload :Txt
+    autoload :Xml
+  end
+
+  module Formatter
+    extend ActiveSupport::Autoload
+    autoload :Base
+    autoload :Json
+    autoload :SerializableHash
+    autoload :Txt
+    autoload :Xml
+  end
+
+  module Parser
+    extend ActiveSupport::Autoload
+    autoload :Base
+    autoload :Json
+    autoload :Xml
+  end
+
+  module Middleware
+    extend ActiveSupport::Autoload
+    autoload :Base
+    autoload :Versioner
+    autoload :Formatter
+    autoload :Error
+    autoload :Globals
+
+    module Auth
+      extend ActiveSupport::Autoload
+      autoload :Base
+      autoload :DSL
+      autoload :StrategyInfo
+      autoload :Strategies
+    end
+
+    module Versioner
+      extend ActiveSupport::Autoload
+      autoload :Path
+      autoload :Header
+      autoload :Param
+      autoload :AcceptVersionHeader
+    end
+  end
+
+  module Util
+    extend ActiveSupport::Autoload
+    autoload :InheritableValues
+    autoload :StackableValues
+    autoload :InheritableSetting
+    autoload :StrictHashConfiguration
+  end
+
+  module DSL
+    extend ActiveSupport::Autoload
+    eager_autoload do
+      autoload :API
+      autoload :Callbacks
+      autoload :Settings
+      autoload :Configuration
+      autoload :InsideRoute
+      autoload :Helpers
+      autoload :Middleware
+      autoload :Parameters
+      autoload :RequestResponse
+      autoload :Routing
+      autoload :Validations
+    end
+  end
+
+  class API
+    extend ActiveSupport::Autoload
+    autoload :Helpers
+  end
+
+  module Presenters
+    extend ActiveSupport::Autoload
+    autoload :Presenter
+  end
+end
+
+require 'grape/validations/validators/base'
+require 'grape/validations/attributes_iterator'
+require 'grape/validations/validators/allow_blank'
+require 'grape/validations/validators/at_least_one_of'
+require 'grape/validations/validators/coerce'
+require 'grape/validations/validators/default'
+require 'grape/validations/validators/exactly_one_of'
+require 'grape/validations/validators/mutual_exclusion'
+require 'grape/validations/validators/presence'
+require 'grape/validations/validators/regexp'
+require 'grape/validations/validators/values'
+require 'grape/validations/params_scope'
+require 'grape/validations/validators/all_or_none'
+
+require 'grape/version'
diff --git a/lib/grape/api.rb b/lib/grape/api.rb
new file mode 100644
index 0000000..daae305
--- /dev/null
+++ b/lib/grape/api.rb
@@ -0,0 +1,194 @@
+module Grape
+  # The API class is the primary entry point for
+  # creating Grape APIs.Users should subclass this
+  # class in order to build an API.
+  class API
+    include Grape::DSL::API
+
+    class << self
+      attr_reader :instance
+      LOCK = Mutex.new
+
+      def reset!
+        @route_set = Rack::Mount::RouteSet.new
+        @endpoints = []
+        @routes = nil
+        reset_validations!
+      end
+
+      def compile
+        @instance ||= new
+      end
+
+      def change!
+        @instance = nil
+      end
+
+      def call(env)
+        LOCK.synchronize { compile } unless instance
+        call!(env)
+      end
+
+      def call!(env)
+        instance.call(env)
+      end
+
+      # Create a scope without affecting the URL.
+      #
+      # @param name [Symbol] Purely placebo, just allows to name the scope to make the code more readable.
+      def scope(_name = nil, &block)
+        within_namespace do
+          nest(block)
+        end
+      end
+
+      def cascade(value = nil)
+        if value.nil?
+          inheritable_setting.namespace_inheritable.keys.include?(:cascade) ? !!namespace_inheritable(:cascade) : true
+        else
+          namespace_inheritable(:cascade, value)
+        end
+      end
+
+      protected
+
+      def prepare_routes
+        endpoints.map(&:routes).flatten
+      end
+
+      # Execute first the provided block, then each of the
+      # block passed in. Allows for simple 'before' setups
+      # of settings stack pushes.
+      def nest(*blocks, &block)
+        blocks.reject!(&:nil?)
+        if blocks.any?
+          instance_eval(&block) if block_given?
+          blocks.each { |b| instance_eval(&b) }
+          reset_validations!
+        else
+          instance_eval(&block)
+        end
+      end
+
+      def inherited(subclass)
+        subclass.reset!
+        subclass.logger = logger.clone
+      end
+
+      def inherit_settings(other_settings)
+        top_level_setting.inherit_from other_settings.point_in_time_copy
+
+        endpoints.each(&:reset_routes!)
+
+        @routes = nil
+      end
+    end
+
+    def initialize
+      @route_set = Rack::Mount::RouteSet.new
+      add_head_not_allowed_methods_and_options_methods
+      self.class.endpoints.each do |endpoint|
+        endpoint.mount_in(@route_set)
+      end
+
+      @route_set.freeze
+    end
+
+    def call(env)
+      result = @route_set.call(env)
+      result[1].delete(Grape::Http::Headers::X_CASCADE) unless cascade?
+      result
+    end
+
+    # Some requests may return a HTTP 404 error if grape cannot find a matching
+    # route. In this case, Rack::Mount adds a X-Cascade header to the response
+    # and sets it to 'pass', indicating to grape's parents they should keep
+    # looking for a matching route on other resources.
+    #
+    # In some applications (e.g. mounting grape on rails), one might need to trap
+    # errors from reaching upstream. This is effectivelly done by unsetting
+    # X-Cascade. Default :cascade is true.
+    def cascade?
+      return !!self.class.namespace_inheritable(:cascade) if self.class.inheritable_setting.namespace_inheritable.keys.include?(:cascade)
+      return !!self.class.namespace_inheritable(:version_options)[:cascade] if self.class.namespace_inheritable(:version_options) && self.class.namespace_inheritable(:version_options).key?(:cascade)
+      true
+    end
+
+    reset!
+
+    private
+
+    # For every resource add a 'OPTIONS' route that returns an HTTP 204 response
+    # with a list of HTTP methods that can be called. Also add a route that
+    # will return an HTTP 405 response for any HTTP method that the resource
+    # cannot handle.
+    def add_head_not_allowed_methods_and_options_methods
+      methods_per_path = {}
+
+      self.class.endpoints.each do |endpoint|
+        routes = endpoint.routes
+        routes.each do |route|
+          methods_per_path[route.route_path] ||= []
+          methods_per_path[route.route_path] << route.route_method
+        end
+      end
+
+      # The paths we collected are prepared (cf. Path#prepare), so they
+      # contain already versioning information when using path versioning.
+      # Disable versioning so adding a route won't prepend versioning
+      # informations again.
+      without_root_prefix do
+        without_versioning do
+          methods_per_path.each do |path, methods|
+            allowed_methods = methods.dup
+            unless self.class.namespace_inheritable(:do_not_route_head)
+              allowed_methods |= [Grape::Http::Headers::HEAD] if allowed_methods.include?(Grape::Http::Headers::GET)
+            end
+
+            allow_header = ([Grape::Http::Headers::OPTIONS] | allowed_methods).join(', ')
+            unless self.class.namespace_inheritable(:do_not_route_options)
+              unless allowed_methods.include?(Grape::Http::Headers::OPTIONS)
+                self.class.options(path, {}) do
+                  header 'Allow', allow_header
+                  status 204
+                  ''
+                end
+              end
+            end
+
+            not_allowed_methods = %w(GET PUT POST DELETE PATCH HEAD) - allowed_methods
+            not_allowed_methods << Grape::Http::Headers::OPTIONS if self.class.namespace_inheritable(:do_not_route_options)
+            self.class.route(not_allowed_methods, path) do
+              header 'Allow', allow_header
+              status 405
+              ''
+            end
+          end
+        end
+      end
+    end
+
+    def without_versioning(&_block)
+      old_version = self.class.namespace_inheritable(:version)
+      old_version_options = self.class.namespace_inheritable(:version_options)
+
+      self.class.namespace_inheritable_to_nil(:version)
+      self.class.namespace_inheritable_to_nil(:version_options)
+
+      yield
+
+      self.class.namespace_inheritable(:version, old_version)
+      self.class.namespace_inheritable(:version_options, old_version_options)
+    end
+
+    def without_root_prefix(&_block)
+      old_prefix = self.class.namespace_inheritable(:root_prefix)
+
+      self.class.namespace_inheritable_to_nil(:root_prefix)
+
+      yield
+
+      self.class.namespace_inheritable(:root_prefix, old_prefix)
+    end
+  end
+end
diff --git a/lib/grape/api/helpers.rb b/lib/grape/api/helpers.rb
new file mode 100644
index 0000000..7fd69e7
--- /dev/null
+++ b/lib/grape/api/helpers.rb
@@ -0,0 +1,7 @@
+module Grape
+  class API
+    module Helpers
+      include Grape::DSL::Helpers::BaseHelper
+    end
+  end
+end
diff --git a/lib/grape/cookies.rb b/lib/grape/cookies.rb
new file mode 100644
index 0000000..cc30ca1
--- /dev/null
+++ b/lib/grape/cookies.rb
@@ -0,0 +1,39 @@
+module Grape
+  class Cookies
+    def initialize
+      @cookies = {}
+      @send_cookies = {}
+    end
+
+    def read(request)
+      request.cookies.each do |name, value|
+        @cookies[name.to_s] = value
+      end
+    end
+
+    def write(header)
+      @cookies.select { |key, _value| @send_cookies[key] == true }.each do |name, value|
+        cookie_value = value.is_a?(Hash) ? value : { value: value }
+        Rack::Utils.set_cookie_header! header, name, cookie_value
+      end
+    end
+
+    def [](name)
+      @cookies[name.to_s]
+    end
+
+    def []=(name, value)
+      @cookies[name.to_s] = value
+      @send_cookies[name.to_s] = true
+    end
+
+    def each(&block)
+      @cookies.each(&block)
+    end
+
+    def delete(name, opts = {})
+      options = opts.merge(value: 'deleted', expires: Time.at(0))
+      self.[]=(name, options)
+    end
+  end
+end
diff --git a/lib/grape/dsl/api.rb b/lib/grape/dsl/api.rb
new file mode 100644
index 0000000..1883170
--- /dev/null
+++ b/lib/grape/dsl/api.rb
@@ -0,0 +1,19 @@
+require 'active_support/concern'
+
+module Grape
+  module DSL
+    module API
+      extend ActiveSupport::Concern
+
+      include Grape::Middleware::Auth::DSL
+
+      include Grape::DSL::Validations
+      include Grape::DSL::Callbacks
+      include Grape::DSL::Configuration
+      include Grape::DSL::Helpers
+      include Grape::DSL::Middleware
+      include Grape::DSL::RequestResponse
+      include Grape::DSL::Routing
+    end
+  end
+end
diff --git a/lib/grape/dsl/callbacks.rb b/lib/grape/dsl/callbacks.rb
new file mode 100644
index 0000000..9045a32
--- /dev/null
+++ b/lib/grape/dsl/callbacks.rb
@@ -0,0 +1,29 @@
+require 'active_support/concern'
+
+module Grape
+  module DSL
+    module Callbacks
+      extend ActiveSupport::Concern
+
+      include Grape::DSL::Configuration
+
+      module ClassMethods
+        def before(&block)
+          namespace_stackable(:befores, block)
+        end
+
+        def before_validation(&block)
+          namespace_stackable(:before_validations, block)
+        end
+
+        def after_validation(&block)
+          namespace_stackable(:after_validations, block)
+        end
+
+        def after(&block)
+          namespace_stackable(:afters, block)
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/dsl/configuration.rb b/lib/grape/dsl/configuration.rb
new file mode 100644
index 0000000..2afacb6
--- /dev/null
+++ b/lib/grape/dsl/configuration.rb
@@ -0,0 +1,71 @@
+require 'active_support/concern'
+
+module Grape
+  module DSL
+    module Configuration
+      extend ActiveSupport::Concern
+
+      module ClassMethods
+        attr_writer :logger
+
+        include Grape::DSL::Settings
+
+        def logger(logger = nil)
+          if logger
+            global_setting(:logger, logger)
+          else
+            global_setting(:logger) || global_setting(:logger, Logger.new($stdout))
+          end
+        end
+
+        # Add a description to the next namespace or function.
+        def desc(description, options = {}, &config_block)
+          if block_given?
+            config_class = Grape::DSL::Configuration.desc_container
+
+            config_class.configure do
+              description description
+            end
+
+            config_class.configure(&config_block)
+            options = config_class.settings
+          else
+            options = options.merge(description: description)
+          end
+
+          namespace_setting :description, options
+          route_setting :description, options
+        end
+      end
+
+      module_function
+
+      def stacked_hash_to_hash(settings)
+        return nil if settings.nil? || settings.blank?
+        settings.each_with_object(ActiveSupport::OrderedHash.new) { |value, result| result.deep_merge!(value) }
+      end
+
+      def desc_container
+        Module.new do
+          include Grape::Util::StrictHashConfiguration.module(
+            :description,
+            :detail,
+            :params,
+            :entity,
+            :http_codes,
+            :named,
+            :headers
+          )
+
+          def config_context.success(*args)
+            entity(*args)
+          end
+
+          def config_context.failure(*args)
+            http_codes(*args)
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/dsl/helpers.rb b/lib/grape/dsl/helpers.rb
new file mode 100644
index 0000000..774a44f
--- /dev/null
+++ b/lib/grape/dsl/helpers.rb
@@ -0,0 +1,85 @@
+require 'active_support/concern'
+
+module Grape
+  module DSL
+    module Helpers
+      extend ActiveSupport::Concern
+      include Grape::DSL::Configuration
+
+      module ClassMethods
+        # Add helper methods that will be accessible from any
+        # endpoint within this namespace (and child namespaces).
+        #
+        # When called without a block, all known helpers within this scope
+        # are included.
+        #
+        # @param [Module] new_mod optional module of methods to include
+        # @param [Block] block optional block of methods to include
+        #
+        # @example Define some helpers.
+        #
+        #     class ExampleAPI < Grape::API
+        #       helpers do
+        #         def current_user
+        #           User.find_by_id(params[:token])
+        #         end
+        #       end
+        #     end
+        #
+        def helpers(new_mod = nil, &block)
+          if block_given? || new_mod
+            mod = new_mod || Module.new
+            if new_mod
+              inject_api_helpers_to_mod(new_mod) if new_mod.is_a?(BaseHelper)
+            end
+            if block_given?
+              inject_api_helpers_to_mod(mod) do
+                mod.class_eval(&block)
+              end
+            end
+
+            namespace_stackable(:helpers, mod)
+          else
+            mod = Module.new
+            namespace_stackable(:helpers).each do |mod_to_include|
+              mod.send :include, mod_to_include
+            end
+            change!
+            mod
+          end
+        end
+
+        protected
+
+        def inject_api_helpers_to_mod(mod, &_block)
+          mod.extend(BaseHelper)
+          yield if block_given?
+          mod.api_changed(self)
+        end
+      end
+
+      # This module extends user defined helpers
+      # to provide some API-specific functionality.
+      module BaseHelper
+        attr_accessor :api
+        def params(name, &block)
+          @named_params ||= {}
+          @named_params[name] = block
+        end
+
+        def api_changed(new_api)
+          @api = new_api
+          process_named_params
+        end
+
+        protected
+
+        def process_named_params
+          if @named_params && @named_params.any?
+            api.namespace_stackable(:named_params, @named_params)
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/dsl/inside_route.rb b/lib/grape/dsl/inside_route.rb
new file mode 100644
index 0000000..b37b856
--- /dev/null
+++ b/lib/grape/dsl/inside_route.rb
@@ -0,0 +1,269 @@
+require 'active_support/concern'
+
+module Grape
+  module DSL
+    module InsideRoute
+      extend ActiveSupport::Concern
+      include Grape::DSL::Settings
+
+      # A filtering method that will return a hash
+      # consisting only of keys that have been declared by a
+      # `params` statement against the current/target endpoint or parent
+      # namespaces.
+      #
+      # @param params [Hash] The initial hash to filter. Usually this will just be `params`
+      # @param options [Hash] Can pass `:include_missing`, `:stringify` and `:include_parent_namespaces`
+      # options. `:include_parent_namespaces` defaults to true, hence must be set to false if
+      # you want only to return params declared against the current/target endpoint.
+      def declared(params, options = {}, declared_params = nil)
+        options[:include_missing] = true unless options.key?(:include_missing)
+        options[:include_parent_namespaces] = true unless options.key?(:include_parent_namespaces)
+
+        if declared_params.nil?
+          declared_params = (!options[:include_parent_namespaces] ? route_setting(:declared_params) :
+              (route_setting(:saved_declared_params) || [])).flatten(1) || []
+        end
+
+        unless declared_params
+          fail ArgumentError, 'Tried to filter for declared parameters but none exist.'
+        end
+
+        if params.is_a? Array
+          params.map do |param|
+            declared(param || {}, options, declared_params)
+          end
+        else
+          declared_params.inject(Hashie::Mash.new) do |hash, key|
+            key = { key => nil } unless key.is_a? Hash
+
+            key.each_pair do |parent, children|
+              output_key = options[:stringify] ? parent.to_s : parent.to_sym
+
+              next unless options[:include_missing] || params.key?(parent)
+
+              hash[output_key] = if children
+                                   children_params = params[parent] || (children.is_a?(Array) ? [] : {})
+                                   declared(children_params, options, Array(children))
+                                 else
+                                   params[parent]
+                                 end
+            end
+
+            hash
+          end
+        end
+      end
+
+      # The API version as specified in the URL.
+      def version
+        env['api.version']
+      end
+
+      # End the request and display an error to the
+      # end user with the specified message.
+      #
+      # @param message [String] The message to display.
+      # @param status [Integer] the HTTP Status Code. Defaults to default_error_status, 500 if not set.
+      def error!(message, status = nil, headers = nil)
+        self.status(status || namespace_inheritable(:default_error_status))
+        throw :error, message: message, status: self.status, headers: headers
+      end
+
+      # Redirect to a new url.
+      #
+      # @param url [String] The url to be redirect.
+      # @param options [Hash] The options used when redirect.
+      #                       :permanent, default false.
+      def redirect(url, options = {})
+        merged_options = { permanent: false }.merge(options)
+        if merged_options[:permanent]
+          status 301
+        else
+          if env[Grape::Http::Headers::HTTP_VERSION] == 'HTTP/1.1' && request.request_method.to_s.upcase != Grape::Http::Headers::GET
+            status 303
+          else
+            status 302
+          end
+        end
+        header 'Location', url
+        body ''
+      end
+
+      # Set or retrieve the HTTP status code.
+      #
+      # @param status [Integer] The HTTP Status Code to return for this request.
+      def status(status = nil)
+        case status
+        when Symbol
+          if Rack::Utils::SYMBOL_TO_STATUS_CODE.keys.include?(status)
+            @status = Rack::Utils.status_code(status)
+          else
+            fail ArgumentError, "Status code :#{status} is invalid."
+          end
+        when Fixnum
+          @status = status
+        when nil
+          return @status if @status
+          case request.request_method.to_s.upcase
+          when Grape::Http::Headers::POST
+            201
+          else
+            200
+          end
+        else
+          fail ArgumentError, 'Status code must be Fixnum or Symbol.'
+        end
+      end
+
+      # Set an individual header or retrieve
+      # all headers that have been set.
+      def header(key = nil, val = nil)
+        if key
+          val ? @header[key.to_s] = val : @header.delete(key.to_s)
+        else
+          @header
+        end
+      end
+
+      # Set response content-type
+      def content_type(val = nil)
+        if val
+          header(Grape::Http::Headers::CONTENT_TYPE, val)
+        else
+          header[Grape::Http::Headers::CONTENT_TYPE]
+        end
+      end
+
+      # Set or get a cookie
+      #
+      # @example
+      #   cookies[:mycookie] = 'mycookie val'
+      #   cookies['mycookie-string'] = 'mycookie string val'
+      #   cookies[:more] = { value: '123', expires: Time.at(0) }
+      #   cookies.delete :more
+      #
+      def cookies
+        @cookies ||= Cookies.new
+      end
+
+      # Allows you to define the response body as something other than the
+      # return value.
+      #
+      # @example
+      #   get '/body' do
+      #     body "Body"
+      #     "Not the Body"
+      #   end
+      #
+      #   GET /body # => "Body"
+      def body(value = nil)
+        if value
+          @body = value
+        elsif value == false
+          @body = ''
+          status 204
+        else
+          @body
+        end
+      end
+
+      # Allows you to define the response as a file-like object.
+      #
+      # @example
+      #   get '/file' do
+      #     file FileStreamer.new(...)
+      #   end
+      #
+      #   GET /file # => "contents of file"
+      def file(value = nil)
+        if value
+          @file = value
+        else
+          @file
+        end
+      end
+
+      # Allows you to make use of Grape Entities by setting
+      # the response body to the serializable hash of the
+      # entity provided in the `:with` option. This has the
+      # added benefit of automatically passing along environment
+      # and version information to the serialization, making it
+      # very easy to do conditional exposures. See Entity docs
+      # for more info.
+      #
+      # @example
+      #
+      #   get '/users/:id' do
+      #     present User.find(params[:id]),
+      #       with: API::Entities::User,
+      #       admin: current_user.admin?
+      #   end
+      def present(*args)
+        options = args.count > 1 ? args.extract_options! : {}
+        key, object = if args.count == 2 && args.first.is_a?(Symbol)
+                        args
+                      else
+                        [nil, args.first]
+                      end
+        entity_class = entity_class_for_obj(object, options)
+
+        root = options.delete(:root)
+
+        representation = if entity_class
+                           entity_representation_for(entity_class, object, options)
+                         else
+                           object
+                         end
+
+        representation = { root => representation } if root
+        if key
+          representation = (@body || {}).merge(key => representation)
+        elsif entity_class.present? && @body
+          fail ArgumentError, "Representation of type #{representation.class} cannot be merged." unless representation.respond_to?('merge')
+          representation = @body.merge(representation)
+        end
+
+        body representation
+      end
+
+      # Returns route information for the current request.
+      #
+      # @example
+      #
+      #   desc "Returns the route description."
+      #   get '/' do
+      #     route.route_description
+      #   end
+      def route
+        env['rack.routing_args'][:route_info]
+      end
+
+      def entity_class_for_obj(object, options)
+        entity_class = options.delete(:with)
+
+        if entity_class.nil?
+          # entity class not explicitely defined, auto-detect from relation#klass or first object in the collection
+          object_class = if object.respond_to?(:klass)
+                           object.klass
+                         else
+                           object.respond_to?(:first) ? object.first.class : object.class
+                         end
+
+          object_class.ancestors.each do |potential|
+            entity_class ||= (Grape::DSL::Configuration.stacked_hash_to_hash(namespace_stackable(:representations)) || {})[potential]
+          end
+
+          entity_class ||= object_class.const_get(:Entity) if object_class.const_defined?(:Entity) && object_class.const_get(:Entity).respond_to?(:represent)
+        end
+
+        entity_class
+      end
+
+      def entity_representation_for(entity_class, object, options)
+        embeds = { env: env }
+        embeds[:version] = env['api.version'] if env['api.version']
+        entity_class.represent(object, embeds.merge(options))
+      end
+    end
+  end
+end
diff --git a/lib/grape/dsl/middleware.rb b/lib/grape/dsl/middleware.rb
new file mode 100644
index 0000000..7dc98ac
--- /dev/null
+++ b/lib/grape/dsl/middleware.rb
@@ -0,0 +1,33 @@
+require 'active_support/concern'
+
+module Grape
+  module DSL
+    module Middleware
+      extend ActiveSupport::Concern
+
+      include Grape::DSL::Configuration
+
+      module ClassMethods
+        # Apply a custom middleware to the API. Applies
+        # to the current namespace and any children, but
+        # not parents.
+        #
+        # @param middleware_class [Class] The class of the middleware you'd like
+        #   to inject.
+        def use(middleware_class, *args, &block)
+          arr = [middleware_class, *args]
+          arr << block if block_given?
+
+          namespace_stackable(:middleware, arr)
+        end
+
+        # Retrieve an array of the middleware classes
+        # and arguments that are currently applied to the
+        # application.
+        def middleware
+          namespace_stackable(:middleware) || []
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/dsl/parameters.rb b/lib/grape/dsl/parameters.rb
new file mode 100644
index 0000000..ee075c0
--- /dev/null
+++ b/lib/grape/dsl/parameters.rb
@@ -0,0 +1,113 @@
+require 'active_support/concern'
+
+module Grape
+  module DSL
+    module Parameters
+      extend ActiveSupport::Concern
+
+      # Include reusable params rules among current.
+      # You can define reusable params with helpers method.
+      #
+      # @example
+      #
+      #     class API < Grape::API
+      #       helpers do
+      #         params :pagination do
+      #           optional :page, type: Integer
+      #           optional :per_page, type: Integer
+      #         end
+      #       end
+      #
+      #       desc "Get collection"
+      #       params do
+      #         use :pagination
+      #       end
+      #       get do
+      #         Collection.page(params[:page]).per(params[:per_page])
+      #       end
+      #     end
+      def use(*names)
+        named_params = Grape::DSL::Configuration.stacked_hash_to_hash(@api.namespace_stackable(:named_params)) || {}
+        options = names.last.is_a?(Hash) ? names.pop : {}
+        names.each do |name|
+          params_block = named_params.fetch(name) do
+            fail "Params :#{name} not found!"
+          end
+          instance_exec(options, &params_block)
+        end
+      end
+      alias_method :use_scope, :use
+      alias_method :includes, :use
+
+      def requires(*attrs, &block)
+        orig_attrs = attrs.clone
+
+        opts = attrs.last.is_a?(Hash) ? attrs.pop.clone : {}
+        opts[:presence] = true
+
+        if opts[:using]
+          require_required_and_optional_fields(attrs.first, opts)
+        else
+          validate_attributes(attrs, opts, &block)
+
+          block_given? ? new_scope(orig_attrs, &block) :
+              push_declared_params(attrs)
+        end
+      end
+
+      def optional(*attrs, &block)
+        orig_attrs = attrs.clone
+
+        opts = attrs.last.is_a?(Hash) ? attrs.pop.clone : {}
+        type = opts[:type]
+
+        # check type for optional parameter group
+        if attrs && block_given?
+          fail Grape::Exceptions::MissingGroupTypeError.new if type.nil?
+          fail Grape::Exceptions::UnsupportedGroupTypeError.new unless [Array, Hash].include?(type)
+        end
+
+        if opts[:using]
+          require_optional_fields(attrs.first, opts)
+        else
+          validate_attributes(attrs, opts, &block)
+
+          block_given? ? new_scope(orig_attrs, true, &block) :
+              push_declared_params(attrs)
+        end
+      end
+
+      def mutually_exclusive(*attrs)
+        validates(attrs, mutual_exclusion: true)
+      end
+
+      def exactly_one_of(*attrs)
+        validates(attrs, exactly_one_of: true)
+      end
+
+      def at_least_one_of(*attrs)
+        validates(attrs, at_least_one_of: true)
+      end
+
+      def all_or_none_of(*attrs)
+        validates(attrs, all_or_none_of: true)
+      end
+
+      alias_method :group, :requires
+
+      def params(params)
+        params = @parent.params(params) if @parent
+        if @element
+          if params.is_a?(Array)
+            params = params.flat_map { |el| el[@element] || {} }
+          elsif params.is_a?(Hash)
+            params = params[@element] || {}
+          else
+            params = {}
+          end
+        end
+        params
+      end
+    end
+  end
+end
diff --git a/lib/grape/dsl/request_response.rb b/lib/grape/dsl/request_response.rb
new file mode 100644
index 0000000..913ecdd
--- /dev/null
+++ b/lib/grape/dsl/request_response.rb
@@ -0,0 +1,155 @@
+require 'active_support/concern'
+
+module Grape
+  module DSL
+    module RequestResponse
+      extend ActiveSupport::Concern
+
+      include Grape::DSL::Configuration
+
+      module ClassMethods
+        # Specify the default format for the API's serializers.
+        # May be `:json` or `:txt` (default).
+        def default_format(new_format = nil)
+          namespace_inheritable(:default_format, new_format.nil? ? nil : new_format.to_sym)
+        end
+
+        # Specify the format for the API's serializers.
+        # May be `:json`, `:xml`, `:txt`, etc.
+        def format(new_format = nil)
+          if new_format
+            namespace_inheritable(:format, new_format.to_sym)
+            # define the default error formatters
+            namespace_inheritable(:default_error_formatter, Grape::ErrorFormatter::Base.formatter_for(new_format, {}))
+            # define a single mime type
+            mime_type = content_types[new_format.to_sym]
+            fail Grape::Exceptions::MissingMimeType.new(new_format) unless mime_type
+            namespace_stackable(:content_types, new_format.to_sym => mime_type)
+          else
+            namespace_inheritable(:format)
+          end
+        end
+
+        # Specify a custom formatter for a content-type.
+        def formatter(content_type, new_formatter)
+          namespace_stackable(:formatters, content_type.to_sym => new_formatter)
+        end
+
+        # Specify a custom parser for a content-type.
+        def parser(content_type, new_parser)
+          namespace_stackable(:parsers, content_type.to_sym => new_parser)
+        end
+
+        # Specify a default error formatter.
+        def default_error_formatter(new_formatter_name = nil)
+          if new_formatter_name
+            new_formatter = Grape::ErrorFormatter::Base.formatter_for(new_formatter_name, {})
+            namespace_inheritable(:default_error_formatter, new_formatter)
+          else
+            namespace_inheritable(:default_error_formatter)
+          end
+        end
+
+        def error_formatter(format, options)
+          if options.is_a?(Hash) && options.key?(:with)
+            formatter = options[:with]
+          else
+            formatter = options
+          end
+
+          namespace_stackable(:error_formatters, format.to_sym => formatter)
+        end
+
+        # Specify additional content-types, e.g.:
+        #   content_type :xls, 'application/vnd.ms-excel'
+        def content_type(key, val)
+          namespace_stackable(:content_types, key.to_sym => val)
+        end
+
+        # All available content types.
+        def content_types
+          c_types = Grape::DSL::Configuration.stacked_hash_to_hash(namespace_stackable(:content_types))
+          Grape::ContentTypes.content_types_for c_types
+        end
+
+        # Specify the default status code for errors.
+        def default_error_status(new_status = nil)
+          namespace_inheritable(:default_error_status, new_status)
+        end
+
+        # Allows you to rescue certain exceptions that occur to return
+        # a grape error rather than raising all the way to the
+        # server level.
+        #
+        # @example Rescue from custom exceptions
+        #     class ExampleAPI < Grape::API
+        #       class CustomError < StandardError; end
+        #
+        #       rescue_from CustomError
+        #     end
+        #
+        # @overload rescue_from(*exception_classes, options = {})
+        #   @param [Array] exception_classes A list of classes that you want to rescue, or
+        #     the symbol :all to rescue from all exceptions.
+        #   @param [Block] block Execution block to handle the given exception.
+        #   @param [Hash] options Options for the rescue usage.
+        #   @option options [Boolean] :backtrace Include a backtrace in the rescue response.
+        #   @option options [Boolean] :rescue_subclasses Also rescue subclasses of exception classes
+        #   @param [Proc] handler Execution proc to handle the given exception as an
+        #     alternative to passing a block.
+        def rescue_from(*args, &block)
+          if args.last.is_a?(Proc)
+            handler = args.pop
+          elsif block_given?
+            handler = block
+          end
+
+          options = args.last.is_a?(Hash) ? args.pop : {}
+          handler ||= proc { options[:with] } if options.key?(:with)
+
+          if args.include?(:all)
+            namespace_inheritable(:rescue_all, true)
+            namespace_inheritable :all_rescue_handler, handler
+          else
+            handler_type =
+                case options[:rescue_subclasses]
+                when nil, true
+                  :rescue_handlers
+                else
+                  :base_only_rescue_handlers
+                end
+
+            namespace_stackable handler_type, Hash[args.map { |arg| [arg, handler] }]
+          end
+
+          namespace_stackable(:rescue_options, options)
+        end
+
+        # Allows you to specify a default representation entity for a
+        # class. This allows you to map your models to their respective
+        # entities once and then simply call `present` with the model.
+        #
+        # @example
+        #   class ExampleAPI < Grape::API
+        #     represent User, with: Entity::User
+        #
+        #     get '/me' do
+        #       present current_user # with: Entity::User is assumed
+        #     end
+        #   end
+        #
+        # Note that Grape will automatically go up the class ancestry to
+        # try to find a representing entity, so if you, for example, define
+        # an entity to represent `Object` then all presented objects will
+        # bubble up and utilize the entity provided on that `represent` call.
+        #
+        # @param model_class [Class] The model class that will be represented.
+        # @option options [Class] :with The entity class that will represent the model.
+        def represent(model_class, options)
+          fail Grape::Exceptions::InvalidWithOptionForRepresent.new unless options[:with] && options[:with].is_a?(Class)
+          namespace_stackable(:representations, model_class => options[:with])
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/dsl/routing.rb b/lib/grape/dsl/routing.rb
new file mode 100644
index 0000000..7f10ecf
--- /dev/null
+++ b/lib/grape/dsl/routing.rb
@@ -0,0 +1,186 @@
+require 'active_support/concern'
+
+module Grape
+  module DSL
+    module Routing
+      extend ActiveSupport::Concern
+      include Grape::DSL::Configuration
+
+      module ClassMethods
+        attr_reader :endpoints, :routes, :route_set
+
+        # Specify an API version.
+        #
+        # @example API with legacy support.
+        #   class MyAPI < Grape::API
+        #     version 'v2'
+        #
+        #     get '/main' do
+        #       {some: 'data'}
+        #     end
+        #
+        #     version 'v1' do
+        #       get '/main' do
+        #         {legacy: 'data'}
+        #       end
+        #     end
+        #   end
+        #
+        def version(*args, &block)
+          if args.any?
+            options = args.pop if args.last.is_a? Hash
+            options ||= {}
+            options = { using: :path }.merge(options)
+
+            fail Grape::Exceptions::MissingVendorOption.new if options[:using] == :header && !options.key?(:vendor)
+
+            @versions = versions | args
+
+            if block_given?
+              within_namespace do
+                namespace_inheritable(:version, args)
+                namespace_inheritable(:version_options, options)
+
+                instance_eval(&block)
+              end
+            else
+              namespace_inheritable(:version, args)
+              namespace_inheritable(:version_options, options)
+            end
+
+            # reset_validations!
+          end
+
+          @versions.last unless @versions.nil?
+        end
+
+        # Define a root URL prefix for your entire API.
+        def prefix(prefix = nil)
+          namespace_inheritable(:root_prefix, prefix)
+        end
+
+        # Do not route HEAD requests to GET requests automatically.
+        def do_not_route_head!
+          namespace_inheritable(:do_not_route_head, true)
+        end
+
+        # Do not automatically route OPTIONS.
+        def do_not_route_options!
+          namespace_inheritable(:do_not_route_options, true)
+        end
+
+        def mount(mounts)
+          mounts = { mounts => '/' } unless mounts.respond_to?(:each_pair)
+          mounts.each_pair do |app, path|
+            in_setting = inheritable_setting
+
+            if app.respond_to?(:inheritable_setting, true)
+              mount_path = Rack::Mount::Utils.normalize_path(path)
+              app.top_level_setting.namespace_stackable[:mount_path] =  mount_path
+
+              app.inherit_settings(inheritable_setting)
+
+              in_setting = app.top_level_setting
+
+              # app.regenerate_endpoints(in_setting)
+
+              app.change!
+              change!
+            end
+
+            endpoints << Grape::Endpoint.new(
+              in_setting,
+              method: :any,
+              path: path,
+              app: app,
+              for: self
+            )
+          end
+        end
+
+        # Defines a route that will be recognized
+        # by the Grape API.
+        #
+        # @param methods [HTTP Verb] One or more HTTP verbs that are accepted by this route. Set to `:any` if you want any verb to be accepted.
+        # @param paths [String] One or more strings representing the URL segment(s) for this route.
+        #
+        # @example Defining a basic route.
+        #   class MyAPI < Grape::API
+        #     route(:any, '/hello') do
+        #       {hello: 'world'}
+        #     end
+        #   end
+        def route(methods, paths = ['/'], route_options = {}, &block)
+          endpoint_options = {
+            method: methods,
+            path: paths,
+            for: self,
+            route_options: ({
+              params: Grape::DSL::Configuration.stacked_hash_to_hash(namespace_stackable(:params)) || {}
+            }).deep_merge(route_setting(:description) || {}).deep_merge(route_options || {})
+          }
+
+          new_endpoint = Grape::Endpoint.new(inheritable_setting, endpoint_options, &block)
+          endpoints << new_endpoint unless endpoints.any? { |e| e.equals?(new_endpoint) }
+
+          route_end
+          reset_validations!
+        end
+
+        %w(get post put head delete options patch).each do |meth|
+          define_method meth do |*args, &block|
+            options = args.extract_options!
+            paths = args.first || ['/']
+            route(meth.upcase, paths, options, &block)
+          end
+        end
+
+        def namespace(space = nil, options = {}, &block)
+          if space || block_given?
+            within_namespace do
+              previous_namespace_description = @namespace_description
+              @namespace_description = (@namespace_description || {}).deep_merge(namespace_setting(:description) || {})
+              nest(block) do
+                if space
+                  namespace_stackable(:namespace, Namespace.new(space, options))
+                end
+              end
+              @namespace_description = previous_namespace_description
+            end
+          else
+            Namespace.joined_space_path(namespace_stackable(:namespace))
+          end
+        end
+
+        alias_method :group, :namespace
+        alias_method :resource, :namespace
+        alias_method :resources, :namespace
+        alias_method :segment, :namespace
+
+        # An array of API routes.
+        def routes
+          @routes ||= prepare_routes
+        end
+
+        def reset_routes!
+          @routes = nil
+        end
+
+        # Thie method allows you to quickly define a parameter route segment
+        # in your API.
+        #
+        # @param param [Symbol] The name of the parameter you wish to declare.
+        # @option options [Regexp] You may supply a regular expression that the declared parameter must meet.
+        def route_param(param, options = {}, &block)
+          options = options.dup
+          options[:requirements] = { param.to_sym => options[:requirements] } if options[:requirements].is_a?(Regexp)
+          namespace(":#{param}", options, &block)
+        end
+
+        def versions
+          @versions ||= []
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/dsl/settings.rb b/lib/grape/dsl/settings.rb
new file mode 100644
index 0000000..5630fab
--- /dev/null
+++ b/lib/grape/dsl/settings.rb
@@ -0,0 +1,110 @@
+require 'active_support/concern'
+
+module Grape
+  module DSL
+    module Settings
+      extend ActiveSupport::Concern
+
+      attr_accessor :inheritable_setting, :top_level_setting
+
+      def top_level_setting
+        @top_level_setting ||= Grape::Util::InheritableSetting.new
+      end
+
+      def inheritable_setting
+        @inheritable_setting ||= Grape::Util::InheritableSetting.new.tap { |new_settings| new_settings.inherit_from top_level_setting }
+      end
+
+      def unset(type, key)
+        setting = inheritable_setting.send(type)
+        setting.delete key
+      end
+
+      def get_or_set(type, key, value)
+        setting = inheritable_setting.send(type)
+        if value.nil?
+          setting[key]
+        else
+          setting[key] = value
+        end
+      end
+
+      def global_setting(key, value = nil)
+        get_or_set :global, key, value
+      end
+
+      def unset_global_setting(key)
+        unset :global, key
+      end
+
+      def route_setting(key, value = nil)
+        get_or_set :route, key, value
+      end
+
+      def unset_route_setting(key)
+        unset :route, key
+      end
+
+      def namespace_setting(key, value = nil)
+        get_or_set :namespace, key, value
+      end
+
+      def unset_namespace_setting(key)
+        unset :namespace_setting, key
+      end
+
+      def namespace_inheritable(key, value = nil)
+        get_or_set :namespace_inheritable, key, value
+      end
+
+      def unset_namespace_inheritable(key)
+        unset :namespace_inheritable, key
+      end
+
+      def namespace_inheritable_to_nil(key)
+        inheritable_setting.namespace_inheritable[key] = nil
+      end
+
+      def namespace_stackable(key, value = nil)
+        get_or_set :namespace_stackable, key, value
+      end
+
+      def unset_namespace_stackable(key)
+        unset :namespace_stackable, key
+      end
+
+      def api_class_setting(key, value = nil)
+        get_or_set :api_class, key, value
+      end
+
+      def unset_api_class_setting(key)
+        unset :api_class_setting, key
+      end
+
+      def namespace_start
+        @inheritable_setting = Grape::Util::InheritableSetting.new.tap { |new_settings| new_settings.inherit_from inheritable_setting }
+      end
+
+      def namespace_end
+        route_end
+        @inheritable_setting = inheritable_setting.parent
+      end
+
+      def route_end
+        inheritable_setting.route_end
+        reset_validations!
+      end
+
+      def within_namespace(&_block)
+        namespace_start
+
+        result = yield if block_given?
+
+        namespace_end
+        reset_validations!
+
+        result
+      end
+    end
+  end
+end
diff --git a/lib/grape/dsl/validations.rb b/lib/grape/dsl/validations.rb
new file mode 100644
index 0000000..b45dd66
--- /dev/null
+++ b/lib/grape/dsl/validations.rb
@@ -0,0 +1,37 @@
+require 'active_support/concern'
+
+module Grape
+  module DSL
+    module Validations
+      extend ActiveSupport::Concern
+
+      include Grape::DSL::Configuration
+
+      module ClassMethods
+        def reset_validations!
+          unset_namespace_stackable :declared_params
+          unset_namespace_stackable :validations
+          unset_namespace_stackable :params
+        end
+
+        def params(&block)
+          Grape::Validations::ParamsScope.new(api: self, type: Hash, &block)
+        end
+
+        def document_attribute(names, opts)
+          route_setting(:description, {}) unless route_setting(:description)
+
+          route_setting(:description)[:params] ||= {}
+
+          setting = route_setting(:description)[:params]
+          Array(names).each do |name|
+            setting[name[:full_name].to_s] ||= {}
+            setting[name[:full_name].to_s].merge!(opts)
+
+            namespace_stackable(:params, name[:full_name].to_s => opts)
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/endpoint.rb b/lib/grape/endpoint.rb
new file mode 100644
index 0000000..3b2bb7c
--- /dev/null
+++ b/lib/grape/endpoint.rb
@@ -0,0 +1,330 @@
+module Grape
+  # An Endpoint is the proxy scope in which all routing
+  # blocks are executed. In other words, any methods
+  # on the instance level of this class may be called
+  # from inside a `get`, `post`, etc.
+  class Endpoint
+    include Grape::DSL::Settings
+
+    attr_accessor :block, :source, :options
+    attr_reader :env, :request, :headers, :params
+
+    include Grape::DSL::InsideRoute
+
+    class << self
+      def before_each(new_setup = false, &block)
+        if new_setup == false
+          if block_given?
+            @before_each = block
+          else
+            return @before_each
+          end
+        else
+          @before_each = new_setup
+        end
+      end
+
+      # @api private
+      #
+      # Create an UnboundMethod that is appropriate for executing an endpoint
+      # route.
+      #
+      # The unbound method allows explicit calls to +return+ without raising a
+      # +LocalJumpError+. The method will be removed, but a +Proc+ reference to
+      # it will be returned. The returned +Proc+ expects a single argument: the
+      # instance of +Endpoint+ to bind to the method during the call.
+      #
+      # @param [String, Symbol] method_name
+      # @return [Proc]
+      # @raise [NameError] an instance method with the same name already exists
+      def generate_api_method(method_name, &block)
+        if instance_methods.include?(method_name.to_sym) || instance_methods.include?(method_name.to_s)
+          fail NameError.new("method #{method_name.inspect} already exists and cannot be used as an unbound method name")
+        end
+        define_method(method_name, &block)
+        method = instance_method(method_name)
+        remove_method(method_name)
+        proc { |endpoint_instance| method.bind(endpoint_instance).call }
+      end
+    end
+
+    def initialize(new_settings, options = {}, &block)
+      require_option(options, :path)
+      require_option(options, :method)
+
+      self.inheritable_setting = new_settings.point_in_time_copy
+
+      route_setting(:saved_declared_params, namespace_stackable(:declared_params))
+      route_setting(:saved_validations, namespace_stackable(:validations))
+
+      namespace_stackable(:representations, []) unless namespace_stackable(:representations)
+      namespace_inheritable(:default_error_status, 500) unless namespace_inheritable(:default_error_status)
+
+      @options = options
+
+      @options[:path] = Array(options[:path])
+      @options[:path] << '/' if options[:path].empty?
+
+      @options[:method] = Array(options[:method])
+      @options[:route_options] ||= {}
+
+      if block_given?
+        @source = block
+        @block = self.class.generate_api_method(method_name, &block)
+      end
+    end
+
+    def require_option(options, key)
+      fail Grape::Exceptions::MissingOption.new(key) unless options.key?(key)
+    end
+
+    def method_name
+      [options[:method],
+       Namespace.joined_space(namespace_stackable(:namespace)),
+       (namespace_stackable(:mount_path) || []).join('/'),
+       options[:path].join('/')
+      ].join(' ')
+    end
+
+    def routes
+      @routes ||= endpoints ? endpoints.collect(&:routes).flatten : prepare_routes
+    end
+
+    def reset_routes!
+      endpoints.map(&:reset_routes!) if endpoints
+      @namespace = nil
+      @routes = nil
+    end
+
+    def mount_in(route_set)
+      if endpoints
+        endpoints.each do |e|
+          e.mount_in(route_set)
+        end
+      else
+        @routes = nil
+
+        routes.each do |route|
+          methods = [route.route_method]
+          if !namespace_inheritable(:do_not_route_head) && route.route_method == Grape::Http::Headers::GET
+            methods << Grape::Http::Headers::HEAD
+          end
+          methods.each do |method|
+            route_set.add_route(self, {
+                                  path_info: route.route_compiled,
+                                  request_method: method
+                                },  route_info: route)
+          end
+        end
+      end
+    end
+
+    def prepare_routes_requirements
+      endpoint_requirements = options[:route_options][:requirements] || {}
+      all_requirements = (namespace_stackable(:namespace).map(&:requirements) << endpoint_requirements)
+      all_requirements.reduce({}) do |base_requirements, single_requirements|
+        base_requirements.merge!(single_requirements)
+      end
+    end
+
+    def prepare_routes_path_params(path)
+      path_params = {}
+
+      # named parameters in the api path
+      regex = Rack::Mount::RegexpWithNamedGroups.new(path)
+      named_params = regex.named_captures.map { |nc| nc[0] } - %w(version format)
+      named_params.each { |named_param| path_params[named_param] = '' }
+
+      # route parameters declared via desc or appended to the api declaration
+      route_params = options[:route_options][:params]
+      path_params.merge! route_params if route_params
+
+      path_params
+    end
+
+    def prepare_routes
+      options[:method].map do |method|
+        options[:path].map do |path|
+          prepared_path = prepare_path(path)
+          anchor = options[:route_options].fetch(:anchor, true)
+          path = compile_path(prepared_path, anchor && !options[:app], prepare_routes_requirements)
+          request_method = (method.to_s.upcase unless method == :any)
+
+          Route.new(options[:route_options].clone.merge(
+                      prefix: namespace_inheritable(:root_prefix),
+                      version: namespace_inheritable(:version) ? namespace_inheritable(:version).join('|') : nil,
+                      namespace: namespace,
+                      method: request_method,
+                      path: prepared_path,
+                      params: prepare_routes_path_params(path),
+                      compiled: path,
+                      settings: inheritable_setting.route.except(:saved_declared_params, :saved_validations)
+          ))
+        end
+      end.flatten
+    end
+
+    def prepare_path(path)
+      path_settings = inheritable_setting.to_hash[:namespace_stackable].merge(inheritable_setting.to_hash[:namespace_inheritable])
+      Path.prepare(path, namespace, path_settings)
+    end
+
+    def namespace
+      @namespace ||= Namespace.joined_space_path(namespace_stackable(:namespace))
+    end
+
+    def compile_path(prepared_path, anchor = true, requirements = {})
+      endpoint_options = {}
+      endpoint_options[:version] = /#{namespace_inheritable(:version).join('|')}/ if namespace_inheritable(:version)
+      endpoint_options.merge!(requirements)
+      Rack::Mount::Strexp.compile(prepared_path, endpoint_options, %w( / . ? ), anchor)
+    end
+
+    def call(env)
+      dup.call!(env)
+    end
+
+    def call!(env)
+      extend helpers
+
+      env['api.endpoint'] = self
+      if options[:app]
+        options[:app].call(env)
+      else
+        builder = build_middleware
+        builder.run ->(arg) { run(arg) }
+        builder.call(env)
+      end
+    end
+
+    # Return the collection of endpoints within this endpoint.
+    # This is the case when an Grape::API mounts another Grape::API.
+    def endpoints
+      options[:app].endpoints if options[:app] && options[:app].respond_to?(:endpoints)
+    end
+
+    def equals?(e)
+      (options == e.options) && (inheritable_setting.to_hash == e.inheritable_setting.to_hash)
+    end
+
+    protected
+
+    def run(env)
+      @env = env
+      @header = {}
+
+      @request = Grape::Request.new(env)
+      @params = @request.params
+      @headers = @request.headers
+
+      cookies.read(@request)
+
+      self.class.before_each.call(self) if self.class.before_each
+
+      run_filters befores
+
+      run_filters before_validations
+
+      # Retrieve validations from this namespace and all parent namespaces.
+      validation_errors = []
+
+      # require 'pry-byebug'; binding.pry
+
+      route_setting(:saved_validations).each do |validator|
+        begin
+          validator.validate!(params)
+        rescue Grape::Exceptions::Validation => e
+          validation_errors << e
+        end
+      end
+
+      if validation_errors.any?
+        fail Grape::Exceptions::ValidationErrors, errors: validation_errors
+      end
+
+      run_filters after_validations
+
+      response_object = @block ? @block.call(self) : nil
+      run_filters afters
+      cookies.write(header)
+
+      # The Body commonly is an Array of Strings, the application instance itself, or a File-like object.
+      response_object = file || [body || response_object]
+      [status, header, response_object]
+    end
+
+    def build_middleware
+      b = Rack::Builder.new
+
+      b.use Rack::Head
+      b.use Grape::Middleware::Error,
+            format: namespace_inheritable(:format),
+            content_types: Grape::DSL::Configuration.stacked_hash_to_hash(namespace_stackable(:content_types)),
+            default_status: namespace_inheritable(:default_error_status),
+            rescue_all: namespace_inheritable(:rescue_all),
+            default_error_formatter: namespace_inheritable(:default_error_formatter),
+            error_formatters: Grape::DSL::Configuration.stacked_hash_to_hash(namespace_stackable(:error_formatters)),
+            rescue_options: Grape::DSL::Configuration.stacked_hash_to_hash(namespace_stackable(:rescue_options)) || {},
+            rescue_handlers: Grape::DSL::Configuration.stacked_hash_to_hash(namespace_stackable(:rescue_handlers)) || {},
+            base_only_rescue_handlers: Grape::DSL::Configuration.stacked_hash_to_hash(namespace_stackable(:base_only_rescue_handlers)) || {},
+            all_rescue_handler: namespace_inheritable(:all_rescue_handler)
+
+      (namespace_stackable(:middleware) || []).each do |m|
+        m = m.dup
+        block = m.pop if m.last.is_a?(Proc)
+        if block
+          b.use(*m, &block)
+        else
+          b.use(*m)
+        end
+      end
+
+      if namespace_inheritable(:version)
+        b.use Grape::Middleware::Versioner.using(namespace_inheritable(:version_options)[:using]),
+              versions: namespace_inheritable(:version) ? namespace_inheritable(:version).flatten : nil,
+              version_options: namespace_inheritable(:version_options),
+              prefix: namespace_inheritable(:root_prefix)
+
+      end
+
+      b.use Grape::Middleware::Formatter,
+            format: namespace_inheritable(:format),
+            default_format: namespace_inheritable(:default_format) || :txt,
+            content_types: Grape::DSL::Configuration.stacked_hash_to_hash(namespace_stackable(:content_types)),
+            formatters: Grape::DSL::Configuration.stacked_hash_to_hash(namespace_stackable(:formatters)),
+            parsers: Grape::DSL::Configuration.stacked_hash_to_hash(namespace_stackable(:parsers))
+
+      b
+    end
+
+    def helpers
+      mod = Module.new
+      (namespace_stackable(:helpers) || []).each do |mod_to_include|
+        mod.send :include, mod_to_include
+      end
+      mod
+    end
+
+    def run_filters(filters)
+      (filters || []).each do |filter|
+        instance_eval(&filter)
+      end
+    end
+
+    def befores
+      namespace_stackable(:befores) || []
+    end
+
+    def before_validations
+      namespace_stackable(:before_validations) || []
+    end
+
+    def after_validations
+      namespace_stackable(:after_validations) || []
+    end
+
+    def afters
+      namespace_stackable(:afters) || []
+    end
+  end
+end
diff --git a/lib/grape/error_formatter/base.rb b/lib/grape/error_formatter/base.rb
new file mode 100644
index 0000000..306f5a8
--- /dev/null
+++ b/lib/grape/error_formatter/base.rb
@@ -0,0 +1,59 @@
+module Grape
+  module ErrorFormatter
+    module Base
+      class << self
+        FORMATTERS = {
+          serializable_hash: Grape::ErrorFormatter::Json,
+          json: Grape::ErrorFormatter::Json,
+          jsonapi: Grape::ErrorFormatter::Json,
+          txt: Grape::ErrorFormatter::Txt,
+          xml: Grape::ErrorFormatter::Xml
+        }
+
+        def formatters(options)
+          FORMATTERS.merge(options[:error_formatters] || {})
+        end
+
+        def formatter_for(api_format, options = {})
+          spec = formatters(options)[api_format]
+          case spec
+          when nil
+            options[:default_error_formatter] || Grape::ErrorFormatter::Txt
+          when Symbol
+            method(spec)
+          else
+            spec
+          end
+        end
+      end
+
+      module_function
+
+      def present(message, env)
+        present_options = {}
+        present_options[:with] = message.delete(:with) if message.is_a?(Hash)
+
+        presenter = env['api.endpoint'].entity_class_for_obj(message, present_options)
+
+        unless presenter || env['rack.routing_args'].nil?
+          # env['api.endpoint'].route does not work when the error occurs within a middleware
+          # the Endpoint does not have a valid env at this moment
+          http_codes = env['rack.routing_args'][:route_info].route_http_codes || []
+          found_code = http_codes.find do |http_code|
+            (http_code[0].to_i == env['api.endpoint'].status) && http_code[2].respond_to?(:represent)
+          end if env['api.endpoint'].request
+
+          presenter = found_code[2] if found_code
+        end
+
+        if presenter
+          embeds = { env: env }
+          embeds[:version] = env['api.version'] if env['api.version']
+          message = presenter.represent(message, embeds).serializable_hash
+        end
+
+        message
+      end
+    end
+  end
+end
diff --git a/lib/grape/error_formatter/json.rb b/lib/grape/error_formatter/json.rb
new file mode 100644
index 0000000..7a9efa5
--- /dev/null
+++ b/lib/grape/error_formatter/json.rb
@@ -0,0 +1,17 @@
+module Grape
+  module ErrorFormatter
+    module Json
+      class << self
+        def call(message, backtrace, options = {}, env = nil)
+          message = Grape::ErrorFormatter::Base.present(message, env)
+
+          result = message.is_a?(String) ? { error: message } : message
+          if (options[:rescue_options] || {})[:backtrace] && backtrace && !backtrace.empty?
+            result = result.merge(backtrace: backtrace)
+          end
+          MultiJson.dump(result)
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/error_formatter/txt.rb b/lib/grape/error_formatter/txt.rb
new file mode 100644
index 0000000..6675f00
--- /dev/null
+++ b/lib/grape/error_formatter/txt.rb
@@ -0,0 +1,18 @@
+module Grape
+  module ErrorFormatter
+    module Txt
+      class << self
+        def call(message, backtrace, options = {}, env = nil)
+          message = Grape::ErrorFormatter::Base.present(message, env)
+
+          result = message.is_a?(Hash) ? MultiJson.dump(message) : message
+          if (options[:rescue_options] || {})[:backtrace] && backtrace && !backtrace.empty?
+            result += "\r\n "
+            result += backtrace.join("\r\n ")
+          end
+          result
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/error_formatter/xml.rb b/lib/grape/error_formatter/xml.rb
new file mode 100644
index 0000000..7ba2e53
--- /dev/null
+++ b/lib/grape/error_formatter/xml.rb
@@ -0,0 +1,17 @@
+module Grape
+  module ErrorFormatter
+    module Xml
+      class << self
+        def call(message, backtrace, options = {}, env = nil)
+          message = Grape::ErrorFormatter::Base.present(message, env)
+
+          result = message.is_a?(Hash) ? message : { message: message }
+          if (options[:rescue_options] || {})[:backtrace] && backtrace && !backtrace.empty?
+            result = result.merge(backtrace: backtrace)
+          end
+          result.respond_to?(:to_xml) ? result.to_xml(root: :error) : result.to_s
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/exceptions/base.rb b/lib/grape/exceptions/base.rb
new file mode 100644
index 0000000..8c3b79a
--- /dev/null
+++ b/lib/grape/exceptions/base.rb
@@ -0,0 +1,72 @@
+module Grape
+  module Exceptions
+    class Base < StandardError
+      BASE_MESSAGES_KEY = 'grape.errors.messages'
+      BASE_ATTRIBUTES_KEY = 'grape.errors.attributes'
+      FALLBACK_LOCALE = :en
+
+      attr_reader :status, :message, :headers
+
+      def initialize(args = {})
+        @status = args[:status] || nil
+        @message = args[:message] || nil
+        @headers = args[:headers] || nil
+      end
+
+      def [](index)
+        send index
+      end
+
+      protected
+
+      # TODO: translate attribute first
+      # if BASE_ATTRIBUTES_KEY.key respond to a string message, then short_message is returned
+      # if BASE_ATTRIBUTES_KEY.key respond to a Hash, means it may have problem , summary and resolution
+      def compose_message(key, attributes = {})
+        short_message = translate_message(key, attributes)
+        if short_message.is_a? Hash
+          @problem = problem(key, attributes)
+          @summary = summary(key, attributes)
+          @resolution = resolution(key, attributes)
+          [['Problem', @problem], ['Summary', @summary], ['Resolution', @resolution]].reduce('') do |message, detail_array|
+            message << "\n#{detail_array[0]}:\n  #{detail_array[1]}" unless detail_array[1].blank?
+            message
+          end
+        else
+          short_message
+        end
+      end
+
+      def problem(key, attributes)
+        translate_message("#{key}.problem", attributes)
+      end
+
+      def summary(key, attributes)
+        translate_message("#{key}.summary", attributes)
+      end
+
+      def resolution(key, attributes)
+        translate_message("#{key}.resolution", attributes)
+      end
+
+      def translate_attributes(keys, options = {})
+        keys.map do |key|
+          translate("#{BASE_ATTRIBUTES_KEY}.#{key}", { default: key }.merge(options))
+        end.join(', ')
+      end
+
+      def translate_attribute(key, options = {})
+        translate("#{BASE_ATTRIBUTES_KEY}.#{key}", { default: key }.merge(options))
+      end
+
+      def translate_message(key, options = {})
+        translate("#{BASE_MESSAGES_KEY}.#{key}", { default: '' }.merge(options))
+      end
+
+      def translate(key, options = {})
+        message = ::I18n.translate(key, options)
+        message.present? ? message : ::I18n.translate(key, options.merge(locale: FALLBACK_LOCALE))
+      end
+    end
+  end
+end
diff --git a/lib/grape/exceptions/incompatible_option_values.rb b/lib/grape/exceptions/incompatible_option_values.rb
new file mode 100644
index 0000000..804581a
--- /dev/null
+++ b/lib/grape/exceptions/incompatible_option_values.rb
@@ -0,0 +1,10 @@
+# encoding: utf-8
+module Grape
+  module Exceptions
+    class IncompatibleOptionValues < Base
+      def initialize(option1, value1, option2, value2)
+        super(message: compose_message('incompatible_option_values', option1: option1, value1: value1, option2: option2, value2: value2))
+      end
+    end
+  end
+end
diff --git a/lib/grape/exceptions/invalid_accept_header.rb b/lib/grape/exceptions/invalid_accept_header.rb
new file mode 100644
index 0000000..0141783
--- /dev/null
+++ b/lib/grape/exceptions/invalid_accept_header.rb
@@ -0,0 +1,10 @@
+# encoding: utf-8
+module Grape
+  module Exceptions
+    class InvalidAcceptHeader < Base
+      def initialize(message, headers)
+        super(message: compose_message('invalid_accept_header', message: message), status: 406, headers: headers)
+      end
+    end
+  end
+end
diff --git a/lib/grape/exceptions/invalid_formatter.rb b/lib/grape/exceptions/invalid_formatter.rb
new file mode 100644
index 0000000..4875429
--- /dev/null
+++ b/lib/grape/exceptions/invalid_formatter.rb
@@ -0,0 +1,10 @@
+# encoding: utf-8
+module Grape
+  module Exceptions
+    class InvalidFormatter < Base
+      def initialize(klass, to_format)
+        super(message: compose_message('invalid_formatter', klass: klass, to_format: to_format))
+      end
+    end
+  end
+end
diff --git a/lib/grape/exceptions/invalid_message_body.rb b/lib/grape/exceptions/invalid_message_body.rb
new file mode 100644
index 0000000..f5866b1
--- /dev/null
+++ b/lib/grape/exceptions/invalid_message_body.rb
@@ -0,0 +1,10 @@
+# encoding: utf-8
+module Grape
+  module Exceptions
+    class InvalidMessageBody < Base
+      def initialize(body_format)
+        super(message: compose_message('invalid_message_body', body_format: body_format), status: 400)
+      end
+    end
+  end
+end
diff --git a/lib/grape/exceptions/invalid_versioner_option.rb b/lib/grape/exceptions/invalid_versioner_option.rb
new file mode 100644
index 0000000..e41ba03
--- /dev/null
+++ b/lib/grape/exceptions/invalid_versioner_option.rb
@@ -0,0 +1,10 @@
+# encoding: utf-8
+module Grape
+  module Exceptions
+    class InvalidVersionerOption < Base
+      def initialize(strategy)
+        super(message: compose_message('invalid_versioner_option', strategy: strategy))
+      end
+    end
+  end
+end
diff --git a/lib/grape/exceptions/invalid_with_option_for_represent.rb b/lib/grape/exceptions/invalid_with_option_for_represent.rb
new file mode 100644
index 0000000..8c3c206
--- /dev/null
+++ b/lib/grape/exceptions/invalid_with_option_for_represent.rb
@@ -0,0 +1,10 @@
+# encoding: utf-8
+module Grape
+  module Exceptions
+    class InvalidWithOptionForRepresent < Base
+      def initialize
+        super(message: compose_message('invalid_with_option_for_represent'))
+      end
+    end
+  end
+end
diff --git a/lib/grape/exceptions/missing_group_type.rb b/lib/grape/exceptions/missing_group_type.rb
new file mode 100644
index 0000000..cb05fdc
--- /dev/null
+++ b/lib/grape/exceptions/missing_group_type.rb
@@ -0,0 +1,10 @@
+# encoding: utf-8
+module Grape
+  module Exceptions
+    class MissingGroupTypeError < Base
+      def initialize
+        super(message: compose_message('missing_group_type'))
+      end
+    end
+  end
+end
diff --git a/lib/grape/exceptions/missing_mime_type.rb b/lib/grape/exceptions/missing_mime_type.rb
new file mode 100644
index 0000000..c6958d1
--- /dev/null
+++ b/lib/grape/exceptions/missing_mime_type.rb
@@ -0,0 +1,10 @@
+# encoding: utf-8
+module Grape
+  module Exceptions
+    class MissingMimeType < Base
+      def initialize(new_format)
+        super(message: compose_message('missing_mime_type', new_format: new_format))
+      end
+    end
+  end
+end
diff --git a/lib/grape/exceptions/missing_option.rb b/lib/grape/exceptions/missing_option.rb
new file mode 100644
index 0000000..8a3a2e3
--- /dev/null
+++ b/lib/grape/exceptions/missing_option.rb
@@ -0,0 +1,10 @@
+# encoding: utf-8
+module Grape
+  module Exceptions
+    class MissingOption < Base
+      def initialize(option)
+        super(message: compose_message('missing_option', option: option))
+      end
+    end
+  end
+end
diff --git a/lib/grape/exceptions/missing_vendor_option.rb b/lib/grape/exceptions/missing_vendor_option.rb
new file mode 100644
index 0000000..adc6a38
--- /dev/null
+++ b/lib/grape/exceptions/missing_vendor_option.rb
@@ -0,0 +1,10 @@
+# encoding: utf-8
+module Grape
+  module Exceptions
+    class MissingVendorOption < Base
+      def initialize
+        super(message: compose_message('missing_vendor_option'))
+      end
+    end
+  end
+end
diff --git a/lib/grape/exceptions/unknown_options.rb b/lib/grape/exceptions/unknown_options.rb
new file mode 100644
index 0000000..a3063e4
--- /dev/null
+++ b/lib/grape/exceptions/unknown_options.rb
@@ -0,0 +1,10 @@
+# encoding: utf-8
+module Grape
+  module Exceptions
+    class UnknownOptions < Base
+      def initialize(options)
+        super(message: compose_message('unknown_options', options: options))
+      end
+    end
+  end
+end
diff --git a/lib/grape/exceptions/unknown_validator.rb b/lib/grape/exceptions/unknown_validator.rb
new file mode 100644
index 0000000..281377c
--- /dev/null
+++ b/lib/grape/exceptions/unknown_validator.rb
@@ -0,0 +1,10 @@
+# encoding: utf-8
+module Grape
+  module Exceptions
+    class UnknownValidator < Base
+      def initialize(validator_type)
+        super(message: compose_message('unknown_validator', validator_type: validator_type))
+      end
+    end
+  end
+end
diff --git a/lib/grape/exceptions/unsupported_group_type.rb b/lib/grape/exceptions/unsupported_group_type.rb
new file mode 100644
index 0000000..1a09941
--- /dev/null
+++ b/lib/grape/exceptions/unsupported_group_type.rb
@@ -0,0 +1,10 @@
+# encoding: utf-8
+module Grape
+  module Exceptions
+    class UnsupportedGroupTypeError < Base
+      def initialize
+        super(message: compose_message('unsupported_group_type'))
+      end
+    end
+  end
+end
diff --git a/lib/grape/exceptions/validation.rb b/lib/grape/exceptions/validation.rb
new file mode 100644
index 0000000..3e598c7
--- /dev/null
+++ b/lib/grape/exceptions/validation.rb
@@ -0,0 +1,26 @@
+require 'grape/exceptions/base'
+
+module Grape
+  module Exceptions
+    class Validation < Grape::Exceptions::Base
+      attr_accessor :params
+
+      def initialize(args = {})
+        fail 'Params are missing:' unless args.key? :params
+        @params = args[:params]
+        args[:message] = translate_message(args[:message_key]) if args.key? :message_key
+        super
+      end
+
+      # remove all the unnecessary stuff from Grape::Exceptions::Base like status
+      # and headers when converting a validation error to json or string
+      def as_json(*_args)
+        to_s
+      end
+
+      def to_s
+        message
+      end
+    end
+  end
+end
diff --git a/lib/grape/exceptions/validation_errors.rb b/lib/grape/exceptions/validation_errors.rb
new file mode 100644
index 0000000..76feeb3
--- /dev/null
+++ b/lib/grape/exceptions/validation_errors.rb
@@ -0,0 +1,56 @@
+require 'grape/exceptions/base'
+
+module Grape
+  module Exceptions
+    class ValidationErrors < Grape::Exceptions::Base
+      include Enumerable
+
+      attr_reader :errors
+
+      def initialize(args = {})
+        @errors = {}
+        args[:errors].each do |validation_error|
+          @errors[validation_error.params] ||= []
+          @errors[validation_error.params] << validation_error
+        end
+        super message: full_messages.join(', '), status: 400
+      end
+
+      def each
+        errors.each_pair do |attribute, errors|
+          errors.each do |error|
+            yield attribute, error
+          end
+        end
+      end
+
+      def as_json(_opts = {})
+        errors.map do |k, v|
+          {
+            params: k,
+            messages: v.map(&:to_s)
+          }
+        end
+      end
+
+      def to_json(_opts = {})
+        as_json.to_json
+      end
+
+      private
+
+      def full_messages
+        map { |attributes, error| full_message(attributes, error) }.uniq
+      end
+
+      def full_message(attributes, error)
+        I18n.t(
+          'grape.errors.format'.to_sym,
+          default: '%{attributes} %{message}',
+          attributes: attributes.count == 1 ? translate_attribute(attributes.first) : translate_attributes(attributes),
+          message: error.message
+        )
+      end
+    end
+  end
+end
diff --git a/lib/grape/formatter/base.rb b/lib/grape/formatter/base.rb
new file mode 100644
index 0000000..3a09327
--- /dev/null
+++ b/lib/grape/formatter/base.rb
@@ -0,0 +1,31 @@
+module Grape
+  module Formatter
+    module Base
+      class << self
+        FORMATTERS = {
+          json: Grape::Formatter::Json,
+          jsonapi: Grape::Formatter::Json,
+          serializable_hash: Grape::Formatter::SerializableHash,
+          txt: Grape::Formatter::Txt,
+          xml: Grape::Formatter::Xml
+        }
+
+        def formatters(options)
+          FORMATTERS.merge(options[:formatters] || {})
+        end
+
+        def formatter_for(api_format, options = {})
+          spec = formatters(options)[api_format]
+          case spec
+          when nil
+            ->(obj, _env) { obj }
+          when Symbol
+            method(spec)
+          else
+            spec
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/formatter/json.rb b/lib/grape/formatter/json.rb
new file mode 100644
index 0000000..353ee32
--- /dev/null
+++ b/lib/grape/formatter/json.rb
@@ -0,0 +1,12 @@
+module Grape
+  module Formatter
+    module Json
+      class << self
+        def call(object, _env)
+          return object.to_json if object.respond_to?(:to_json)
+          MultiJson.dump(object)
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/formatter/serializable_hash.rb b/lib/grape/formatter/serializable_hash.rb
new file mode 100644
index 0000000..24069d8
--- /dev/null
+++ b/lib/grape/formatter/serializable_hash.rb
@@ -0,0 +1,35 @@
+module Grape
+  module Formatter
+    module SerializableHash
+      class << self
+        def call(object, _env)
+          return object if object.is_a?(String)
+          return MultiJson.dump(serialize(object)) if serializable?(object)
+          return object.to_json if object.respond_to?(:to_json)
+          MultiJson.dump(object)
+        end
+
+        private
+
+        def serializable?(object)
+          object.respond_to?(:serializable_hash) || object.is_a?(Array) && !object.map { |o| o.respond_to? :serializable_hash }.include?(false) || object.is_a?(Hash)
+        end
+
+        def serialize(object)
+          if object.respond_to? :serializable_hash
+            object.serializable_hash
+          elsif object.is_a?(Array) && !object.map { |o| o.respond_to? :serializable_hash }.include?(false)
+            object.map(&:serializable_hash)
+          elsif object.is_a?(Hash)
+            object.inject({}) do |h, (k, v)|
+              h[k] = serialize(v)
+              h
+            end
+          else
+            object
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/formatter/txt.rb b/lib/grape/formatter/txt.rb
new file mode 100644
index 0000000..175944e
--- /dev/null
+++ b/lib/grape/formatter/txt.rb
@@ -0,0 +1,11 @@
+module Grape
+  module Formatter
+    module Txt
+      class << self
+        def call(object, _env)
+          object.respond_to?(:to_txt) ? object.to_txt : object.to_s
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/formatter/xml.rb b/lib/grape/formatter/xml.rb
new file mode 100644
index 0000000..1f89a81
--- /dev/null
+++ b/lib/grape/formatter/xml.rb
@@ -0,0 +1,12 @@
+module Grape
+  module Formatter
+    module Xml
+      class << self
+        def call(object, _env)
+          return object.to_xml if object.respond_to?(:to_xml)
+          fail Grape::Exceptions::InvalidFormatter.new(object.class, 'xml')
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/http/headers.rb b/lib/grape/http/headers.rb
new file mode 100644
index 0000000..09d2aaa
--- /dev/null
+++ b/lib/grape/http/headers.rb
@@ -0,0 +1,27 @@
+module Grape
+  module Http
+    module Headers
+      # https://github.com/rack/rack/blob/master/lib/rack.rb
+      HTTP_VERSION    = 'HTTP_VERSION'.freeze
+      PATH_INFO       = 'PATH_INFO'.freeze
+      QUERY_STRING    = 'QUERY_STRING'.freeze
+      CONTENT_TYPE    = 'Content-Type'.freeze
+
+      GET     = 'GET'.freeze
+      POST    = 'POST'.freeze
+      PUT     = 'PUT'.freeze
+      PATCH   = 'PATCH'.freeze
+      DELETE  = 'DELETE'.freeze
+      HEAD    = 'HEAD'.freeze
+      OPTIONS = 'OPTIONS'.freeze
+
+      HTTP_ACCEPT_VERSION    = 'HTTP_ACCEPT_VERSION'.freeze
+      X_CASCADE              = 'X-Cascade'.freeze
+      HTTP_TRANSFER_ENCODING = 'HTTP_TRANSFER_ENCODING'.freeze
+      HTTP_ACCEPT            = 'HTTP_ACCEPT'.freeze
+
+      ACCEPT                 = 'accept'.freeze
+      FORMAT                 = 'format'.freeze
+    end
+  end
+end
diff --git a/lib/grape/http/request.rb b/lib/grape/http/request.rb
new file mode 100644
index 0000000..90a2a6e
--- /dev/null
+++ b/lib/grape/http/request.rb
@@ -0,0 +1,27 @@
+module Grape
+  class Request < Rack::Request
+    def params
+      @params ||= begin
+        params = Hashie::Mash.new(super)
+        if env['rack.routing_args']
+          args = env['rack.routing_args'].dup
+          # preserve version from query string parameters
+          args.delete(:version)
+          args.delete(:route_info)
+          params.deep_merge!(args)
+        end
+        params
+      end
+    end
+
+    def headers
+      @headers ||= env.dup.inject({}) do |h, (k, v)|
+        if k.to_s.start_with? 'HTTP_'
+          k = k[5..-1].tr('_', '-').downcase.gsub(/^.|[-_\s]./) { |x| x.upcase }
+          h[k] = v
+        end
+        h
+      end
+    end
+  end
+end
diff --git a/lib/grape/locale/en.yml b/lib/grape/locale/en.yml
new file mode 100644
index 0000000..a26ad41
--- /dev/null
+++ b/lib/grape/locale/en.yml
@@ -0,0 +1,47 @@
+en:
+  grape:
+    errors:
+      format: ! '%{attributes} %{message}'
+      messages:
+        coerce: 'is invalid'
+        presence: 'is missing'
+        regexp: 'is invalid'
+        blank: 'is empty'
+        values: 'does not have a valid value'
+        missing_vendor_option:
+          problem: 'missing :vendor option.'
+          summary: 'when version using header, you must specify :vendor option. '
+          resolution: "eg: version 'v1', using: :header, vendor: 'twitter'"
+        missing_mime_type:
+          problem: 'missing mime type for %{new_format}'
+          resolution:
+            "you can choose existing mime type from Grape::ContentTypes::CONTENT_TYPES
+            or add your own with content_type :%{new_format}, 'application/%{new_format}'
+            "
+        invalid_with_option_for_represent:
+          problem: 'You must specify an entity class in the :with option.'
+          resolution: 'eg: represent User, :with => Entity::User'
+        missing_option: 'You must specify :%{option} options.'
+        invalid_formatter: 'cannot convert %{klass} to %{to_format}'
+        invalid_versioner_option:
+          problem: 'Unknown :using for versioner: %{strategy}'
+          resolution: 'available strategy for :using is :path, :header, :param'
+        unknown_validator: 'unknown validator: %{validator_type}'
+        unknown_options: 'unknown options: %{options}'
+        incompatible_option_values: '%{option1}: %{value1} is incompatible with %{option2}: %{value2}'
+        mutual_exclusion: 'are mutually exclusive'
+        at_least_one: 'are missing, at least one parameter must be provided'
+        exactly_one: 'are missing, exactly one parameter must be provided'
+        all_or_none: 'provide all or none of parameters'
+        missing_group_type: 'group type is required'
+        unsupported_group_type: 'group type must be Array or Hash'
+        invalid_message_body:
+          problem: "message body does not match declared format"
+          resolution:
+            "when specifying %{body_format} as content-type, you must pass valid
+            %{body_format} in the request's 'body'
+            "
+        invalid_accept_header:
+          problem: 'Invalid accept header'
+          resolution: '%{message}'
+
diff --git a/lib/grape/middleware/auth/base.rb b/lib/grape/middleware/auth/base.rb
new file mode 100644
index 0000000..37c4908
--- /dev/null
+++ b/lib/grape/middleware/auth/base.rb
@@ -0,0 +1,46 @@
+require 'rack/auth/basic'
+
+module Grape
+  module Middleware
+    module Auth
+      class Base
+        attr_accessor :options, :app, :env
+
+        def initialize(app, options = {})
+          @app = app
+          @options = options || {}
+        end
+
+        def context
+          env['api.endpoint']
+        end
+
+        def call(env)
+          dup._call(env)
+        end
+
+        def _call(env)
+          self.env = env
+
+          if options.key?(:type)
+            auth_proc         = options[:proc]
+            auth_proc_context = context
+
+            strategy_info   = Grape::Middleware::Auth::Strategies[options[:type]]
+
+            throw(:error, status: 401, message: 'API Authorization Failed.') unless strategy_info.present?
+
+            strategy = strategy_info.create(@app, options) do |*args|
+              auth_proc_context.instance_exec(*args, &auth_proc)
+            end
+
+            strategy.call(env)
+
+          else
+            app.call(env)
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/middleware/auth/dsl.rb b/lib/grape/middleware/auth/dsl.rb
new file mode 100644
index 0000000..d595679
--- /dev/null
+++ b/lib/grape/middleware/auth/dsl.rb
@@ -0,0 +1,40 @@
+require 'rack/auth/basic'
+require 'active_support/concern'
+
+module Grape
+  module Middleware
+    module Auth
+      module DSL
+        extend ActiveSupport::Concern
+
+        module ClassMethods
+          # Add an authentication type to the API. Currently
+          # only `:http_basic`, `:http_digest` are supported.
+          def auth(type = nil, options = {}, &block)
+            if type
+              namespace_inheritable(:auth, { type: type.to_sym, proc: block }.merge(options))
+              use Grape::Middleware::Auth::Base, namespace_inheritable(:auth)
+            else
+              namespace_inheritable(:auth)
+            end
+          end
+
+          # Add HTTP Basic authorization to the API.
+          #
+          # @param [Hash] options A hash of options.
+          # @option options [String] :realm "API Authorization" The HTTP Basic realm.
+          def http_basic(options = {}, &block)
+            options[:realm] ||= 'API Authorization'
+            auth :http_basic, options, &block
+          end
+
+          def http_digest(options = {}, &block)
+            options[:realm] ||= 'API Authorization'
+            options[:opaque] ||= 'secret'
+            auth :http_digest, options, &block
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/middleware/auth/strategies.rb b/lib/grape/middleware/auth/strategies.rb
new file mode 100644
index 0000000..16312a9
--- /dev/null
+++ b/lib/grape/middleware/auth/strategies.rb
@@ -0,0 +1,24 @@
+module Grape
+  module Middleware
+    module Auth
+      module Strategies
+        module_function
+
+        def add(label, strategy, option_fetcher = ->(_) { [] })
+          auth_strategies[label] =  StrategyInfo.new(strategy, option_fetcher)
+        end
+
+        def auth_strategies
+          @auth_strategies ||= {
+            http_basic: StrategyInfo.new(Rack::Auth::Basic, ->(settings) { [settings[:realm]] }),
+            http_digest: StrategyInfo.new(Rack::Auth::Digest::MD5, ->(settings) { [settings[:realm], settings[:opaque]] })
+          }
+        end
+
+        def [](label)
+          auth_strategies[label]
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/middleware/auth/strategy_info.rb b/lib/grape/middleware/auth/strategy_info.rb
new file mode 100644
index 0000000..e04656b
--- /dev/null
+++ b/lib/grape/middleware/auth/strategy_info.rb
@@ -0,0 +1,13 @@
+module Grape
+  module Middleware
+    module Auth
+      StrategyInfo = Struct.new(:auth_class, :settings_fetcher) do
+        def create(app, options, &block)
+          strategy_args = settings_fetcher.call(options)
+
+          auth_class.new(app, *strategy_args, &block)
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/middleware/base.rb b/lib/grape/middleware/base.rb
new file mode 100644
index 0000000..3bbaf6b
--- /dev/null
+++ b/lib/grape/middleware/base.rb
@@ -0,0 +1,62 @@
+module Grape
+  module Middleware
+    class Base
+      attr_reader :app, :env, :options
+
+      # @param [Rack Application] app The standard argument for a Rack middleware.
+      # @param [Hash] options A hash of options, simply stored for use by subclasses.
+      def initialize(app, options = {})
+        @app = app
+        @options = default_options.merge(options)
+      end
+
+      def default_options
+        {}
+      end
+
+      def call(env)
+        dup.call!(env)
+      end
+
+      def call!(env)
+        @env = env
+        before
+        @app_response = @app.call(@env)
+        after || @app_response
+      end
+
+      # @abstract
+      # Called before the application is called in the middleware lifecycle.
+      def before
+      end
+
+      # @abstract
+      # Called after the application is called in the middleware lifecycle.
+      # @return [Response, nil] a Rack SPEC response or nil to call the application afterwards.
+      def after
+      end
+
+      def response
+        Rack::Response.new(@app_response[2], @app_response[0], @app_response[1])
+      end
+
+      def content_type_for(format)
+        HashWithIndifferentAccess.new(content_types)[format]
+      end
+
+      def content_types
+        ContentTypes.content_types_for(options[:content_types])
+      end
+
+      def content_type
+        content_type_for(env['api.format'] || options[:format]) || 'text/html'
+      end
+
+      def mime_types
+        content_types.each_with_object({}) do |(k, v), types_without_params|
+          types_without_params[k] = v.split(';').first
+        end.invert
+      end
+    end
+  end
+end
diff --git a/lib/grape/middleware/error.rb b/lib/grape/middleware/error.rb
new file mode 100644
index 0000000..074aa24
--- /dev/null
+++ b/lib/grape/middleware/error.rb
@@ -0,0 +1,92 @@
+require 'grape/middleware/base'
+
+module Grape
+  module Middleware
+    class Error < Base
+      def default_options
+        {
+          default_status: 500, # default status returned on error
+          default_message: '',
+          format: :txt,
+          formatters: {},
+          error_formatters: {},
+          rescue_all: false, # true to rescue all exceptions
+          rescue_subclasses: true, # rescue subclasses of exceptions listed
+          rescue_options: { backtrace: false }, # true to display backtrace
+          rescue_handlers: {}, # rescue handler blocks
+          base_only_rescue_handlers: {}, # rescue handler blocks rescuing only the base class
+          all_rescue_handler: nil # rescue handler block to rescue from all exceptions
+        }
+      end
+
+      def call!(env)
+        @env = env
+
+        begin
+          error_response(catch(:error) do
+            return @app.call(@env)
+          end)
+        rescue StandardError => e
+          is_rescuable = rescuable?(e.class)
+          if e.is_a?(Grape::Exceptions::Base) && !is_rescuable
+            handler = ->(arg) { error_response(arg) }
+          else
+            raise unless is_rescuable
+            handler = find_handler(e.class)
+          end
+
+          handler.nil? ? handle_error(e) : exec_handler(e, &handler)
+        end
+      end
+
+      def find_handler(klass)
+        handler = options[:rescue_handlers].find(-> { [] }) { |error, _| klass <= error }[1]
+        handler ||= options[:base_only_rescue_handlers][klass]
+        handler ||= options[:all_rescue_handler]
+        handler
+      end
+
+      def rescuable?(klass)
+        options[:rescue_all] || (options[:rescue_handlers] || []).any? { |error, _handler| klass <= error } || (options[:base_only_rescue_handlers] || []).include?(klass)
+      end
+
+      def exec_handler(e, &handler)
+        if handler.lambda? && handler.arity == 0
+          instance_exec(&handler)
+        else
+          instance_exec(e, &handler)
+        end
+      end
+
+      def error!(message, status = options[:default_status], headers = {}, backtrace = [])
+        headers = { Grape::Http::Headers::CONTENT_TYPE => content_type }.merge(headers)
+        rack_response(format_message(message, backtrace), status, headers)
+      end
+
+      def handle_error(e)
+        error_response(message: e.message, backtrace: e.backtrace)
+      end
+
+      # TODO: This method is deprecated. Refactor out.
+      def error_response(error = {})
+        status = error[:status] || options[:default_status]
+        message = error[:message] || options[:default_message]
+        headers = { Grape::Http::Headers::CONTENT_TYPE => content_type }
+        headers.merge!(error[:headers]) if error[:headers].is_a?(Hash)
+        backtrace = error[:backtrace] || []
+        rack_response(format_message(message, backtrace), status, headers)
+      end
+
+      def rack_response(message, status = options[:default_status], headers = { Grape::Http::Headers::CONTENT_TYPE => content_type })
+        Rack::Response.new([message], status, headers).finish
+      end
+
+      def format_message(message, backtrace)
+        format = env['api.format'] || options[:format]
+        formatter = Grape::ErrorFormatter::Base.formatter_for(format, options)
+        throw :error, status: 406, message: "The requested format '#{format}' is not supported." unless formatter
+        formatter.call(message, backtrace, options, env)
+      end
+    end
+  end
+end
diff --git a/lib/grape/middleware/filter.rb b/lib/grape/middleware/filter.rb
new file mode 100644
index 0000000..ac83b85
--- /dev/null
+++ b/lib/grape/middleware/filter.rb
@@ -0,0 +1,17 @@
+module Grape
+  module Middleware
+    # This is a simple middleware for adding before and after filters
+    # to Grape APIs. It is used like so:
+    #
+    #     use Grape::Middleware::Filter, before: lambda { do_something }, after: lambda { do_something }
+    class Filter < Base
+      def before
+        app.instance_eval(&options[:before]) if options[:before]
+      end
+
+      def after
+        app.instance_eval(&options[:after]) if options[:after]
+      end
+    end
+  end
+end
diff --git a/lib/grape/middleware/formatter.rb b/lib/grape/middleware/formatter.rb
new file mode 100644
index 0000000..3bf3126
--- /dev/null
+++ b/lib/grape/middleware/formatter.rb
@@ -0,0 +1,156 @@
+require 'grape/middleware/base'
+
+module Grape
+  module Middleware
+    class Formatter < Base
+      def default_options
+        {
+          default_format: :txt,
+          formatters: {},
+          parsers: {}
+        }
+      end
+
+      def headers
+        env.dup.inject({}) do |h, (k, v)|
+          h[k.to_s.downcase[5..-1]] = v if k.to_s.downcase.start_with?('http_')
+          h
+        end
+      end
+
+      def before
+        negotiate_content_type
+        read_body_input
+      end
+
+      def after
+        status, headers, bodies = *@app_response
+        # allow content-type to be explicitly overwritten
+        api_format = mime_types[headers[Grape::Http::Headers::CONTENT_TYPE]] || env['api.format']
+        formatter = Grape::Formatter::Base.formatter_for api_format, options
+        begin
+          bodymap = if bodies.respond_to?(:collect)
+                      bodies.collect do |body|
+                        formatter.call body, env
+                      end
+                    else
+                      bodies
+                    end
+        rescue Grape::Exceptions::InvalidFormatter => e
+          throw :error, status: 500, message: e.message
+        end
+        headers[Grape::Http::Headers::CONTENT_TYPE] = content_type_for(env['api.format']) unless headers[Grape::Http::Headers::CONTENT_TYPE]
+        Rack::Response.new(bodymap, status, headers)
+      end
+
+      private
+
+      def request
+        @request ||= Rack::Request.new(env)
+      end
+
+      # store read input in env['api.request.input']
+      def read_body_input
+        if (request.post? || request.put? || request.patch? || request.delete?) &&
+           (!request.form_data? || !request.media_type) &&
+           (!request.parseable_data?) &&
+           (request.content_length.to_i > 0 || request.env[Grape::Http::Headers::HTTP_TRANSFER_ENCODING] == 'chunked')
+
+          if (input = env['rack.input'])
+            input.rewind
+            body = env['api.request.input'] = input.read
+            begin
+              read_rack_input(body) if body && body.length > 0
+            ensure
+              input.rewind
+            end
+          end
+        end
+      end
+
+      # store parsed input in env['api.request.body']
+      def read_rack_input(body)
+        fmt = mime_types[request.media_type] if request.media_type
+        fmt ||= options[:default_format]
+        if content_type_for(fmt)
+          parser = Grape::Parser::Base.parser_for fmt, options
+          if parser
+            begin
+              body = (env['api.request.body'] = parser.call(body, env))
+              if body.is_a?(Hash)
+                if env['rack.request.form_hash']
+                  env['rack.request.form_hash'] = env['rack.request.form_hash'].merge(body)
+                else
+                  env['rack.request.form_hash'] = body
+                end
+                env['rack.request.form_input'] = env['rack.input']
+              end
+            rescue Grape::Exceptions::Base => e
+              raise e
+            rescue StandardError => e
+              throw :error, status: 400, message: e.message
+            end
+          else
+            env['api.request.body'] = body
+          end
+        else
+          throw :error, status: 406, message: "The requested content-type '#{request.media_type}' is not supported."
+        end
+      end
+
+      def negotiate_content_type
+        fmt = format_from_extension || format_from_params || options[:format] || format_from_header || options[:default_format]
+        if content_type_for(fmt)
+          env['api.format'] = fmt
+        else
+          throw :error, status: 406, message: "The requested format '#{fmt}' is not supported."
+        end
+      end
+
+      def format_from_extension
+        parts = request.path.split('.')
+
+        if parts.size > 1
+          extension = parts.last
+          # avoid symbol memory leak on an unknown format
+          return extension.to_sym if content_type_for(extension)
+        end
+        nil
+      end
+
+      def format_from_params
+        fmt = Rack::Utils.parse_nested_query(env[Grape::Http::Headers::QUERY_STRING])[Grape::Http::Headers::FORMAT]
+        # avoid symbol memory leak on an unknown format
+        return fmt.to_sym if content_type_for(fmt)
+        fmt
+      end
+
+      def format_from_header
+        mime_array.each do |t|
+          return mime_types[t] if mime_types.key?(t)
+        end
+        nil
+      end
+
+      def mime_array
+        accept = headers[Grape::Http::Headers::ACCEPT]
+        return [] unless accept
+
+        accept_into_mime_and_quality = %r{
+          (
+            \w+/[\w+.-]+)     # eg application/vnd.example.myformat+xml
+          (?:
+           (?:;[^,]*?)?       # optionally multiple formats in a row
+           ;\s*q=([\d.]+)     # optional "quality" preference (eg q=0.5)
+          )?
+        }x
+
+        vendor_prefix_pattern = /vnd\.[^+]+\+/
+
+        accept.scan(accept_into_mime_and_quality)
+          .sort_by { |_, quality_preference| -quality_preference.to_f }
+          .map { |mime, _| mime.sub(vendor_prefix_pattern, '') }
+      end
+    end
+  end
+end
diff --git a/lib/grape/middleware/globals.rb b/lib/grape/middleware/globals.rb
new file mode 100644
index 0000000..126d8a5
--- /dev/null
+++ b/lib/grape/middleware/globals.rb
@@ -0,0 +1,14 @@
+require 'grape/middleware/base'
+
+module Grape
+  module Middleware
+    class Globals < Base
+      def before
+        request = Grape::Request.new(@env)
+        @env['grape.request'] = request
+        @env['grape.request.headers'] = request.headers
+        @env['grape.request.params'] = request.params if @env['rack.input']
+      end
+    end
+  end
+end
diff --git a/lib/grape/middleware/versioner.rb b/lib/grape/middleware/versioner.rb
new file mode 100644
index 0000000..9caea49
--- /dev/null
+++ b/lib/grape/middleware/versioner.rb
@@ -0,0 +1,32 @@
+# Versioners set env['api.version'] when a version is defined on an API and
+# on the requests. The current methods for determining version are:
+#
+#   :header - version from HTTP Accept header.
+#   :path   - version from uri. e.g. /v1/resource
+#   :param  - version from uri query string, e.g. /v1/resource?apiver=v1
+#
+# See individual classes for details.
+module Grape
+  module Middleware
+    module Versioner
+      module_function
+
+      # @param strategy [Symbol] :path, :header or :param
+      # @return a middleware class based on strategy
+      def using(strategy)
+        case strategy
+        when :path
+          Path
+        when :header
+          Header
+        when :param
+          Param
+        when :accept_version_header
+          AcceptVersionHeader
+        else
+          fail Grape::Exceptions::InvalidVersionerOption.new(strategy)
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/middleware/versioner/accept_version_header.rb b/lib/grape/middleware/versioner/accept_version_header.rb
new file mode 100644
index 0000000..8c07ff4
--- /dev/null
+++ b/lib/grape/middleware/versioner/accept_version_header.rb
@@ -0,0 +1,67 @@
+require 'grape/middleware/base'
+
+module Grape
+  module Middleware
+    module Versioner
+      # This middleware sets various version related rack environment variables
+      # based on the HTTP Accept-Version header
+      #
+      # Example: For request header
+      #    Accept-Version: v1
+      #
+      # The following rack env variables are set:
+      #
+      #    env['api.version']  => 'v1'
+      #
+      # If version does not match this route, then a 406 is raised with
+      # X-Cascade header to alert Rack::Mount to attempt the next matched
+      # route.
+      class AcceptVersionHeader < Base
+        def before
+          potential_version = (env[Grape::Http::Headers::HTTP_ACCEPT_VERSION] || '').strip
+
+          if strict?
+            # If no Accept-Version header:
+            if potential_version.empty?
+              throw :error, status: 406, headers: error_headers, message: 'Accept-Version header must be set.'
+            end
+          end
+
+          unless potential_version.empty?
+            # If the requested version is not supported:
+            unless versions.any? { |v| v.to_s == potential_version }
+              throw :error, status: 406, headers: error_headers, message: 'The requested version is not supported.'
+            end
+
+            env['api.version'] = potential_version
+          end
+        end
+
+        private
+
+        def versions
+          options[:versions] || []
+        end
+
+        def strict?
+          options[:version_options] && options[:version_options][:strict]
+        end
+
+        # By default those errors contain an `X-Cascade` header set to `pass`, which allows nesting and stacking
+        # of routes (see [Rack::Mount](https://github.com/josh/rack-mount) for more information). To prevent
+        # this behavior, and not add the `X-Cascade` header, one can set the `:cascade` option to `false`.
+        def cascade?
+          if options[:version_options] && options[:version_options].key?(:cascade)
+            !!options[:version_options][:cascade]
+          else
+            true
+          end
+        end
+
+        def error_headers
+          cascade? ? { Grape::Http::Headers::X_CASCADE => 'pass' } : {}
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/middleware/versioner/header.rb b/lib/grape/middleware/versioner/header.rb
new file mode 100644
index 0000000..1da5675
--- /dev/null
+++ b/lib/grape/middleware/versioner/header.rb
@@ -0,0 +1,135 @@
+require 'grape/middleware/base'
+
+module Grape
+  module Middleware
+    module Versioner
+      # This middleware sets various version related rack environment variables
+      # based on the HTTP Accept header with the pattern:
+      # application/vnd.:vendor-:version+:format
+      #
+      # Example: For request header
+      #    Accept: application/vnd.mycompany-v1+json
+      #
+      # The following rack env variables are set:
+      #
+      #    env['api.type']    => 'application'
+      #    env['api.subtype'] => 'vnd.mycompany-v1+json'
+      #    env['api.vendor]   => 'mycompany'
+      #    env['api.version]  => 'v1'
+      #    env['api.format]   => 'json'
+      #
+      # If version does not match this route, then a 406 is raised with
+      # X-Cascade header to alert Rack::Mount to attempt the next matched
+      # route.
+      class Header < Base
+        def before
+          header = rack_accept_header
+
+          if strict?
+            # If no Accept header:
+            if header.qvalues.empty?
+              fail Grape::Exceptions::InvalidAcceptHeader.new('Accept header must be set.', error_headers)
+            end
+            # Remove any acceptable content types with ranges.
+            header.qvalues.reject! do |media_type, _|
+              Rack::Accept::Header.parse_media_type(media_type).find { |s| s == '*' }
+            end
+            # If all Accept headers included a range:
+            if header.qvalues.empty?
+              fail Grape::Exceptions::InvalidAcceptHeader.new('Accept header must not contain ranges ("*").',
+                                                              error_headers)
+            end
+          end
+
+          media_type = header.best_of available_media_types
+
+          if media_type
+            type, subtype = Rack::Accept::Header.parse_media_type media_type
+            env['api.type']    = type
+            env['api.subtype'] = subtype
+
+            if /\Avnd\.([a-z0-9*.]+)(?:-([a-z0-9*\-.]+))?(?:\+([a-z0-9*\-.+]+))?\z/ =~ subtype
+              env['api.vendor']  = Regexp.last_match[1]
+              env['api.version'] = Regexp.last_match[2]
+              env['api.format']  = Regexp.last_match[3]  # weird that Grape::Middleware::Formatter also does this
+            end
+          # If none of the available content types are acceptable:
+          elsif strict?
+            fail Grape::Exceptions::InvalidAcceptHeader.new('406 Not Acceptable', error_headers)
+          # If all acceptable content types specify a vendor or version that doesn't exist:
+          elsif header.values.all? { |header_value| has_vendor?(header_value) || version?(header_value) }
+            fail Grape::Exceptions::InvalidAcceptHeader.new('API vendor or version not found.', error_headers)
+          end
+        end
+
+        private
+
+        def available_media_types
+          available_media_types = []
+
+          content_types.each do |extension, _media_type|
+            versions.reverse_each do |version|
+              available_media_types += ["application/vnd.#{vendor}-#{version}+#{extension}", "application/vnd.#{vendor}-#{version}"]
+            end
+            available_media_types << "application/vnd.#{vendor}+#{extension}"
+          end
+
+          available_media_types << "application/vnd.#{vendor}"
+
+          content_types.each do |_, media_type|
+            available_media_types << media_type
+          end
+
+          available_media_types.flatten
+        end
+
+        def rack_accept_header
+          Rack::Accept::MediaType.new env[Grape::Http::Headers::HTTP_ACCEPT]
+        rescue RuntimeError => e
+          raise Grape::Exceptions::InvalidAcceptHeader.new(e.message, error_headers)
+        end
+
+        def versions
+          options[:versions] || []
+        end
+
+        def vendor
+          options[:version_options] && options[:version_options][:vendor]
+        end
+
+        def strict?
+          options[:version_options] && options[:version_options][:strict]
+        end
+
+        # By default those errors contain an `X-Cascade` header set to `pass`, which allows nesting and stacking
+        # of routes (see [Rack::Mount](https://github.com/josh/rack-mount) for more information). To prevent
+        # this behavior, and not add the `X-Cascade` header, one can set the `:cascade` option to `false`.
+        def cascade?
+          if options[:version_options] && options[:version_options].key?(:cascade)
+            !!options[:version_options][:cascade]
+          else
+            true
+          end
+        end
+
+        def error_headers
+          cascade? ? { Grape::Http::Headers::X_CASCADE => 'pass' } : {}
+        end
+
+        # @param [String] media_type a content type
+        # @return [Boolean] whether the content type sets a vendor
+        def has_vendor?(media_type)
+          _, subtype = Rack::Accept::Header.parse_media_type media_type
+          subtype[/\Avnd\.[a-z0-9*.]+/]
+        end
+
+        # @param [String] media_type a content type
+        # @return [Boolean] whether the content type sets an API version
+        def version?(media_type)
+          _, subtype = Rack::Accept::Header.parse_media_type media_type
+          subtype[/\Avnd\.[a-z0-9*.]+-[a-z0-9*\-.]+/]
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/middleware/versioner/param.rb b/lib/grape/middleware/versioner/param.rb
new file mode 100644
index 0000000..363fc66
--- /dev/null
+++ b/lib/grape/middleware/versioner/param.rb
@@ -0,0 +1,42 @@
+require 'grape/middleware/base'
+
+module Grape
+  module Middleware
+    module Versioner
+      # This middleware sets various version related rack environment variables
+      # based on the request parameters and removes that parameter from the
+      # request parameters for subsequent middleware and API.
+      # If the version substring does not match any potential initialized
+      # versions, a 404 error is thrown.
+      # If the version substring is not passed the version (highest mounted)
+      # version will be used.
+      #
+      # Example: For a uri path
+      #   /resource?apiver=v1
+      #
+      # The following rack env variables are set and path is rewritten to
+      # '/resource':
+      #
+      #   env['api.version'] => 'v1'
+      class Param < Base
+        def default_options
+          {
+            parameter: 'apiver'
+          }
+        end
+
+        def before
+          paramkey = options[:parameter]
+          potential_version = Rack::Utils.parse_nested_query(env[Grape::Http::Headers::QUERY_STRING])[paramkey]
+          unless potential_version.nil?
+            if options[:versions] && !options[:versions].find { |v| v.to_s == potential_version }
+              throw :error, status: 404, message: '404 API Version Not Found', headers: { Grape::Http::Headers::X_CASCADE => 'pass' }
+            end
+            env['api.version'] = potential_version
+            env['rack.request.query_hash'].delete(paramkey) if env.key? 'rack.request.query_hash'
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/middleware/versioner/path.rb b/lib/grape/middleware/versioner/path.rb
new file mode 100644
index 0000000..09e5847
--- /dev/null
+++ b/lib/grape/middleware/versioner/path.rb
@@ -0,0 +1,52 @@
+require 'grape/middleware/base'
+
+module Grape
+  module Middleware
+    module Versioner
+      # This middleware sets various version related rack environment variables
+      # based on the uri path and removes the version substring from the uri
+      # path. If the version substring does not match any potential initialized
+      # versions, a 404 error is thrown.
+      #
+      # Example: For a uri path
+      #   /v1/resource
+      #
+      # The following rack env variables are set and path is rewritten to
+      # '/resource':
+      #
+      #   env['api.version'] => 'v1'
+      #
+      class Path < Base
+        def default_options
+          {
+            pattern: /.*/i
+          }
+        end
+
+        def before
+          path = env[Grape::Http::Headers::PATH_INFO].dup
+
+          if prefix && path.index(prefix) == 0
+            path.sub!(prefix, '')
+            path = Rack::Mount::Utils.normalize_path(path)
+          end
+
+          pieces = path.split('/')
+          potential_version = pieces[1]
+          if potential_version =~ options[:pattern]
+            if options[:versions] && !options[:versions].find { |v| v.to_s == potential_version }
+              throw :error, status: 404, message: '404 API Version Not Found'
+            end
+            env['api.version'] = potential_version
+          end
+        end
+
+        private
+
+        def prefix
+          Rack::Mount::Utils.normalize_path(options[:prefix].to_s) if options[:prefix]
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/namespace.rb b/lib/grape/namespace.rb
new file mode 100644
index 0000000..caf7ed0
--- /dev/null
+++ b/lib/grape/namespace.rb
@@ -0,0 +1,24 @@
+module Grape
+  class Namespace
+    attr_reader :space, :options
+
+    # options:
+    #   requirements: a hash
+    def initialize(space, options = {})
+      @space = space.to_s
+      @options = options
+    end
+
+    def requirements
+      options[:requirements] || {}
+    end
+
+    def self.joined_space(settings)
+      (settings || []).map(&:space).join('/')
+    end
+
+    def self.joined_space_path(settings)
+      Rack::Mount::Utils.normalize_path(joined_space(settings))
+    end
+  end
+end
diff --git a/lib/grape/parser/base.rb b/lib/grape/parser/base.rb
new file mode 100644
index 0000000..afa6b21
--- /dev/null
+++ b/lib/grape/parser/base.rb
@@ -0,0 +1,29 @@
+module Grape
+  module Parser
+    module Base
+      class << self
+        PARSERS = {
+          json: Grape::Parser::Json,
+          jsonapi: Grape::Parser::Json,
+          xml: Grape::Parser::Xml
+        }
+
+        def parsers(options)
+          PARSERS.merge(options[:parsers] || {})
+        end
+
+        def parser_for(api_format, options = {})
+          spec = parsers(options)[api_format]
+          case spec
+          when nil
+            nil
+          when Symbol
+            method(spec)
+          else
+            spec
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/parser/json.rb b/lib/grape/parser/json.rb
new file mode 100644
index 0000000..99c5ba0
--- /dev/null
+++ b/lib/grape/parser/json.rb
@@ -0,0 +1,14 @@
+module Grape
+  module Parser
+    module Json
+      class << self
+        def call(object, _env)
+          MultiJson.load(object)
+        rescue MultiJson::ParseError
+          # handle JSON parsing errors via the rescue handlers or provide error message
+          raise Grape::Exceptions::InvalidMessageBody, 'application/json'
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/parser/xml.rb b/lib/grape/parser/xml.rb
new file mode 100644
index 0000000..5fa06f5
--- /dev/null
+++ b/lib/grape/parser/xml.rb
@@ -0,0 +1,14 @@
+module Grape
+  module Parser
+    module Xml
+      class << self
+        def call(object, _env)
+          MultiXml.parse(object)
+        rescue MultiXml::ParseError
+          # handle XML parsing errors via the rescue handlers or provide error message
+          raise Grape::Exceptions::InvalidMessageBody, 'application/xml'
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/path.rb b/lib/grape/path.rb
new file mode 100644
index 0000000..2830835
--- /dev/null
+++ b/lib/grape/path.rb
@@ -0,0 +1,76 @@
+module Grape
+  class Path
+    def self.prepare(raw_path, namespace, settings)
+      Path.new(raw_path, namespace, settings).path_with_suffix
+    end
+
+    attr_reader :raw_path, :namespace, :settings
+
+    def initialize(raw_path, namespace, settings)
+      @raw_path = raw_path
+      @namespace = namespace
+      @settings = settings
+    end
+
+    def mount_path
+      settings[:mount_path]
+    end
+
+    def root_prefix
+      split_setting(:root_prefix)
+    end
+
+    def uses_specific_format?
+      !!(settings[:format] && settings[:content_types].size == 1)
+    end
+
+    def uses_path_versioning?
+      !!(settings[:version] && settings[:version_options][:using] == :path)
+    end
+
+    def has_namespace?
+      namespace && namespace.to_s =~ /^\S/ && namespace != '/'
+    end
+
+    def has_path?
+      raw_path && raw_path.to_s =~ /^\S/ && raw_path != '/'
+    end
+
+    def suffix
+      if uses_specific_format?
+        "(.#{settings[:format]})"
+      elsif !uses_path_versioning? || (has_namespace? || has_path?)
+        '(.:format)'
+      else
+        '(/.:format)'
+      end
+    end
+
+    def path
+      Rack::Mount::Utils.normalize_path(parts.join('/'))
+    end
+
+    def path_with_suffix
+      "#{path}#{suffix}"
+    end
+
+    def to_s
+      path_with_suffix
+    end
+
+    private
+
+    def parts
+      parts = [mount_path, root_prefix].compact
+      parts << ':version' if uses_path_versioning?
+      parts << namespace.to_s
+      parts << raw_path.to_s
+      parts.flatten.reject { |part| part == '/' }
+    end
+
+    def split_setting(key)
+      return if settings[key].nil?
+      settings[key].to_s.split('/')
+    end
+  end
+end
diff --git a/lib/grape/presenters/presenter.rb b/lib/grape/presenters/presenter.rb
new file mode 100644
index 0000000..30dbd55
--- /dev/null
+++ b/lib/grape/presenters/presenter.rb
@@ -0,0 +1,9 @@
+module Grape
+  module Presenters
+    class Presenter
+      def self.represent(object, _options = {})
+        object
+      end
+    end
+  end
+end
diff --git a/lib/grape/route.rb b/lib/grape/route.rb
new file mode 100644
index 0000000..dac21c7
--- /dev/null
+++ b/lib/grape/route.rb
@@ -0,0 +1,27 @@
+module Grape
+  # A compiled route for inspection.
+  class Route
+    def initialize(options = {})
+      @options = options || {}
+    end
+
+    def method_missing(method_id, *arguments)
+      match = /route_([_a-zA-Z]\w*)/.match(method_id.to_s)
+      if match
+        @options[match.captures.last.to_sym]
+      else
+        super
+      end
+    end
+
+    def to_s
+      "version=#{route_version}, method=#{route_method}, path=#{route_path}"
+    end
+
+    private
+
+    def to_ary
+      nil
+    end
+  end
+end
diff --git a/lib/grape/util/content_types.rb b/lib/grape/util/content_types.rb
new file mode 100644
index 0000000..acf39a7
--- /dev/null
+++ b/lib/grape/util/content_types.rb
@@ -0,0 +1,26 @@
+module Grape
+  module ContentTypes
+    # Content types are listed in order of preference.
+    CONTENT_TYPES = ActiveSupport::OrderedHash[
+                    :xml,  'application/xml',
+                    :serializable_hash, 'application/json',
+                    :json, 'application/json',
+                    :binary, 'application/octet-stream',
+                    :txt,  'text/plain'
+   ]
+
+    def self.content_types_for_settings(settings)
+      return nil if settings.nil? || settings.blank?
+
+      settings.each_with_object(ActiveSupport::OrderedHash.new) { |value, result| result.merge!(value) }
+    end
+
+    def self.content_types_for(from_settings)
+      if from_settings.present?
+        from_settings
+      else
+        Grape::ContentTypes::CONTENT_TYPES
+      end
+    end
+  end
+end
diff --git a/lib/grape/util/inheritable_setting.rb b/lib/grape/util/inheritable_setting.rb
new file mode 100644
index 0000000..0185154
--- /dev/null
+++ b/lib/grape/util/inheritable_setting.rb
@@ -0,0 +1,74 @@
+module Grape
+  module Util
+    class InheritableSetting
+      attr_accessor :route, :api_class, :namespace, :namespace_inheritable, :namespace_stackable
+      attr_accessor :parent, :point_in_time_copies
+
+      def self.global
+        @global ||= {}
+      end
+
+      def self.reset_global! # only for testing
+        @global = {}
+      end
+
+      def initialize
+        self.route = {}
+        self.api_class = {}
+        self.namespace = InheritableValues.new # only inheritable from a parent when
+        # used with a mount, or should every API::Class be a seperate namespace by default?
+        self.namespace_inheritable = InheritableValues.new
+        self.namespace_stackable = StackableValues.new
+
+        self.point_in_time_copies = []
+
+        self.parent = nil
+      end
+
+      def global
+        self.class.global
+      end
+
+      def inherit_from(parent)
+        return if parent.nil?
+
+        self.parent = parent
+
+        namespace_inheritable.inherited_values = parent.namespace_inheritable
+        namespace_stackable.inherited_values = parent.namespace_stackable
+        self.route = parent.route.merge(route)
+
+        point_in_time_copies.map { |cloned_one| cloned_one.inherit_from parent }
+      end
+
+      def point_in_time_copy
+        self.class.new.tap do |new_setting|
+          point_in_time_copies << new_setting
+          new_setting.point_in_time_copies = []
+
+          new_setting.namespace = namespace.clone
+          new_setting.namespace_inheritable = namespace_inheritable.clone
+          new_setting.namespace_stackable = namespace_stackable.clone
+          new_setting.route = route.clone
+          new_setting.api_class = api_class
+
+          new_setting.inherit_from(parent)
+        end
+      end
+
+      def route_end
+        @route = {}
+      end
+
+      def to_hash
+        {
+          global: global.clone,
+          route: route.clone,
+          namespace: namespace.to_hash,
+          namespace_inheritable: namespace_inheritable.to_hash,
+          namespace_stackable: namespace_stackable.to_hash
+        }
+      end
+    end
+  end
+end
diff --git a/lib/grape/util/inheritable_values.rb b/lib/grape/util/inheritable_values.rb
new file mode 100644
index 0000000..21a55d8
--- /dev/null
+++ b/lib/grape/util/inheritable_values.rb
@@ -0,0 +1,49 @@
+module Grape
+  module Util
+    class InheritableValues
+      attr_accessor :inherited_values
+      attr_accessor :new_values
+
+      def initialize(inherited_values = {})
+        self.inherited_values = inherited_values
+        self.new_values = {}
+      end
+
+      def [](name)
+        values[name]
+      end
+
+      def []=(name, value)
+        new_values[name] = value
+      end
+
+      def delete(key)
+        new_values.delete key
+      end
+
+      def merge(new_hash)
+        values.merge(new_hash)
+      end
+
+      def keys
+        (new_values.keys + inherited_values.keys).sort.uniq
+      end
+
+      def to_hash
+        values.clone
+      end
+
+      def initialize_copy(other)
+        super
+        self.inherited_values = other.inherited_values
+        self.new_values = other.new_values.deep_dup
+      end
+
+      protected
+
+      def values
+        @inherited_values.merge(@new_values)
+      end
+    end
+  end
+end
diff --git a/lib/grape/util/stackable_values.rb b/lib/grape/util/stackable_values.rb
new file mode 100644
index 0000000..4937445
--- /dev/null
+++ b/lib/grape/util/stackable_values.rb
@@ -0,0 +1,52 @@
+module Grape
+  module Util
+    class StackableValues
+      attr_accessor :inherited_values
+      attr_reader :new_values
+      attr_reader :froozen_values
+
+      def initialize(inherited_values = {})
+        @inherited_values = inherited_values
+        @new_values = {}
+        @froozen_values = {}
+      end
+
+      def [](name)
+        return @froozen_values[name] if @froozen_values.key? name
+        [@inherited_values[name], @new_values[name]].compact.flatten(1)
+      end
+
+      def []=(name, value)
+        fail if @froozen_values.key? name
+        @new_values[name] ||= []
+        @new_values[name].push value
+      end
+
+      def delete(key)
+        new_values.delete key
+      end
+
+      attr_writer :new_values
+
+      def keys
+        (@new_values.keys + @inherited_values.keys).sort.uniq
+      end
+
+      def to_hash
+        keys.each_with_object({}) do |key, result|
+          result[key] = self[key]
+        end
+      end
+
+      def freeze_value(key)
+        @froozen_values[key] = self[key].freeze
+      end
+
+      def initialize_copy(other)
+        super
+        self.inherited_values = other.inherited_values
+        self.new_values = other.new_values.deep_dup
+      end
+    end
+  end
+end
diff --git a/lib/grape/util/strict_hash_configuration.rb b/lib/grape/util/strict_hash_configuration.rb
new file mode 100644
index 0000000..c549dfb
--- /dev/null
+++ b/lib/grape/util/strict_hash_configuration.rb
@@ -0,0 +1,106 @@
+module Grape
+  module Util
+    module StrictHashConfiguration
+      extend ActiveSupport::Concern
+
+      module DSL
+        extend ActiveSupport::Concern
+
+        module ClassMethods
+          def settings
+            config_context.to_hash
+          end
+
+          def configure(&block)
+            config_context.instance_exec(&block)
+          end
+        end
+      end
+
+      class SettingsContainer
+        def initialize
+          @settings = {}
+          @contexts = {}
+        end
+
+        def to_hash
+          @settings.to_hash
+        end
+      end
+
+      def self.config_class(*args)
+        new_config_class = Class.new(SettingsContainer)
+
+        args.each do |setting_name|
+          if setting_name.respond_to? :values
+            nested_settings_methods(setting_name, new_config_class)
+          else
+            simple_settings_methods(setting_name, new_config_class)
+          end
+        end
+
+        new_config_class
+      end
+
+      def self.simple_settings_methods(setting_name, new_config_class)
+        setting_name_sym = setting_name.to_sym
+        new_config_class.class_eval do
+          define_method setting_name do |new_value|
+            @settings[setting_name_sym] = new_value
+          end
+        end
+      end
+
+      def self.nested_settings_methods(setting_name, new_config_class)
+        new_config_class.class_eval do
+          setting_name.each_pair do |key, value|
+            define_method "#{key}_context" do
+              @contexts[key] ||= Grape::Util::StrictHashConfiguration.config_class(*value).new
+            end
+
+            define_method key do |&block|
+              send("#{key}_context").instance_exec(&block)
+            end
+          end
+
+          define_method 'to_hash' do
+            merge_hash = setting_name.keys.each_with_object({}) { |k, hash| hash[k] = send("#{k}_context").to_hash }
+
+            @settings.to_hash.merge(
+              merge_hash
+            )
+          end
+        end
+      end
+
+      def self.module(*args)
+        new_module = Module.new do
+          extend ActiveSupport::Concern
+          include DSL
+        end
+
+        new_module.tap do |mod|
+          class_mod = create_class_mod(args)
+
+          mod.const_set(:ClassMethods, class_mod)
+        end
+      end
+
+      def self.create_class_mod(args)
+        new_module = Module.new do
+          def config_context
+            @config_context ||= config_class.new
+          end
+        end
+
+        new_module.tap do |class_mod|
+          new_config_class = config_class(*args)
+
+          class_mod.send(:define_method, :config_class) do
+            @config_context ||= new_config_class
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/validations.rb b/lib/grape/validations.rb
new file mode 100644
index 0000000..f970acd
--- /dev/null
+++ b/lib/grape/validations.rb
@@ -0,0 +1,13 @@
+module Grape
+  module Validations
+    class << self
+      attr_accessor :validators
+    end
+
+    self.validators = {}
+
+    def self.register_validator(short_name, klass)
+      validators[short_name] = klass
+    end
+  end
+end
diff --git a/lib/grape/validations/attributes_iterator.rb b/lib/grape/validations/attributes_iterator.rb
new file mode 100644
index 0000000..7fe92fc
--- /dev/null
+++ b/lib/grape/validations/attributes_iterator.rb
@@ -0,0 +1,21 @@
+module Grape
+  module Validations
+    class AttributesIterator
+      include Enumerable
+
+      def initialize(validator, scope, params)
+        @attrs = validator.attrs
+        @params = scope.params(params)
+        @params = (@params.is_a?(Array) ? @params : [@params])
+      end
+
+      def each
+        @params.each do |resource_params|
+          @attrs.each do |attr_name|
+            yield resource_params, attr_name
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/validations/params_scope.rb b/lib/grape/validations/params_scope.rb
new file mode 100644
index 0000000..e4f1494
--- /dev/null
+++ b/lib/grape/validations/params_scope.rb
@@ -0,0 +1,192 @@
+module Grape
+  module Validations
+    class ParamsScope
+      attr_accessor :element, :parent
+
+      include Grape::DSL::Parameters
+
+      def initialize(opts, &block)
+        @element  = opts[:element]
+        @parent   = opts[:parent]
+        @api      = opts[:api]
+        @optional = opts[:optional] || false
+        @type     = opts[:type]
+        @declared_params = []
+
+        instance_eval(&block) if block_given?
+
+        configure_declared_params
+      end
+
+      def should_validate?(parameters)
+        return false if @optional && params(parameters).respond_to?(:all?) && params(parameters).all?(&:blank?)
+        return true if parent.nil?
+        parent.should_validate?(parameters)
+      end
+
+      def full_name(name)
+        return "#{@parent.full_name(@element)}[#{name}]" if @parent
+        name.to_s
+      end
+
+      def root?
+        !@parent
+      end
+
+      def required?
+        !@optional
+      end
+
+      protected
+
+      def push_declared_params(attrs)
+        @declared_params.concat attrs
+      end
+
+      private
+
+      def require_required_and_optional_fields(context, opts)
+        if context == :all
+          optional_fields = Array(opts[:except])
+          required_fields = opts[:using].keys - optional_fields
+        else # context == :none
+          required_fields = Array(opts[:except])
+          optional_fields = opts[:using].keys - required_fields
+        end
+        required_fields.each do |field|
+          field_opts = opts[:using][field]
+          fail ArgumentError, "required field not exist: #{field}" unless field_opts
+          requires(field, field_opts)
+        end
+        optional_fields.each do |field|
+          field_opts = opts[:using][field]
+          optional(field, field_opts) if field_opts
+        end
+      end
+
+      def require_optional_fields(context, opts)
+        optional_fields = opts[:using].keys
+        optional_fields -= Array(opts[:except]) unless context == :all
+        optional_fields.each do |field|
+          field_opts = opts[:using][field]
+          optional(field, field_opts) if field_opts
+        end
+      end
+
+      def validate_attributes(attrs, opts, &block)
+        validations = opts.clone
+        validations[:type] ||= Array if block
+        validates(attrs, validations)
+      end
+
+      def new_scope(attrs, optional = false, &block)
+        # if required params are grouped and no type or unsupported type is provided, raise an error
+        type = attrs[1] ? attrs[1][:type] : nil
+        if attrs.first && !optional
+          fail Grape::Exceptions::MissingGroupTypeError.new if type.nil?
+          fail Grape::Exceptions::UnsupportedGroupTypeError.new unless [Array, Hash].include?(type)
+        end
+
+        opts = attrs[1] || { type: Array }
+        ParamsScope.new(api: @api, element: attrs.first, parent: self, optional: optional, type: opts[:type], &block)
+      end
+
+      # Pushes declared params to parent or settings
+      def configure_declared_params
+        if @parent
+          @parent.push_declared_params [element => @declared_params]
+        else
+          @api.namespace_stackable(:declared_params, @declared_params)
+
+          @api.route_setting(:declared_params, []) unless @api.route_setting(:declared_params)
+          @api.route_setting(:declared_params).concat @declared_params
+        end
+      end
+
+      def validates(attrs, validations)
+        doc_attrs = { required: validations.keys.include?(:presence) }
+
+        # special case (type = coerce)
+        validations[:coerce] = validations.delete(:type) if validations.key?(:type)
+
+        coerce_type = validations[:coerce]
+
+        doc_attrs[:type] = coerce_type.to_s if coerce_type
+
+        desc = validations.delete(:desc) || validations.delete(:description)
+        doc_attrs[:desc] = desc if desc
+
+        default = validations[:default]
+        doc_attrs[:default] = default if validations.key?(:default)
+
+        values = validations[:values]
+        doc_attrs[:values] = values if values
+
+        coerce_type = guess_coerce_type(coerce_type, values)
+
+        # default value should be present in values array, if both exist and are not procs
+        check_incompatible_option_values(values, default)
+
+        # type should be compatible with values array, if both exist
+        validate_value_coercion(coerce_type, values)
+
+        doc_attrs[:documentation] = validations.delete(:documentation) if validations.key?(:documentation)
+
+        full_attrs = attrs.collect { |name| { name: name, full_name: full_name(name) } }
+        @api.document_attribute(full_attrs, doc_attrs)
+
+        # Validate for presence before any other validators
+        if validations.key?(:presence) && validations[:presence]
+          validate('presence', validations[:presence], attrs, doc_attrs)
+          validations.delete(:presence)
+        end
+
+        # Before we run the rest of the validators, lets handle
+        # whatever coercion so that we are working with correctly
+        # type casted values
+        if validations.key? :coerce
+          validate('coerce', validations[:coerce], attrs, doc_attrs)
+          validations.delete(:coerce)
+        end
+
+        validations.each do |type, options|
+          validate(type, options, attrs, doc_attrs)
+        end
+      end
+
+      def guess_coerce_type(coerce_type, values)
+        return coerce_type if !values || values.is_a?(Proc)
+        return values.first.class if coerce_type == Array && (values.is_a?(Range) || !values.empty?)
+        coerce_type
+      end
+
+      def check_incompatible_option_values(values, default)
+        return unless values && default
+        return if values.is_a?(Proc) || default.is_a?(Proc)
+        return if values.include?(default)
+        fail Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :values, values)
+      end
+
+      def validate(type, options, attrs, doc_attrs)
+        validator_class = Validations.validators[type.to_s]
+
+        if validator_class
+          value = validator_class.new(attrs, options, doc_attrs[:required], self)
+          @api.namespace_stackable(:validations, value)
+        else
+          fail Grape::Exceptions::UnknownValidator.new(type)
+        end
+      end
+
+      def validate_value_coercion(coerce_type, values)
+        return unless coerce_type && values
+        return if values.is_a?(Proc)
+        coerce_type = coerce_type.first if coerce_type.is_a?(Array)
+        value_types = values.is_a?(Range) ? [values.begin, values.end] : values
+        if value_types.any? { |v| !v.is_a?(coerce_type) }
+          fail Grape::Exceptions::IncompatibleOptionValues.new(:type, coerce_type, :values, values)
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/validations/validators/all_or_none.rb b/lib/grape/validations/validators/all_or_none.rb
new file mode 100644
index 0000000..9c7fe0c
--- /dev/null
+++ b/lib/grape/validations/validators/all_or_none.rb
@@ -0,0 +1,20 @@
+module Grape
+  module Validations
+    require 'grape/validations/validators/multiple_params_base'
+    class AllOrNoneOfValidator < MultipleParamsBase
+      def validate!(params)
+        super
+        if scope_requires_params && only_subset_present
+          fail Grape::Exceptions::Validation, params: all_keys, message_key: :all_or_none
+        end
+        params
+      end
+
+      private
+
+      def only_subset_present
+        scoped_params.any? { |resource_params| keys_in_common(resource_params).length > 0 && keys_in_common(resource_params).length < attrs.length  }
+      end
+    end
+  end
+end
diff --git a/lib/grape/validations/validators/allow_blank.rb b/lib/grape/validations/validators/allow_blank.rb
new file mode 100644
index 0000000..d407874
--- /dev/null
+++ b/lib/grape/validations/validators/allow_blank.rb
@@ -0,0 +1,30 @@
+module Grape
+  module Validations
+    class AllowBlankValidator < Base
+      def validate_param!(attr_name, params)
+        return if @option || !params.is_a?(Hash)
+
+        value = params[attr_name]
+        value = value.strip if value.respond_to?(:strip)
+
+        key_exists = params.key?(attr_name)
+
+        if @scope.root?
+          # root scope. validate if it's a required param. if it's optional, validate only if key exists in hash
+          should_validate = @required || key_exists
+        else # nested scope
+          should_validate = # required param, and scope contains some values (if scoping element contains no values, treat as blank)
+            (@required && params.present?) ||
+            # optional param but key inside scoping element exists
+            (!@required && params.key?(attr_name))
+        end
+
+        return unless should_validate
+
+        unless value == false || value.present?
+          fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message_key: :blank
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/validations/validators/at_least_one_of.rb b/lib/grape/validations/validators/at_least_one_of.rb
new file mode 100644
index 0000000..c46b27d
--- /dev/null
+++ b/lib/grape/validations/validators/at_least_one_of.rb
@@ -0,0 +1,20 @@
+module Grape
+  module Validations
+    require 'grape/validations/validators/multiple_params_base'
+    class AtLeastOneOfValidator < MultipleParamsBase
+      def validate!(params)
+        super
+        if scope_requires_params && no_exclusive_params_are_present
+          fail Grape::Exceptions::Validation, params: all_keys, message_key: :at_least_one
+        end
+        params
+      end
+
+      private
+
+      def no_exclusive_params_are_present
+        scoped_params.any? { |resource_params| keys_in_common(resource_params).empty? }
+      end
+    end
+  end
+end
diff --git a/lib/grape/validations/validators/base.rb b/lib/grape/validations/validators/base.rb
new file mode 100644
index 0000000..7d28653
--- /dev/null
+++ b/lib/grape/validations/validators/base.rb
@@ -0,0 +1,37 @@
+module Grape
+  module Validations
+    class Base
+      attr_reader :attrs
+
+      def initialize(attrs, options, required, scope)
+        @attrs = Array(attrs)
+        @option = options
+        @required = required
+        @scope = scope
+      end
+
+      def validate!(params)
+        attributes = AttributesIterator.new(self, @scope, params)
+        attributes.each do |resource_params, attr_name|
+          if @required || (resource_params.respond_to?(:key?) && resource_params.key?(attr_name))
+            validate_param!(attr_name, resource_params)
+          end
+        end
+      end
+
+      def self.convert_to_short_name(klass)
+        ret = klass.name.gsub(/::/, '/')
+              .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
+              .gsub(/([a-z\d])([A-Z])/, '\1_\2')
+              .tr('-', '_')
+              .downcase
+        File.basename(ret, '_validator')
+      end
+
+      def self.inherited(klass)
+        short_name = convert_to_short_name(klass)
+        Validations.register_validator(short_name, klass)
+      end
+    end
+  end
+end
diff --git a/lib/grape/validations/validators/coerce.rb b/lib/grape/validations/validators/coerce.rb
new file mode 100644
index 0000000..6b8b500
--- /dev/null
+++ b/lib/grape/validations/validators/coerce.rb
@@ -0,0 +1,67 @@
+module Grape
+  class API
+    Boolean = Virtus::Attribute::Boolean # rubocop:disable ConstantName
+  end
+
+  module Validations
+    class CoerceValidator < Base
+      def validate_param!(attr_name, params)
+        fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message_key: :coerce unless params.is_a? Hash
+        new_value = coerce_value(@option, params[attr_name])
+        if valid_type?(new_value)
+          params[attr_name] = new_value
+        else
+          fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message_key: :coerce
+        end
+      end
+
+      class InvalidValue; end
+
+      private
+
+      def _valid_array_type?(type, values)
+        values.all? do |val|
+          _valid_single_type?(type, val)
+        end
+      end
+
+      def _valid_single_type?(klass, val)
+        # allow nil, to ignore when a parameter is absent
+        return true if val.nil?
+        if klass == Virtus::Attribute::Boolean
+          val.is_a?(TrueClass) || val.is_a?(FalseClass) || (val.is_a?(String) && val.empty?)
+        elsif klass == Rack::Multipart::UploadedFile
+          val.is_a?(Hashie::Mash) && val.key?(:tempfile)
+        elsif [DateTime, Date, Numeric].any? { |vclass| vclass >= klass }
+          return true if val.is_a?(String) && val.empty?
+          val.is_a?(klass)
+        else
+          val.is_a?(klass)
+        end
+      end
+
+      def valid_type?(val)
+        if @option.is_a?(Array) || @option.is_a?(Set)
+          _valid_array_type?(@option.first, val)
+        else
+          _valid_single_type?(@option, val)
+        end
+      end
+
+      def coerce_value(type, val)
+        # Don't coerce things other than nil to Arrays or Hashes
+        return val || []      if type == Array
+        return val || Set.new if type == Set
+        return val || {}      if type == Hash
+
+        converter = Virtus::Attribute.build(type)
+        converter.coerce(val)
+
+      # not the prettiest but some invalid coercion can currently trigger
+      # errors in Virtus (see coerce_spec.rb:75)
+      rescue
+        InvalidValue.new
+      end
+    end
+  end
+end
diff --git a/lib/grape/validations/validators/default.rb b/lib/grape/validations/validators/default.rb
new file mode 100644
index 0000000..7826ff1
--- /dev/null
+++ b/lib/grape/validations/validators/default.rb
@@ -0,0 +1,25 @@
+module Grape
+  module Validations
+    class DefaultValidator < Base
+      def initialize(attrs, options, required, scope)
+        @default = options
+        super
+      end
+
+      def validate_param!(attr_name, params)
+        params[attr_name] = @default.is_a?(Proc) ? @default.call : @default unless params.key?(attr_name)
+      end
+
+      def validate!(params)
+        return unless @scope.should_validate?(params)
+
+        attrs = AttributesIterator.new(self, @scope, params)
+        attrs.each do |resource_params, attr_name|
+          if resource_params[attr_name].nil?
+            validate_param!(attr_name, resource_params)
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/validations/validators/exactly_one_of.rb b/lib/grape/validations/validators/exactly_one_of.rb
new file mode 100644
index 0000000..f323dac
--- /dev/null
+++ b/lib/grape/validations/validators/exactly_one_of.rb
@@ -0,0 +1,20 @@
+module Grape
+  module Validations
+    require 'grape/validations/validators/mutual_exclusion'
+    class ExactlyOneOfValidator < MutualExclusionValidator
+      def validate!(params)
+        super
+        if scope_requires_params && none_of_restricted_params_is_present
+          fail Grape::Exceptions::Validation, params: all_keys, message_key: :exactly_one
+        end
+        params
+      end
+
+      private
+
+      def none_of_restricted_params_is_present
+        scoped_params.any? { |resource_params| keys_in_common(resource_params).empty? }
+      end
+    end
+  end
+end
diff --git a/lib/grape/validations/validators/multiple_params_base.rb b/lib/grape/validations/validators/multiple_params_base.rb
new file mode 100644
index 0000000..14209df
--- /dev/null
+++ b/lib/grape/validations/validators/multiple_params_base.rb
@@ -0,0 +1,27 @@
+module Grape
+  module Validations
+    class MultipleParamsBase < Base
+      attr_reader :scoped_params
+
+      def validate!(params)
+        @scoped_params = [@scope.params(params)].flatten
+        params
+      end
+
+      private
+
+      def scope_requires_params
+        @scope.required? || scoped_params.any?(&:any?)
+      end
+
+      def keys_in_common(resource_params)
+        return [] unless resource_params.is_a?(Hash)
+        (all_keys & resource_params.stringify_keys.keys).map(&:to_s)
+      end
+
+      def all_keys
+        attrs.map(&:to_s)
+      end
+    end
+  end
+end
diff --git a/lib/grape/validations/validators/mutual_exclusion.rb b/lib/grape/validations/validators/mutual_exclusion.rb
new file mode 100644
index 0000000..9f6dada
--- /dev/null
+++ b/lib/grape/validations/validators/mutual_exclusion.rb
@@ -0,0 +1,25 @@
+module Grape
+  module Validations
+    require 'grape/validations/validators/multiple_params_base'
+    class MutualExclusionValidator < MultipleParamsBase
+      attr_reader :processing_keys_in_common
+
+      def validate!(params)
+        super
+        if two_or_more_exclusive_params_are_present
+          fail Grape::Exceptions::Validation, params: processing_keys_in_common, message_key: :mutual_exclusion
+        end
+        params
+      end
+
+      private
+
+      def two_or_more_exclusive_params_are_present
+        scoped_params.any? do |resource_params|
+          @processing_keys_in_common = keys_in_common(resource_params)
+          @processing_keys_in_common.length > 1
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/validations/validators/presence.rb b/lib/grape/validations/validators/presence.rb
new file mode 100644
index 0000000..4590e3c
--- /dev/null
+++ b/lib/grape/validations/validators/presence.rb
@@ -0,0 +1,16 @@
+module Grape
+  module Validations
+    class PresenceValidator < Base
+      def validate!(params)
+        return unless @scope.should_validate?(params)
+        super
+      end
+
+      def validate_param!(attr_name, params)
+        unless params.respond_to?(:key?) && params.key?(attr_name)
+          fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message_key: :presence
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/validations/validators/regexp.rb b/lib/grape/validations/validators/regexp.rb
new file mode 100644
index 0000000..5e64ddd
--- /dev/null
+++ b/lib/grape/validations/validators/regexp.rb
@@ -0,0 +1,12 @@
+module Grape
+  module Validations
+    class RegexpValidator < Base
+      def validate_param!(attr_name, params)
+        if params.key?(attr_name) &&
+           !params[attr_name].nil? && !(params[attr_name].to_s =~ @option)
+          fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message_key: :regexp
+        end
+      end
+    end
+  end
+end
diff --git a/lib/grape/validations/validators/values.rb b/lib/grape/validations/validators/values.rb
new file mode 100644
index 0000000..6c22b2b
--- /dev/null
+++ b/lib/grape/validations/validators/values.rb
@@ -0,0 +1,26 @@
+module Grape
+  module Validations
+    class ValuesValidator < Base
+      def initialize(attrs, options, required, scope)
+        @values = options
+        super
+      end
+
+      def validate_param!(attr_name, params)
+        return unless params[attr_name] || required_for_root_scope?
+
+        values = @values.is_a?(Proc) ? @values.call : @values
+        param_array = params[attr_name].nil? ? [nil] : Array.wrap(params[attr_name])
+        unless param_array.all? { |param| values.include?(param) }
+          fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message_key: :values
+        end
+      end
+
+      private
+
+      def required_for_root_scope?
+        @required && @scope.root?
+      end
+    end
+  end
+end
diff --git a/lib/grape/version.rb b/lib/grape/version.rb
new file mode 100644
index 0000000..1c62ed1
--- /dev/null
+++ b/lib/grape/version.rb
@@ -0,0 +1,3 @@
+module Grape
+  VERSION = '0.12.0'
+end
diff --git a/metadata.yml b/metadata.yml
new file mode 100644
index 0000000..d9a17d7
--- /dev/null
+++ b/metadata.yml
@@ -0,0 +1,569 @@
+--- !ruby/object:Gem::Specification
+name: grape
+version: !ruby/object:Gem::Version
+  version: 0.12.0
+platform: ruby
+authors:
+- Michael Bleigh
+autorequire: 
+bindir: bin
+cert_chain: []
+date: 2015-06-18 00:00:00.000000000 Z
+dependencies:
+- !ruby/object:Gem::Dependency
+  name: rack
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: 1.3.0
+  type: :runtime
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: 1.3.0
+- !ruby/object:Gem::Dependency
+  name: rack-mount
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+  type: :runtime
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+- !ruby/object:Gem::Dependency
+  name: rack-accept
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+  type: :runtime
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+- !ruby/object:Gem::Dependency
+  name: activesupport
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+  type: :runtime
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+- !ruby/object:Gem::Dependency
+  name: multi_json
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: 1.3.2
+  type: :runtime
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: 1.3.2
+- !ruby/object:Gem::Dependency
+  name: multi_xml
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: 0.5.2
+  type: :runtime
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: 0.5.2
+- !ruby/object:Gem::Dependency
+  name: hashie
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: 2.1.0
+  type: :runtime
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: 2.1.0
+- !ruby/object:Gem::Dependency
+  name: virtus
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: 1.0.0
+  type: :runtime
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: 1.0.0
+- !ruby/object:Gem::Dependency
+  name: builder
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+  type: :runtime
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+- !ruby/object:Gem::Dependency
+  name: grape-entity
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: 0.4.4
+  type: :development
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: 0.4.4
+- !ruby/object:Gem::Dependency
+  name: rake
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+  type: :development
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+- !ruby/object:Gem::Dependency
+  name: maruku
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+  type: :development
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+- !ruby/object:Gem::Dependency
+  name: yard
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+  type: :development
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+- !ruby/object:Gem::Dependency
+  name: rack-test
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+  type: :development
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+- !ruby/object:Gem::Dependency
+  name: rspec
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - ~>
+      - !ruby/object:Gem::Version
+        version: '3.0'
+  type: :development
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - ~>
+      - !ruby/object:Gem::Version
+        version: '3.0'
+- !ruby/object:Gem::Dependency
+  name: bundler
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+  type: :development
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+- !ruby/object:Gem::Dependency
+  name: cookiejar
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+  type: :development
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+- !ruby/object:Gem::Dependency
+  name: rack-contrib
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+  type: :development
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+- !ruby/object:Gem::Dependency
+  name: mime-types
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+  type: :development
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+- !ruby/object:Gem::Dependency
+  name: appraisal
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+  type: :development
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+description: A Ruby framework for rapid API development with great conventions.
+email:
+- michael at intridea.com
+executables: []
+extensions: []
+extra_rdoc_files: []
+files:
+- .gitignore
+- .rspec
+- .rubocop.yml
+- .rubocop_todo.yml
+- .travis.yml
+- .yardopts
+- Appraisals
+- CHANGELOG.md
+- CONTRIBUTING.md
+- Gemfile
+- Guardfile
+- LICENSE
+- README.md
+- RELEASING.md
+- Rakefile
+- UPGRADING.md
+- gemfiles/rails_3.gemfile
+- gemfiles/rails_4.gemfile
+- grape.gemspec
+- grape.png
+- lib/backports/active_support/deep_dup.rb
+- lib/backports/active_support/duplicable.rb
+- lib/grape.rb
+- lib/grape/api.rb
+- lib/grape/api/helpers.rb
+- lib/grape/cookies.rb
+- lib/grape/dsl/api.rb
+- lib/grape/dsl/callbacks.rb
+- lib/grape/dsl/configuration.rb
+- lib/grape/dsl/helpers.rb
+- lib/grape/dsl/inside_route.rb
+- lib/grape/dsl/middleware.rb
+- lib/grape/dsl/parameters.rb
+- lib/grape/dsl/request_response.rb
+- lib/grape/dsl/routing.rb
+- lib/grape/dsl/settings.rb
+- lib/grape/dsl/validations.rb
+- lib/grape/endpoint.rb
+- lib/grape/error_formatter/base.rb
+- lib/grape/error_formatter/json.rb
+- lib/grape/error_formatter/txt.rb
+- lib/grape/error_formatter/xml.rb
+- lib/grape/exceptions/base.rb
+- lib/grape/exceptions/incompatible_option_values.rb
+- lib/grape/exceptions/invalid_accept_header.rb
+- lib/grape/exceptions/invalid_formatter.rb
+- lib/grape/exceptions/invalid_message_body.rb
+- lib/grape/exceptions/invalid_versioner_option.rb
+- lib/grape/exceptions/invalid_with_option_for_represent.rb
+- lib/grape/exceptions/missing_group_type.rb
+- lib/grape/exceptions/missing_mime_type.rb
+- lib/grape/exceptions/missing_option.rb
+- lib/grape/exceptions/missing_vendor_option.rb
+- lib/grape/exceptions/unknown_options.rb
+- lib/grape/exceptions/unknown_validator.rb
+- lib/grape/exceptions/unsupported_group_type.rb
+- lib/grape/exceptions/validation.rb
+- lib/grape/exceptions/validation_errors.rb
+- lib/grape/formatter/base.rb
+- lib/grape/formatter/json.rb
+- lib/grape/formatter/serializable_hash.rb
+- lib/grape/formatter/txt.rb
+- lib/grape/formatter/xml.rb
+- lib/grape/http/headers.rb
+- lib/grape/http/request.rb
+- lib/grape/locale/en.yml
+- lib/grape/middleware/auth/base.rb
+- lib/grape/middleware/auth/dsl.rb
+- lib/grape/middleware/auth/strategies.rb
+- lib/grape/middleware/auth/strategy_info.rb
+- lib/grape/middleware/base.rb
+- lib/grape/middleware/error.rb
+- lib/grape/middleware/filter.rb
+- lib/grape/middleware/formatter.rb
+- lib/grape/middleware/globals.rb
+- lib/grape/middleware/versioner.rb
+- lib/grape/middleware/versioner/accept_version_header.rb
+- lib/grape/middleware/versioner/header.rb
+- lib/grape/middleware/versioner/param.rb
+- lib/grape/middleware/versioner/path.rb
+- lib/grape/namespace.rb
+- lib/grape/parser/base.rb
+- lib/grape/parser/json.rb
+- lib/grape/parser/xml.rb
+- lib/grape/path.rb
+- lib/grape/presenters/presenter.rb
+- lib/grape/route.rb
+- lib/grape/util/content_types.rb
+- lib/grape/util/inheritable_setting.rb
+- lib/grape/util/inheritable_values.rb
+- lib/grape/util/stackable_values.rb
+- lib/grape/util/strict_hash_configuration.rb
+- lib/grape/validations.rb
+- lib/grape/validations/attributes_iterator.rb
+- lib/grape/validations/params_scope.rb
+- lib/grape/validations/validators/all_or_none.rb
+- lib/grape/validations/validators/allow_blank.rb
+- lib/grape/validations/validators/at_least_one_of.rb
+- lib/grape/validations/validators/base.rb
+- lib/grape/validations/validators/coerce.rb
+- lib/grape/validations/validators/default.rb
+- lib/grape/validations/validators/exactly_one_of.rb
+- lib/grape/validations/validators/multiple_params_base.rb
+- lib/grape/validations/validators/mutual_exclusion.rb
+- lib/grape/validations/validators/presence.rb
+- lib/grape/validations/validators/regexp.rb
+- lib/grape/validations/validators/values.rb
+- lib/grape/version.rb
+- spec/grape/api/custom_validations_spec.rb
+- spec/grape/api/deeply_included_options_spec.rb
+- spec/grape/api/nested_helpers_spec.rb
+- spec/grape/api/shared_helpers_spec.rb
+- spec/grape/api_spec.rb
+- spec/grape/dsl/callbacks_spec.rb
+- spec/grape/dsl/configuration_spec.rb
+- spec/grape/dsl/helpers_spec.rb
+- spec/grape/dsl/inside_route_spec.rb
+- spec/grape/dsl/middleware_spec.rb
+- spec/grape/dsl/parameters_spec.rb
+- spec/grape/dsl/request_response_spec.rb
+- spec/grape/dsl/routing_spec.rb
+- spec/grape/dsl/settings_spec.rb
+- spec/grape/dsl/validations_spec.rb
+- spec/grape/endpoint_spec.rb
+- spec/grape/entity_spec.rb
+- spec/grape/exceptions/body_parse_errors_spec.rb
+- spec/grape/exceptions/invalid_accept_header_spec.rb
+- spec/grape/exceptions/invalid_formatter_spec.rb
+- spec/grape/exceptions/invalid_versioner_option_spec.rb
+- spec/grape/exceptions/missing_mime_type_spec.rb
+- spec/grape/exceptions/missing_option_spec.rb
+- spec/grape/exceptions/unknown_options_spec.rb
+- spec/grape/exceptions/unknown_validator_spec.rb
+- spec/grape/exceptions/validation_errors_spec.rb
+- spec/grape/integration/rack_spec.rb
+- spec/grape/loading_spec.rb
+- spec/grape/middleware/auth/base_spec.rb
+- spec/grape/middleware/auth/dsl_spec.rb
+- spec/grape/middleware/auth/strategies_spec.rb
+- spec/grape/middleware/base_spec.rb
+- spec/grape/middleware/error_spec.rb
+- spec/grape/middleware/exception_spec.rb
+- spec/grape/middleware/formatter_spec.rb
+- spec/grape/middleware/globals_spec.rb
+- spec/grape/middleware/versioner/accept_version_header_spec.rb
+- spec/grape/middleware/versioner/header_spec.rb
+- spec/grape/middleware/versioner/param_spec.rb
+- spec/grape/middleware/versioner/path_spec.rb
+- spec/grape/middleware/versioner_spec.rb
+- spec/grape/path_spec.rb
+- spec/grape/presenters/presenter_spec.rb
+- spec/grape/util/inheritable_setting_spec.rb
+- spec/grape/util/inheritable_values_spec.rb
+- spec/grape/util/stackable_values_spec.rb
+- spec/grape/util/strict_hash_configuration_spec.rb
+- spec/grape/validations/attributes_iterator_spec.rb
+- spec/grape/validations/params_scope_spec.rb
+- spec/grape/validations/validators/all_or_none_spec.rb
+- spec/grape/validations/validators/allow_blank_spec.rb
+- spec/grape/validations/validators/at_least_one_of_spec.rb
+- spec/grape/validations/validators/coerce_spec.rb
+- spec/grape/validations/validators/default_spec.rb
+- spec/grape/validations/validators/exactly_one_of_spec.rb
+- spec/grape/validations/validators/mutual_exclusion_spec.rb
+- spec/grape/validations/validators/presence_spec.rb
+- spec/grape/validations/validators/regexp_spec.rb
+- spec/grape/validations/validators/values_spec.rb
+- spec/grape/validations/validators/zh-CN.yml
+- spec/grape/validations_spec.rb
+- spec/shared/versioning_examples.rb
+- spec/spec_helper.rb
+- spec/support/basic_auth_encode_helpers.rb
+- spec/support/content_type_helpers.rb
+- spec/support/endpoint_faker.rb
+- spec/support/file_streamer.rb
+- spec/support/versioned_helpers.rb
+homepage: https://github.com/intridea/grape
+licenses:
+- MIT
+metadata: {}
+post_install_message: 
+rdoc_options: []
+require_paths:
+- lib
+required_ruby_version: !ruby/object:Gem::Requirement
+  requirements:
+  - - '>='
+    - !ruby/object:Gem::Version
+      version: '0'
+required_rubygems_version: !ruby/object:Gem::Requirement
+  requirements:
+  - - '>='
+    - !ruby/object:Gem::Version
+      version: '0'
+requirements: []
+rubyforge_project: grape
+rubygems_version: 2.4.5
+signing_key: 
+specification_version: 4
+summary: A simple Ruby framework for building REST-like APIs.
+test_files:
+- spec/grape/api/custom_validations_spec.rb
+- spec/grape/api/deeply_included_options_spec.rb
+- spec/grape/api/nested_helpers_spec.rb
+- spec/grape/api/shared_helpers_spec.rb
+- spec/grape/api_spec.rb
+- spec/grape/dsl/callbacks_spec.rb
+- spec/grape/dsl/configuration_spec.rb
+- spec/grape/dsl/helpers_spec.rb
+- spec/grape/dsl/inside_route_spec.rb
+- spec/grape/dsl/middleware_spec.rb
+- spec/grape/dsl/parameters_spec.rb
+- spec/grape/dsl/request_response_spec.rb
+- spec/grape/dsl/routing_spec.rb
+- spec/grape/dsl/settings_spec.rb
+- spec/grape/dsl/validations_spec.rb
+- spec/grape/endpoint_spec.rb
+- spec/grape/entity_spec.rb
+- spec/grape/exceptions/body_parse_errors_spec.rb
+- spec/grape/exceptions/invalid_accept_header_spec.rb
+- spec/grape/exceptions/invalid_formatter_spec.rb
+- spec/grape/exceptions/invalid_versioner_option_spec.rb
+- spec/grape/exceptions/missing_mime_type_spec.rb
+- spec/grape/exceptions/missing_option_spec.rb
+- spec/grape/exceptions/unknown_options_spec.rb
+- spec/grape/exceptions/unknown_validator_spec.rb
+- spec/grape/exceptions/validation_errors_spec.rb
+- spec/grape/integration/rack_spec.rb
+- spec/grape/loading_spec.rb
+- spec/grape/middleware/auth/base_spec.rb
+- spec/grape/middleware/auth/dsl_spec.rb
+- spec/grape/middleware/auth/strategies_spec.rb
+- spec/grape/middleware/base_spec.rb
+- spec/grape/middleware/error_spec.rb
+- spec/grape/middleware/exception_spec.rb
+- spec/grape/middleware/formatter_spec.rb
+- spec/grape/middleware/globals_spec.rb
+- spec/grape/middleware/versioner/accept_version_header_spec.rb
+- spec/grape/middleware/versioner/header_spec.rb
+- spec/grape/middleware/versioner/param_spec.rb
+- spec/grape/middleware/versioner/path_spec.rb
+- spec/grape/middleware/versioner_spec.rb
+- spec/grape/path_spec.rb
+- spec/grape/presenters/presenter_spec.rb
+- spec/grape/util/inheritable_setting_spec.rb
+- spec/grape/util/inheritable_values_spec.rb
+- spec/grape/util/stackable_values_spec.rb
+- spec/grape/util/strict_hash_configuration_spec.rb
+- spec/grape/validations/attributes_iterator_spec.rb
+- spec/grape/validations/params_scope_spec.rb
+- spec/grape/validations/validators/all_or_none_spec.rb
+- spec/grape/validations/validators/allow_blank_spec.rb
+- spec/grape/validations/validators/at_least_one_of_spec.rb
+- spec/grape/validations/validators/coerce_spec.rb
+- spec/grape/validations/validators/default_spec.rb
+- spec/grape/validations/validators/exactly_one_of_spec.rb
+- spec/grape/validations/validators/mutual_exclusion_spec.rb
+- spec/grape/validations/validators/presence_spec.rb
+- spec/grape/validations/validators/regexp_spec.rb
+- spec/grape/validations/validators/values_spec.rb
+- spec/grape/validations/validators/zh-CN.yml
+- spec/grape/validations_spec.rb
+- spec/shared/versioning_examples.rb
+- spec/spec_helper.rb
+- spec/support/basic_auth_encode_helpers.rb
+- spec/support/content_type_helpers.rb
+- spec/support/endpoint_faker.rb
+- spec/support/file_streamer.rb
+- spec/support/versioned_helpers.rb
+has_rdoc: 
diff --git a/spec/grape/api/custom_validations_spec.rb b/spec/grape/api/custom_validations_spec.rb
new file mode 100644
index 0000000..ee83ab5
--- /dev/null
+++ b/spec/grape/api/custom_validations_spec.rb
@@ -0,0 +1,47 @@
+require 'spec_helper'
+
+describe Grape::Validations do
+  before do
+    class DefaultLength < Grape::Validations::Base
+      def validate_param!(attr_name, params)
+        @option = params[:max].to_i if params.key?(:max)
+        unless params[attr_name].length <= @option
+          fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: "must be at the most #{@option} characters long"
+        end
+      end
+    end
+  end
+
+  subject do
+    Class.new(Grape::API) do
+      params do
+        requires :text, default_length: 140
+      end
+      get do
+        'bacon'
+      end
+    end
+  end
+
+  def app
+    subject
+  end
+
+  context 'using a custom length validator' do
+    it 'under 140 characters' do
+      get '/', text: 'abc'
+      expect(last_response.status).to eq 200
+      expect(last_response.body).to eq 'bacon'
+    end
+    it 'over 140 characters' do
+      get '/', text: 'a' * 141
+      expect(last_response.status).to eq 400
+      expect(last_response.body).to eq 'text must be at the most 140 characters long'
+    end
+    it 'specified in the query string' do
+      get '/', text: 'a' * 141, max: 141
+      expect(last_response.status).to eq 200
+      expect(last_response.body).to eq 'bacon'
+    end
+  end
+end
diff --git a/spec/grape/api/deeply_included_options_spec.rb b/spec/grape/api/deeply_included_options_spec.rb
new file mode 100644
index 0000000..4ab111c
--- /dev/null
+++ b/spec/grape/api/deeply_included_options_spec.rb
@@ -0,0 +1,56 @@
+require 'spec_helper'
+
+module API
+  module Defaults
+    extend ActiveSupport::Concern
+    included do
+      format :json
+    end
+  end
+
+  module Admin
+    module Defaults
+      extend ActiveSupport::Concern
+      include API::Defaults
+    end
+
+    class Users < Grape::API
+      include API::Admin::Defaults
+
+      resource :users do
+        get do
+          status 200
+        end
+      end
+    end
+  end
+end
+
+class Main < Grape::API
+  mount API::Admin::Users
+end
+
+describe Grape::API do
+  subject { Main }
+
+  def app
+    subject
+  end
+
+  it 'works for unspecified format' do
+    get '/users'
+    expect(last_response.status).to eql 200
+    expect(last_response.content_type).to eql 'application/json'
+  end
+
+  it 'works for specified format' do
+    get '/users.json'
+    expect(last_response.status).to eql 200
+    expect(last_response.content_type).to eql 'application/json'
+  end
+
+  it "doesn't work for format different than specified" do
+    get '/users.txt'
+    expect(last_response.status).to eql 404
+  end
+end
diff --git a/spec/grape/api/nested_helpers_spec.rb b/spec/grape/api/nested_helpers_spec.rb
new file mode 100644
index 0000000..5b790a2
--- /dev/null
+++ b/spec/grape/api/nested_helpers_spec.rb
@@ -0,0 +1,48 @@
+require 'spec_helper'
+
+describe Grape::API::Helpers do
+  subject do
+    module HelperMethods
+      extend Grape::API::Helpers
+      def current_user
+        @current_user ||= params[:current_user]
+      end
+    end
+
+    class Nested < Grape::API
+      resource :level1 do
+        helpers HelperMethods
+
+        get do
+          current_user
+        end
+
+        resource :level2 do
+          get do
+            current_user
+          end
+        end
+      end
+    end
+
+    class Main < Grape::API
+      mount Nested
+    end
+
+    Main
+  end
+
+  def app
+    subject
+  end
+
+  it 'can access helpers from a mounted resource' do
+    get '/level1', current_user: 'hello'
+    expect(last_response.body).to eq('hello')
+  end
+
+  it 'can access helpers from a mounted resource in a nested resource' do
+    get '/level1/level2', current_user: 'world'
+    expect(last_response.body).to eq('world')
+  end
+end
diff --git a/spec/grape/api/shared_helpers_spec.rb b/spec/grape/api/shared_helpers_spec.rb
new file mode 100644
index 0000000..79af193
--- /dev/null
+++ b/spec/grape/api/shared_helpers_spec.rb
@@ -0,0 +1,36 @@
+require 'spec_helper'
+
+describe Grape::API::Helpers do
+  module SharedParams
+    extend Grape::API::Helpers
+
+    params :pagination do
+      optional :page, type: Integer
+      optional :size, type: Integer
+    end
+  end
+
+  subject do
+    Class.new(Grape::API) do
+      helpers SharedParams
+      format :json
+
+      params do
+        use :pagination
+      end
+      get do
+        declared(params, include_missing: true)
+      end
+    end
+  end
+
+  def app
+    subject
+  end
+
+  it 'defines parameters' do
+    get '/'
+    expect(last_response.status).to eq 200
+    expect(last_response.body).to eq({ page: nil, size: nil }.to_json)
+  end
+end
diff --git a/spec/grape/api_spec.rb b/spec/grape/api_spec.rb
new file mode 100644
index 0000000..a24a6c6
--- /dev/null
+++ b/spec/grape/api_spec.rb
@@ -0,0 +1,2907 @@
+require 'spec_helper'
+require 'shared/versioning_examples'
+require 'grape-entity'
+
+describe Grape::API do
+  subject { Class.new(Grape::API) }
+
+  def app
+    subject
+  end
+
+  describe '.prefix' do
+    it 'routes root through with the prefix' do
+      subject.prefix 'awesome/sauce'
+      subject.get do
+        'Hello there.'
+      end
+
+      get 'awesome/sauce/'
+      expect(last_response.status).to eql 200
+      expect(last_response.body).to eql 'Hello there.'
+    end
+
+    it 'routes through with the prefix' do
+      subject.prefix 'awesome/sauce'
+      subject.get :hello do
+        'Hello there.'
+      end
+
+      get 'awesome/sauce/hello'
+      expect(last_response.body).to eql 'Hello there.'
+
+      get '/hello'
+      expect(last_response.status).to eql 404
+    end
+
+    it 'supports OPTIONS' do
+      subject.prefix 'awesome/sauce'
+      subject.get do
+        'Hello there.'
+      end
+
+      options 'awesome/sauce'
+      expect(last_response.status).to eql 204
+      expect(last_response.body).to be_blank
+    end
+
+    it 'disallows POST' do
+      subject.prefix 'awesome/sauce'
+      subject.get
+
+      post 'awesome/sauce'
+      expect(last_response.status).to eql 405
+    end
+  end
+
+  describe '.version' do
+    context 'when defined' do
+      it 'returns version value' do
+        subject.version 'v1'
+        expect(subject.version).to eq('v1')
+      end
+    end
+
+    context 'when not defined' do
+      it 'returns nil' do
+        expect(subject.version).to be_nil
+      end
+    end
+  end
+
+  describe '.version using path' do
+    it_should_behave_like 'versioning' do
+      let(:macro_options) do
+        {
+          using: :path
+        }
+      end
+    end
+  end
+
+  describe '.version using param' do
+    it_should_behave_like 'versioning' do
+      let(:macro_options) do
+        {
+          using: :param,
+          parameter: 'apiver'
+        }
+      end
+    end
+  end
+
+  describe '.version using header' do
+    it_should_behave_like 'versioning' do
+      let(:macro_options) do
+        {
+          using: :header,
+          vendor: 'mycompany',
+          format: 'json'
+        }
+      end
+    end
+
+    # Behavior as defined by rfc2616 when no header is defined
+    # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
+    describe 'no specified accept header' do
+      # subject.version 'v1', using: :header
+      # subject.get '/hello' do
+      #   'hello'
+      # end
+
+      # it 'routes' do
+      #   get '/hello'
+      #   last_response.status.should eql 200
+      # end
+    end
+
+    # pending 'routes if any media type is allowed'
+  end
+
+  describe '.version using accept_version_header' do
+    it_should_behave_like 'versioning' do
+      let(:macro_options) do
+        {
+          using: :accept_version_header
+        }
+      end
+    end
+  end
+
+  describe '.represent' do
+    it 'requires a :with option' do
+      expect { subject.represent Object, {} }.to raise_error(Grape::Exceptions::InvalidWithOptionForRepresent)
+    end
+
+    it 'adds the association to the :representations setting' do
+      klass = Class.new
+      subject.represent Object, with: klass
+      expect(Grape::DSL::Configuration.stacked_hash_to_hash(subject.namespace_stackable(:representations))[Object]).to eq(klass)
+    end
+  end
+
+  describe '.namespace' do
+    it 'is retrievable and converted to a path' do
+      internal_namespace = nil
+      subject.namespace :awesome do
+        internal_namespace = namespace
+      end
+      expect(internal_namespace).to eql('/awesome')
+    end
+
+    it 'comes after the prefix and version' do
+      subject.prefix :rad
+      subject.version 'v1', using: :path
+
+      subject.namespace :awesome do
+        get('/hello') { 'worked' }
+      end
+
+      get '/rad/v1/awesome/hello'
+      expect(last_response.body).to eq('worked')
+    end
+
+    it 'cancels itself after the block is over' do
+      internal_namespace = nil
+      subject.namespace :awesome do
+        internal_namespace = namespace
+      end
+      expect(subject.namespace).to eql('/')
+    end
+
+    it 'is stackable' do
+      internal_namespace = nil
+      internal_second_namespace = nil
+      subject.namespace :awesome do
+        internal_namespace = namespace
+        namespace :rad do
+          internal_second_namespace = namespace
+        end
+      end
+      expect(internal_namespace).to eq('/awesome')
+      expect(internal_second_namespace).to eq('/awesome/rad')
+    end
+
+    it 'accepts path segments correctly' do
+      inner_namespace = nil
+      subject.namespace :members do
+        namespace '/:member_id' do
+          inner_namespace = namespace
+          get '/' do
+            params[:member_id]
+          end
+        end
+      end
+      get '/members/23'
+      expect(last_response.body).to eq('23')
+      expect(inner_namespace).to eq('/members/:member_id')
+    end
+
+    it 'is callable with nil just to push onto the stack' do
+      subject.namespace do
+        version 'v2', using: :path
+        get('/hello') { 'inner' }
+      end
+      subject.get('/hello') { 'outer' }
+
+      get '/v2/hello'
+      expect(last_response.body).to eq('inner')
+      get '/hello'
+      expect(last_response.body).to eq('outer')
+    end
+
+    %w(group resource resources segment).each do |als|
+      it '`.#{als}` is an alias' do
+        inner_namespace = nil
+        subject.send(als, :awesome) do
+          inner_namespace = namespace
+        end
+        expect(inner_namespace).to eq '/awesome'
+      end
+    end
+  end
+
+  describe '.route_param' do
+    it 'adds a parameterized route segment namespace' do
+      subject.namespace :users do
+        route_param :id do
+          get do
+            params[:id]
+          end
+        end
+      end
+
+      get '/users/23'
+      expect(last_response.body).to eq('23')
+    end
+
+    it 'should be able to define requirements with a single hash' do
+      subject.namespace :users do
+        route_param :id, requirements: /[0-9]+/ do
+          get do
+            params[:id]
+          end
+        end
+      end
+
+      get '/users/michael'
+      expect(last_response.status).to eq(404)
+      get '/users/23'
+      expect(last_response.status).to eq(200)
+    end
+  end
+
+  describe '.route' do
+    it 'allows for no path' do
+      subject.namespace :votes do
+        get do
+          'Votes'
+        end
+        post do
+          'Created a Vote'
+        end
+      end
+
+      get '/votes'
+      expect(last_response.body).to eql 'Votes'
+      post '/votes'
+      expect(last_response.body).to eql 'Created a Vote'
+    end
+
+    it 'handles empty calls' do
+      subject.get '/'
+      get '/'
+      expect(last_response.body).to eql ''
+    end
+
+    describe 'root routes should work with' do
+      before do
+        subject.format :txt
+        subject.content_type :json, 'application/json'
+        subject.formatter :json, ->(object, _env) { object }
+        def subject.enable_root_route!
+          get('/') { 'root' }
+        end
+      end
+
+      after do
+        expect(last_response.body).to eql 'root'
+      end
+
+      describe 'path versioned APIs' do
+        before do
+          subject.version 'v1', using: :path
+          subject.enable_root_route!
+        end
+
+        it 'without a format' do
+          versioned_get '/', 'v1', using: :path
+        end
+
+        it 'with a format' do
+          get '/v1/.json'
+        end
+      end
+
+      it 'header versioned APIs' do
+        subject.version 'v1', using: :header, vendor: 'test'
+        subject.enable_root_route!
+
+        versioned_get '/', 'v1', using: :header, vendor: 'test'
+      end
+
+      it 'header versioned APIs with multiple headers' do
+        subject.version %w(v1 v2), using: :header, vendor: 'test'
+        subject.enable_root_route!
+
+        versioned_get '/', 'v1', using: :header, vendor: 'test'
+        versioned_get '/', 'v2', using: :header, vendor: 'test'
+      end
+
+      it 'param versioned APIs' do
+        subject.version 'v1', using: :param
+        subject.enable_root_route!
+
+        versioned_get '/', 'v1', using: :param
+      end
+
+      it 'Accept-Version header versioned APIs' do
+        subject.version 'v1', using: :accept_version_header
+        subject.enable_root_route!
+
+        versioned_get '/', 'v1', using: :accept_version_header
+      end
+
+      it 'unversioned APIs' do
+        subject.enable_root_route!
+
+        get '/'
+      end
+    end
+
+    it 'allows for multiple paths' do
+      subject.get(['/abc', '/def']) do
+        'foo'
+      end
+
+      get '/abc'
+      expect(last_response.body).to eql 'foo'
+      get '/def'
+      expect(last_response.body).to eql 'foo'
+    end
+
+    context 'format' do
+      before(:each) do
+        allow_any_instance_of(Object).to receive(:to_json).and_return('abc')
+        allow_any_instance_of(Object).to receive(:to_txt).and_return('def')
+
+        subject.get('/abc') do
+          Object.new
+        end
+      end
+
+      it 'allows .json' do
+        get '/abc.json'
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eql 'abc' # json-encoded symbol
+      end
+
+      it 'allows .txt' do
+        get '/abc.txt'
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eql 'def' # raw text
+      end
+    end
+
+    it 'allows for format without corrupting a param' do
+      subject.get('/:id') do
+        { 'id' => params[:id] }
+      end
+
+      get '/awesome.json'
+      expect(last_response.body).to eql '{"id":"awesome"}'
+    end
+
+    it 'allows for format in namespace with no path' do
+      subject.namespace :abc do
+        get do
+          ['json']
+        end
+      end
+
+      get '/abc.json'
+      expect(last_response.body).to eql '["json"]'
+    end
+
+    it 'allows for multiple verbs' do
+      subject.route([:get, :post], '/abc') do
+        'hiya'
+      end
+
+      subject.endpoints.first.routes.each do |route|
+        expect(route.route_path).to eql '/abc(.:format)'
+      end
+
+      get '/abc'
+      expect(last_response.body).to eql 'hiya'
+      post '/abc'
+      expect(last_response.body).to eql 'hiya'
+    end
+
+    [:put, :post].each do |verb|
+      context verb do
+        ['string', :symbol, 1, -1.1, {}, [], true, false, nil].each do |object|
+          it "allows a(n) #{object.class} json object in params" do
+            subject.format :json
+            subject.send(verb) do
+              env['api.request.body']
+            end
+            send verb, '/', MultiJson.dump(object), 'CONTENT_TYPE' => 'application/json'
+            expect(last_response.status).to eq(verb == :post ? 201 : 200)
+            expect(last_response.body).to eql MultiJson.dump(object)
+            expect(last_request.params).to eql({})
+          end
+          it 'stores input in api.request.input' do
+            subject.format :json
+            subject.send(verb) do
+              env['api.request.input']
+            end
+            send verb, '/', MultiJson.dump(object), 'CONTENT_TYPE' => 'application/json'
+            expect(last_response.status).to eq(verb == :post ? 201 : 200)
+            expect(last_response.body).to eql MultiJson.dump(object).to_json
+          end
+          context 'chunked transfer encoding' do
+            it 'stores input in api.request.input' do
+              subject.format :json
+              subject.send(verb) do
+                env['api.request.input']
+              end
+              send verb, '/', MultiJson.dump(object), 'CONTENT_TYPE' => 'application/json', 'HTTP_TRANSFER_ENCODING' => 'chunked', 'CONTENT_LENGTH' => nil
+              expect(last_response.status).to eq(verb == :post ? 201 : 200)
+              expect(last_response.body).to eql MultiJson.dump(object).to_json
+            end
+          end
+        end
+      end
+    end
+
+    it 'allows for multipart paths' do
+      subject.route([:get, :post], '/:id/first') do
+        'first'
+      end
+
+      subject.route([:get, :post], '/:id') do
+        'ola'
+      end
+      subject.route([:get, :post], '/:id/first/second') do
+        'second'
+      end
+
+      get '/1'
+      expect(last_response.body).to eql 'ola'
+      post '/1'
+      expect(last_response.body).to eql 'ola'
+      get '/1/first'
+      expect(last_response.body).to eql 'first'
+      post '/1/first'
+      expect(last_response.body).to eql 'first'
+      get '/1/first/second'
+      expect(last_response.body).to eql 'second'
+    end
+
+    it 'allows for :any as a verb' do
+      subject.route(:any, '/abc') do
+        'lol'
+      end
+
+      %w(get post put delete options patch).each do |m|
+        send(m, '/abc')
+        expect(last_response.body).to eql 'lol'
+      end
+    end
+
+    verbs = %w(post get head delete put options patch)
+    verbs.each do |verb|
+      it 'allows and properly constrain a #{verb.upcase} method' do
+        subject.send(verb, '/example') do
+          verb
+        end
+        send(verb, '/example')
+        expect(last_response.body).to eql verb == 'head' ? '' : verb
+        # Call it with a method other than the properly constrained one.
+        send(used_verb = verbs[(verbs.index(verb) + 2) % verbs.size], '/example')
+        expect(last_response.status).to eql used_verb == 'options' ? 204 : 405
+      end
+    end
+
+    it 'returns a 201 response code for POST by default' do
+      subject.post('example') do
+        'Created'
+      end
+
+      post '/example'
+      expect(last_response.status).to eql 201
+      expect(last_response.body).to eql 'Created'
+    end
+
+    it 'returns a 405 for an unsupported method with an X-Custom-Header' do
+      subject.before { header 'X-Custom-Header', 'foo' }
+      subject.get 'example' do
+        'example'
+      end
+      put '/example'
+      expect(last_response.status).to eql 405
+      expect(last_response.body).to eql ''
+      expect(last_response.headers['X-Custom-Header']).to eql 'foo'
+    end
+
+    specify '405 responses includes an Allow header specifying supported methods' do
+      subject.get 'example' do
+        'example'
+      end
+      subject.post 'example' do
+        'example'
+      end
+      put '/example'
+      expect(last_response.headers['Allow']).to eql 'OPTIONS, GET, POST, HEAD'
+    end
+
+    specify '405 responses includes an Content-Type header' do
+      subject.get 'example' do
+        'example'
+      end
+      subject.post 'example' do
+        'example'
+      end
+      put '/example'
+      expect(last_response.headers['Content-Type']).to eql 'text/plain'
+    end
+
+    it 'adds an OPTIONS route that returns a 204, an Allow header and a X-Custom-Header' do
+      subject.before { header 'X-Custom-Header', 'foo' }
+      subject.get 'example' do
+        'example'
+      end
+      options '/example'
+      expect(last_response.status).to eql 204
+      expect(last_response.body).to eql ''
+      expect(last_response.headers['Allow']).to eql 'OPTIONS, GET, HEAD'
+      expect(last_response.headers['X-Custom-Header']).to eql 'foo'
+    end
+
+    it 'allows HEAD on a GET request' do
+      subject.get 'example' do
+        'example'
+      end
+      head '/example'
+      expect(last_response.status).to eql 200
+      expect(last_response.body).to eql ''
+    end
+
+    it 'overwrites the default HEAD request' do
+      subject.head 'example' do
+        error! 'nothing to see here', 400
+      end
+      subject.get 'example' do
+        'example'
+      end
+      head '/example'
+      expect(last_response.status).to eql 400
+    end
+  end
+
+  context 'do_not_route_head!' do
+    before :each do
+      subject.do_not_route_head!
+      subject.get 'example' do
+        'example'
+      end
+    end
+    it 'options does not contain HEAD' do
+      options '/example'
+      expect(last_response.status).to eql 204
+      expect(last_response.body).to eql ''
+      expect(last_response.headers['Allow']).to eql 'OPTIONS, GET'
+    end
+    it 'does not allow HEAD on a GET request' do
+      head '/example'
+      expect(last_response.status).to eql 405
+    end
+  end
+
+  context 'do_not_route_options!' do
+    before :each do
+      subject.do_not_route_options!
+      subject.get 'example' do
+        'example'
+      end
+    end
+    it 'options does not exist' do
+      options '/example'
+      expect(last_response.status).to eql 405
+    end
+  end
+
+  describe 'filters' do
+    it 'adds a before filter' do
+      subject.before { @foo = 'first'  }
+      subject.before { @bar = 'second' }
+      subject.get '/' do
+        "#{@foo} #{@bar}"
+      end
+
+      get '/'
+      expect(last_response.body).to eql 'first second'
+    end
+
+    it 'adds a before filter to current and child namespaces only' do
+      subject.get '/' do
+        "root - #{@foo}"
+      end
+      subject.namespace :blah do
+        before { @foo = 'foo' }
+        get '/' do
+          "blah - #{@foo}"
+        end
+
+        namespace :bar do
+          get '/' do
+            "blah - bar - #{@foo}"
+          end
+        end
+      end
+
+      get '/'
+      expect(last_response.body).to eql 'root - '
+      get '/blah'
+      expect(last_response.body).to eql 'blah - foo'
+      get '/blah/bar'
+      expect(last_response.body).to eql 'blah - bar - foo'
+    end
+
+    it 'adds a after_validation filter' do
+      subject.after_validation { @foo = "first #{params[:id] }:#{params[:id].class}"  }
+      subject.after_validation { @bar = 'second' }
+      subject.params do
+        requires :id, type: Integer
+      end
+      subject.get '/' do
+        "#{@foo} #{@bar}"
+      end
+
+      get '/', id: '32'
+      expect(last_response.body).to eql 'first 32:Fixnum second'
+    end
+
+    it 'adds a after filter' do
+      m = double('after mock')
+      subject.after { m.do_something! }
+      subject.after { m.do_something! }
+      subject.get '/' do
+        @var ||= 'default'
+      end
+
+      expect(m).to receive(:do_something!).exactly(2).times
+      get '/'
+      expect(last_response.body).to eql 'default'
+    end
+
+    it 'calls all filters when validation passes' do
+      a = double('before mock')
+      b = double('before_validation mock')
+      c = double('after_validation mock')
+      d = double('after mock')
+
+      subject.params do
+        requires :id, type: Integer
+      end
+      subject.resource ':id' do
+        before { a.do_something! }
+        before_validation { b.do_something! }
+        after_validation { c.do_something! }
+        after { d.do_something! }
+        get do
+          'got it'
+        end
+      end
+
+      expect(a).to receive(:do_something!).exactly(1).times
+      expect(b).to receive(:do_something!).exactly(1).times
+      expect(c).to receive(:do_something!).exactly(1).times
+      expect(d).to receive(:do_something!).exactly(1).times
+
+      get '/123'
+      expect(last_response.status).to eql 200
+      expect(last_response.body).to eql 'got it'
+    end
+
+    it 'calls only before filters when validation fails' do
+      a = double('before mock')
+      b = double('before_validation mock')
+      c = double('after_validation mock')
+      d = double('after mock')
+
+      subject.params do
+        requires :id, type: Integer
+      end
+      subject.resource ':id' do
+        before { a.do_something! }
+        before_validation { b.do_something! }
+        after_validation { c.do_something! }
+        after { d.do_something! }
+        get do
+          'got it'
+        end
+      end
+
+      expect(a).to receive(:do_something!).exactly(1).times
+      expect(b).to receive(:do_something!).exactly(1).times
+      expect(c).to receive(:do_something!).exactly(0).times
+      expect(d).to receive(:do_something!).exactly(0).times
+
+      get '/abc'
+      expect(last_response.status).to eql 400
+      expect(last_response.body).to eql 'id is invalid'
+    end
+
+    it 'calls filters in the correct order' do
+      i = 0
+      a = double('before mock')
+      b = double('before_validation mock')
+      c = double('after_validation mock')
+      d = double('after mock')
+
+      subject.params do
+        requires :id, type: Integer
+      end
+      subject.resource ':id' do
+        before { a.here(i += 1) }
+        before_validation { b.here(i += 1) }
+        after_validation { c.here(i += 1) }
+        after { d.here(i += 1) }
+        get do
+          'got it'
+        end
+      end
+
+      expect(a).to receive(:here).with(1).exactly(1).times
+      expect(b).to receive(:here).with(2).exactly(1).times
+      expect(c).to receive(:here).with(3).exactly(1).times
+      expect(d).to receive(:here).with(4).exactly(1).times
+
+      get '/123'
+      expect(last_response.status).to eql 200
+      expect(last_response.body).to eql 'got it'
+    end
+  end
+
+  context 'format' do
+    before do
+      subject.get('/foo') { 'bar' }
+    end
+
+    it 'sets content type for txt format' do
+      get '/foo'
+      expect(last_response.headers['Content-Type']).to eq('text/plain')
+    end
+
+    it 'sets content type for xml' do
+      get '/foo.xml'
+      expect(last_response.headers['Content-Type']).to eq('application/xml')
+    end
+
+    it 'sets content type for json' do
+      get '/foo.json'
+      expect(last_response.headers['Content-Type']).to eq('application/json')
+    end
+
+    it 'sets content type for serializable hash format' do
+      get '/foo.serializable_hash'
+      expect(last_response.headers['Content-Type']).to eq('application/json')
+    end
+
+    it 'sets content type for binary format' do
+      get '/foo.binary'
+      expect(last_response.headers['Content-Type']).to eq('application/octet-stream')
+    end
+
+    it 'returns raw data when content type binary' do
+      image_filename = 'grape.png'
+      file = File.open(image_filename, 'rb') { |io| io.read }
+      subject.format :binary
+      subject.get('/binary_file') { File.binread(image_filename) }
+      get '/binary_file'
+      expect(last_response.headers['Content-Type']).to eq('application/octet-stream')
+      expect(last_response.body).to eq(file)
+    end
+
+    it 'sets content type for error' do
+      subject.get('/error') { error!('error in plain text', 500) }
+      get '/error'
+      expect(last_response.headers['Content-Type']).to eql 'text/plain'
+    end
+
+    it 'sets content type for json error' do
+      subject.format :json
+      subject.get('/error') { error!('error in json', 500) }
+      get '/error.json'
+      expect(last_response.status).to eql 500
+      expect(last_response.headers['Content-Type']).to eql 'application/json'
+    end
+
+    it 'sets content type for xml error' do
+      subject.format :xml
+      subject.get('/error') { error!('error in xml', 500) }
+      get '/error'
+      expect(last_response.status).to eql 500
+      expect(last_response.headers['Content-Type']).to eql 'application/xml'
+    end
+
+    context 'with a custom content_type' do
+      before do
+        subject.content_type :custom, 'application/custom'
+        subject.formatter :custom, ->(_object, _env) { 'custom' }
+
+        subject.get('/custom') { 'bar' }
+        subject.get('/error') { error!('error in custom', 500) }
+      end
+
+      it 'sets content type' do
+        get '/custom.custom'
+        expect(last_response.headers['Content-Type']).to eql 'application/custom'
+      end
+
+      it 'sets content type for error' do
+        get '/error.custom'
+        expect(last_response.headers['Content-Type']).to eql 'application/custom'
+      end
+    end
+
+    context 'env["api.format"]' do
+      before do
+        subject.post 'attachment' do
+          filename = params[:file][:filename]
+          content_type MIME::Types.type_for(filename)[0].to_s
+          env['api.format'] = :binary # there's no formatter for :binary, data will be returned "as is"
+          header 'Content-Disposition', "attachment; filename*=UTF-8''#{URI.escape(filename)}"
+          params[:file][:tempfile].read
+        end
+      end
+
+      ['/attachment.png', 'attachment'].each do |url|
+        it "uploads and downloads a PNG file via #{url}" do
+          image_filename = 'grape.png'
+          post url, file: Rack::Test::UploadedFile.new(image_filename, 'image/png', true)
+          expect(last_response.status).to eq(201)
+          expect(last_response.headers['Content-Type']).to eq('image/png')
+          expect(last_response.headers['Content-Disposition']).to eq("attachment; filename*=UTF-8''grape.png")
+          File.open(image_filename, 'rb') do |io|
+            expect(last_response.body).to eq io.read
+          end
+        end
+      end
+
+      it 'uploads and downloads a Ruby file' do
+        filename = __FILE__
+        post '/attachment.rb', file: Rack::Test::UploadedFile.new(filename, 'application/x-ruby', true)
+        expect(last_response.status).to eq(201)
+        expect(last_response.headers['Content-Type']).to eq('application/x-ruby')
+        expect(last_response.headers['Content-Disposition']).to eq("attachment; filename*=UTF-8''api_spec.rb")
+        File.open(filename, 'rb') do |io|
+          expect(last_response.body).to eq io.read
+        end
+      end
+    end
+  end
+
+  context 'custom middleware' do
+    module ApiSpec
+      class PhonyMiddleware
+        def initialize(app, *args)
+          @args = args
+          @app = app
+          @block = true if block_given?
+        end
+
+        def call(env)
+          env['phony.args'] ||= []
+          env['phony.args'] << @args
+          env['phony.block'] = true if @block
+          @app.call(env)
+        end
+      end
+    end
+
+    describe '.middleware' do
+      it 'includes middleware arguments from settings' do
+        subject.use ApiSpec::PhonyMiddleware, 'abc', 123
+        expect(subject.middleware).to eql [[ApiSpec::PhonyMiddleware, 'abc', 123]]
+      end
+
+      it 'includes all middleware from stacked settings' do
+        subject.use ApiSpec::PhonyMiddleware, 123
+        subject.use ApiSpec::PhonyMiddleware, 'abc'
+        subject.use ApiSpec::PhonyMiddleware, 'foo'
+
+        expect(subject.middleware).to eql [
+          [ApiSpec::PhonyMiddleware, 123],
+          [ApiSpec::PhonyMiddleware, 'abc'],
+          [ApiSpec::PhonyMiddleware, 'foo']
+        ]
+      end
+    end
+
+    describe '.use' do
+      it 'adds middleware' do
+        subject.use ApiSpec::PhonyMiddleware, 123
+        expect(subject.middleware).to eql [[ApiSpec::PhonyMiddleware, 123]]
+      end
+
+      it 'does not show up outside the namespace' do
+        inner_middleware = nil
+        subject.use ApiSpec::PhonyMiddleware, 123
+        subject.namespace :awesome do
+          use ApiSpec::PhonyMiddleware, 'abc'
+          inner_middleware = middleware
+        end
+
+        expect(subject.middleware).to eql [[ApiSpec::PhonyMiddleware, 123]]
+        expect(inner_middleware).to eql [[ApiSpec::PhonyMiddleware, 123], [ApiSpec::PhonyMiddleware, 'abc']]
+      end
+
+      it 'calls the middleware' do
+        subject.use ApiSpec::PhonyMiddleware, 'hello'
+        subject.get '/' do
+          env['phony.args'].first.first
+        end
+
+        get '/'
+        expect(last_response.body).to eql 'hello'
+      end
+
+      it 'adds a block if one is given' do
+        block = -> {}
+        subject.use ApiSpec::PhonyMiddleware, &block
+        expect(subject.middleware).to eql [[ApiSpec::PhonyMiddleware, block]]
+      end
+
+      it 'uses a block if one is given' do
+        block = -> {}
+        subject.use ApiSpec::PhonyMiddleware, &block
+        subject.get '/' do
+          env['phony.block'].inspect
+        end
+
+        get '/'
+        expect(last_response.body).to eq('true')
+      end
+
+      it 'does not destroy the middleware settings on multiple runs' do
+        block = -> {}
+        subject.use ApiSpec::PhonyMiddleware, &block
+        subject.get '/' do
+          env['phony.block'].inspect
+        end
+
+        2.times do
+          get '/'
+          expect(last_response.body).to eq('true')
+        end
+      end
+
+      it 'mounts behind error middleware' do
+        m = Class.new(Grape::Middleware::Base) do
+          def before
+            throw :error, message: 'Caught in the Net', status: 400
+          end
+        end
+        subject.use m
+        subject.get '/' do
+        end
+        get '/'
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('Caught in the Net')
+      end
+    end
+  end
+  describe '.http_basic' do
+    it 'protects any resources on the same scope' do
+      subject.http_basic do |u, _p|
+        u == 'allow'
+      end
+      subject.get(:hello) { 'Hello, world.' }
+      get '/hello'
+      expect(last_response.status).to eql 401
+      get '/hello', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('allow', 'whatever')
+      expect(last_response.status).to eql 200
+    end
+
+    it 'is scopable' do
+      subject.get(:hello) { 'Hello, world.' }
+      subject.namespace :admin do
+        http_basic do |u, _p|
+          u == 'allow'
+        end
+
+        get(:hello) { 'Hello, world.' }
+      end
+
+      get '/hello'
+      expect(last_response.status).to eql 200
+      get '/admin/hello'
+      expect(last_response.status).to eql 401
+    end
+
+    it 'is callable via .auth as well' do
+      subject.auth :http_basic do |u, _p|
+        u == 'allow'
+      end
+
+      subject.get(:hello) { 'Hello, world.' }
+      get '/hello'
+      expect(last_response.status).to eql 401
+      get '/hello', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('allow', 'whatever')
+      expect(last_response.status).to eql 200
+    end
+
+    it 'has access to the current endpoint' do
+      basic_auth_context = nil
+
+      subject.http_basic do |u, _p|
+        basic_auth_context = self
+
+        u == 'allow'
+      end
+
+      subject.get(:hello) { 'Hello, world.' }
+      get '/hello', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('allow', 'whatever')
+      expect(basic_auth_context).to be_an_instance_of(Grape::Endpoint)
+    end
+
+    it 'has access to helper methods' do
+      subject.helpers do
+        def authorize(u, p)
+          u == 'allow' && p == 'whatever'
+        end
+      end
+
+      subject.http_basic do |u, p|
+        authorize(u, p)
+      end
+
+      subject.get(:hello) { 'Hello, world.' }
+      get '/hello', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('allow', 'whatever')
+      expect(last_response.status).to eql 200
+      get '/hello', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('disallow', 'whatever')
+      expect(last_response.status).to eql 401
+    end
+
+    it 'can set instance variables accessible to routes' do
+      subject.http_basic do |u, _p|
+        @hello = 'Hello, world.'
+
+        u == 'allow'
+      end
+
+      subject.get(:hello) { @hello }
+      get '/hello', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('allow', 'whatever')
+      expect(last_response.status).to eql 200
+      expect(last_response.body).to eql 'Hello, world.'
+    end
+  end
+
+  describe '.logger' do
+    subject do
+      Class.new(Grape::API) do
+        def self.io
+          @io ||= StringIO.new
+        end
+        logger ::Logger.new(io)
+      end
+    end
+
+    it 'returns an instance of Logger class by default' do
+      expect(subject.logger.class).to eql Logger
+    end
+
+    it 'allows setting a custom logger' do
+      mylogger = Class.new
+      subject.logger mylogger
+      expect(mylogger).to receive(:info).exactly(1).times
+      subject.logger.info 'this will be logged'
+    end
+
+    it 'defaults to a standard logger log format' do
+      t = Time.at(100)
+      allow(Time).to receive(:now).and_return(t)
+      if ActiveSupport::VERSION::MAJOR >= 4
+        expect(subject.io).to receive(:write).with("I, [#{Logger::Formatter.new.send(:format_datetime, t)}\##{Process.pid}]  INFO -- : this will be logged\n")
+      else
+        expect(subject.io).to receive(:write).with("this will be logged\n")
+      end
+      subject.logger.info 'this will be logged'
+    end
+  end
+
+  describe '.helpers' do
+    it 'is accessible from the endpoint' do
+      subject.helpers do
+        def hello
+          'Hello, world.'
+        end
+      end
+
+      subject.get '/howdy' do
+        hello
+      end
+
+      get '/howdy'
+      expect(last_response.body).to eql 'Hello, world.'
+    end
+
+    it 'is scopable' do
+      subject.helpers do
+        def generic
+          'always there'
+        end
+      end
+
+      subject.namespace :admin do
+        helpers do
+          def secret
+            'only in admin'
+          end
+        end
+
+        get '/secret' do
+          [generic, secret].join ':'
+        end
+      end
+
+      subject.get '/generic' do
+        [generic, respond_to?(:secret)].join ':'
+      end
+
+      get '/generic'
+      expect(last_response.body).to eql 'always there:false'
+      get '/admin/secret'
+      expect(last_response.body).to eql 'always there:only in admin'
+    end
+
+    it 'is reopenable' do
+      subject.helpers do
+        def one
+          1
+        end
+      end
+
+      subject.helpers do
+        def two
+          2
+        end
+      end
+
+      subject.get 'howdy' do
+        [one, two]
+      end
+
+      expect { get '/howdy' }.not_to raise_error
+    end
+
+    it 'allows for modules' do
+      mod = Module.new do
+        def hello
+          'Hello, world.'
+        end
+      end
+      subject.helpers mod
+
+      subject.get '/howdy' do
+        hello
+      end
+
+      get '/howdy'
+      expect(last_response.body).to eql 'Hello, world.'
+    end
+
+    it 'allows multiple calls with modules and blocks' do
+      subject.helpers Module.new do
+        def one
+          1
+        end
+      end
+      subject.helpers Module.new do
+        def two
+          2
+        end
+      end
+      subject.helpers do
+        def three
+          3
+        end
+      end
+      subject.get 'howdy' do
+        [one, two, three]
+      end
+      expect { get '/howdy' }.not_to raise_error
+    end
+  end
+
+  describe '.scope' do
+    # TODO: refactor this to not be tied to versioning. How about a generic
+    # .setting macro?
+    it 'scopes the various settings' do
+      subject.prefix 'new'
+
+      subject.scope :legacy do
+        prefix 'legacy'
+        get '/abc' do
+          'abc'
+        end
+      end
+
+      subject.get '/def' do
+        'def'
+      end
+
+      get '/new/abc'
+      expect(last_response.status).to eql 404
+      get '/legacy/abc'
+      expect(last_response.status).to eql 200
+      get '/legacy/def'
+      expect(last_response.status).to eql 404
+      get '/new/def'
+      expect(last_response.status).to eql 200
+    end
+  end
+
+  describe '.rescue_from' do
+    it 'does not rescue errors when rescue_from is not set' do
+      subject.get '/exception' do
+        fail 'rain!'
+      end
+      expect { get '/exception' }.to raise_error
+    end
+
+    it 'rescues all errors if rescue_from :all is called' do
+      subject.rescue_from :all
+      subject.get '/exception' do
+        fail 'rain!'
+      end
+      get '/exception'
+      expect(last_response.status).to eql 500
+      expect(last_response.body).to eq 'rain!'
+    end
+
+    it 'rescues all errors with a json formatter' do
+      subject.format :json
+      subject.default_format :json
+      subject.rescue_from :all
+      subject.get '/exception' do
+        fail 'rain!'
+      end
+      get '/exception'
+      expect(last_response.status).to eql 500
+      expect(last_response.body).to eq({ error: 'rain!' }.to_json)
+    end
+
+    it 'rescues only certain errors if rescue_from is called with specific errors' do
+      subject.rescue_from ArgumentError
+      subject.get('/rescued') { fail ArgumentError }
+      subject.get('/unrescued') { fail 'beefcake' }
+
+      get '/rescued'
+      expect(last_response.status).to eql 500
+
+      expect { get '/unrescued' }.to raise_error
+    end
+
+    context 'CustomError subclass of Grape::Exceptions::Base' do
+      before do
+        class CustomError < Grape::Exceptions::Base; end
+      end
+
+      it 'does not re-raise exceptions of type Grape::Exceptions::Base' do
+        subject.get('/custom_exception') { fail CustomError }
+
+        expect { get '/custom_exception' }.not_to raise_error
+      end
+
+      it 'rescues custom grape exceptions' do
+        subject.rescue_from CustomError do |e|
+          rack_response('New Error', e.status)
+        end
+        subject.get '/custom_error' do
+          fail CustomError, status: 400, message: 'Custom Error'
+        end
+
+        get '/custom_error'
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('New Error')
+      end
+    end
+
+    it 'can rescue exceptions raised in the formatter' do
+      formatter = double(:formatter)
+      allow(formatter).to receive(:call) { fail StandardError }
+      allow(Grape::Formatter::Base).to receive(:formatter_for) { formatter }
+
+      subject.rescue_from :all do |_e|
+        rack_response('Formatter Error', 500)
+      end
+      subject.get('/formatter_exception') { 'Hello world' }
+
+      get '/formatter_exception'
+      expect(last_response.status).to eql 500
+      expect(last_response.body).to eq('Formatter Error')
+    end
+  end
+
+  describe '.rescue_from klass, block' do
+    it 'rescues Exception' do
+      subject.rescue_from RuntimeError do |e|
+        rack_response("rescued from #{e.message}", 202)
+      end
+      subject.get '/exception' do
+        fail 'rain!'
+      end
+      get '/exception'
+      expect(last_response.status).to eql 202
+      expect(last_response.body).to eq('rescued from rain!')
+    end
+
+    context 'custom errors' do
+      before do
+        class ConnectionError < RuntimeError; end
+        class DatabaseError < RuntimeError; end
+        class CommunicationError < StandardError; end
+      end
+
+      it 'rescues an error via rescue_from :all' do
+        subject.rescue_from :all do |e|
+          rack_response("rescued from #{e.class.name}", 500)
+        end
+        subject.get '/exception' do
+          fail ConnectionError
+        end
+        get '/exception'
+        expect(last_response.status).to eql 500
+        expect(last_response.body).to eq('rescued from ConnectionError')
+      end
+      it 'rescues a specific error' do
+        subject.rescue_from ConnectionError do |e|
+          rack_response("rescued from #{e.class.name}", 500)
+        end
+        subject.get '/exception' do
+          fail ConnectionError
+        end
+        get '/exception'
+        expect(last_response.status).to eql 500
+        expect(last_response.body).to eq('rescued from ConnectionError')
+      end
+      it 'rescues a subclass of an error by default' do
+        subject.rescue_from RuntimeError do |e|
+          rack_response("rescued from #{e.class.name}", 500)
+        end
+        subject.get '/exception' do
+          fail ConnectionError
+        end
+        get '/exception'
+        expect(last_response.status).to eql 500
+        expect(last_response.body).to eq('rescued from ConnectionError')
+      end
+      it 'rescues multiple specific errors' do
+        subject.rescue_from ConnectionError do |e|
+          rack_response("rescued from #{e.class.name}", 500)
+        end
+        subject.rescue_from DatabaseError do |e|
+          rack_response("rescued from #{e.class.name}", 500)
+        end
+        subject.get '/connection' do
+          fail ConnectionError
+        end
+        subject.get '/database' do
+          fail DatabaseError
+        end
+        get '/connection'
+        expect(last_response.status).to eql 500
+        expect(last_response.body).to eq('rescued from ConnectionError')
+        get '/database'
+        expect(last_response.status).to eql 500
+        expect(last_response.body).to eq('rescued from DatabaseError')
+      end
+      it 'does not rescue a different error' do
+        subject.rescue_from RuntimeError do |e|
+          rack_response("rescued from #{e.class.name}", 500)
+        end
+        subject.get '/uncaught' do
+          fail CommunicationError
+        end
+        expect { get '/uncaught' }.to raise_error(CommunicationError)
+      end
+    end
+  end
+
+  describe '.rescue_from klass, lambda' do
+    it 'rescues an error with the lambda' do
+      subject.rescue_from ArgumentError, -> {
+        rack_response('rescued with a lambda', 400)
+      }
+      subject.get('/rescue_lambda') { fail ArgumentError }
+
+      get '/rescue_lambda'
+      expect(last_response.status).to eq(400)
+      expect(last_response.body).to eq('rescued with a lambda')
+    end
+
+    it 'can execute the lambda with an argument' do
+      subject.rescue_from ArgumentError, ->(e) {
+        rack_response(e.message, 400)
+      }
+      subject.get('/rescue_lambda') { fail ArgumentError, 'lambda takes an argument' }
+
+      get '/rescue_lambda'
+      expect(last_response.status).to eq(400)
+      expect(last_response.body).to eq('lambda takes an argument')
+    end
+  end
+
+  describe '.rescue_from klass, with: method' do
+    it 'rescues an error with the specified message' do
+      def rescue_arg_error
+        Rack::Response.new('rescued with a method', 400)
+      end
+
+      subject.rescue_from ArgumentError, with: rescue_arg_error
+      subject.get('/rescue_method') { fail ArgumentError }
+
+      get '/rescue_method'
+      expect(last_response.status).to eq(400)
+      expect(last_response.body).to eq('rescued with a method')
+    end
+  end
+
+  describe '.rescue_from klass, rescue_subclasses: boolean' do
+    before do
+      module APIErrors
+        class ParentError < StandardError; end
+        class ChildError < ParentError; end
+      end
+    end
+
+    it 'rescues error as well as subclass errors with rescue_subclasses option set' do
+      subject.rescue_from APIErrors::ParentError, rescue_subclasses: true do |e|
+        rack_response("rescued from #{e.class.name}", 500)
+      end
+      subject.get '/caught_child' do
+        fail APIErrors::ChildError
+      end
+      subject.get '/caught_parent' do
+        fail APIErrors::ParentError
+      end
+      subject.get '/uncaught_parent' do
+        fail StandardError
+      end
+
+      get '/caught_child'
+      expect(last_response.status).to eql 500
+      get '/caught_parent'
+      expect(last_response.status).to eql 500
+      expect { get '/uncaught_parent' }.to raise_error(StandardError)
+    end
+
+    it 'sets rescue_subclasses to true by default' do
+      subject.rescue_from APIErrors::ParentError do |e|
+        rack_response("rescued from #{e.class.name}", 500)
+      end
+      subject.get '/caught_child' do
+        fail APIErrors::ChildError
+      end
+
+      get '/caught_child'
+      expect(last_response.status).to eql 500
+    end
+
+    it 'does not rescue child errors if rescue_subclasses is false' do
+      subject.rescue_from APIErrors::ParentError, rescue_subclasses: false do |e|
+        rack_response("rescued from #{e.class.name}", 500)
+      end
+      subject.get '/uncaught' do
+        fail APIErrors::ChildError
+      end
+      expect { get '/uncaught' }.to raise_error(APIErrors::ChildError)
+    end
+  end
+
+  describe '.error_format' do
+    it 'rescues all errors and return :txt' do
+      subject.rescue_from :all
+      subject.format :txt
+      subject.get '/exception' do
+        fail 'rain!'
+      end
+      get '/exception'
+      expect(last_response.body).to eql 'rain!'
+    end
+
+    it 'rescues all errors and return :txt with backtrace' do
+      subject.rescue_from :all, backtrace: true
+      subject.format :txt
+      subject.get '/exception' do
+        fail 'rain!'
+      end
+      get '/exception'
+      expect(last_response.body.start_with?("rain!\r\n")).to be true
+    end
+
+    it 'rescues all errors with a default formatter' do
+      subject.default_format :foo
+      subject.content_type :foo, 'text/foo'
+      subject.rescue_from :all
+      subject.get '/exception' do
+        fail 'rain!'
+      end
+      get '/exception.foo'
+      expect(last_response.body).to start_with 'rain!'
+    end
+
+    it 'defaults the error formatter to format' do
+      subject.format :json
+      subject.rescue_from :all
+      subject.content_type :json, 'application/json'
+      subject.content_type :foo, 'text/foo'
+      subject.get '/exception' do
+        fail 'rain!'
+      end
+      get '/exception.json'
+      expect(last_response.body).to eq('{"error":"rain!"}')
+      get '/exception.foo'
+      expect(last_response.body).to eq('{"error":"rain!"}')
+    end
+
+    context 'class' do
+      before :each do
+        class CustomErrorFormatter
+          def self.call(message, _backtrace, _options, _env)
+            "message: #{message} @backtrace"
+          end
+        end
+      end
+      it 'returns a custom error format' do
+        subject.rescue_from :all, backtrace: true
+        subject.error_formatter :txt, CustomErrorFormatter
+        subject.get '/exception' do
+          fail 'rain!'
+        end
+        get '/exception'
+        expect(last_response.body).to eq('message: rain! @backtrace')
+      end
+    end
+
+    describe 'with' do
+      context 'class' do
+        before :each do
+          class CustomErrorFormatter
+            def self.call(message, _backtrace, _option, _env)
+              "message: #{message} @backtrace"
+            end
+          end
+        end
+
+        it 'returns a custom error format' do
+          subject.rescue_from :all, backtrace: true
+          subject.error_formatter :txt, with: CustomErrorFormatter
+          subject.get('/exception') { fail 'rain!' }
+
+          get '/exception'
+          expect(last_response.body).to eq('message: rain! @backtrace')
+        end
+      end
+    end
+
+    it 'rescues all errors and return :json' do
+      subject.rescue_from :all
+      subject.format :json
+      subject.get '/exception' do
+        fail 'rain!'
+      end
+      get '/exception'
+      expect(last_response.body).to eql '{"error":"rain!"}'
+    end
+    it 'rescues all errors and return :json with backtrace' do
+      subject.rescue_from :all, backtrace: true
+      subject.format :json
+      subject.get '/exception' do
+        fail 'rain!'
+      end
+      get '/exception'
+      json = MultiJson.load(last_response.body)
+      expect(json['error']).to eql 'rain!'
+      expect(json['backtrace'].length).to be > 0
+    end
+    it 'rescues error! and return txt' do
+      subject.format :txt
+      subject.get '/error' do
+        error!('Access Denied', 401)
+      end
+      get '/error'
+      expect(last_response.body).to eql 'Access Denied'
+    end
+    it 'rescues error! and return json' do
+      subject.format :json
+      subject.get '/error' do
+        error!('Access Denied', 401)
+      end
+      get '/error'
+      expect(last_response.body).to eql '{"error":"Access Denied"}'
+    end
+  end
+
+  describe '.content_type' do
+    it 'sets additional content-type' do
+      subject.content_type :xls, 'application/vnd.ms-excel'
+      subject.get :excel do
+        'some binary content'
+      end
+      get '/excel.xls'
+      expect(last_response.content_type).to eq('application/vnd.ms-excel')
+    end
+    it 'allows to override content-type' do
+      subject.get :content do
+        content_type 'text/javascript'
+        'var x = 1;'
+      end
+      get '/content'
+      expect(last_response.content_type).to eq('text/javascript')
+    end
+    it 'removes existing content types' do
+      subject.content_type :xls, 'application/vnd.ms-excel'
+      subject.get :excel do
+        'some binary content'
+      end
+      get '/excel.json'
+      expect(last_response.status).to eq(406)
+      expect(last_response.body).to eq("The requested format 'txt' is not supported.")
+    end
+  end
+
+  describe '.formatter' do
+    context 'multiple formatters' do
+      before :each do
+        subject.formatter :json, ->(object, _env) { "{\"custom_formatter\":\"#{object[:some] }\"}" }
+        subject.formatter :txt, ->(object, _env) { "custom_formatter: #{object[:some] }" }
+        subject.get :simple do
+          { some: 'hash' }
+        end
+      end
+      it 'sets one formatter' do
+        get '/simple.json'
+        expect(last_response.body).to eql '{"custom_formatter":"hash"}'
+      end
+      it 'sets another formatter' do
+        get '/simple.txt'
+        expect(last_response.body).to eql 'custom_formatter: hash'
+      end
+    end
+    context 'custom formatter' do
+      before :each do
+        subject.content_type :json, 'application/json'
+        subject.content_type :custom, 'application/custom'
+        subject.formatter :custom, ->(object, _env) { "{\"custom_formatter\":\"#{object[:some] }\"}" }
+        subject.get :simple do
+          { some: 'hash' }
+        end
+      end
+      it 'uses json' do
+        get '/simple.json'
+        expect(last_response.body).to eql '{"some":"hash"}'
+      end
+      it 'uses custom formatter' do
+        get '/simple.custom', 'HTTP_ACCEPT' => 'application/custom'
+        expect(last_response.body).to eql '{"custom_formatter":"hash"}'
+      end
+    end
+    context 'custom formatter class' do
+      module CustomFormatter
+        def self.call(object, _env)
+          "{\"custom_formatter\":\"#{object[:some] }\"}"
+        end
+      end
+      before :each do
+        subject.content_type :json, 'application/json'
+        subject.content_type :custom, 'application/custom'
+        subject.formatter :custom, CustomFormatter
+        subject.get :simple do
+          { some: 'hash' }
+        end
+      end
+      it 'uses json' do
+        get '/simple.json'
+        expect(last_response.body).to eql '{"some":"hash"}'
+      end
+      it 'uses custom formatter' do
+        get '/simple.custom', 'HTTP_ACCEPT' => 'application/custom'
+        expect(last_response.body).to eql '{"custom_formatter":"hash"}'
+      end
+    end
+  end
+
+  describe '.parser' do
+    it 'parses data in format requested by content-type' do
+      subject.format :json
+      subject.post '/data' do
+        { x: params[:x] }
+      end
+      post '/data', '{"x":42}', 'CONTENT_TYPE' => 'application/json'
+      expect(last_response.status).to eq(201)
+      expect(last_response.body).to eq('{"x":42}')
+    end
+    context 'lambda parser' do
+      before :each do
+        subject.content_type :txt, 'text/plain'
+        subject.content_type :custom, 'text/custom'
+        subject.parser :custom, ->(object, _env) { { object.to_sym => object.to_s.reverse } }
+        subject.put :simple do
+          params[:simple]
+        end
+      end
+      ['text/custom', 'text/custom; charset=UTF-8'].each do |content_type|
+        it "uses parser for #{content_type}" do
+          put '/simple', 'simple', 'CONTENT_TYPE' => content_type
+          expect(last_response.status).to eq(200)
+          expect(last_response.body).to eql 'elpmis'
+        end
+      end
+    end
+    context 'custom parser class' do
+      module CustomParser
+        def self.call(object, _env)
+          { object.to_sym => object.to_s.reverse }
+        end
+      end
+      before :each do
+        subject.content_type :txt, 'text/plain'
+        subject.content_type :custom, 'text/custom'
+        subject.parser :custom, CustomParser
+        subject.put :simple do
+          params[:simple]
+        end
+      end
+      it 'uses custom parser' do
+        put '/simple', 'simple', 'CONTENT_TYPE' => 'text/custom'
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eql 'elpmis'
+      end
+    end
+    context 'multi_xml' do
+      it "doesn't parse yaml" do
+        subject.put :yaml do
+          params[:tag]
+        end
+        put '/yaml', '<tag type="symbol">a123</tag>', 'CONTENT_TYPE' => 'application/xml'
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eql 'Disallowed type attribute: "symbol"'
+      end
+    end
+    context 'none parser class' do
+      before :each do
+        subject.parser :json, nil
+        subject.put 'data' do
+          "body: #{env['api.request.body'] }"
+        end
+      end
+      it 'does not parse data' do
+        put '/data', 'not valid json', 'CONTENT_TYPE' => 'application/json'
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('body: not valid json')
+      end
+    end
+  end
+
+  describe '.default_format' do
+    before :each do
+      subject.format :json
+      subject.default_format :json
+    end
+    it 'returns data in default format' do
+      subject.get '/data' do
+        { x: 42 }
+      end
+      get '/data'
+      expect(last_response.status).to eq(200)
+      expect(last_response.body).to eq('{"x":42}')
+    end
+    it 'parses data in default format' do
+      subject.post '/data' do
+        { x: params[:x] }
+      end
+      post '/data', '{"x":42}', 'CONTENT_TYPE' => ''
+      expect(last_response.status).to eq(201)
+      expect(last_response.body).to eq('{"x":42}')
+    end
+  end
+
+  describe '.default_error_status' do
+    it 'allows setting default_error_status' do
+      subject.rescue_from :all
+      subject.default_error_status 200
+      subject.get '/exception' do
+        fail 'rain!'
+      end
+      get '/exception'
+      expect(last_response.status).to eql 200
+    end
+    it 'has a default error status' do
+      subject.rescue_from :all
+      subject.get '/exception' do
+        fail 'rain!'
+      end
+      get '/exception'
+      expect(last_response.status).to eql 500
+    end
+    it 'uses the default error status in error!' do
+      subject.rescue_from :all
+      subject.default_error_status 400
+      subject.get '/exception' do
+        error! 'rain!'
+      end
+      get '/exception'
+      expect(last_response.status).to eql 400
+    end
+  end
+
+  context 'http_codes' do
+    let(:error_presenter) do
+      Class.new(Grape::Entity) do
+        expose :code
+        expose :static
+
+        def static
+          'some static text'
+        end
+      end
+    end
+
+    it 'is used as presenter' do
+      subject.desc 'some desc', http_codes: [
+        [408, 'Unauthorized', error_presenter]
+      ]
+
+      subject.get '/exception' do
+        error!({ code: 408 }, 408)
+      end
+
+      get '/exception'
+      expect(last_response.status).to eql 408
+      expect(last_response.body).to eql({ code: 408, static: 'some static text' }.to_json)
+    end
+
+    it 'presented with' do
+      error = { code: 408, with: error_presenter }
+      subject.get '/exception' do
+        error! error, 408
+      end
+
+      get '/exception'
+      expect(last_response.status).to eql 408
+      expect(last_response.body).to eql({ code: 408, static: 'some static text' }.to_json)
+    end
+  end
+
+  context 'routes' do
+    describe 'empty api structure' do
+      it 'returns an empty array of routes' do
+        expect(subject.routes).to eq([])
+      end
+    end
+    describe 'single method api structure' do
+      before(:each) do
+        subject.get :ping do
+          'pong'
+        end
+      end
+      it 'returns one route' do
+        expect(subject.routes.size).to eq(1)
+        route = subject.routes[0]
+        expect(route.route_version).to be_nil
+        expect(route.route_path).to eq('/ping(.:format)')
+        expect(route.route_method).to eq('GET')
+      end
+    end
+    describe 'api structure with two versions and a namespace' do
+      before :each do
+        subject.version 'v1', using: :path
+        subject.get 'version' do
+          api.version
+        end
+        # version v2
+        subject.version 'v2', using: :path
+        subject.prefix 'p'
+        subject.namespace 'n1' do
+          namespace 'n2' do
+            get 'version' do
+              api.version
+            end
+          end
+        end
+      end
+      it 'returns the latest version set' do
+        expect(subject.version).to eq('v2')
+      end
+      it 'returns versions' do
+        expect(subject.versions).to eq(%w(v1 v2))
+      end
+      it 'sets route paths' do
+        expect(subject.routes.size).to be >= 2
+        expect(subject.routes[0].route_path).to eq('/:version/version(.:format)')
+        expect(subject.routes[1].route_path).to eq('/p/:version/n1/n2/version(.:format)')
+      end
+      it 'sets route versions' do
+        expect(subject.routes[0].route_version).to eq('v1')
+        expect(subject.routes[1].route_version).to eq('v2')
+      end
+      it 'sets a nested namespace' do
+        expect(subject.routes[1].route_namespace).to eq('/n1/n2')
+      end
+      it 'sets prefix' do
+        expect(subject.routes[1].route_prefix).to eq('p')
+      end
+    end
+    describe 'api structure with additional parameters' do
+      before(:each) do
+        subject.params do
+          requires :token, desc: 'a token'
+          optional :limit, desc: 'the limit'
+        end
+        subject.get 'split/:string' do
+          params[:string].split(params[:token], (params[:limit] || 0).to_i)
+        end
+      end
+      it 'splits a string' do
+        get '/split/a,b,c.json', token: ','
+        expect(last_response.body).to eq('["a","b","c"]')
+      end
+      it 'splits a string with limit' do
+        get '/split/a,b,c.json', token: ',', limit: '2'
+        expect(last_response.body).to eq('["a","b,c"]')
+      end
+      it 'sets route_params' do
+        expect(subject.routes.map { |route|
+          { params: route.route_params }
+        }).to eq [
+          {
+            params: {
+              'string' => '',
+              'token' => { required: true, desc: 'a token' },
+              'limit' => { required: false, desc: 'the limit' }
+            }
+          }
+        ]
+      end
+    end
+    describe 'api structure with multiple apis' do
+      before(:each) do
+        subject.params do
+          requires :one, desc: 'a token'
+          optional :two, desc: 'the limit'
+        end
+        subject.get 'one' do
+        end
+
+        subject.params do
+          requires :three, desc: 'a token'
+          optional :four, desc: 'the limit'
+        end
+        subject.get 'two' do
+        end
+      end
+      it 'sets route_params' do
+        expect(subject.routes.map { |route|
+          { params: route.route_params }
+        }).to eq [
+          {
+            params: {
+              'one' => { required: true, desc: 'a token' },
+              'two' => { required: false, desc: 'the limit' }
+            }
+          },
+          {
+            params: {
+              'three' => { required: true, desc: 'a token' },
+              'four' => { required: false, desc: 'the limit' }
+            }
+          }
+        ]
+      end
+    end
+    describe 'api structure with an api without params' do
+      before(:each) do
+        subject.params do
+          requires :one, desc: 'a token'
+          optional :two, desc: 'the limit'
+        end
+        subject.get 'one' do
+        end
+
+        subject.get 'two' do
+        end
+      end
+      it 'sets route_params' do
+        expect(subject.routes.map { |route|
+          { params: route.route_params }
+        }).to eq [
+          {
+            params: {
+              'one' => { required: true, desc: 'a token' },
+              'two' => { required: false, desc: 'the limit' }
+            }
+          },
+          {
+            params: {}
+          }
+        ]
+      end
+    end
+    describe 'api with a custom route setting' do
+      before(:each) do
+        subject.route_setting :custom, key: 'value'
+        subject.get 'one'
+      end
+      it 'exposed' do
+        expect(subject.routes.count).to eq 1
+        route = subject.routes.first
+        expect(route.route_settings[:custom]).to eq(key: 'value')
+      end
+    end
+    describe 'status' do
+      it 'can be set to arbitrary Fixnum value' do
+        subject.get '/foo' do
+          status 210
+        end
+        get '/foo'
+        expect(last_response.status).to eq 210
+      end
+      it 'can be set with a status code symbol' do
+        subject.get '/foo' do
+          status :see_other
+        end
+        get '/foo'
+        expect(last_response.status).to eq 303
+      end
+    end
+  end
+
+  context 'desc' do
+    it 'empty array of routes' do
+      expect(subject.routes).to eq([])
+    end
+    it 'empty array of routes' do
+      subject.desc 'grape api'
+      expect(subject.routes).to eq([])
+    end
+    it 'describes a method' do
+      subject.desc 'first method'
+      subject.get :first do; end
+      expect(subject.routes.length).to eq(1)
+      route = subject.routes.first
+      expect(route.route_description).to eq('first method')
+      expect(route.route_foo).to be_nil
+      expect(route.route_params).to eq({})
+    end
+    it 'describes methods separately' do
+      subject.desc 'first method'
+      subject.get :first do; end
+      subject.desc 'second method'
+      subject.get :second do; end
+      expect(subject.routes.count).to eq(2)
+      expect(subject.routes.map { |route|
+        { description: route.route_description, params: route.route_params }
+      }).to eq [
+        { description: 'first method', params: {} },
+        { description: 'second method', params: {} }
+      ]
+    end
+    it 'resets desc' do
+      subject.desc 'first method'
+      subject.get :first do; end
+      subject.get :second do; end
+      expect(subject.routes.map { |route|
+        { description: route.route_description, params: route.route_params }
+      }).to eq [
+        { description: 'first method', params: {} },
+        { description: nil, params: {} }
+      ]
+    end
+    it 'namespaces and describe arbitrary parameters' do
+      subject.namespace 'ns' do
+        desc 'ns second', foo: 'bar'
+        get 'second' do; end
+      end
+      expect(subject.routes.map { |route|
+        { description: route.route_description, foo: route.route_foo, params: route.route_params }
+      }).to eq [
+        { description: 'ns second', foo: 'bar', params: {} }
+      ]
+    end
+    it 'includes details' do
+      subject.desc 'method', details: 'method details'
+      subject.get 'method' do; end
+      expect(subject.routes.map { |route|
+        { description: route.route_description, details: route.route_details, params: route.route_params }
+      }).to eq [
+        { description: 'method', details: 'method details', params: {} }
+      ]
+    end
+    it 'describes a method with parameters' do
+      subject.desc 'Reverses a string.', params: { 's' => { desc: 'string to reverse', type: 'string' } }
+      subject.get 'reverse' do
+        params[:s].reverse
+      end
+      expect(subject.routes.map { |route|
+        { description: route.route_description, params: route.route_params }
+      }).to eq [
+        { description: 'Reverses a string.', params: { 's' => { desc: 'string to reverse', type: 'string' } } }
+      ]
+    end
+    it 'merges the parameters of the namespace with the parameters of the method' do
+      subject.desc 'namespace'
+      subject.params do
+        requires :ns_param, desc: 'namespace parameter'
+      end
+      subject.namespace 'ns' do
+        desc 'method'
+        params do
+          optional :method_param, desc: 'method parameter'
+        end
+        get 'method' do; end
+      end
+
+      routes_doc = subject.routes.map { |route|
+        { description: route.route_description, params: route.route_params }
+      }
+      expect(routes_doc).to eq [
+        { description: 'method',
+          params: {
+            'ns_param' => { required: true, desc: 'namespace parameter' },
+            'method_param' => { required: false, desc: 'method parameter' }
+          }
+        }
+      ]
+    end
+    it 'merges the parameters of nested namespaces' do
+      subject.desc 'ns1'
+      subject.params do
+        optional :ns_param, desc: 'ns param 1'
+        requires :ns1_param, desc: 'ns1 param'
+      end
+      subject.namespace 'ns1' do
+        desc 'ns2'
+        params do
+          requires :ns_param, desc: 'ns param 2'
+          requires :ns2_param, desc: 'ns2 param'
+        end
+        namespace 'ns2' do
+          desc 'method'
+          params do
+            optional :method_param, desc: 'method param'
+          end
+          get 'method' do; end
+        end
+      end
+      expect(subject.routes.map { |route|
+        { description: route.route_description, params: route.route_params }
+      }).to eq [
+        { description: 'method',
+          params: {
+            'ns_param' => { required: true, desc: 'ns param 2' },
+            'ns1_param' => { required: true, desc: 'ns1 param' },
+            'ns2_param' => { required: true, desc: 'ns2 param' },
+            'method_param' => { required: false, desc: 'method param' }
+          }
+        }
+      ]
+    end
+    it 'groups nested params and prevents overwriting of params with same name in different groups' do
+      subject.desc 'method'
+      subject.params do
+        group :group1, type: Array do
+          optional :param1, desc: 'group1 param1 desc'
+          requires :param2, desc: 'group1 param2 desc'
+        end
+        group :group2, type: Array do
+          optional :param1, desc: 'group2 param1 desc'
+          requires :param2, desc: 'group2 param2 desc'
+        end
+      end
+      subject.get 'method' do; end
+
+      expect(subject.routes.map(&:route_params)).to eq [{
+        'group1'         => { required: true, type: 'Array' },
+        'group1[param1]' => { required: false, desc: 'group1 param1 desc' },
+        'group1[param2]' => { required: true, desc: 'group1 param2 desc' },
+        'group2'         => { required: true, type: 'Array' },
+        'group2[param1]' => { required: false, desc: 'group2 param1 desc' },
+        'group2[param2]' => { required: true, desc: 'group2 param2 desc' }
+      }]
+    end
+    it 'uses full name of parameters in nested groups' do
+      subject.desc 'nesting'
+      subject.params do
+        requires :root_param, desc: 'root param'
+        group :nested, type: Array do
+          requires :nested_param, desc: 'nested param'
+        end
+      end
+      subject.get 'method' do; end
+      expect(subject.routes.map { |route|
+        { description: route.route_description, params: route.route_params }
+      }).to eq [
+        { description: 'nesting',
+          params: {
+            'root_param' => { required: true, desc: 'root param' },
+            'nested' => { required: true, type: 'Array' },
+            'nested[nested_param]' => { required: true, desc: 'nested param' }
+          }
+        }
+      ]
+    end
+    it 'allows to set the type attribute on :group element' do
+      subject.params do
+        group :foo, type: Array do
+          optional :bar
+        end
+      end
+    end
+    it 'parses parameters when no description is given' do
+      subject.params do
+        requires :one_param, desc: 'one param'
+      end
+      subject.get 'method' do; end
+      expect(subject.routes.map { |route|
+        { description: route.route_description, params: route.route_params }
+      }).to eq [
+        { description: nil, params: { 'one_param' => { required: true, desc: 'one param' } } }
+      ]
+    end
+    it 'does not symbolize params' do
+      subject.desc 'Reverses a string.', params: { 's' => { desc: 'string to reverse', type: 'string' } }
+      subject.get 'reverse/:s' do
+        params[:s].reverse
+      end
+      expect(subject.routes.map { |route|
+        { description: route.route_description, params: route.route_params }
+      }).to eq [
+        { description: 'Reverses a string.', params: { 's' => { desc: 'string to reverse', type: 'string' } } }
+      ]
+    end
+  end
+
+  describe '.mount' do
+    let(:mounted_app) { ->(_env) { [200, {}, ['MOUNTED']] } }
+
+    context 'with a bare rack app' do
+      before do
+        subject.mount mounted_app => '/mounty'
+      end
+
+      it 'makes a bare Rack app available at the endpoint' do
+        get '/mounty'
+        expect(last_response.body).to eq('MOUNTED')
+      end
+
+      it 'anchors the routes, passing all subroutes to it' do
+        get '/mounty/awesome'
+        expect(last_response.body).to eq('MOUNTED')
+      end
+
+      it 'is able to cascade' do
+        subject.mount lambda { |env|
+          headers = {}
+          headers['X-Cascade'] == 'pass' unless env['PATH_INFO'].include?('boo')
+          [200, headers, ['Farfegnugen']]
+        } => '/'
+
+        get '/boo'
+        expect(last_response.body).to eq('Farfegnugen')
+        get '/mounty'
+        expect(last_response.body).to eq('MOUNTED')
+      end
+    end
+
+    context 'without a hash' do
+      it 'calls through setting the route to "/"' do
+        subject.mount mounted_app
+        get '/'
+        expect(last_response.body).to eq('MOUNTED')
+      end
+    end
+
+    context 'mounting an API' do
+      it 'applies the settings of the mounting api' do
+        subject.version 'v1', using: :path
+
+        subject.namespace :cool do
+          app = Class.new(Grape::API)
+          app.get('/awesome') do
+            'yo'
+          end
+
+          mount app
+        end
+
+        get '/v1/cool/awesome'
+        expect(last_response.body).to eq('yo')
+      end
+
+      it 'applies the settings to nested mounted apis' do
+        subject.version 'v1', using: :path
+
+        subject.namespace :cool do
+          inner_app = Class.new(Grape::API)
+          inner_app.get('/awesome') do
+            'yo'
+          end
+
+          app = Class.new(Grape::API)
+          app.mount inner_app
+          mount app
+        end
+
+        get '/v1/cool/awesome'
+        expect(last_response.body).to eq('yo')
+      end
+
+      it 'inherits rescues even when some defined by mounted' do
+        subject.rescue_from :all do |e|
+          rack_response("rescued from #{e.message}", 202)
+        end
+
+        app = Class.new(Grape::API)
+
+        subject.namespace :mounted do
+          app.rescue_from ArgumentError
+          app.get('/fail') { fail 'doh!' }
+          mount app
+        end
+
+        get '/mounted/fail'
+        expect(last_response.status).to eql 202
+        expect(last_response.body).to eq('rescued from doh!')
+      end
+
+      it 'collects the routes of the mounted api' do
+        subject.namespace :cool do
+          app = Class.new(Grape::API)
+          app.get('/awesome') {}
+          app.post('/sauce') {}
+          mount app
+        end
+        expect(subject.routes.size).to eq(2)
+        expect(subject.routes.first.route_path).to match(%r{\/cool\/awesome})
+        expect(subject.routes.last.route_path).to match(%r{\/cool\/sauce})
+      end
+
+      it 'mounts on a path' do
+        subject.namespace :cool do
+          app = Class.new(Grape::API)
+          app.get '/awesome' do
+            'sauce'
+          end
+          mount app => '/mounted'
+        end
+        get '/mounted/cool/awesome'
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('sauce')
+      end
+
+      it 'mounts on a nested path' do
+        APP1 = Class.new(Grape::API)
+        APP2 = Class.new(Grape::API)
+        APP2.get '/nice' do
+          'play'
+        end
+        # note that the reverse won't work, mount from outside-in
+        APP3 = subject
+        APP3.mount APP1 => '/app1'
+        APP1.mount APP2 => '/app2'
+        get '/app1/app2/nice'
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('play')
+        options '/app1/app2/nice'
+        expect(last_response.status).to eq(204)
+      end
+
+      it 'responds to options' do
+        app = Class.new(Grape::API)
+        app.get '/colour' do
+          'red'
+        end
+        app.namespace :pears do
+          get '/colour' do
+            'green'
+          end
+        end
+        subject.namespace :apples do
+          mount app
+        end
+
+        get '/apples/colour'
+        expect(last_response.status).to eql 200
+        expect(last_response.body).to eq('red')
+        options '/apples/colour'
+        expect(last_response.status).to eql 204
+        get '/apples/pears/colour'
+        expect(last_response.status).to eql 200
+        expect(last_response.body).to eq('green')
+        options '/apples/pears/colour'
+        expect(last_response.status).to eql 204
+      end
+
+      it 'responds to options with path versioning' do
+        subject.version 'v1', using: :path
+        subject.namespace :apples do
+          app = Class.new(Grape::API)
+          app.get('/colour') do
+            'red'
+          end
+          mount app
+        end
+
+        get '/v1/apples/colour'
+        expect(last_response.status).to eql 200
+        expect(last_response.body).to eq('red')
+        options '/v1/apples/colour'
+        expect(last_response.status).to eql 204
+      end
+
+      it 'mounts a versioned API with nested resources' do
+        api = Class.new(Grape::API) do
+          version 'v1'
+          resources :users do
+            get :hello do
+              'hello users'
+            end
+          end
+        end
+        subject.mount api
+
+        get '/v1/users/hello'
+        expect(last_response.body).to eq('hello users')
+      end
+
+      it 'mounts a prefixed API with nested resources' do
+        api = Class.new(Grape::API) do
+          prefix 'api'
+          resources :users do
+            get :hello do
+              'hello users'
+            end
+          end
+        end
+        subject.mount api
+
+        get '/api/users/hello'
+        expect(last_response.body).to eq('hello users')
+      end
+
+      it 'applies format to a mounted API with nested resources' do
+        api = Class.new(Grape::API) do
+          format :json
+          resources :users do
+            get do
+              { users: true }
+            end
+          end
+        end
+        subject.mount api
+
+        get '/users'
+        expect(last_response.body).to eq({ users: true }.to_json)
+      end
+
+      it 'applies auth to a mounted API with nested resources' do
+        api = Class.new(Grape::API) do
+          format :json
+          http_basic do |username, password|
+            username == 'username' && password == 'password'
+          end
+          resources :users do
+            get do
+              { users: true }
+            end
+          end
+        end
+        subject.mount api
+
+        get '/users'
+        expect(last_response.status).to eq(401)
+
+        get '/users', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('username', 'password')
+        expect(last_response.body).to eq({ users: true }.to_json)
+      end
+
+      it 'mounts multiple versioned APIs with nested resources' do
+        api1 = Class.new(Grape::API) do
+          version 'one', using: :header, vendor: 'test'
+          resources :users do
+            get :hello do
+              'one'
+            end
+          end
+        end
+
+        api2 = Class.new(Grape::API) do
+          version 'two', using: :header, vendor: 'test'
+          resources :users do
+            get :hello do
+              'two'
+            end
+          end
+        end
+
+        subject.mount api1
+        subject.mount api2
+
+        versioned_get '/users/hello', 'one', using: :header, vendor: 'test'
+        expect(last_response.body).to eq('one')
+        versioned_get '/users/hello', 'two', using: :header, vendor: 'test'
+        expect(last_response.body).to eq('two')
+      end
+    end
+  end
+
+  describe '.endpoints' do
+    it 'adds one for each route created' do
+      subject.get '/'
+      subject.post '/'
+      expect(subject.endpoints.size).to eq(2)
+    end
+  end
+
+  describe '.compile' do
+    it 'sets the instance' do
+      expect(subject.instance).to be_nil
+      subject.compile
+      expect(subject.instance).to be_kind_of(subject)
+    end
+  end
+
+  describe '.change!' do
+    it 'invalidates any compiled instance' do
+      subject.compile
+      subject.change!
+      expect(subject.instance).to be_nil
+    end
+  end
+
+  describe '.endpoint' do
+    before(:each) do
+      subject.format :json
+      subject.get '/endpoint/options' do
+        {
+          path: options[:path],
+          source_location: source.source_location
+        }
+      end
+    end
+    it 'path' do
+      get '/endpoint/options'
+      options = MultiJson.load(last_response.body)
+      expect(options['path']).to eq(['/endpoint/options'])
+      expect(options['source_location'][0]).to include 'api_spec.rb'
+      expect(options['source_location'][1].to_i).to be > 0
+    end
+  end
+
+  describe '.route' do
+    context 'plain' do
+      before(:each) do
+        subject.get '/' do
+          route.route_path
+        end
+        subject.get '/path' do
+          route.route_path
+        end
+      end
+      it 'provides access to route info' do
+        get '/'
+        expect(last_response.body).to eq('/(.:format)')
+        get '/path'
+        expect(last_response.body).to eq('/path(.:format)')
+      end
+    end
+    context 'with desc' do
+      before(:each) do
+        subject.desc 'returns description'
+        subject.get '/description' do
+          route.route_description
+        end
+        subject.desc 'returns parameters', params: { 'x' => 'y' }
+        subject.get '/params/:id' do
+          route.route_params[params[:id]]
+        end
+      end
+      it 'returns route description' do
+        get '/description'
+        expect(last_response.body).to eq('returns description')
+      end
+      it 'returns route parameters' do
+        get '/params/x'
+        expect(last_response.body).to eq('y')
+      end
+    end
+  end
+  describe '.format' do
+    context ':txt' do
+      before(:each) do
+        subject.format :txt
+        subject.content_type :json, 'application/json'
+        subject.get '/meaning_of_life' do
+          { meaning_of_life: 42 }
+        end
+      end
+      it 'forces txt without an extension' do
+        get '/meaning_of_life'
+        expect(last_response.body).to eq({ meaning_of_life: 42 }.to_s)
+      end
+      it 'does not force txt with an extension' do
+        get '/meaning_of_life.json'
+        expect(last_response.body).to eq({ meaning_of_life: 42 }.to_json)
+      end
+      it 'forces txt from a non-accepting header' do
+        get '/meaning_of_life', {}, 'HTTP_ACCEPT' => 'application/json'
+        expect(last_response.body).to eq({ meaning_of_life: 42 }.to_s)
+      end
+    end
+    context ':txt only' do
+      before(:each) do
+        subject.format :txt
+        subject.get '/meaning_of_life' do
+          { meaning_of_life: 42 }
+        end
+      end
+      it 'forces txt without an extension' do
+        get '/meaning_of_life'
+        expect(last_response.body).to eq({ meaning_of_life: 42 }.to_s)
+      end
+      it 'accepts specified extension' do
+        get '/meaning_of_life.txt'
+        expect(last_response.body).to eq({ meaning_of_life: 42 }.to_s)
+      end
+      it 'does not accept extensions other than specified' do
+        get '/meaning_of_life.json'
+        expect(last_response.status).to eq(404)
+      end
+      it 'forces txt from a non-accepting header' do
+        get '/meaning_of_life', {}, 'HTTP_ACCEPT' => 'application/json'
+        expect(last_response.body).to eq({ meaning_of_life: 42 }.to_s)
+      end
+    end
+    context ':json' do
+      before(:each) do
+        subject.format :json
+        subject.content_type :txt, 'text/plain'
+        subject.get '/meaning_of_life' do
+          { meaning_of_life: 42 }
+        end
+      end
+      it 'forces json without an extension' do
+        get '/meaning_of_life'
+        expect(last_response.body).to eq({ meaning_of_life: 42 }.to_json)
+      end
+      it 'does not force json with an extension' do
+        get '/meaning_of_life.txt'
+        expect(last_response.body).to eq({ meaning_of_life: 42 }.to_s)
+      end
+      it 'forces json from a non-accepting header' do
+        get '/meaning_of_life', {}, 'HTTP_ACCEPT' => 'text/html'
+        expect(last_response.body).to eq({ meaning_of_life: 42 }.to_json)
+      end
+      it 'can be overwritten with an explicit content type' do
+        subject.get '/meaning_of_life_with_content_type' do
+          content_type 'text/plain'
+          { meaning_of_life: 42 }.to_s
+        end
+        get '/meaning_of_life_with_content_type'
+        expect(last_response.body).to eq({ meaning_of_life: 42 }.to_s)
+      end
+      it 'raised :error from middleware' do
+        middleware = Class.new(Grape::Middleware::Base) do
+          def before
+            throw :error, message: 'Unauthorized', status: 42
+          end
+        end
+        subject.use middleware
+        subject.get do
+        end
+        get '/'
+        expect(last_response.status).to eq(42)
+        expect(last_response.body).to eq({ error: 'Unauthorized' }.to_json)
+      end
+    end
+    context ':serializable_hash' do
+      before(:each) do
+        class SerializableHashExample
+          def serializable_hash
+            { abc: 'def' }
+          end
+        end
+        subject.format :serializable_hash
+      end
+      it 'instance' do
+        subject.get '/example' do
+          SerializableHashExample.new
+        end
+        get '/example'
+        expect(last_response.body).to eq('{"abc":"def"}')
+      end
+      it 'root' do
+        subject.get '/example' do
+          { 'root' => SerializableHashExample.new }
+        end
+        get '/example'
+        expect(last_response.body).to eq('{"root":{"abc":"def"}}')
+      end
+      it 'array' do
+        subject.get '/examples' do
+          [SerializableHashExample.new, SerializableHashExample.new]
+        end
+        get '/examples'
+        expect(last_response.body).to eq('[{"abc":"def"},{"abc":"def"}]')
+      end
+    end
+    context ':xml' do
+      before(:each) do
+        subject.format :xml
+      end
+      it 'string' do
+        subject.get '/example' do
+          'example'
+        end
+        get '/example'
+        expect(last_response.status).to eq(500)
+        expect(last_response.body).to eq <<-XML
+<?xml version="1.0" encoding="UTF-8"?>
+<error>
+  <message>cannot convert String to xml</message>
+</error>
+XML
+      end
+      it 'hash' do
+        subject.get '/example' do
+          ActiveSupport::OrderedHash[
+            :example1, 'example1',
+            :example2, 'example2'
+        ]
+        end
+        get '/example'
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq <<-XML
+<?xml version="1.0" encoding="UTF-8"?>
+<hash>
+  <example1>example1</example1>
+  <example2>example2</example2>
+</hash>
+XML
+      end
+      it 'array' do
+        subject.get '/example' do
+          %w(example1 example2)
+        end
+        get '/example'
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq <<-XML
+<?xml version="1.0" encoding="UTF-8"?>
+<strings type="array">
+  <string>example1</string>
+  <string>example2</string>
+</strings>
+XML
+      end
+      it 'raised :error from middleware' do
+        middleware = Class.new(Grape::Middleware::Base) do
+          def before
+            throw :error, message: 'Unauthorized', status: 42
+          end
+        end
+        subject.use middleware
+        subject.get do
+        end
+        get '/'
+        expect(last_response.status).to eq(42)
+        expect(last_response.body).to eq <<-XML
+<?xml version="1.0" encoding="UTF-8"?>
+<error>
+  <message>Unauthorized</message>
+</error>
+XML
+      end
+    end
+  end
+
+  context 'catch-all' do
+    before do
+      api1 = Class.new(Grape::API)
+      api1.version 'v1', using: :path
+      api1.get 'hello' do
+        'v1'
+      end
+      api2 = Class.new(Grape::API)
+      api2.version 'v2', using: :path
+      api2.get 'hello' do
+        'v2'
+      end
+      subject.mount api1
+      subject.mount api2
+    end
+    [true, false].each do |anchor|
+      it "anchor=#{anchor}" do
+        subject.route :any, '*path', anchor: anchor do
+          error!("Unrecognized request path: #{params[:path] } - #{env['PATH_INFO'] }#{env['SCRIPT_NAME'] }", 404)
+        end
+        get '/v1/hello'
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('v1')
+        get '/v2/hello'
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('v2')
+        get '/foobar'
+        expect(last_response.status).to eq(404)
+        expect(last_response.body).to eq('Unrecognized request path: foobar - /foobar')
+      end
+    end
+  end
+
+  context 'cascading' do
+    context 'via version' do
+      it 'cascades' do
+        subject.version 'v1', using: :path, cascade: true
+        get '/v1/hello'
+        expect(last_response.status).to eq(404)
+        expect(last_response.headers['X-Cascade']).to eq('pass')
+      end
+      it 'does not cascade' do
+        subject.version 'v2', using: :path, cascade: false
+        get '/v2/hello'
+        expect(last_response.status).to eq(404)
+        expect(last_response.headers.keys).not_to include 'X-Cascade'
+      end
+    end
+    context 'via endpoint' do
+      it 'cascades' do
+        subject.cascade true
+        get '/hello'
+        expect(last_response.status).to eq(404)
+        expect(last_response.headers['X-Cascade']).to eq('pass')
+      end
+      it 'does not cascade' do
+        subject.cascade false
+        get '/hello'
+        expect(last_response.status).to eq(404)
+        expect(last_response.headers.keys).not_to include 'X-Cascade'
+      end
+    end
+  end
+
+  context 'with json default_error_formatter' do
+    it 'returns json error' do
+      subject.content_type :json, 'application/json'
+      subject.default_error_formatter :json
+      subject.get '/something' do
+        'foo'
+      end
+      get '/something'
+      expect(last_response.status).to eq(406)
+      expect(last_response.body).to eq("{\"error\":\"The requested format 'txt' is not supported.\"}")
+    end
+  end
+
+  context 'body' do
+    context 'false' do
+      before do
+        subject.get '/blank' do
+          body false
+        end
+      end
+      it 'returns blank body' do
+        get '/blank'
+        expect(last_response.status).to eq(204)
+        expect(last_response.body).to be_blank
+      end
+    end
+    context 'plain text' do
+      before do
+        subject.get '/text' do
+          content_type 'text/plain'
+          body 'Hello World'
+          'ignored'
+        end
+      end
+      it 'returns blank body' do
+        get '/text'
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq 'Hello World'
+      end
+    end
+  end
+end
diff --git a/spec/grape/dsl/callbacks_spec.rb b/spec/grape/dsl/callbacks_spec.rb
new file mode 100644
index 0000000..db3fb79
--- /dev/null
+++ b/spec/grape/dsl/callbacks_spec.rb
@@ -0,0 +1,44 @@
+require 'spec_helper'
+
+module Grape
+  module DSL
+    module CallbacksSpec
+      class Dummy
+        include Grape::DSL::Callbacks
+      end
+    end
+
+    describe Callbacks do
+      subject { Class.new(CallbacksSpec::Dummy) }
+      let(:proc) { ->() {} }
+
+      describe '.before' do
+        it 'adds a block to "before"' do
+          expect(subject).to receive(:namespace_stackable).with(:befores, proc)
+          subject.before(&proc)
+        end
+      end
+
+      describe '.before_validation' do
+        it 'adds a block to "before_validation"' do
+          expect(subject).to receive(:namespace_stackable).with(:before_validations, proc)
+          subject.before_validation(&proc)
+        end
+      end
+
+      describe '.after_validation' do
+        it 'adds a block to "after_validation"' do
+          expect(subject).to receive(:namespace_stackable).with(:after_validations, proc)
+          subject.after_validation(&proc)
+        end
+      end
+
+      describe '.after' do
+        it 'adds a block to "after"' do
+          expect(subject).to receive(:namespace_stackable).with(:afters, proc)
+          subject.after(&proc)
+        end
+      end
+    end
+  end
+end
diff --git a/spec/grape/dsl/configuration_spec.rb b/spec/grape/dsl/configuration_spec.rb
new file mode 100644
index 0000000..f585d12
--- /dev/null
+++ b/spec/grape/dsl/configuration_spec.rb
@@ -0,0 +1,76 @@
+require 'spec_helper'
+
+module Grape
+  module DSL
+    module ConfigurationSpec
+      class Dummy
+        include Grape::DSL::Configuration
+      end
+    end
+    describe Configuration do
+      subject { Class.new(ConfigurationSpec::Dummy) }
+      let(:logger) { double(:logger) }
+
+      describe '.logger' do
+        it 'sets a logger' do
+          subject.logger logger
+          expect(subject.logger).to eq logger
+        end
+
+        it 'returns a logger' do
+          expect(subject.logger logger).to eq logger
+        end
+      end
+
+      describe '.desc' do
+        it 'sets a description' do
+          desc_text = 'The description'
+          options = { message: 'none' }
+          subject.desc desc_text, options
+          expect(subject.namespace_setting(:description)).to eq(options.merge(description: desc_text))
+          expect(subject.route_setting(:description)).to eq(options.merge(description: desc_text))
+        end
+
+        it 'can be set with a block' do
+          expected_options = {
+            description: 'The description',
+            detail: 'more details',
+            params: { first: :param },
+            entity: Object,
+            http_codes: [[401, 'Unauthorized', 'Entities::Error']],
+            named: 'My named route',
+            headers: [XAuthToken: {
+              description: 'Valdates your identity',
+              required: true
+            },
+                      XOptionalHeader: {
+                        description: 'Not really needed',
+                        required: false
+                      }
+                     ]
+          }
+
+          subject.desc 'The description' do
+            detail 'more details'
+            params(first: :param)
+            success Object
+            failure [[401, 'Unauthorized', 'Entities::Error']]
+            named 'My named route'
+            headers [XAuthToken: {
+              description: 'Valdates your identity',
+              required: true
+            },
+                     XOptionalHeader: {
+                       description: 'Not really needed',
+                       required: false
+                     }
+                    ]
+          end
+
+          expect(subject.namespace_setting(:description)).to eq(expected_options)
+          expect(subject.route_setting(:description)).to eq(expected_options)
+        end
+      end
+    end
+  end
+end
diff --git a/spec/grape/dsl/helpers_spec.rb b/spec/grape/dsl/helpers_spec.rb
new file mode 100644
index 0000000..ada0aef
--- /dev/null
+++ b/spec/grape/dsl/helpers_spec.rb
@@ -0,0 +1,47 @@
+require 'spec_helper'
+
+module Grape
+  module DSL
+    module HelpersSpec
+      class Dummy
+        include Grape::DSL::Helpers
+
+        # rubocop:disable TrivialAccessors
+        def self.mod
+          namespace_stackable(:helpers).first
+        end
+        # rubocop:enable TrivialAccessors
+      end
+    end
+    describe Helpers do
+      subject { Class.new(HelpersSpec::Dummy) }
+      let(:proc) do
+        lambda do |*|
+          def test
+            :test
+          end
+        end
+      end
+
+      describe '.helpers' do
+        it 'adds a module with the given block' do
+          expect(subject).to receive(:namespace_stackable).with(:helpers, kind_of(Grape::DSL::Helpers::BaseHelper)).and_call_original
+          expect(subject).to receive(:namespace_stackable).with(:helpers).and_call_original
+          subject.helpers(&proc)
+
+          expect(subject.mod.instance_methods).to include(:test)
+        end
+
+        it 'uses provided modules' do
+          mod = Module.new
+
+          expect(subject).to receive(:namespace_stackable).with(:helpers, kind_of(Grape::DSL::Helpers::BaseHelper)).and_call_original
+          expect(subject).to receive(:namespace_stackable).with(:helpers).and_call_original
+          subject.helpers(mod, &proc)
+
+          expect(subject.mod).to eq mod
+        end
+      end
+    end
+  end
+end
diff --git a/spec/grape/dsl/inside_route_spec.rb b/spec/grape/dsl/inside_route_spec.rb
new file mode 100644
index 0000000..88cb7bb
--- /dev/null
+++ b/spec/grape/dsl/inside_route_spec.rb
@@ -0,0 +1,317 @@
+require 'spec_helper'
+
+module Grape
+  module DSL
+    module InsideRouteSpec
+      class Dummy
+        include Grape::DSL::InsideRoute
+
+        attr_reader :env, :request, :new_settings
+
+        def initialize
+          @env = {}
+          @header = {}
+          @new_settings = { namespace_inheritable: {}, namespace_stackable: {} }
+        end
+      end
+    end
+
+    describe Endpoint do
+      subject { InsideRouteSpec::Dummy.new }
+
+      describe '#version' do
+        it 'defaults to nil' do
+          expect(subject.version).to be nil
+        end
+
+        it 'returns env[api.version]' do
+          subject.env['api.version'] = 'dummy'
+          expect(subject.version).to eq 'dummy'
+        end
+      end
+
+      describe '#error!' do
+        it 'throws :error' do
+          expect { subject.error! 'Not Found', 404 }.to throw_symbol(:error)
+        end
+
+        describe 'thrown' do
+          before do
+            catch(:error) { subject.error! 'Not Found', 404 }
+          end
+          it 'sets status' do
+            expect(subject.status).to eq 404
+          end
+        end
+
+        describe 'default_error_status' do
+          before do
+            subject.namespace_inheritable(:default_error_status, 500)
+            catch(:error) { subject.error! 'Unknown' }
+          end
+          it 'sets status to default_error_status' do
+            expect(subject.status).to eq 500
+          end
+        end
+
+        # self.status(status || settings[:default_error_status])
+        # throw :error, message: message, status: self.status, headers: headers
+      end
+
+      describe '#redirect' do
+        describe 'default' do
+          before do
+            subject.redirect '/'
+          end
+
+          it 'sets status to 302' do
+            expect(subject.status).to eq 302
+          end
+
+          it 'sets location header' do
+            expect(subject.header['Location']).to eq '/'
+          end
+        end
+
+        describe 'permanent' do
+          before do
+            subject.redirect '/', permanent: true
+          end
+
+          it 'sets status to 301' do
+            expect(subject.status).to eq 301
+          end
+
+          it 'sets location header' do
+            expect(subject.header['Location']).to eq '/'
+          end
+        end
+      end
+
+      describe '#status' do
+        %w(GET PUT DELETE OPTIONS).each do |method|
+          it 'defaults to 200 on GET' do
+            request = Grape::Request.new(Rack::MockRequest.env_for('/', method: method))
+            expect(subject).to receive(:request).and_return(request)
+            expect(subject.status).to eq 200
+          end
+        end
+
+        it 'defaults to 201 on POST' do
+          request = Grape::Request.new(Rack::MockRequest.env_for('/', method: 'POST'))
+          expect(subject).to receive(:request).and_return(request)
+          expect(subject.status).to eq 201
+        end
+
+        it 'returns status set' do
+          subject.status 501
+          expect(subject.status).to eq 501
+        end
+
+        it 'accepts symbol for status' do
+          subject.status :see_other
+          expect(subject.status).to eq 303
+        end
+
+        it 'raises error if unknow symbol is passed' do
+          expect { subject.status :foo_bar }
+            .to raise_error(ArgumentError, 'Status code :foo_bar is invalid.')
+        end
+
+        it 'accepts unknown Fixnum status codes' do
+          expect { subject.status 210 }.to_not raise_error
+        end
+
+        it 'raises error if status is not a fixnum or symbol' do
+          expect { subject.status Object.new }
+            .to raise_error(ArgumentError, 'Status code must be Fixnum or Symbol.')
+        end
+      end
+
+      describe '#header' do
+        describe 'set' do
+          before do
+            subject.header 'Name', 'Value'
+          end
+
+          it 'returns value' do
+            expect(subject.header['Name']).to eq 'Value'
+            expect(subject.header('Name')).to eq 'Value'
+          end
+        end
+
+        it 'returns nil' do
+          expect(subject.header['Name']).to be nil
+          expect(subject.header('Name')).to be nil
+        end
+      end
+
+      describe '#content_type' do
+        describe 'set' do
+          before do
+            subject.content_type 'text/plain'
+          end
+
+          it 'returns value' do
+            expect(subject.content_type).to eq 'text/plain'
+          end
+        end
+
+        it 'returns default' do
+          expect(subject.content_type).to be nil
+        end
+      end
+
+      describe '#cookies' do
+        it 'returns an instance of Cookies' do
+          expect(subject.cookies).to be_a Grape::Cookies
+        end
+      end
+
+      describe '#body' do
+        describe 'set' do
+          before do
+            subject.body 'body'
+          end
+
+          it 'returns value' do
+            expect(subject.body).to eq 'body'
+          end
+        end
+
+        describe 'false' do
+          before do
+            subject.body false
+          end
+
+          it 'sets status to 204' do
+            expect(subject.body).to eq ''
+            expect(subject.status).to eq 204
+          end
+        end
+
+        it 'returns default' do
+          expect(subject.body).to be nil
+        end
+      end
+
+      describe '#file' do
+        describe 'set' do
+          before do
+            subject.file 'file'
+          end
+
+          it 'returns value' do
+            expect(subject.file).to eq 'file'
+          end
+        end
+
+        it 'returns default' do
+          expect(subject.file).to be nil
+        end
+      end
+
+      describe '#route' do
+        before do
+          subject.env['rack.routing_args'] = {}
+          subject.env['rack.routing_args'][:route_info] = 'dummy'
+        end
+
+        it 'returns route_info' do
+          expect(subject.route).to eq 'dummy'
+        end
+      end
+
+      describe '#present' do
+        # see entity_spec.rb for entity representation spec coverage
+
+        describe 'dummy' do
+          before do
+            subject.present 'dummy'
+          end
+
+          it 'presents dummy object' do
+            expect(subject.body).to eq 'dummy'
+          end
+        end
+
+        describe 'with' do
+          describe 'entity' do
+            let(:entity_mock) do
+              entity_mock = Object.new
+              allow(entity_mock).to receive(:represent).and_return('dummy')
+              entity_mock
+            end
+
+            describe 'instance' do
+              before do
+                subject.present 'dummy', with: entity_mock
+              end
+
+              it 'presents dummy object' do
+                expect(subject.body).to eq 'dummy'
+              end
+            end
+          end
+        end
+
+        describe 'multiple entities' do
+          let(:entity_mock1) do
+            entity_mock1 = Object.new
+            allow(entity_mock1).to receive(:represent).and_return(dummy1: 'dummy1')
+            entity_mock1
+          end
+
+          let(:entity_mock2) do
+            entity_mock2 = Object.new
+            allow(entity_mock2).to receive(:represent).and_return(dummy2: 'dummy2')
+            entity_mock2
+          end
+
+          describe 'instance' do
+            before do
+              subject.present 'dummy1', with: entity_mock1
+              subject.present 'dummy2', with: entity_mock2
+            end
+
+            it 'presents both dummy objects' do
+              expect(subject.body[:dummy1]).to eq 'dummy1'
+              expect(subject.body[:dummy2]).to eq 'dummy2'
+            end
+          end
+        end
+
+        describe 'non mergeable entity' do
+          let(:entity_mock1) do
+            entity_mock1 = Object.new
+            allow(entity_mock1).to receive(:represent).and_return(dummy1: 'dummy1')
+            entity_mock1
+          end
+
+          let(:entity_mock2) do
+            entity_mock2 = Object.new
+            allow(entity_mock2).to receive(:represent).and_return('not a hash')
+            entity_mock2
+          end
+
+          describe 'instance' do
+            it 'fails' do
+              subject.present 'dummy1', with: entity_mock1
+              expect do
+                subject.present 'dummy2', with: entity_mock2
+              end.to raise_error ArgumentError, 'Representation of type String cannot be merged.'
+            end
+          end
+        end
+      end
+
+      describe '#declared' do
+        # see endpoint_spec.rb#declared for spec coverage
+
+        it 'returns an empty hash' do
+          expect(subject.declared({})).to eq({})
+        end
+      end
+    end
+  end
+end
diff --git a/spec/grape/dsl/middleware_spec.rb b/spec/grape/dsl/middleware_spec.rb
new file mode 100644
index 0000000..2b93065
--- /dev/null
+++ b/spec/grape/dsl/middleware_spec.rb
@@ -0,0 +1,31 @@
+require 'spec_helper'
+
+module Grape
+  module DSL
+    module MiddlewareSpec
+      class Dummy
+        include Grape::DSL::Middleware
+      end
+    end
+    describe Middleware do
+      subject { Class.new(MiddlewareSpec::Dummy) }
+      let(:proc) { ->() {} }
+
+      describe '.use' do
+        it 'adds a middleware' do
+          expect(subject).to receive(:namespace_stackable).with(:middleware, [:my_middleware, :arg1, proc])
+
+          subject.use :my_middleware, :arg1, &proc
+        end
+      end
+
+      describe '.middleware' do
+        it 'returns the middleware stack' do
+          subject.use :my_middleware, :arg1, &proc
+
+          expect(subject.middleware).to eq [[:my_middleware, :arg1, proc]]
+        end
+      end
+    end
+  end
+end
diff --git a/spec/grape/dsl/parameters_spec.rb b/spec/grape/dsl/parameters_spec.rb
new file mode 100644
index 0000000..f75e4ae
--- /dev/null
+++ b/spec/grape/dsl/parameters_spec.rb
@@ -0,0 +1,164 @@
+require 'spec_helper'
+
+module Grape
+  module DSL
+    module ParametersSpec
+      class Dummy
+        include Grape::DSL::Parameters
+        attr_accessor :api, :element, :parent
+
+        def validate_attributes(*args)
+          @validate_attributes = *args
+        end
+
+        # rubocop:disable TrivialAccessors
+        def validate_attributes_reader
+          @validate_attributes
+        end
+        # rubocop:enable TrivialAccessors
+
+        def push_declared_params(*args)
+          @push_declared_params = args
+        end
+
+        # rubocop:disable TrivialAccessors
+        def push_declared_params_reader
+          @push_declared_params
+        end
+        # rubocop:enable TrivialAccessors
+
+        def validates(*args)
+          @validates = *args
+        end
+
+        # rubocop:disable TrivialAccessors
+        def validates_reader
+          @validates
+        end
+        # rubocop:enable TrivialAccessors
+      end
+    end
+
+    describe Parameters do
+      subject { ParametersSpec::Dummy.new }
+
+      describe '#use' do
+        before do
+          allow_message_expectations_on_nil
+          allow(subject.api).to receive(:namespace_stackable).with(:named_params)
+        end
+        let(:options) { { option: 'value' } }
+        let(:named_params) { { params_group: proc {} } }
+
+        it 'calls processes associated with named params' do
+          allow(Grape::DSL::Configuration).to receive(:stacked_hash_to_hash).and_return(named_params)
+          expect(subject).to receive(:instance_exec).with(options).and_yield
+          subject.use :params_group, options
+        end
+
+        it 'raises error when non-existent named param is called' do
+          allow(Grape::DSL::Configuration).to receive(:stacked_hash_to_hash).and_return({})
+          expect { subject.use :params_group }.to raise_error('Params :params_group not found!')
+        end
+      end
+
+      describe '#use_scope' do
+        it 'is alias to #use' do
+          expect(subject.method(:use_scope)).to eq subject.method(:use)
+        end
+      end
+
+      describe '#includes' do
+        it 'is alias to #use' do
+          expect(subject.method(:includes)).to eq subject.method(:use)
+        end
+      end
+
+      describe '#requires' do
+        it 'adds a required parameter' do
+          subject.requires :id, type: Integer, desc: 'Identity.'
+
+          expect(subject.validate_attributes_reader).to eq([[:id], { type: Integer, desc: 'Identity.', presence: true }])
+          expect(subject.push_declared_params_reader).to eq([[:id]])
+        end
+      end
+
+      describe '#optional' do
+        it 'adds an optional parameter' do
+          subject.optional :id, type: Integer, desc: 'Identity.'
+
+          expect(subject.validate_attributes_reader).to eq([[:id], { type: Integer, desc: 'Identity.' }])
+          expect(subject.push_declared_params_reader).to eq([[:id]])
+        end
+      end
+
+      describe '#mutually_exclusive' do
+        it 'adds an mutally exclusive parameter validation' do
+          subject.mutually_exclusive :media, :audio
+
+          expect(subject.validates_reader).to eq([[:media, :audio], { mutual_exclusion: true }])
+        end
+      end
+
+      describe '#exactly_one_of' do
+        it 'adds an exactly of one parameter validation' do
+          subject.exactly_one_of :media, :audio
+
+          expect(subject.validates_reader).to eq([[:media, :audio], { exactly_one_of: true }])
+        end
+      end
+
+      describe '#at_least_one_of' do
+        it 'adds an at least one of parameter validation' do
+          subject.at_least_one_of :media, :audio
+
+          expect(subject.validates_reader).to eq([[:media, :audio], { at_least_one_of: true }])
+        end
+      end
+
+      describe '#all_or_none_of' do
+        it 'adds an all or none of parameter validation' do
+          subject.all_or_none_of :media, :audio
+
+          expect(subject.validates_reader).to eq([[:media, :audio], { all_or_none_of: true }])
+        end
+      end
+
+      describe '#group' do
+        it 'is alias to #requires' do
+          expect(subject.method(:group)).to eq subject.method(:requires)
+        end
+      end
+
+      describe '#params' do
+        it 'inherits params from parent' do
+          parent_params = { foo: 'bar' }
+          subject.parent = Object.new
+          allow(subject.parent).to receive(:params).and_return(parent_params)
+          expect(subject.params({})).to eq parent_params
+        end
+
+        describe 'when params argument is an array of hashes' do
+          it 'returns values of each hash for @element key' do
+            subject.element = :foo
+            expect(subject.params([{ foo: 'bar' }, { foo: 'baz' }])).to eq(%w(bar baz))
+          end
+        end
+
+        describe 'when params argument is a hash' do
+          it 'returns value for @element key' do
+            subject.element = :foo
+            expect(subject.params(foo: 'bar')).to eq('bar')
+          end
+        end
+
+        describe 'when params argument is not a array or a hash' do
+          it 'returns empty hash' do
+            subject.element = Object.new
+            expect(subject.params(Object.new)).to eq({})
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/spec/grape/dsl/request_response_spec.rb b/spec/grape/dsl/request_response_spec.rb
new file mode 100644
index 0000000..94f5a82
--- /dev/null
+++ b/spec/grape/dsl/request_response_spec.rb
@@ -0,0 +1,177 @@
+require 'spec_helper'
+
+module Grape
+  module DSL
+    module RequestResponseSpec
+      class Dummy
+        include Grape::DSL::RequestResponse
+
+        def self.set(key, value)
+          settings[key.to_sym] = value
+        end
+
+        def self.imbue(key, value)
+          settings.imbue(key, value)
+        end
+      end
+    end
+
+    describe RequestResponse do
+      subject { Class.new(RequestResponseSpec::Dummy) }
+      let(:c_type) { 'application/json' }
+      let(:format) { 'txt' }
+
+      describe '.default_format' do
+        it 'sets the default format' do
+          expect(subject).to receive(:namespace_inheritable).with(:default_format, :format)
+          subject.default_format :format
+        end
+
+        it 'returns the format without paramter' do
+          subject.default_format :format
+
+          expect(subject.default_format).to eq :format
+        end
+      end
+
+      describe '.format' do
+        it 'sets a new format' do
+          expect(subject).to receive(:namespace_inheritable).with(:format, format.to_sym)
+          expect(subject).to receive(:namespace_inheritable).with(:default_error_formatter, Grape::ErrorFormatter::Txt)
+
+          subject.format format
+        end
+      end
+
+      describe '.formatter' do
+        it 'sets the formatter for a content type' do
+          expect(subject).to receive(:namespace_stackable).with(:formatters, c_type.to_sym => :formatter)
+          subject.formatter c_type, :formatter
+        end
+      end
+
+      describe '.parser' do
+        it 'sets a parser for a content type' do
+          expect(subject).to receive(:namespace_stackable).with(:parsers, c_type.to_sym => :parser)
+          subject.parser c_type, :parser
+        end
+      end
+
+      describe '.default_error_formatter' do
+        it 'sets a new error formatter' do
+          expect(subject).to receive(:namespace_inheritable).with(:default_error_formatter, Grape::ErrorFormatter::Json)
+          subject.default_error_formatter :json
+        end
+      end
+
+      describe '.error_formatter' do
+        it 'sets a error_formatter' do
+          format = 'txt'
+          expect(subject).to receive(:namespace_stackable).with(:error_formatters, format.to_sym => :error_formatter)
+          subject.error_formatter format, :error_formatter
+        end
+
+        it 'understands syntactic sugar' do
+          expect(subject).to receive(:namespace_stackable).with(:error_formatters, format.to_sym => :error_formatter)
+          subject.error_formatter format, with: :error_formatter
+        end
+      end
+
+      describe '.content_type' do
+        it 'sets a content type for a format' do
+          expect(subject).to receive(:namespace_stackable).with(:content_types, format.to_sym => c_type)
+          subject.content_type format, c_type
+        end
+      end
+
+      describe '.content_types' do
+        it 'returns all content types' do
+          expect(subject.content_types).to eq(xml: 'application/xml',
+                                              serializable_hash: 'application/json',
+                                              json: 'application/json',
+                                              txt: 'text/plain',
+                                              binary: 'application/octet-stream')
+        end
+      end
+
+      describe '.default_error_status' do
+        it 'sets a default error status' do
+          expect(subject).to receive(:namespace_inheritable).with(:default_error_status, 500)
+          subject.default_error_status 500
+        end
+      end
+
+      describe '.rescue_from' do
+        describe ':all' do
+          it 'sets rescue all to true' do
+            expect(subject).to receive(:namespace_inheritable).with(:rescue_all, true)
+            expect(subject).to receive(:namespace_inheritable).with(:all_rescue_handler, nil)
+            subject.rescue_from :all
+          end
+
+          it 'sets given proc as rescue handler' do
+            rescue_handler_proc = proc {}
+            expect(subject).to receive(:namespace_inheritable).with(:rescue_all, true)
+            expect(subject).to receive(:namespace_inheritable).with(:all_rescue_handler, rescue_handler_proc)
+            subject.rescue_from :all, rescue_handler_proc
+          end
+
+          it 'sets given block as rescue handler' do
+            rescue_handler_proc = proc {}
+            expect(subject).to receive(:namespace_inheritable).with(:rescue_all, true)
+            expect(subject).to receive(:namespace_inheritable).with(:all_rescue_handler, rescue_handler_proc)
+            subject.rescue_from :all, &rescue_handler_proc
+          end
+
+          it 'sets a rescue handler declared through :with option' do
+            expect(subject).to receive(:namespace_inheritable).with(:rescue_all, true)
+            expect(subject).to receive(:namespace_inheritable).with(:all_rescue_handler, an_instance_of(Proc))
+            subject.rescue_from :all, with: 'ExampleHandler'
+          end
+        end
+
+        describe 'list of exceptions is passed' do
+          it 'sets hash of exceptions as rescue handlers' do
+            expect(subject).to receive(:namespace_stackable).with(:rescue_handlers, StandardError => nil)
+            expect(subject).to receive(:namespace_stackable).with(:rescue_options, {})
+            subject.rescue_from StandardError
+          end
+
+          it 'rescues only base handlers if rescue_subclasses: false option is passed' do
+            expect(subject).to receive(:namespace_stackable).with(:base_only_rescue_handlers, StandardError => nil)
+            expect(subject).to receive(:namespace_stackable).with(:rescue_options, rescue_subclasses: false)
+            subject.rescue_from StandardError, rescue_subclasses: false
+          end
+
+          it 'sets given proc as rescue handler for each key in hash' do
+            rescue_handler_proc = proc {}
+            expect(subject).to receive(:namespace_stackable).with(:rescue_handlers, StandardError => rescue_handler_proc)
+            expect(subject).to receive(:namespace_stackable).with(:rescue_options, {})
+            subject.rescue_from StandardError, rescue_handler_proc
+          end
+
+          it 'sets given block as rescue handler for each key in hash' do
+            rescue_handler_proc = proc {}
+            expect(subject).to receive(:namespace_stackable).with(:rescue_handlers, StandardError => rescue_handler_proc)
+            expect(subject).to receive(:namespace_stackable).with(:rescue_options, {})
+            subject.rescue_from StandardError, &rescue_handler_proc
+          end
+
+          it 'sets a rescue handler declared through :with option for each key in hash' do
+            expect(subject).to receive(:namespace_stackable).with(:rescue_handlers, StandardError => an_instance_of(Proc))
+            expect(subject).to receive(:namespace_stackable).with(:rescue_options, with: 'ExampleHandler')
+            subject.rescue_from StandardError, with: 'ExampleHandler'
+          end
+        end
+      end
+
+      describe '.represent' do
+        it 'sets a presenter for a class' do
+          presenter = Class.new
+          expect(subject).to receive(:namespace_stackable).with(:representations, ThisClass: presenter)
+          subject.represent :ThisClass, with: presenter
+        end
+      end
+    end
+  end
+end
diff --git a/spec/grape/dsl/routing_spec.rb b/spec/grape/dsl/routing_spec.rb
new file mode 100644
index 0000000..bf53e87
--- /dev/null
+++ b/spec/grape/dsl/routing_spec.rb
@@ -0,0 +1,255 @@
+require 'spec_helper'
+
+module Grape
+  module DSL
+    module RoutingSpec
+      class Dummy
+        include Grape::DSL::Routing
+      end
+    end
+
+    describe Routing do
+      subject { Class.new(RoutingSpec::Dummy) }
+      let(:proc) { ->() {} }
+      let(:options) { { a: :b } }
+      let(:path) { '/dummy' }
+
+      describe '.version' do
+        it 'sets a version for route' do
+          version = 'v1'
+          expect(subject).to receive(:namespace_inheritable).with(:version, [version])
+          expect(subject).to receive(:namespace_inheritable).with(:version_options, using: :path)
+          expect(subject.version(version)).to eq(version)
+        end
+      end
+
+      describe '.prefix' do
+        it 'sets a prefix for route' do
+          prefix = '/api'
+          expect(subject).to receive(:namespace_inheritable).with(:root_prefix, prefix)
+          subject.prefix prefix
+        end
+      end
+
+      describe '.do_not_route_head!' do
+        it 'sets do not route head option' do
+          expect(subject).to receive(:namespace_inheritable).with(:do_not_route_head, true)
+          subject.do_not_route_head!
+        end
+      end
+
+      describe '.do_not_route_options!' do
+        it 'sets do not route options option' do
+          expect(subject).to receive(:namespace_inheritable).with(:do_not_route_options, true)
+          subject.do_not_route_options!
+        end
+      end
+
+      describe '.mount' do
+        it 'mounts on a nested path' do
+          subject = Class.new(Grape::API)
+          app1 = Class.new(Grape::API)
+          app2 = Class.new(Grape::API)
+          app2.get '/nice' do
+            'play'
+          end
+
+          subject.mount app1 => '/app1'
+          app1.mount app2 => '/app2'
+
+          expect(subject.inheritable_setting.to_hash[:namespace]).to eq({})
+          expect(subject.inheritable_setting.to_hash[:namespace_inheritable]).to eq({})
+          expect(app1.inheritable_setting.to_hash[:namespace_stackable]).to eq(mount_path: ['/app1'])
+
+          expect(app2.inheritable_setting.to_hash[:namespace_stackable]).to eq(mount_path: ['/app1', '/app2'])
+        end
+      end
+
+      describe '.route' do
+        before do
+          allow(subject).to receive(:endpoints).and_return([])
+          allow(subject).to receive(:route_end)
+          allow(subject).to receive(:reset_validations!)
+        end
+
+        it 'marks end of the route' do
+          expect(subject).to receive(:route_end)
+          subject.route(:any)
+        end
+
+        it 'resets validations' do
+          expect(subject).to receive(:reset_validations!)
+          subject.route(:any)
+        end
+
+        it 'defines a new endpoint' do
+          expect { subject.route(:any) }
+            .to change { subject.endpoints.count }.from(0).to(1)
+        end
+
+        it 'does not duplicate identical endpoints' do
+          subject.route(:any)
+          expect { subject.route(:any) }
+            .to_not change(subject.endpoints, :count)
+        end
+
+        it 'generates correct endpoint options' do
+          allow(subject).to receive(:route_setting).with(:description).and_return(fiz: 'baz')
+          allow(Grape::DSL::Configuration).to receive(:stacked_hash_to_hash).and_return(nuz: 'naz')
+
+          expect(Grape::Endpoint).to receive(:new) do |_inheritable_setting, endpoint_options|
+            expect(endpoint_options[:method]).to eq :get
+            expect(endpoint_options[:path]).to eq '/foo'
+            expect(endpoint_options[:for]).to eq subject
+            expect(endpoint_options[:route_options]).to eq(foo: 'bar', fiz: 'baz', params: { nuz: 'naz' })
+          end.and_yield
+
+          subject.route(:get, '/foo', { foo: 'bar' }, &proc {})
+        end
+      end
+
+      describe '.get' do
+        it 'delegates to .route' do
+          expect(subject).to receive(:route).with('GET', path, options)
+          subject.get path, options, &proc
+        end
+      end
+
+      describe '.post' do
+        it 'delegates to .route' do
+          expect(subject).to receive(:route).with('POST', path, options)
+          subject.post path, options, &proc
+        end
+      end
+
+      describe '.put' do
+        it 'delegates to .route' do
+          expect(subject).to receive(:route).with('PUT', path, options)
+          subject.put path, options, &proc
+        end
+      end
+
+      describe '.head' do
+        it 'delegates to .route' do
+          expect(subject).to receive(:route).with('HEAD', path, options)
+          subject.head path, options, &proc
+        end
+      end
+
+      describe '.delete' do
+        it 'delegates to .route' do
+          expect(subject).to receive(:route).with('DELETE', path, options)
+          subject.delete path, options, &proc
+        end
+      end
+
+      describe '.options' do
+        it 'delegates to .route' do
+          expect(subject).to receive(:route).with('OPTIONS', path, options)
+          subject.options path, options, &proc
+        end
+      end
+
+      describe '.patch' do
+        it 'delegates to .route' do
+          expect(subject).to receive(:route).with('PATCH', path, options)
+          subject.patch path, options, &proc
+        end
+      end
+
+      describe '.namespace' do
+        let(:new_namespace) { Object.new }
+
+        it 'creates a new namespace with given name and options' do
+          expect(subject).to receive(:within_namespace).and_yield
+          expect(subject).to receive(:nest).and_yield
+          expect(Namespace).to receive(:new).with(:foo, foo: 'bar').and_return(new_namespace)
+          expect(subject).to receive(:namespace_stackable).with(:namespace, new_namespace)
+
+          subject.namespace :foo, foo: 'bar', &proc {}
+        end
+
+        it 'calls #joined_space_path on Namespace' do
+          result_of_namspace_stackable = Object.new
+          allow(subject).to receive(:namespace_stackable).and_return(result_of_namspace_stackable)
+          expect(Namespace).to receive(:joined_space_path).with(result_of_namspace_stackable)
+          subject.namespace
+        end
+      end
+
+      describe '.group' do
+        it 'is alias to #namespace' do
+          expect(subject.method(:group)).to eq subject.method(:namespace)
+        end
+      end
+
+      describe '.resource' do
+        it 'is alias to #namespace' do
+          expect(subject.method(:resource)).to eq subject.method(:namespace)
+        end
+      end
+
+      describe '.resources' do
+        it 'is alias to #namespace' do
+          expect(subject.method(:resources)).to eq subject.method(:namespace)
+        end
+      end
+
+      describe '.segment' do
+        it 'is alias to #namespace' do
+          expect(subject.method(:segment)).to eq subject.method(:namespace)
+        end
+      end
+
+      describe '.routes' do
+        let(:routes) { Object.new }
+
+        it 'returns value received from #prepare_routes' do
+          expect(subject).to receive(:prepare_routes).and_return(routes)
+          expect(subject.routes).to eq routes
+        end
+
+        context 'when #routes was already called once' do
+          before do
+            allow(subject).to receive(:prepare_routes).and_return(routes)
+            subject.routes
+          end
+          it 'it does not call prepare_routes again' do
+            expect(subject).to_not receive(:prepare_routes)
+            expect(subject.routes).to eq routes
+          end
+        end
+      end
+
+      describe '.route_param' do
+        it 'calls #namespace with given params' do
+          expect(subject).to receive(:namespace).with(':foo', {}).and_yield
+          subject.route_param('foo', {}, &proc {})
+        end
+
+        let(:regex) { /(.*)/ }
+        let!(:options) {  { requirements: regex } }
+        it 'nests requirements option under param name' do
+          expect(subject).to receive(:namespace) do |_param, options|
+            expect(options[:requirements][:foo]).to eq regex
+          end
+          subject.route_param('foo', options, &proc {})
+        end
+
+        it 'does not modify options parameter' do
+          allow(subject).to receive(:namespace)
+          expect { subject.route_param('foo', options, &proc {}) }
+            .to_not change { options }
+        end
+      end
+
+      describe '.versions' do
+        it 'returns last defined version' do
+          subject.version 'v1'
+          subject.version 'v2'
+          expect(subject.version).to eq('v2')
+        end
+      end
+    end
+  end
+end
diff --git a/spec/grape/dsl/settings_spec.rb b/spec/grape/dsl/settings_spec.rb
new file mode 100644
index 0000000..4acabe2
--- /dev/null
+++ b/spec/grape/dsl/settings_spec.rb
@@ -0,0 +1,219 @@
+require 'spec_helper'
+
+module Grape
+  module DSL
+    module SettingsSpec
+      class Dummy
+        include Grape::DSL::Settings
+
+        def reset_validations!; end
+      end
+    end
+
+    describe Settings do
+      subject { SettingsSpec::Dummy.new }
+
+      describe '#unset' do
+        it 'deletes a key from settings' do
+          subject.namespace_setting :dummy, 1
+          expect(subject.inheritable_setting.namespace.keys).to include(:dummy)
+
+          subject.unset :namespace, :dummy
+          expect(subject.inheritable_setting.namespace.keys).not_to include(:dummy)
+        end
+      end
+
+      describe '#get_or_set' do
+        it 'sets a values' do
+          subject.get_or_set :namespace, :dummy, 1
+          expect(subject.namespace_setting(:dummy)).to eq 1
+        end
+
+        it 'returns a value when nil is new value is provided' do
+          subject.get_or_set :namespace, :dummy, 1
+          expect(subject.get_or_set(:namespace, :dummy, nil)).to eq 1
+        end
+      end
+
+      describe '#global_setting' do
+        it 'delegates to get_or_set' do
+          expect(subject).to receive(:get_or_set).with(:global, :dummy, 1)
+          subject.global_setting(:dummy, 1)
+        end
+      end
+
+      describe '#route_setting' do
+        it 'delegates to get_or_set' do
+          expect(subject).to receive(:get_or_set).with(:route, :dummy, 1)
+          subject.route_setting(:dummy, 1)
+        end
+
+        it 'sets a value until the next route' do
+          subject.route_setting :some_thing, :foo_bar
+          expect(subject.route_setting(:some_thing)).to eq :foo_bar
+
+          subject.route_end
+
+          expect(subject.route_setting(:some_thing)).to be_nil
+        end
+      end
+
+      describe '#namespace_setting' do
+        it 'delegates to get_or_set' do
+          expect(subject).to receive(:get_or_set).with(:namespace, :dummy, 1)
+          subject.namespace_setting(:dummy, 1)
+        end
+
+        it 'sets a value until the end of a namespace' do
+          subject.namespace_start
+
+          subject.namespace_setting :some_thing, :foo_bar
+          expect(subject.namespace_setting(:some_thing)).to eq :foo_bar
+
+          subject.namespace_end
+
+          expect(subject.namespace_setting(:some_thing)).to be_nil
+        end
+
+        it 'resets values after leaving nested namespaces' do
+          subject.namespace_start
+
+          subject.namespace_setting :some_thing, :foo_bar
+          expect(subject.namespace_setting(:some_thing)).to eq :foo_bar
+
+          subject.namespace_start
+
+          expect(subject.namespace_setting(:some_thing)).to be_nil
+
+          subject.namespace_end
+          expect(subject.namespace_setting(:some_thing)).to eq :foo_bar
+
+          subject.namespace_end
+
+          expect(subject.namespace_setting(:some_thing)).to be_nil
+        end
+      end
+
+      describe '#namespace_inheritable' do
+        it 'delegates to get_or_set' do
+          expect(subject).to receive(:get_or_set).with(:namespace_inheritable, :dummy, 1)
+          subject.namespace_inheritable(:dummy, 1)
+        end
+
+        it 'inherits values from surrounding namespace' do
+          subject.namespace_start
+
+          subject.namespace_inheritable(:some_thing, :foo_bar)
+          expect(subject.namespace_inheritable(:some_thing)).to eq :foo_bar
+
+          subject.namespace_start
+
+          expect(subject.namespace_inheritable(:some_thing)).to eq :foo_bar
+
+          subject.namespace_inheritable(:some_thing, :foo_bar_2)
+
+          expect(subject.namespace_inheritable(:some_thing)).to eq :foo_bar_2
+
+          subject.namespace_end
+          expect(subject.namespace_inheritable(:some_thing)).to eq :foo_bar
+          subject.namespace_end
+        end
+      end
+
+      describe '#namespace_stackable' do
+        it 'delegates to get_or_set' do
+          expect(subject).to receive(:get_or_set).with(:namespace_stackable, :dummy, 1)
+          subject.namespace_stackable(:dummy, 1)
+        end
+
+        it 'stacks values from surrounding namespace' do
+          subject.namespace_start
+
+          subject.namespace_stackable(:some_thing, :foo_bar)
+          expect(subject.namespace_stackable(:some_thing)).to eq [:foo_bar]
+
+          subject.namespace_start
+
+          expect(subject.namespace_stackable(:some_thing)).to eq [:foo_bar]
+
+          subject.namespace_stackable(:some_thing, :foo_bar_2)
+
+          expect(subject.namespace_stackable(:some_thing)).to eq [:foo_bar, :foo_bar_2]
+
+          subject.namespace_end
+          expect(subject.namespace_stackable(:some_thing)).to eq [:foo_bar]
+          subject.namespace_end
+        end
+      end
+
+      describe '#api_class_setting' do
+        it 'delegates to get_or_set' do
+          expect(subject).to receive(:get_or_set).with(:api_class, :dummy, 1)
+          subject.api_class_setting(:dummy, 1)
+        end
+      end
+
+      describe '#within_namespace' do
+        it 'calls start and end for a namespace' do
+          expect(subject).to receive :namespace_start
+          expect(subject).to receive :namespace_end
+
+          subject.within_namespace do
+          end
+        end
+
+        it 'returns the last result' do
+          result = subject.within_namespace do
+            1
+          end
+
+          expect(result).to eq 1
+        end
+      end
+
+      describe 'complex scenario' do
+        it 'plays well' do
+          obj1 = SettingsSpec::Dummy.new
+          obj2 = SettingsSpec::Dummy.new
+          obj3 = SettingsSpec::Dummy.new
+
+          obj1_copy = nil
+          obj2_copy = nil
+          obj3_copy = nil
+
+          obj1.within_namespace do
+            obj1.namespace_stackable(:some_thing, :obj1)
+            expect(obj1.namespace_stackable(:some_thing)).to eq [:obj1]
+            obj1_copy = obj1.inheritable_setting.point_in_time_copy
+          end
+
+          expect(obj1.namespace_stackable(:some_thing)).to eq []
+          expect(obj1_copy.namespace_stackable[:some_thing]).to eq [:obj1]
+
+          obj2.within_namespace do
+            obj2.namespace_stackable(:some_thing, :obj2)
+            expect(obj2.namespace_stackable(:some_thing)).to eq [:obj2]
+            obj2_copy = obj2.inheritable_setting.point_in_time_copy
+          end
+
+          expect(obj2.namespace_stackable(:some_thing)).to eq []
+          expect(obj2_copy.namespace_stackable[:some_thing]).to eq [:obj2]
+
+          obj3.within_namespace do
+            obj3.namespace_stackable(:some_thing, :obj3)
+            expect(obj3.namespace_stackable(:some_thing)).to eq [:obj3]
+            obj3_copy = obj3.inheritable_setting.point_in_time_copy
+          end
+
+          expect(obj3.namespace_stackable(:some_thing)).to eq []
+          expect(obj3_copy.namespace_stackable[:some_thing]).to eq [:obj3]
+
+          obj1.top_level_setting.inherit_from obj2_copy.point_in_time_copy
+          obj2.top_level_setting.inherit_from obj3_copy.point_in_time_copy
+
+          expect(obj1_copy.namespace_stackable[:some_thing]).to eq [:obj3, :obj2, :obj1]
+        end
+      end
+    end
+  end
+end
diff --git a/spec/grape/dsl/validations_spec.rb b/spec/grape/dsl/validations_spec.rb
new file mode 100644
index 0000000..36bd5a3
--- /dev/null
+++ b/spec/grape/dsl/validations_spec.rb
@@ -0,0 +1,52 @@
+require 'spec_helper'
+
+module Grape
+  module DSL
+    module ValidationsSpec
+      class Dummy
+        include Grape::DSL::Validations
+      end
+    end
+
+    describe Validations do
+      subject { ValidationsSpec::Dummy }
+
+      describe '.reset_validations!' do
+        before do
+          subject.namespace_stackable :declared_params, ['dummy']
+          subject.namespace_stackable :validations, ['dummy']
+          subject.reset_validations!
+        end
+
+        it 'resets declared params' do
+          expect(subject.namespace_stackable(:declared_params)).to eq []
+        end
+
+        it 'resets validations' do
+          expect(subject.namespace_stackable(:validations)).to eq []
+        end
+      end
+
+      describe '.params' do
+        it 'returns a ParamsScope' do
+          expect(subject.params).to be_a Grape::Validations::ParamsScope
+        end
+
+        it 'evaluates block' do
+          expect { subject.params { fail 'foo' } }.to raise_error RuntimeError, 'foo'
+        end
+      end
+
+      describe '.document_attribute' do
+        before do
+          subject.document_attribute([full_name: 'xxx'], foo: 'bar')
+        end
+
+        it 'creates a param documentation' do
+          expect(subject.namespace_stackable(:params)).to eq(['xxx' => { foo: 'bar' }])
+          expect(subject.route_setting(:description)).to eq(params: { 'xxx' => { foo: 'bar' } })
+        end
+      end
+    end
+  end
+end
diff --git a/spec/grape/endpoint_spec.rb b/spec/grape/endpoint_spec.rb
new file mode 100644
index 0000000..cb5bec0
--- /dev/null
+++ b/spec/grape/endpoint_spec.rb
@@ -0,0 +1,944 @@
+require 'spec_helper'
+
+describe Grape::Endpoint do
+  subject { Class.new(Grape::API) }
+
+  def app
+    subject
+  end
+
+  describe '.before_each' do
+    after { Grape::Endpoint.before_each(nil) }
+
+    it 'should be settable via block' do
+      block = lambda { |_endpoint| 'noop' }
+      Grape::Endpoint.before_each(&block)
+      expect(Grape::Endpoint.before_each).to eq(block)
+    end
+
+    it 'should be settable via reference' do
+      block = lambda { |_endpoint| 'noop' }
+      Grape::Endpoint.before_each block
+      expect(Grape::Endpoint.before_each).to eq(block)
+    end
+
+    it 'should be able to override a helper' do
+      subject.get('/') { current_user }
+      expect { get '/' }.to raise_error(NameError)
+
+      Grape::Endpoint.before_each do |endpoint|
+        allow(endpoint).to receive(:current_user).and_return('Bob')
+      end
+
+      get '/'
+      expect(last_response.body).to eq('Bob')
+
+      Grape::Endpoint.before_each(nil)
+      expect { get '/' }.to raise_error(NameError)
+    end
+  end
+
+  describe '#initialize' do
+    it 'takes a settings stack, options, and a block' do
+      p = proc {}
+      expect do
+        Grape::Endpoint.new(Grape::Util::InheritableSetting.new, {
+                              path: '/',
+                              method: :get
+                            }, &p)
+      end.not_to raise_error
+    end
+  end
+
+  it 'sets itself in the env upon call' do
+    subject.get('/') { 'Hello world.' }
+    get '/'
+    expect(last_request.env['api.endpoint']).to be_kind_of(Grape::Endpoint)
+  end
+
+  describe '#status' do
+    it 'is callable from within a block' do
+      subject.get('/home') do
+        status 206
+        'Hello'
+      end
+
+      get '/home'
+      expect(last_response.status).to eq(206)
+      expect(last_response.body).to eq('Hello')
+    end
+
+    it 'is set as default to 200 for get' do
+      memoized_status = nil
+      subject.get('/home') do
+        memoized_status = status
+        'Hello'
+      end
+
+      get '/home'
+      expect(last_response.status).to eq(200)
+      expect(memoized_status).to eq(200)
+      expect(last_response.body).to eq('Hello')
+    end
+
+    it 'is set as default to 201 for post' do
+      memoized_status = nil
+      subject.post('/home') do
+        memoized_status = status
+        'Hello'
+      end
+
+      post '/home'
+      expect(last_response.status).to eq(201)
+      expect(memoized_status).to eq(201)
+      expect(last_response.body).to eq('Hello')
+    end
+  end
+
+  describe '#header' do
+    it 'is callable from within a block' do
+      subject.get('/hey') do
+        header 'X-Awesome', 'true'
+        'Awesome'
+      end
+
+      get '/hey'
+      expect(last_response.headers['X-Awesome']).to eq('true')
+    end
+  end
+
+  describe '#headers' do
+    before do
+      subject.get('/headers') do
+        headers.to_json
+      end
+    end
+    it 'includes request headers' do
+      get '/headers'
+      expect(JSON.parse(last_response.body)).to eq(
+        'Host' => 'example.org',
+        'Cookie' => ''
+      )
+    end
+    it 'includes additional request headers' do
+      get '/headers', nil, 'HTTP_X_GRAPE_CLIENT' => '1'
+      expect(JSON.parse(last_response.body)['X-Grape-Client']).to eq('1')
+    end
+    it 'includes headers passed as symbols' do
+      env = Rack::MockRequest.env_for('/headers')
+      env['HTTP_SYMBOL_HEADER'.to_sym] = 'Goliath passes symbols'
+      body = subject.call(env)[2].body.first
+      expect(JSON.parse(body)['Symbol-Header']).to eq('Goliath passes symbols')
+    end
+  end
+
+  describe '#cookies' do
+    it 'is callable from within a block' do
+      subject.get('/get/cookies') do
+        cookies['my-awesome-cookie1'] = 'is cool'
+        cookies['my-awesome-cookie2'] = {
+          value: 'is cool too',
+          domain: 'my.example.com',
+          path: '/',
+          secure: true
+        }
+        cookies[:cookie3] = 'symbol'
+        cookies['cookie4'] = 'secret code here'
+      end
+
+      get('/get/cookies')
+
+      expect(last_response.headers['Set-Cookie'].split("\n").sort).to eql [
+        'cookie3=symbol',
+        'cookie4=secret+code+here',
+        'my-awesome-cookie1=is+cool',
+        'my-awesome-cookie2=is+cool+too; domain=my.example.com; path=/; secure'
+      ]
+    end
+
+    it 'sets browser cookies and does not set response cookies' do
+      subject.get('/username') do
+        cookies[:username]
+      end
+      get('/username', {}, 'HTTP_COOKIE' => 'username=mrplum; sandbox=true')
+
+      expect(last_response.body).to eq('mrplum')
+      expect(last_response.headers['Set-Cookie']).to be_nil
+    end
+
+    it 'sets and update browser cookies' do
+      subject.get('/username') do
+        cookies[:sandbox] = true if cookies[:sandbox] == 'false'
+        cookies[:username] += '_test'
+      end
+      get('/username', {}, 'HTTP_COOKIE' => 'username=user; sandbox=false')
+      expect(last_response.body).to eq('user_test')
+      expect(last_response.headers['Set-Cookie']).to match(/username=user_test/)
+      expect(last_response.headers['Set-Cookie']).to match(/sandbox=true/)
+    end
+
+    it 'deletes cookie' do
+      subject.get('/test') do
+        sum = 0
+        cookies.each do |name, val|
+          sum += val.to_i
+          cookies.delete name
+        end
+        sum
+      end
+      get '/test', {}, 'HTTP_COOKIE' => 'delete_this_cookie=1; and_this=2'
+      expect(last_response.body).to eq('3')
+      cookies = Hash[last_response.headers['Set-Cookie'].split("\n").map do |set_cookie|
+        cookie = CookieJar::Cookie.from_set_cookie 'http://localhost/test', set_cookie
+        [cookie.name, cookie]
+      end]
+      expect(cookies.size).to eq(2)
+      %w(and_this delete_this_cookie).each do |cookie_name|
+        cookie = cookies[cookie_name]
+        expect(cookie).not_to be_nil
+        expect(cookie.value).to eq('deleted')
+        expect(cookie.expired?).to be true
+      end
+    end
+
+    it 'deletes cookies with path' do
+      subject.get('/test') do
+        sum = 0
+        cookies.each do |name, val|
+          sum += val.to_i
+          cookies.delete name, path: '/test'
+        end
+        sum
+      end
+      get('/test', {}, 'HTTP_COOKIE' => 'delete_this_cookie=1; and_this=2')
+      expect(last_response.body).to eq('3')
+      cookies = Hash[last_response.headers['Set-Cookie'].split("\n").map do |set_cookie|
+        cookie = CookieJar::Cookie.from_set_cookie 'http://localhost/test', set_cookie
+        [cookie.name, cookie]
+      end]
+      expect(cookies.size).to eq(2)
+      %w(and_this delete_this_cookie).each do |cookie_name|
+        cookie = cookies[cookie_name]
+        expect(cookie).not_to be_nil
+        expect(cookie.value).to eq('deleted')
+        expect(cookie.path).to eq('/test')
+        expect(cookie.expired?).to be true
+      end
+    end
+  end
+
+  describe '#declared' do
+    before do
+      subject.params do
+        requires :first
+        optional :second
+        optional :third, default: 'third-default'
+        optional :nested, type: Hash do
+          optional :fourth
+        end
+      end
+    end
+
+    it 'has as many keys as there are declared params' do
+      inner_params = nil
+      subject.get '/declared' do
+        inner_params = declared(params).keys
+        ''
+      end
+      get '/declared?first=present'
+      expect(last_response.status).to eq(200)
+      expect(inner_params.size).to eq(4)
+    end
+
+    it 'has a optional param with default value all the time' do
+      inner_params = nil
+      subject.get '/declared' do
+        inner_params = declared(params)
+        ''
+      end
+      get '/declared?first=one'
+      expect(last_response.status).to eq(200)
+      expect(inner_params[:third]).to eql('third-default')
+    end
+
+    it 'builds nested params' do
+      inner_params = nil
+      subject.get '/declared' do
+        inner_params = declared(params)
+        ''
+      end
+
+      get '/declared?first=present&nested[fourth]=1'
+      expect(last_response.status).to eq(200)
+      expect(inner_params[:nested].keys.size).to eq 1
+    end
+
+    it 'builds nested params when given array' do
+      subject.get '/dummy' do
+      end
+      subject.params do
+        requires :first
+        optional :second
+        optional :third, default: 'third-default'
+        optional :nested, type: Array do
+          optional :fourth
+        end
+      end
+      inner_params = nil
+      subject.get '/declared' do
+        inner_params = declared(params)
+        ''
+      end
+
+      get '/declared?first=present&nested[][fourth]=1&nested[][fourth]=2'
+      expect(last_response.status).to eq(200)
+      expect(inner_params[:nested].size).to eq 2
+    end
+
+    it 'builds nested params' do
+      inner_params = nil
+      subject.get '/declared' do
+        inner_params = declared(params)
+        ''
+      end
+
+      get '/declared?first=present&nested[fourth]=1'
+      expect(last_response.status).to eq(200)
+      expect(inner_params[:nested].keys.size).to eq 1
+    end
+
+    context 'sets nested array when the param is missing' do
+      it 'to be array when include_missing is true' do
+        inner_params = nil
+        subject.get '/declared' do
+          inner_params = declared(params, include_missing: true)
+          ''
+        end
+
+        get '/declared?first=present'
+        expect(last_response.status).to eq(200)
+        expect(inner_params[:nested]).to be_a(Array)
+      end
+
+      it 'to be nil when include_missing is false' do
+        inner_params = nil
+        subject.get '/declared' do
+          inner_params = declared(params, include_missing: false)
+          ''
+        end
+
+        get '/declared?first=present'
+        expect(last_response.status).to eq(200)
+        expect(inner_params[:nested]).to be_nil
+      end
+    end
+
+    it 'filters out any additional params that are given' do
+      inner_params = nil
+      subject.get '/declared' do
+        inner_params = declared(params)
+        ''
+      end
+      get '/declared?first=one&other=two'
+      expect(last_response.status).to eq(200)
+      expect(inner_params.key?(:other)).to eq false
+    end
+
+    it 'stringifies if that option is passed' do
+      inner_params = nil
+      subject.get '/declared' do
+        inner_params = declared(params, stringify: true)
+        ''
+      end
+
+      get '/declared?first=one&other=two'
+      expect(last_response.status).to eq(200)
+      expect(inner_params['first']).to eq 'one'
+    end
+
+    it 'does not include missing attributes if that option is passed' do
+      subject.get '/declared' do
+        error! 400, 'expected nil' if declared(params, include_missing: false)[:second]
+        ''
+      end
+
+      get '/declared?first=one&other=two'
+      expect(last_response.status).to eq(200)
+    end
+
+    it 'includes attributes with value that evaluates to false' do
+      subject.params do
+        requires :first
+        optional :boolean
+      end
+
+      subject.post '/declared' do
+        error!('expected false', 400) if declared(params, include_missing: false)[:boolean] != false
+        ''
+      end
+
+      post '/declared', MultiJson.dump(first: 'one', boolean: false), 'CONTENT_TYPE' => 'application/json'
+      expect(last_response.status).to eq(201)
+    end
+
+    it 'includes attributes with value that evaluates to nil' do
+      subject.params do
+        requires :first
+        optional :second
+      end
+
+      subject.post '/declared' do
+        error!('expected nil', 400) unless declared(params, include_missing: false)[:second].nil?
+        ''
+      end
+
+      post '/declared', MultiJson.dump(first: 'one', second: nil), 'CONTENT_TYPE' => 'application/json'
+      expect(last_response.status).to eq(201)
+    end
+
+    it 'does not include missing attributes when there are nested hashes' do
+      subject.get '/dummy' do
+      end
+
+      subject.params do
+        requires :first
+        optional :second
+        optional :third, default: nil
+        optional :nested, type: Hash do
+          optional :fourth, default: nil
+          optional :fifth, default: nil
+          requires :nested_nested, type: Hash do
+            optional :sixth, default: 'sixth-default'
+            optional :seven, default: nil
+          end
+        end
+      end
+
+      inner_params = nil
+      subject.get '/declared' do
+        inner_params = declared(params, include_missing: false)
+        ''
+      end
+
+      get '/declared?first=present&nested[fourth]=&nested[nested_nested][sixth]=sixth'
+
+      expect(last_response.status).to eq(200)
+      expect(inner_params[:first]).to eq 'present'
+      expect(inner_params[:nested].keys).to eq %w(fourth fifth nested_nested)
+      expect(inner_params[:nested][:fourth]).to eq ''
+      expect(inner_params[:nested][:nested_nested].keys).to eq %w(sixth seven)
+      expect(inner_params[:nested][:nested_nested][:sixth]).to eq 'sixth'
+    end
+  end
+
+  describe '#declared; call from child namespace' do
+    before do
+      subject.format :json
+      subject.namespace :something do
+        params do
+          requires :id, type: Integer
+        end
+        resource ':id' do
+          params do
+            requires :foo
+            optional :bar
+          end
+          get do
+            {
+              params: params,
+              declared_params: declared(params)
+            }
+          end
+          params do
+            requires :happy
+            optional :days
+          end
+          get '/test' do
+            {
+              params: params,
+              declared_params: declared(params, include_parent_namespaces: false)
+            }
+          end
+        end
+      end
+    end
+
+    it 'should include params defined in the parent namespace' do
+      get '/something/123', foo: 'test', extra: 'hello'
+      expect(last_response.status).to eq 200
+      json = JSON.parse(last_response.body, symbolize_names: true)
+      expect(json[:params][:id]).to eq 123
+      expect(json[:declared_params].keys).to match_array [:foo, :bar, :id]
+    end
+
+    it 'does not include params defined in the parent namespace with include_parent_namespaces: false' do
+      get '/something/123/test', happy: 'test', extra: 'hello'
+      expect(last_response.status).to eq 200
+      json = JSON.parse(last_response.body, symbolize_names: true)
+      expect(json[:params][:id]).to eq 123
+      expect(json[:declared_params].keys).to match_array [:happy, :days]
+    end
+  end
+
+  describe '#params' do
+    it 'is available to the caller' do
+      subject.get('/hey') do
+        params[:howdy]
+      end
+
+      get '/hey?howdy=hey'
+      expect(last_response.body).to eq('hey')
+    end
+
+    it 'parses from path segments' do
+      subject.get('/hey/:id') do
+        params[:id]
+      end
+
+      get '/hey/12'
+      expect(last_response.body).to eq('12')
+    end
+
+    it 'deeply converts nested params' do
+      subject.get '/location' do
+        params[:location][:city]
+      end
+      get '/location?location[city]=Dallas'
+      expect(last_response.body).to eq('Dallas')
+    end
+
+    context 'with special requirements' do
+      it 'parses email param with provided requirements for params' do
+        subject.get('/:person_email', requirements: { person_email: /.*/ }) do
+          params[:person_email]
+        end
+
+        get '/someone at example.com'
+        expect(last_response.body).to eq('someone at example.com')
+
+        get 'someone at example.com.pl'
+        expect(last_response.body).to eq('someone at example.com.pl')
+      end
+
+      it 'parses many params with provided regexps' do
+        subject.get('/:person_email/test/:number', requirements: { person_email: /someone@(.*).com/, number: /[0-9]/ }) do
+          params[:person_email] << params[:number]
+        end
+
+        get '/someone at example.com/test/1'
+        expect(last_response.body).to eq('someone at example.com1')
+
+        get '/someone at testing.wrong/test/1'
+        expect(last_response.status).to eq(404)
+
+        get 'someone at test.com/test/wrong_number'
+        expect(last_response.status).to eq(404)
+
+        get 'someone at test.com/wrong_middle/1'
+        expect(last_response.status).to eq(404)
+      end
+
+      context 'namespace requirements' do
+        before :each do
+          subject.namespace :outer, requirements: { person_email: /abc@(.*).com/ } do
+            get('/:person_email') do
+              params[:person_email]
+            end
+
+            namespace :inner, requirements: { number: /[0-9]/, person_email: /someone@(.*).com/ }do
+              get '/:person_email/test/:number' do
+                params[:person_email] << params[:number]
+              end
+            end
+          end
+        end
+        it 'parse email param with provided requirements for params' do
+          get '/outer/abc at example.com'
+          expect(last_response.body).to eq('abc at example.com')
+        end
+
+        it "should override outer namespace's requirements" do
+          get '/outer/inner/someone at testing.wrong/test/1'
+          expect(last_response.status).to eq(404)
+
+          get '/outer/inner/someone at testing.com/test/1'
+          expect(last_response.status).to eq(200)
+          expect(last_response.body).to eq('someone at testing.com1')
+        end
+      end
+    end
+
+    context 'from body parameters' do
+      before(:each) do
+        subject.post '/request_body' do
+          params[:user]
+        end
+        subject.put '/request_body' do
+          params[:user]
+        end
+      end
+
+      it 'converts JSON bodies to params' do
+        post '/request_body', MultiJson.dump(user: 'Bobby T.'), 'CONTENT_TYPE' => 'application/json'
+        expect(last_response.body).to eq('Bobby T.')
+      end
+
+      it 'does not convert empty JSON bodies to params' do
+        put '/request_body', '', 'CONTENT_TYPE' => 'application/json'
+        expect(last_response.body).to eq('')
+      end
+
+      it 'converts XML bodies to params' do
+        post '/request_body', '<user>Bobby T.</user>', 'CONTENT_TYPE' => 'application/xml'
+        expect(last_response.body).to eq('Bobby T.')
+      end
+
+      it 'converts XML bodies to params' do
+        put '/request_body', '<user>Bobby T.</user>', 'CONTENT_TYPE' => 'application/xml'
+        expect(last_response.body).to eq('Bobby T.')
+      end
+
+      it 'does not include parameters not defined by the body' do
+        subject.post '/omitted_params' do
+          error! 400, 'expected nil' if params[:version]
+          params[:user]
+        end
+        post '/omitted_params', MultiJson.dump(user: 'Bob'), 'CONTENT_TYPE' => 'application/json'
+        expect(last_response.status).to eq(201)
+        expect(last_response.body).to eq('Bob')
+      end
+    end
+
+    it 'responds with a 406 for an unsupported content-type' do
+      subject.format :json
+      # subject.content_type :json, "application/json"
+      subject.put '/request_body' do
+        params[:user]
+      end
+      put '/request_body', '<user>Bobby T.</user>', 'CONTENT_TYPE' => 'application/xml'
+      expect(last_response.status).to eq(406)
+      expect(last_response.body).to eq('{"error":"The requested content-type \'application/xml\' is not supported."}')
+    end
+
+    context 'content type with params' do
+      before do
+        subject.format :json
+        subject.content_type :json, 'application/json; charset=utf-8'
+
+        subject.post do
+          params[:data]
+        end
+        post '/', MultiJson.dump(data: { some: 'payload' }), 'CONTENT_TYPE' => 'application/json'
+      end
+
+      it 'should not response with 406 for same type without params' do
+        expect(last_response.status).not_to be 406
+      end
+
+      it 'should response with given content type in headers' do
+        expect(last_response.headers['Content-Type']).to eq 'application/json; charset=utf-8'
+      end
+    end
+
+    context 'precedence' do
+      before do
+        subject.format :json
+        subject.namespace '/:id' do
+          get do
+            {
+              params: params[:id]
+            }
+          end
+          post do
+            {
+              params: params[:id]
+            }
+          end
+          put do
+            {
+              params: params[:id]
+            }
+          end
+        end
+      end
+
+      it 'route string params have higher precedence than body params' do
+        post '/123', { id: 456 }.to_json
+        expect(JSON.parse(last_response.body)['params']).to eq '123'
+        put '/123', { id: 456 }.to_json
+        expect(JSON.parse(last_response.body)['params']).to eq '123'
+      end
+
+      it 'route string params have higher precedence than URL params' do
+        get '/123?id=456'
+        expect(JSON.parse(last_response.body)['params']).to eq '123'
+        post '/123?id=456'
+        expect(JSON.parse(last_response.body)['params']).to eq '123'
+      end
+    end
+  end
+
+  describe '#error!' do
+    it 'accepts a message' do
+      subject.get('/hey') do
+        error! 'This is not valid.'
+        'This is valid.'
+      end
+
+      get '/hey'
+      expect(last_response.status).to eq(500)
+      expect(last_response.body).to eq('This is not valid.')
+    end
+
+    it 'accepts a code' do
+      subject.get('/hey') do
+        error! 'Unauthorized.', 401
+      end
+
+      get '/hey'
+      expect(last_response.status).to eq(401)
+      expect(last_response.body).to eq('Unauthorized.')
+    end
+
+    it 'accepts an object and render it in format' do
+      subject.get '/hey' do
+        error!({ 'dude' => 'rad' }, 403)
+      end
+
+      get '/hey.json'
+      expect(last_response.status).to eq(403)
+      expect(last_response.body).to eq('{"dude":"rad"}')
+    end
+
+    it 'can specifiy headers' do
+      subject.get '/hey' do
+        error!({ 'dude' => 'rad' }, 403, 'X-Custom' => 'value')
+      end
+
+      get '/hey.json'
+      expect(last_response.status).to eq(403)
+      expect(last_response.headers['X-Custom']).to eq('value')
+    end
+
+    it 'sets the status code for the endpoint' do
+      memoized_endpoint = nil
+
+      subject.get '/hey' do
+        memoized_endpoint = self
+        error!({ 'dude' => 'rad' }, 403, 'X-Custom' => 'value')
+      end
+
+      get '/hey.json'
+
+      expect(memoized_endpoint.status).to eq(403)
+    end
+  end
+
+  describe '#redirect' do
+    it 'redirects to a url with status 302' do
+      subject.get('/hey') do
+        redirect '/ha'
+      end
+      get '/hey'
+      expect(last_response.status).to eq 302
+      expect(last_response.headers['Location']).to eq '/ha'
+      expect(last_response.body).to eq ''
+    end
+
+    it 'has status code 303 if it is not get request and it is http 1.1' do
+      subject.post('/hey') do
+        redirect '/ha'
+      end
+      post '/hey', {}, 'HTTP_VERSION' => 'HTTP/1.1'
+      expect(last_response.status).to eq 303
+      expect(last_response.headers['Location']).to eq '/ha'
+    end
+
+    it 'support permanent redirect' do
+      subject.get('/hey') do
+        redirect '/ha', permanent: true
+      end
+      get '/hey'
+      expect(last_response.status).to eq 301
+      expect(last_response.headers['Location']).to eq '/ha'
+      expect(last_response.body).to eq ''
+    end
+  end
+
+  it 'does not persist params between calls' do
+    subject.post('/new') do
+      params[:text]
+    end
+
+    post '/new', text: 'abc'
+    expect(last_response.body).to eq('abc')
+
+    post '/new', text: 'def'
+    expect(last_response.body).to eq('def')
+  end
+
+  it 'resets all instance variables (except block) between calls' do
+    subject.helpers do
+      def memoized
+        @memoized ||= params[:howdy]
+      end
+    end
+
+    subject.get('/hello') do
+      memoized
+    end
+
+    get '/hello?howdy=hey'
+    expect(last_response.body).to eq('hey')
+    get '/hello?howdy=yo'
+    expect(last_response.body).to eq('yo')
+  end
+
+  it 'allows explicit return calls' do
+    subject.get('/home') do
+      return 'Hello'
+    end
+
+    get '/home'
+    expect(last_response.status).to eq(200)
+    expect(last_response.body).to eq('Hello')
+  end
+
+  describe '.generate_api_method' do
+    it 'raises NameError if the method name is already in use' do
+      expect do
+        Grape::Endpoint.generate_api_method('version', &proc {})
+      end.to raise_error(NameError)
+    end
+    it 'raises ArgumentError if a block is not given' do
+      expect do
+        Grape::Endpoint.generate_api_method('GET without a block method')
+      end.to raise_error(ArgumentError)
+    end
+    it 'returns a Proc' do
+      expect(Grape::Endpoint.generate_api_method('GET test for a proc', &proc {})).to be_a Proc
+    end
+  end
+
+  context 'filters' do
+    describe 'before filters' do
+      it 'runs the before filter if set' do
+        subject.before { env['before_test'] = 'OK' }
+        subject.get('/before_test') { env['before_test'] }
+
+        get '/before_test'
+        expect(last_response.body).to eq('OK')
+      end
+    end
+
+    describe 'after filters' do
+      it 'overrides the response body if it sets it' do
+        subject.after { body 'after' }
+        subject.get('/after_test') { 'during' }
+        get '/after_test'
+        expect(last_response.body).to eq('after')
+      end
+
+      it 'does not override the response body with its return' do
+        subject.after { 'after' }
+        subject.get('/after_test') { 'body' }
+        get '/after_test'
+        expect(last_response.body).to eq('body')
+      end
+    end
+  end
+
+  context 'anchoring' do
+    verbs = %w(post get head delete put options patch)
+
+    verbs.each do |verb|
+      it 'allows for the anchoring option with a #{verb.upcase} method' do
+        subject.send(verb, '/example', anchor: true) do
+          verb
+        end
+        send(verb, '/example/and/some/more')
+        expect(last_response.status).to eql 404
+      end
+
+      it 'anchors paths by default for the #{verb.upcase} method' do
+        subject.send(verb, '/example') do
+          verb
+        end
+        send(verb, '/example/and/some/more')
+        expect(last_response.status).to eql 404
+      end
+
+      it 'responds to /example/and/some/more for the non-anchored #{verb.upcase} method' do
+        subject.send(verb, '/example', anchor: false) do
+          verb
+        end
+        send(verb, '/example/and/some/more')
+        expect(last_response.status).to eql verb == 'post' ? 201 : 200
+        expect(last_response.body).to eql verb == 'head' ? '' : verb
+      end
+    end
+  end
+
+  context 'request' do
+    it 'should be set to the url requested' do
+      subject.get('/url') do
+        request.url
+      end
+      get '/url'
+      expect(last_response.body).to eq('http://example.org/url')
+    end
+    ['v1', :v1].each do |version|
+      it 'should include version #{version}' do
+        subject.version version, using: :path
+        subject.get('/url') do
+          request.url
+        end
+        get "/#{version}/url"
+        expect(last_response.body).to eq("http://example.org/#{version}/url")
+      end
+    end
+    it 'should include prefix' do
+      subject.version 'v1', using: :path
+      subject.prefix 'api'
+      subject.get('/url') do
+        request.url
+      end
+      get '/api/v1/url'
+      expect(last_response.body).to eq('http://example.org/api/v1/url')
+    end
+  end
+
+  context 'version headers' do
+    before do
+      # NOTE: a 404 is returned instead of the 406 if cascade: false is not set.
+      subject.version 'v1', using: :header, vendor: 'ohanapi', cascade: false
+      subject.get '/test' do
+        'Hello!'
+      end
+    end
+
+    it 'result in a 406 response if they are invalid' do
+      get '/test', {}, 'HTTP_ACCEPT' => 'application/vnd.ohanapi.v1+json'
+      expect(last_response.status).to eq(406)
+    end
+
+    it 'result in a 406 response if they cannot be parsed by rack-accept' do
+      get '/test', {}, 'HTTP_ACCEPT' => 'application/vnd.ohanapi.v1+json; version=1'
+      expect(last_response.status).to eq(406)
+    end
+  end
+
+  context 'binary' do
+    before do
+      subject.get do
+        file FileStreamer.new(__FILE__)
+      end
+    end
+
+    it 'suports stream objects in response' do
+      get '/'
+      expect(last_response.status).to eq 200
+      expect(last_response.body).to eq File.read(__FILE__)
+    end
+  end
+end
diff --git a/spec/grape/entity_spec.rb b/spec/grape/entity_spec.rb
new file mode 100644
index 0000000..2677dd4
--- /dev/null
+++ b/spec/grape/entity_spec.rb
@@ -0,0 +1,329 @@
+require 'spec_helper'
+require 'grape_entity'
+
+describe Grape::Entity do
+  subject { Class.new(Grape::API) }
+
+  def app
+    subject
+  end
+
+  describe '#present' do
+    it 'sets the object as the body if no options are provided' do
+      inner_body = nil
+      subject.get '/example' do
+        present(abc: 'def')
+        inner_body = body
+      end
+      get '/example'
+      expect(inner_body).to eql(abc: 'def')
+    end
+
+    it 'calls through to the provided entity class if one is given' do
+      entity_mock = Object.new
+      allow(entity_mock).to receive(:represent)
+
+      subject.get '/example' do
+        present Object.new, with: entity_mock
+      end
+      get '/example'
+    end
+
+    it 'pulls a representation from the class options if it exists' do
+      entity = Class.new(Grape::Entity)
+      allow(entity).to receive(:represent).and_return('Hiya')
+
+      subject.represent Object, with: entity
+      subject.get '/example' do
+        present Object.new
+      end
+      get '/example'
+      expect(last_response.body).to eq('Hiya')
+    end
+
+    it 'pulls a representation from the class options if the presented object is a collection of objects' do
+      entity = Class.new(Grape::Entity)
+      allow(entity).to receive(:represent).and_return('Hiya')
+
+      class TestObject
+      end
+
+      class FakeCollection
+        def first
+          TestObject.new
+        end
+      end
+
+      subject.represent TestObject, with: entity
+      subject.get '/example' do
+        present [TestObject.new]
+      end
+
+      subject.get '/example2' do
+        present FakeCollection.new
+      end
+
+      get '/example'
+      expect(last_response.body).to eq('Hiya')
+
+      get '/example2'
+      expect(last_response.body).to eq('Hiya')
+    end
+
+    it 'pulls a representation from the class ancestor if it exists' do
+      entity = Class.new(Grape::Entity)
+      allow(entity).to receive(:represent).and_return('Hiya')
+
+      subclass = Class.new(Object)
+
+      subject.represent Object, with: entity
+      subject.get '/example' do
+        present subclass.new
+      end
+      get '/example'
+      expect(last_response.body).to eq('Hiya')
+    end
+
+    it 'automatically uses Klass::Entity if that exists' do
+      some_model = Class.new
+      entity = Class.new(Grape::Entity)
+      allow(entity).to receive(:represent).and_return('Auto-detect!')
+
+      some_model.const_set :Entity, entity
+
+      subject.get '/example' do
+        present some_model.new
+      end
+      get '/example'
+      expect(last_response.body).to eq('Auto-detect!')
+    end
+
+    it 'automatically uses Klass::Entity based on the first object in the collection being presented' do
+      some_model = Class.new
+      entity = Class.new(Grape::Entity)
+      allow(entity).to receive(:represent).and_return('Auto-detect!')
+
+      some_model.const_set :Entity, entity
+
+      subject.get '/example' do
+        present [some_model.new]
+      end
+      get '/example'
+      expect(last_response.body).to eq('Auto-detect!')
+    end
+
+    it 'does not run autodetection for Entity when explicitely provided' do
+      entity = Class.new(Grape::Entity)
+      some_array = []
+
+      subject.get '/example' do
+        present some_array, with: entity
+      end
+
+      expect(some_array).not_to receive(:first)
+      get '/example'
+    end
+
+    it 'does not use #first method on ActiveRecord::Relation to prevent needless sql query' do
+      entity = Class.new(Grape::Entity)
+      some_relation = Class.new
+      some_model = Class.new
+
+      allow(entity).to receive(:represent).and_return('Auto-detect!')
+      allow(some_relation).to receive(:first)
+      allow(some_relation).to receive(:klass).and_return(some_model)
+
+      some_model.const_set :Entity, entity
+
+      subject.get '/example' do
+        present some_relation
+      end
+
+      expect(some_relation).not_to receive(:first)
+      get '/example'
+      expect(last_response.body).to eq('Auto-detect!')
+    end
+
+    it 'autodetection does not use Entity if it is not a presenter' do
+      some_model = Class.new
+      entity = Class.new
+
+      some_model.class.const_set :Entity, entity
+
+      subject.get '/example' do
+        present some_model
+      end
+      get '/example'
+      expect(entity).not_to receive(:represent)
+    end
+
+    it 'adds a root key to the output if one is given' do
+      inner_body = nil
+      subject.get '/example' do
+        present({ abc: 'def' }, root: :root)
+        inner_body = body
+      end
+      get '/example'
+      expect(inner_body).to eql(root: { abc: 'def' })
+    end
+
+    [:json, :serializable_hash].each do |format|
+      it 'presents with #{format}' do
+        entity = Class.new(Grape::Entity)
+        entity.root 'examples', 'example'
+        entity.expose :id
+
+        subject.format format
+        subject.get '/example' do
+          c = Class.new do
+            attr_reader :id
+            def initialize(id)
+              @id = id
+            end
+          end
+          present c.new(1), with: entity
+        end
+
+        get '/example'
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('{"example":{"id":1}}')
+      end
+
+      it 'presents with #{format} collection' do
+        entity = Class.new(Grape::Entity)
+        entity.root 'examples', 'example'
+        entity.expose :id
+
+        subject.format format
+        subject.get '/examples' do
+          c = Class.new do
+            attr_reader :id
+            def initialize(id)
+              @id = id
+            end
+          end
+          examples = [c.new(1), c.new(2)]
+          present examples, with: entity
+        end
+
+        get '/examples'
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('{"examples":[{"id":1},{"id":2}]}')
+      end
+    end
+
+    it 'presents with xml' do
+      entity = Class.new(Grape::Entity)
+      entity.root 'examples', 'example'
+      entity.expose :name
+
+      subject.format :xml
+
+      subject.get '/example' do
+        c = Class.new do
+          attr_reader :name
+          def initialize(args)
+            @name = args[:name] || 'no name set'
+          end
+        end
+        present c.new(name: 'johnnyiller'), with: entity
+      end
+      get '/example'
+      expect(last_response.status).to eq(200)
+      expect(last_response.headers['Content-type']).to eq('application/xml')
+      expect(last_response.body).to eq <<-XML
+<?xml version="1.0" encoding="UTF-8"?>
+<hash>
+  <example>
+    <name>johnnyiller</name>
+  </example>
+</hash>
+XML
+    end
+
+    it 'presents with json' do
+      entity = Class.new(Grape::Entity)
+      entity.root 'examples', 'example'
+      entity.expose :name
+
+      subject.format :json
+
+      subject.get '/example' do
+        c = Class.new do
+          attr_reader :name
+          def initialize(args)
+            @name = args[:name] || 'no name set'
+          end
+        end
+        present c.new(name: 'johnnyiller'), with: entity
+      end
+      get '/example'
+      expect(last_response.status).to eq(200)
+      expect(last_response.headers['Content-type']).to eq('application/json')
+      expect(last_response.body).to eq('{"example":{"name":"johnnyiller"}}')
+    end
+
+    it 'presents with jsonp utilising Rack::JSONP' do
+      require 'rack/contrib'
+
+      # Include JSONP middleware
+      subject.use Rack::JSONP
+
+      entity = Class.new(Grape::Entity)
+      entity.root 'examples', 'example'
+      entity.expose :name
+
+      # Rack::JSONP expects a standard JSON response in UTF-8 format
+      subject.format :json
+      subject.formatter :json, lambda { |object, _|
+        object.to_json.encode('utf-8')
+      }
+
+      subject.get '/example' do
+        c = Class.new do
+          attr_reader :name
+          def initialize(args)
+            @name = args[:name] || 'no name set'
+          end
+        end
+
+        present c.new(name: 'johnnyiller'), with: entity
+      end
+
+      get '/example?callback=abcDef'
+      expect(last_response.status).to eq(200)
+      expect(last_response.headers['Content-type']).to eq('application/javascript')
+      expect(last_response.body).to include 'abcDef({"example":{"name":"johnnyiller"}})'
+    end
+
+    context 'present with multiple entities' do
+      it 'present with multiple entities using optional symbol' do
+        user = Class.new do
+          attr_reader :name
+          def initialize(args)
+            @name = args[:name] || 'no name set'
+          end
+        end
+        user1 = user.new(name: 'user1')
+        user2 = user.new(name: 'user2')
+
+        entity = Class.new(Grape::Entity)
+        entity.expose :name
+
+        subject.format :json
+        subject.get '/example' do
+          present :page, 1
+          present :user1, user1, with: entity
+          present :user2, user2, with: entity
+        end
+        get '/example'
+        expect_response_json = {
+          'page'  => 1,
+          'user1' => { 'name' => 'user1' },
+          'user2' => { 'name' => 'user2' }
+        }
+        expect(JSON(last_response.body)).to eq(expect_response_json)
+      end
+    end
+  end
+end
diff --git a/spec/grape/exceptions/body_parse_errors_spec.rb b/spec/grape/exceptions/body_parse_errors_spec.rb
new file mode 100644
index 0000000..2bd9179
--- /dev/null
+++ b/spec/grape/exceptions/body_parse_errors_spec.rb
@@ -0,0 +1,105 @@
+require 'spec_helper'
+
+describe Grape::Exceptions::ValidationErrors do
+  context 'api with rescue_from :all handler' do
+    subject { Class.new(Grape::API) }
+    before do
+      subject.rescue_from :all do |_e|
+        rack_response 'message was processed', 400
+      end
+      subject.params do
+        requires :beer
+      end
+      subject.post '/beer' do
+        'beer received'
+      end
+    end
+
+    def app
+      subject
+    end
+
+    context 'with content_type json' do
+      it 'can recover from failed body parsing' do
+        post '/beer', 'test', 'CONTENT_TYPE' => 'application/json'
+        expect(last_response.status).to eq 400
+        expect(last_response.body).to eq('message was processed')
+      end
+    end
+
+    context 'with content_type xml' do
+      it 'can recover from failed body parsing' do
+        post '/beer', 'test', 'CONTENT_TYPE' => 'application/xml'
+        expect(last_response.status).to eq 400
+        expect(last_response.body).to eq('message was processed')
+      end
+    end
+
+    context 'with content_type text' do
+      it 'can recover from failed body parsing' do
+        post '/beer', 'test', 'CONTENT_TYPE' => 'text/plain'
+        expect(last_response.status).to eq 400
+        expect(last_response.body).to eq('message was processed')
+      end
+    end
+
+    context 'with no specific content_type' do
+      it 'can recover from failed body parsing' do
+        post '/beer', 'test', {}
+        expect(last_response.status).to eq 400
+        expect(last_response.body).to eq('message was processed')
+      end
+    end
+  end
+
+  context 'api without a rescue handler' do
+    subject { Class.new(Grape::API) }
+    before do
+      subject.params do
+        requires :beer
+      end
+      subject.post '/beer' do
+        'beer received'
+      end
+    end
+
+    def app
+      subject
+    end
+
+    context 'and with content_type json' do
+      it 'can recover from failed body parsing' do
+        post '/beer', 'test', 'CONTENT_TYPE' => 'application/json'
+        expect(last_response.status).to eq 400
+        expect(last_response.body).to include('message body does not match declared format')
+        expect(last_response.body).to include('application/json')
+      end
+    end
+
+    context 'with content_type xml' do
+      it 'can recover from failed body parsing' do
+        post '/beer', 'test', 'CONTENT_TYPE' => 'application/xml'
+        expect(last_response.status).to eq 400
+        expect(last_response.body).to include('message body does not match declared format')
+        expect(last_response.body).to include('application/xml')
+      end
+    end
+
+    context 'with content_type text' do
+      it 'can recover from failed body parsing' do
+        post '/beer', 'test', 'CONTENT_TYPE' => 'text/plain'
+        expect(last_response.status).to eq 400
+        expect(last_response.body).to eq('beer is missing')
+      end
+    end
+
+    context 'and with no specific content_type' do
+      it 'can recover from failed body parsing' do
+        post '/beer', 'test', {}
+        expect(last_response.status).to eq 400
+        # plain response with text/html
+        expect(last_response.body).to eq('beer is missing')
+      end
+    end
+  end
+end
diff --git a/spec/grape/exceptions/invalid_accept_header_spec.rb b/spec/grape/exceptions/invalid_accept_header_spec.rb
new file mode 100644
index 0000000..ad202f5
--- /dev/null
+++ b/spec/grape/exceptions/invalid_accept_header_spec.rb
@@ -0,0 +1,330 @@
+require 'spec_helper'
+
+describe Grape::Exceptions::InvalidAcceptHeader do
+  shared_examples_for 'a valid request' do
+    it 'does return with status 200' do
+      expect(last_response.status).to eq 200
+    end
+    it 'does return the expected result' do
+      expect(last_response.body).to eq('beer received')
+    end
+  end
+  shared_examples_for 'a cascaded request' do
+    it 'does not find a matching route' do
+      expect(last_response.status).to eq 404
+    end
+  end
+  shared_examples_for 'a not-cascaded request' do
+    it 'does not include the X-Cascade=pass header' do
+      expect(last_response.headers['X-Cascade']).to be_nil
+    end
+    it 'does not accept the request' do
+      expect(last_response.status).to eq 406
+    end
+  end
+  shared_examples_for 'a rescued request' do
+    it 'does not include the X-Cascade=pass header' do
+      expect(last_response.headers['X-Cascade']).to be_nil
+    end
+    it 'does show rescue handler processing' do
+      expect(last_response.status).to eq 400
+      expect(last_response.body).to eq('message was processed')
+    end
+  end
+
+  context 'API with cascade=false and rescue_from :all handler' do
+    subject { Class.new(Grape::API) }
+    before do
+      subject.version 'v99', using: :header, vendor: 'vendorname', format: :json, cascade: false
+      subject.rescue_from :all do |e|
+        rack_response 'message was processed', 400, e[:headers]
+      end
+      subject.get '/beer' do
+        'beer received'
+      end
+    end
+
+    def app
+      subject
+    end
+
+    context 'that received a request with correct vendor and version' do
+      before { get '/beer', {}, 'HTTP_ACCEPT' => 'application/vnd.vendorname-v99' }
+      it_should_behave_like 'a valid request'
+    end
+
+    context 'that receives' do
+      context 'an invalid version in the request' do
+        before do
+          get '/beer', {}, 'HTTP_ACCEPT' => 'application/vnd.vendorname-v77',
+                           'CONTENT_TYPE' => 'application/json'
+        end
+        it_should_behave_like 'a rescued request'
+      end
+      context 'an invalid vendor in the request' do
+        before do
+          get '/beer', {}, 'HTTP_ACCEPT' => 'application/vnd.invalidvendor-v99',
+                           'CONTENT_TYPE' => 'application/json'
+        end
+        it_should_behave_like 'a rescued request'
+      end
+    end
+  end
+
+  context 'API with cascade=false and without a rescue handler' do
+    subject { Class.new(Grape::API) }
+    before do
+      subject.version 'v99', using: :header, vendor: 'vendorname', format: :json, cascade: false
+      subject.get '/beer' do
+        'beer received'
+      end
+    end
+
+    def app
+      subject
+    end
+
+    context 'that received a request with correct vendor and version' do
+      before { get '/beer', {}, 'HTTP_ACCEPT' => 'application/vnd.vendorname-v99' }
+      it_should_behave_like 'a valid request'
+    end
+
+    context 'that receives' do
+      context 'an invalid version in the request' do
+        before { get '/beer', {}, 'HTTP_ACCEPT' => 'application/vnd.vendorname-v77' }
+        it_should_behave_like 'a not-cascaded request'
+      end
+      context 'an invalid vendor in the request' do
+        before { get '/beer', {}, 'HTTP_ACCEPT' => 'application/vnd.invalidvendor-v99' }
+        it_should_behave_like 'a not-cascaded request'
+      end
+    end
+  end
+
+  context 'API with cascade=false and with rescue_from :all handler and http_codes' do
+    subject { Class.new(Grape::API) }
+    before do
+      subject.version 'v99', using: :header, vendor: 'vendorname', format: :json, cascade: false
+      subject.rescue_from :all do |e|
+        rack_response 'message was processed', 400, e[:headers]
+      end
+      subject.desc 'Get beer' do
+        failure [[400, 'Bad Request'], [401, 'Unauthorized'], [403, 'Forbidden'],
+                 [404, 'Resource not found'], [406, 'API vendor or version not found'],
+                 [500, 'Internal processing error']]
+      end
+      subject.get '/beer' do
+        'beer received'
+      end
+    end
+
+    def app
+      subject
+    end
+
+    context 'that received a request with correct vendor and version' do
+      before { get '/beer', {}, 'HTTP_ACCEPT' => 'application/vnd.vendorname-v99' }
+      it_should_behave_like 'a valid request'
+    end
+
+    context 'that receives' do
+      context 'an invalid version in the request' do
+        before do
+          get '/beer', {}, 'HTTP_ACCEPT' => 'application/vnd.vendorname-v77',
+                           'CONTENT_TYPE' => 'application/json'
+        end
+        it_should_behave_like 'a rescued request'
+      end
+      context 'an invalid vendor in the request' do
+        before do
+          get '/beer', {}, 'HTTP_ACCEPT' => 'application/vnd.invalidvendor-v99',
+                           'CONTENT_TYPE' => 'application/json'
+        end
+        it_should_behave_like 'a rescued request'
+      end
+    end
+  end
+
+  context 'API with cascade=false, http_codes but without a rescue handler' do
+    subject { Class.new(Grape::API) }
+    before do
+      subject.version 'v99', using: :header, vendor: 'vendorname', format: :json, cascade: false
+      subject.desc 'Get beer' do
+        failure [[400, 'Bad Request'], [401, 'Unauthorized'], [403, 'Forbidden'],
+                 [404, 'Resource not found'], [406, 'API vendor or version not found'],
+                 [500, 'Internal processing error']]
+      end
+      subject.get '/beer' do
+        'beer received'
+      end
+    end
+
+    def app
+      subject
+    end
+
+    context 'that received a request with correct vendor and version' do
+      before { get '/beer', {}, 'HTTP_ACCEPT' => 'application/vnd.vendorname-v99' }
+      it_should_behave_like 'a valid request'
+    end
+
+    context 'that receives' do
+      context 'an invalid version in the request' do
+        before { get '/beer', {}, 'HTTP_ACCEPT' => 'application/vnd.vendorname-v77' }
+        it_should_behave_like 'a not-cascaded request'
+      end
+      context 'an invalid vendor in the request' do
+        before { get '/beer', {}, 'HTTP_ACCEPT' => 'application/vnd.invalidvendor-v99' }
+        it_should_behave_like 'a not-cascaded request'
+      end
+    end
+  end
+
+  context 'API with cascade=true and rescue_from :all handler' do
+    subject { Class.new(Grape::API) }
+    before do
+      subject.version 'v99', using: :header, vendor: 'vendorname', format: :json, cascade: true
+      subject.rescue_from :all do |e|
+        rack_response 'message was processed', 400, e[:headers]
+      end
+      subject.get '/beer' do
+        'beer received'
+      end
+    end
+
+    def app
+      subject
+    end
+
+    context 'that received a request with correct vendor and version' do
+      before { get '/beer', {}, 'HTTP_ACCEPT' => 'application/vnd.vendorname-v99' }
+      it_should_behave_like 'a valid request'
+    end
+
+    context 'that receives' do
+      context 'an invalid version in the request' do
+        before do
+          get '/beer', {}, 'HTTP_ACCEPT' => 'application/vnd.vendorname-v77',
+                           'CONTENT_TYPE' => 'application/json'
+        end
+        it_should_behave_like 'a cascaded request'
+      end
+      context 'an invalid vendor in the request' do
+        before do
+          get '/beer', {}, 'HTTP_ACCEPT' => 'application/vnd.invalidvendor-v99',
+                           'CONTENT_TYPE' => 'application/json'
+        end
+        it_should_behave_like 'a cascaded request'
+      end
+    end
+  end
+
+  context 'API with cascade=true and without a rescue handler' do
+    subject { Class.new(Grape::API) }
+    before do
+      subject.version 'v99', using: :header, vendor: 'vendorname', format: :json, cascade: true
+      subject.get '/beer' do
+        'beer received'
+      end
+    end
+
+    def app
+      subject
+    end
+
+    context 'that received a request with correct vendor and version' do
+      before { get '/beer', {}, 'HTTP_ACCEPT' => 'application/vnd.vendorname-v99' }
+      it_should_behave_like 'a valid request'
+    end
+
+    context 'that receives' do
+      context 'an invalid version in the request' do
+        before { get '/beer', {}, 'HTTP_ACCEPT' => 'application/vnd.vendorname-v77' }
+        it_should_behave_like 'a cascaded request'
+      end
+      context 'an invalid vendor in the request' do
+        before { get '/beer', {}, 'HTTP_ACCEPT' => 'application/vnd.invalidvendor-v99' }
+        it_should_behave_like 'a cascaded request'
+      end
+    end
+  end
+
+  context 'API with cascade=true and with rescue_from :all handler and http_codes' do
+    subject { Class.new(Grape::API) }
+    before do
+      subject.version 'v99', using: :header, vendor: 'vendorname', format: :json, cascade: true
+      subject.rescue_from :all do |e|
+        rack_response 'message was processed', 400, e[:headers]
+      end
+      subject.desc 'Get beer' do
+        failure [[400, 'Bad Request'], [401, 'Unauthorized'], [403, 'Forbidden'],
+                 [404, 'Resource not found'], [406, 'API vendor or version not found'],
+                 [500, 'Internal processing error']]
+      end
+      subject.get '/beer' do
+        'beer received'
+      end
+    end
+
+    def app
+      subject
+    end
+
+    context 'that received a request with correct vendor and version' do
+      before { get '/beer', {}, 'HTTP_ACCEPT' => 'application/vnd.vendorname-v99' }
+      it_should_behave_like 'a valid request'
+    end
+
+    context 'that receives' do
+      context 'an invalid version in the request' do
+        before do
+          get '/beer', {}, 'HTTP_ACCEPT' => 'application/vnd.vendorname-v77',
+                           'CONTENT_TYPE' => 'application/json'
+        end
+        it_should_behave_like 'a cascaded request'
+      end
+      context 'an invalid vendor in the request' do
+        before do
+          get '/beer', {}, 'HTTP_ACCEPT' => 'application/vnd.invalidvendor-v99',
+                           'CONTENT_TYPE' => 'application/json'
+        end
+        it_should_behave_like 'a cascaded request'
+      end
+    end
+  end
+
+  context 'API with cascade=true, http_codes but without a rescue handler' do
+    subject { Class.new(Grape::API) }
+    before do
+      subject.version 'v99', using: :header, vendor: 'vendorname', format: :json, cascade: true
+      subject.desc 'Get beer' do
+        failure [[400, 'Bad Request'], [401, 'Unauthorized'], [403, 'Forbidden'],
+                 [404, 'Resource not found'], [406, 'API vendor or version not found'],
+                 [500, 'Internal processing error']]
+      end
+      subject.get '/beer' do
+        'beer received'
+      end
+    end
+
+    def app
+      subject
+    end
+
+    context 'that received a request with correct vendor and version' do
+      before { get '/beer', {}, 'HTTP_ACCEPT' => 'application/vnd.vendorname-v99' }
+      it_should_behave_like 'a valid request'
+    end
+
+    context 'that receives' do
+      context 'an invalid version in the request' do
+        before { get '/beer', {},  'HTTP_ACCEPT' => 'application/vnd.vendorname-v77' }
+        it_should_behave_like 'a cascaded request'
+      end
+      context 'an invalid vendor in the request' do
+        before { get '/beer', {}, 'HTTP_ACCEPT' => 'application/vnd.invalidvendor-v99' }
+        it_should_behave_like 'a cascaded request'
+      end
+    end
+  end
+end
diff --git a/spec/grape/exceptions/invalid_formatter_spec.rb b/spec/grape/exceptions/invalid_formatter_spec.rb
new file mode 100644
index 0000000..e98ab75
--- /dev/null
+++ b/spec/grape/exceptions/invalid_formatter_spec.rb
@@ -0,0 +1,16 @@
+# encoding: utf-8
+require 'spec_helper'
+
+describe Grape::Exceptions::InvalidFormatter do
+  describe '#message' do
+    let(:error) do
+      described_class.new(String, 'xml')
+    end
+
+    it 'contains the problem in the message' do
+      expect(error.message).to include(
+        'cannot convert String to xml'
+      )
+    end
+  end
+end
diff --git a/spec/grape/exceptions/invalid_versioner_option_spec.rb b/spec/grape/exceptions/invalid_versioner_option_spec.rb
new file mode 100644
index 0000000..1e0dde0
--- /dev/null
+++ b/spec/grape/exceptions/invalid_versioner_option_spec.rb
@@ -0,0 +1,16 @@
+# encoding: utf-8
+require 'spec_helper'
+
+describe Grape::Exceptions::InvalidVersionerOption do
+  describe '#message' do
+    let(:error) do
+      described_class.new('headers')
+    end
+
+    it 'contains the problem in the message' do
+      expect(error.message).to include(
+        'Unknown :using for versioner: headers'
+      )
+    end
+  end
+end
diff --git a/spec/grape/exceptions/missing_mime_type_spec.rb b/spec/grape/exceptions/missing_mime_type_spec.rb
new file mode 100644
index 0000000..5ae6970
--- /dev/null
+++ b/spec/grape/exceptions/missing_mime_type_spec.rb
@@ -0,0 +1,17 @@
+require 'spec_helper'
+
+describe Grape::Exceptions::MissingMimeType do
+  describe '#message' do
+    let(:error) do
+      described_class.new('new_json')
+    end
+
+    it 'contains the problem in the message' do
+      expect(error.message).to include 'missing mime type for new_json'
+    end
+
+    it 'contains the resolution in the message' do
+      expect(error.message).to include "or add your own with content_type :new_json, 'application/new_json' "
+    end
+  end
+end
diff --git a/spec/grape/exceptions/missing_option_spec.rb b/spec/grape/exceptions/missing_option_spec.rb
new file mode 100644
index 0000000..2aaac32
--- /dev/null
+++ b/spec/grape/exceptions/missing_option_spec.rb
@@ -0,0 +1,16 @@
+# encoding: utf-8
+require 'spec_helper'
+
+describe Grape::Exceptions::MissingOption do
+  describe '#message' do
+    let(:error) do
+      described_class.new(:path)
+    end
+
+    it 'contains the problem in the message' do
+      expect(error.message).to include(
+        'You must specify :path options.'
+      )
+    end
+  end
+end
diff --git a/spec/grape/exceptions/unknown_options_spec.rb b/spec/grape/exceptions/unknown_options_spec.rb
new file mode 100644
index 0000000..ca49321
--- /dev/null
+++ b/spec/grape/exceptions/unknown_options_spec.rb
@@ -0,0 +1,16 @@
+# encoding: utf-8
+require 'spec_helper'
+
+describe Grape::Exceptions::UnknownOptions do
+  describe '#message' do
+    let(:error) do
+      described_class.new([:a, :b])
+    end
+
+    it 'contains the problem in the message' do
+      expect(error.message).to include(
+        'unknown options: '
+      )
+    end
+  end
+end
diff --git a/spec/grape/exceptions/unknown_validator_spec.rb b/spec/grape/exceptions/unknown_validator_spec.rb
new file mode 100644
index 0000000..8015ba9
--- /dev/null
+++ b/spec/grape/exceptions/unknown_validator_spec.rb
@@ -0,0 +1,16 @@
+# encoding: utf-8
+require 'spec_helper'
+
+describe Grape::Exceptions::UnknownValidator do
+  describe '#message' do
+    let(:error) do
+      described_class.new('gt_10')
+    end
+
+    it 'contains the problem in the message' do
+      expect(error.message).to include(
+        'unknown validator: gt_10'
+      )
+    end
+  end
+end
diff --git a/spec/grape/exceptions/validation_errors_spec.rb b/spec/grape/exceptions/validation_errors_spec.rb
new file mode 100644
index 0000000..a3fc2fc
--- /dev/null
+++ b/spec/grape/exceptions/validation_errors_spec.rb
@@ -0,0 +1,49 @@
+require 'spec_helper'
+require 'ostruct'
+
+describe Grape::Exceptions::ValidationErrors do
+  let(:validation_message) { 'FooBar is invalid' }
+  let(:validation_error) { OpenStruct.new(params: [validation_message]) }
+
+  context 'message' do
+    context 'is not repeated' do
+      let(:error) do
+        described_class.new(errors: [validation_error, validation_error])
+      end
+      subject(:message) { error.message.split(',').map(&:strip) }
+
+      it { expect(message).to include validation_message }
+      it { expect(message.size).to eq 1 }
+    end
+  end
+
+  context 'api' do
+    subject { Class.new(Grape::API) }
+
+    def app
+      subject
+    end
+
+    it 'can return structured json with separate fields' do
+      subject.format :json
+      subject.rescue_from Grape::Exceptions::ValidationErrors do |e|
+        error!(e, 400)
+      end
+      subject.params do
+        optional :beer
+        optional :wine
+        optional :juice
+        exactly_one_of :beer, :wine, :juice
+      end
+      subject.get '/exactly_one_of' do
+        'exactly_one_of works!'
+      end
+      get '/exactly_one_of', beer: 'string', wine: 'anotherstring'
+      expect(last_response.status).to eq(400)
+      expect(JSON.parse(last_response.body)).to eq([
+        'params' => %w(beer wine),
+        'messages' => ['are mutually exclusive']
+      ])
+    end
+  end
+end
diff --git a/spec/grape/integration/rack_spec.rb b/spec/grape/integration/rack_spec.rb
new file mode 100644
index 0000000..259deef
--- /dev/null
+++ b/spec/grape/integration/rack_spec.rb
@@ -0,0 +1,33 @@
+require 'spec_helper'
+
+describe Rack do
+  it 'correctly populates params from a Tempfile' do
+    input = Tempfile.new 'rubbish'
+    begin
+      app = Class.new(Grape::API) do
+        format :json
+        post do
+          { params_keys: params.keys }
+        end
+      end
+      input.write({ test: '123' * 10_000 }.to_json)
+      input.rewind
+      options = {
+        input: input,
+        method: 'POST',
+        'CONTENT_TYPE' => 'application/json'
+      }
+      env = Rack::MockRequest.env_for('/', options)
+
+      unless RUBY_PLATFORM == 'java'
+        major, minor, release = Rack.release.split('.').map(&:to_i)
+        pending 'Rack 1.5.3 or 1.6.1 required' unless major >= 1 && ((minor == 5 && release >= 3) || (minor >= 6))
+      end
+
+      expect(JSON.parse(app.call(env)[2].body.first)['params_keys']).to match_array('test')
+    ensure
+      input.close
+      input.unlink
+    end
+  end
+end
diff --git a/spec/grape/loading_spec.rb b/spec/grape/loading_spec.rb
new file mode 100644
index 0000000..a3ed887
--- /dev/null
+++ b/spec/grape/loading_spec.rb
@@ -0,0 +1,44 @@
+require 'spec_helper'
+
+describe Grape::API do
+  let(:jobs_api) do
+    Class.new(Grape::API) do
+      namespace :one do
+        namespace :two do
+          namespace :three do
+            get :one do
+            end
+            get :two do
+            end
+          end
+        end
+      end
+    end
+  end
+
+  let(:combined_api) do
+    JobsApi = jobs_api
+    Class.new(Grape::API) do
+      version :v1, using: :accept_version_header, cascade: true
+      mount JobsApi
+    end
+  end
+
+  subject do
+    CombinedApi = combined_api
+    Class.new(Grape::API) do
+      format :json
+      mount CombinedApi => '/'
+    end
+  end
+
+  def app
+    subject
+  end
+
+  it 'execute first request in reasonable time' do
+    started = Time.now
+    get '/mount1/nested/test_method'
+    expect(Time.now - started).to be < 5
+  end
+end
diff --git a/spec/grape/middleware/auth/base_spec.rb b/spec/grape/middleware/auth/base_spec.rb
new file mode 100644
index 0000000..2a2a6ab
--- /dev/null
+++ b/spec/grape/middleware/auth/base_spec.rb
@@ -0,0 +1,30 @@
+require 'spec_helper'
+require 'base64'
+
+describe Grape::Middleware::Auth::Base do
+  subject do
+    Class.new(Grape::API) do
+      http_basic realm: 'my_realm' do |user, password|
+        user && password && user == password
+      end
+      get '/authorized' do
+        'DONE'
+      end
+    end
+  end
+
+  def app
+    subject
+  end
+
+  it 'authenticates if given valid creds' do
+    get '/authorized', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('admin', 'admin')
+    expect(last_response.status).to eq(200)
+    expect(last_response.body).to eq('DONE')
+  end
+
+  it 'throws a 401 is wrong auth is given' do
+    get '/authorized', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('admin', 'wrong')
+    expect(last_response.status).to eq(401)
+  end
+end
diff --git a/spec/grape/middleware/auth/dsl_spec.rb b/spec/grape/middleware/auth/dsl_spec.rb
new file mode 100644
index 0000000..4f969b4
--- /dev/null
+++ b/spec/grape/middleware/auth/dsl_spec.rb
@@ -0,0 +1,51 @@
+require 'spec_helper'
+
+describe Grape::Middleware::Auth::DSL do
+  subject { Class.new(Grape::API) }
+
+  let(:block) { ->() {} }
+  let(:settings) do
+    {
+      opaque: 'secret',
+      proc: block,
+      realm: 'API Authorization',
+      type: :http_digest
+    }
+  end
+
+  describe '.auth' do
+    it 'stets auth parameters' do
+      expect(subject).to receive(:use).with(Grape::Middleware::Auth::Base, settings)
+
+      subject.auth :http_digest, realm: settings[:realm], opaque: settings[:opaque], &settings[:proc]
+      expect(subject.auth).to eq(settings)
+    end
+
+    it 'can be called multiple times' do
+      expect(subject).to receive(:use).with(Grape::Middleware::Auth::Base, settings)
+      expect(subject).to receive(:use).with(Grape::Middleware::Auth::Base, settings.merge(realm: 'super_secret'))
+
+      subject.auth :http_digest, realm: settings[:realm], opaque: settings[:opaque], &settings[:proc]
+      first_settings = subject.auth
+
+      subject.auth :http_digest, realm: 'super_secret', opaque: settings[:opaque], &settings[:proc]
+
+      expect(subject.auth).to eq(settings.merge(realm: 'super_secret'))
+      expect(subject.auth.object_id).not_to eq(first_settings.object_id)
+    end
+  end
+
+  describe '.http_basic' do
+    it 'stets auth parameters' do
+      subject.http_basic realm: 'my_realm', &settings[:proc]
+      expect(subject.auth).to eq(realm: 'my_realm', type: :http_basic, proc: block)
+    end
+  end
+
+  describe '.http_digest' do
+    it 'stets auth parameters' do
+      subject.http_digest realm: 'my_realm', opaque: 'my_opaque', &settings[:proc]
+      expect(subject.auth).to eq(realm: 'my_realm', type: :http_digest, proc: block, opaque: 'my_opaque')
+    end
+  end
+end
diff --git a/spec/grape/middleware/auth/strategies_spec.rb b/spec/grape/middleware/auth/strategies_spec.rb
new file mode 100644
index 0000000..9a43e7b
--- /dev/null
+++ b/spec/grape/middleware/auth/strategies_spec.rb
@@ -0,0 +1,80 @@
+require 'spec_helper'
+
+require 'base64'
+
+describe Grape::Middleware::Auth::Strategies do
+  context 'Basic Auth' do
+    def app
+      proc = ->(u, p) { u && p && u == p }
+      Rack::Builder.new do |b|
+        b.use Grape::Middleware::Error
+        b.use(Grape::Middleware::Auth::Base, type: :http_basic, proc: proc)
+        b.run ->(_env) { [200, {}, ['Hello there.']] }
+      end
+    end
+
+    it 'throws a 401 if no auth is given' do
+      @proc = -> { false }
+      get '/whatever'
+      expect(last_response.status).to eq(401)
+    end
+
+    it 'authenticates if given valid creds' do
+      get '/whatever', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('admin', 'admin')
+      expect(last_response.status).to eq(200)
+    end
+
+    it 'throws a 401 is wrong auth is given' do
+      get '/whatever', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('admin', 'wrong')
+      expect(last_response.status).to eq(401)
+    end
+  end
+
+  context 'Digest MD5 Auth' do
+    RSpec::Matchers.define :be_challenge do
+      match do |actual_response|
+        actual_response.status == 401 &&
+          actual_response['WWW-Authenticate'] =~ /^Digest / &&
+          actual_response.body.empty?
+      end
+    end
+
+    module StrategiesSpec
+      class Test < Grape::API
+        http_digest(realm: 'Test Api', opaque: 'secret') do |username|
+          { 'foo' => 'bar' }[username]
+        end
+
+        get '/test' do
+          [{ hey: 'you' }, { there: 'bar' }, { foo: 'baz' }]
+        end
+      end
+    end
+
+    def app
+      StrategiesSpec::Test
+    end
+
+    it 'is a digest authentication challenge' do
+      get '/test'
+      expect(last_response).to be_challenge
+    end
+
+    it 'throws a 401 if no auth is given' do
+      get '/test'
+      expect(last_response.status).to eq(401)
+    end
+
+    it 'authenticates if given valid creds' do
+      digest_authorize 'foo', 'bar'
+      get '/test'
+      expect(last_response.status).to eq(200)
+    end
+
+    it 'throws a 401 if given invalid creds' do
+      digest_authorize 'bar', 'foo'
+      get '/test'
+      expect(last_response.status).to eq(401)
+    end
+  end
+end
diff --git a/spec/grape/middleware/base_spec.rb b/spec/grape/middleware/base_spec.rb
new file mode 100644
index 0000000..b45634a
--- /dev/null
+++ b/spec/grape/middleware/base_spec.rb
@@ -0,0 +1,78 @@
+require 'spec_helper'
+
+describe Grape::Middleware::Base do
+  subject { Grape::Middleware::Base.new(blank_app) }
+  let(:blank_app) { ->(_) { [200, {}, 'Hi there.'] } }
+
+  before do
+    # Keep it one object for testing.
+    allow(subject).to receive(:dup).and_return(subject)
+  end
+
+  it 'has the app as an accessor' do
+    expect(subject.app).to eq(blank_app)
+  end
+
+  it 'calls through to the app' do
+    expect(subject.call({})).to eq([200, {}, 'Hi there.'])
+  end
+
+  context 'callbacks' do
+    it 'calls #before' do
+      expect(subject).to receive(:before)
+    end
+
+    it 'calls #after' do
+      expect(subject).to receive(:after)
+    end
+
+    after { subject.call!({}) }
+  end
+
+  it 'is able to access the response' do
+    subject.call({})
+    expect(subject.response).to be_kind_of(Rack::Response)
+  end
+
+  describe '#response' do
+    subject { Grape::Middleware::Base.new(response) }
+    let(:response) { ->(_) { [204, { abc: 1 }, 'test'] } }
+
+    it 'status' do
+      subject.call({})
+      expect(subject.response.status).to eq(204)
+    end
+
+    it 'body' do
+      subject.call({})
+      expect(subject.response.body).to eq(['test'])
+    end
+
+    it 'header' do
+      subject.call({})
+      expect(subject.response.header).to have_key(:abc)
+    end
+  end
+
+  context 'options' do
+    it 'persists options passed at initialization' do
+      expect(Grape::Middleware::Base.new(blank_app, abc: true).options[:abc]).to be true
+    end
+
+    context 'defaults' do
+      class ExampleWare < Grape::Middleware::Base
+        def default_options
+          { monkey: true }
+        end
+      end
+
+      it 'persists the default options' do
+        expect(ExampleWare.new(blank_app).options[:monkey]).to be true
+      end
+
+      it 'overrides default options when provided' do
+        expect(ExampleWare.new(blank_app, monkey: false).options[:monkey]).to be false
+      end
+    end
+  end
+end
diff --git a/spec/grape/middleware/error_spec.rb b/spec/grape/middleware/error_spec.rb
new file mode 100644
index 0000000..5b2fcdc
--- /dev/null
+++ b/spec/grape/middleware/error_spec.rb
@@ -0,0 +1,77 @@
+require 'spec_helper'
+require 'grape-entity'
+
+describe Grape::Middleware::Error do
+  module ErrorSpec
+    class ErrorEntity < Grape::Entity
+      expose :code
+      expose :static
+
+      def static
+        'static text'
+      end
+    end
+  end
+  class ErrApp
+    class << self
+      attr_accessor :error
+      attr_accessor :format
+
+      def call(_env)
+        throw :error, error
+      end
+    end
+  end
+
+  def app
+    opts = options
+    Rack::Builder.app do
+      use Spec::Support::EndpointFaker
+      use Grape::Middleware::Error, opts
+      run ErrApp
+    end
+  end
+
+  let(:options) {  { default_message: 'Aww, hamburgers.' } }
+
+  it 'sets the status code appropriately' do
+    ErrApp.error = { status: 410 }
+    get '/'
+    expect(last_response.status).to eq(410)
+  end
+
+  it 'sets the error message appropriately' do
+    ErrApp.error = { message: 'Awesome stuff.' }
+    get '/'
+    expect(last_response.body).to eq('Awesome stuff.')
+  end
+
+  it 'defaults to a 500 status' do
+    ErrApp.error = {}
+    get '/'
+    expect(last_response.status).to eq(500)
+  end
+
+  it 'has a default message' do
+    ErrApp.error = {}
+    get '/'
+    expect(last_response.body).to eq('Aww, hamburgers.')
+  end
+
+  context 'with http code' do
+    let(:options) {  { default_message: 'Aww, hamburgers.' } }
+    it 'adds the status code if wanted' do
+      ErrApp.error = { message: { code: 200 } }
+      get '/'
+
+      expect(last_response.body).to eq({ code: 200 }.to_json)
+    end
+
+    it 'presents an error message' do
+      ErrApp.error = { message: { code: 200, with: ErrorSpec::ErrorEntity } }
+      get '/'
+
+      expect(last_response.body).to eq({ code: 200, static: 'static text' }.to_json)
+    end
+  end
+end
diff --git a/spec/grape/middleware/exception_spec.rb b/spec/grape/middleware/exception_spec.rb
new file mode 100644
index 0000000..34dc7a9
--- /dev/null
+++ b/spec/grape/middleware/exception_spec.rb
@@ -0,0 +1,195 @@
+require 'spec_helper'
+require 'active_support/core_ext/hash'
+
+describe Grape::Middleware::Error do
+  # raises a text exception
+  class ExceptionApp
+    class << self
+      def call(_env)
+        fail 'rain!'
+      end
+    end
+  end
+
+  # raises a hash error
+  class ErrorHashApp
+    class << self
+      def error!(message, status)
+        throw :error, message: { error: message, detail: 'missing widget' }, status: status
+      end
+
+      def call(_env)
+        error!('rain!', 401)
+      end
+    end
+  end
+
+  # raises an error!
+  class AccessDeniedApp
+    class << self
+      def error!(message, status)
+        throw :error, message: message, status: status
+      end
+
+      def call(_env)
+        error!('Access Denied', 401)
+      end
+    end
+  end
+
+  # raises a custom error
+  class CustomError < Grape::Exceptions::Base
+  end
+
+  class CustomErrorApp
+    class << self
+      def call(_env)
+        fail CustomError, status: 400, message: 'failed validation'
+      end
+    end
+  end
+
+  attr_reader :app
+
+  it 'does not trap errors by default' do
+    @app ||= Rack::Builder.app do
+      use Spec::Support::EndpointFaker
+      use Grape::Middleware::Error
+      run ExceptionApp
+    end
+    expect { get '/' }.to raise_error
+  end
+
+  context 'with rescue_all set to true' do
+    it 'sets the message appropriately' do
+      @app ||= Rack::Builder.app do
+        use Spec::Support::EndpointFaker
+        use Grape::Middleware::Error, rescue_all: true
+        run ExceptionApp
+      end
+      get '/'
+      expect(last_response.body).to eq('rain!')
+    end
+
+    it 'defaults to a 500 status' do
+      @app ||= Rack::Builder.app do
+        use Spec::Support::EndpointFaker
+        use Grape::Middleware::Error, rescue_all: true
+        run ExceptionApp
+      end
+      get '/'
+      expect(last_response.status).to eq(500)
+    end
+
+    it 'is possible to specify a different default status code' do
+      @app ||= Rack::Builder.app do
+        use Spec::Support::EndpointFaker
+        use Grape::Middleware::Error, rescue_all: true, default_status: 500
+        run ExceptionApp
+      end
+      get '/'
+      expect(last_response.status).to eq(500)
+    end
+
+    it 'is possible to return errors in json format' do
+      @app ||= Rack::Builder.app do
+        use Spec::Support::EndpointFaker
+        use Grape::Middleware::Error, rescue_all: true, format: :json
+        run ExceptionApp
+      end
+      get '/'
+      expect(last_response.body).to eq('{"error":"rain!"}')
+    end
+
+    it 'is possible to return hash errors in json format' do
+      @app ||= Rack::Builder.app do
+        use Spec::Support::EndpointFaker
+        use Grape::Middleware::Error, rescue_all: true, format: :json
+        run ErrorHashApp
+      end
+      get '/'
+      expect(['{"error":"rain!","detail":"missing widget"}',
+              '{"detail":"missing widget","error":"rain!"}']).to include(last_response.body)
+    end
+
+    it 'is possible to return errors in jsonapi format' do
+      @app ||= Rack::Builder.app do
+        use Spec::Support::EndpointFaker
+        use Grape::Middleware::Error, rescue_all: true, format: :jsonapi
+        run ExceptionApp
+      end
+      get '/'
+      expect(last_response.body).to eq('{"error":"rain!"}')
+    end
+
+    it 'is possible to return hash errors in jsonapi format' do
+      @app ||= Rack::Builder.app do
+        use Spec::Support::EndpointFaker
+        use Grape::Middleware::Error, rescue_all: true, format: :jsonapi
+        run ErrorHashApp
+      end
+      get '/'
+      expect(['{"error":"rain!","detail":"missing widget"}',
+              '{"detail":"missing widget","error":"rain!"}']).to include(last_response.body)
+    end
+
+    it 'is possible to return errors in xml format' do
+      @app ||= Rack::Builder.app do
+        use Spec::Support::EndpointFaker
+        use Grape::Middleware::Error, rescue_all: true, format: :xml
+        run ExceptionApp
+      end
+      get '/'
+      expect(last_response.body).to eq("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<error>\n  <message>rain!</message>\n</error>\n")
+    end
+
+    it 'is possible to return hash errors in xml format' do
+      @app ||= Rack::Builder.app do
+        use Spec::Support::EndpointFaker
+        use Grape::Middleware::Error, rescue_all: true, format: :xml
+        run ErrorHashApp
+      end
+      get '/'
+      expect(["<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<error>\n  <detail>missing widget</detail>\n  <error>rain!</error>\n</error>\n",
+              "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<error>\n  <error>rain!</error>\n  <detail>missing widget</detail>\n</error>\n"]).to include(last_response.body)
+    end
+
+    it 'is possible to specify a custom formatter' do
+      @app ||= Rack::Builder.app do
+        use Spec::Support::EndpointFaker
+        use Grape::Middleware::Error, rescue_all: true,
+                                      format: :custom,
+                                      error_formatters: {
+                                        custom: lambda do |message, _backtrace, _options, _env|
+                                          { custom_formatter: message }.inspect
+                                        end
+                                      }
+        run ExceptionApp
+      end
+      get '/'
+      expect(last_response.body).to eq('{:custom_formatter=>"rain!"}')
+    end
+
+    it 'does not trap regular error! codes' do
+      @app ||= Rack::Builder.app do
+        use Spec::Support::EndpointFaker
+        use Grape::Middleware::Error
+        run AccessDeniedApp
+      end
+      get '/'
+      expect(last_response.status).to eq(401)
+    end
+
+    it 'responds to custom Grape exceptions appropriately' do
+      @app ||= Rack::Builder.app do
+        use Spec::Support::EndpointFaker
+        use Grape::Middleware::Error, rescue_all: false
+        run CustomErrorApp
+      end
+
+      get '/'
+      expect(last_response.status).to eq(400)
+      expect(last_response.body).to eq('failed validation')
+    end
+  end
+end
diff --git a/spec/grape/middleware/formatter_spec.rb b/spec/grape/middleware/formatter_spec.rb
new file mode 100644
index 0000000..282730a
--- /dev/null
+++ b/spec/grape/middleware/formatter_spec.rb
@@ -0,0 +1,256 @@
+require 'spec_helper'
+
+describe Grape::Middleware::Formatter do
+  subject { Grape::Middleware::Formatter.new(app) }
+  before { allow(subject).to receive(:dup).and_return(subject) }
+
+  let(:app) { ->(_env) { [200, {}, [@body || { 'foo' => 'bar' }]] } }
+
+  context 'serialization' do
+    it 'looks at the bodies for possibly serializable data' do
+      @body = { 'abc' => 'def' }
+      _, _, bodies = *subject.call('PATH_INFO' => '/somewhere', 'HTTP_ACCEPT' => 'application/json')
+      bodies.each { |b| expect(b).to eq(MultiJson.dump(@body)) }
+    end
+
+    it 'calls #to_json since default format is json' do
+      @body = ['foo']
+      @body.instance_eval do
+        def to_json
+          "\"bar\""
+        end
+      end
+
+      subject.call('PATH_INFO' => '/somewhere', 'HTTP_ACCEPT' => 'application/json').to_a.last.each { |b| expect(b).to eq('"bar"') }
+    end
+
+    it 'calls #to_json if the content type is jsonapi' do
+      @body = { 'foos' => [{ 'bar' => 'baz' }] }
+      @body.instance_eval do
+        def to_json
+          "{\"foos\":[{\"bar\":\"baz\"}] }"
+        end
+      end
+
+      subject.call('PATH_INFO' => '/somewhere', 'HTTP_ACCEPT' => 'application/vnd.api+json').to_a.last.each { |b| expect(b).to eq('{"foos":[{"bar":"baz"}] }') }
+    end
+
+    it 'calls #to_xml if the content type is xml' do
+      @body = 'string'
+      @body.instance_eval do
+        def to_xml
+          '<bar/>'
+        end
+      end
+
+      subject.call('PATH_INFO' => '/somewhere.xml', 'HTTP_ACCEPT' => 'application/json').to_a.last.each { |b| expect(b).to eq('<bar/>') }
+    end
+  end
+
+  context 'error handling' do
+    let(:formatter) { double(:formatter) }
+    before do
+      allow(Grape::Formatter::Base).to receive(:formatter_for) { formatter }
+    end
+
+    it 'rescues formatter-specific exceptions' do
+      allow(formatter).to receive(:call) { fail Grape::Exceptions::InvalidFormatter.new(String, 'xml') }
+
+      expect do
+        catch(:error) { subject.call('PATH_INFO' => '/somewhere.xml', 'HTTP_ACCEPT' => 'application/json') }
+      end.to_not raise_error
+    end
+
+    it 'does not rescue other exceptions' do
+      allow(formatter).to receive(:call) { fail StandardError }
+
+      expect do
+        catch(:error) { subject.call('PATH_INFO' => '/somewhere.xml', 'HTTP_ACCEPT' => 'application/json') }
+      end.to raise_error
+    end
+  end
+
+  context 'detection' do
+    it 'uses the xml extension if one is provided' do
+      subject.call('PATH_INFO' => '/info.xml')
+      expect(subject.env['api.format']).to eq(:xml)
+    end
+
+    it 'uses the json extension if one is provided' do
+      subject.call('PATH_INFO' => '/info.json')
+      expect(subject.env['api.format']).to eq(:json)
+    end
+
+    it 'uses the format parameter if one is provided' do
+      subject.call('PATH_INFO' => '/info', 'QUERY_STRING' => 'format=json')
+      expect(subject.env['api.format']).to eq(:json)
+      subject.call('PATH_INFO' => '/info', 'QUERY_STRING' => 'format=xml')
+      expect(subject.env['api.format']).to eq(:xml)
+    end
+
+    it 'uses the default format if none is provided' do
+      subject.call('PATH_INFO' => '/info')
+      expect(subject.env['api.format']).to eq(:txt)
+    end
+
+    it 'uses the requested format if provided in headers' do
+      subject.call('PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/json')
+      expect(subject.env['api.format']).to eq(:json)
+    end
+
+    it 'uses the file extension format if provided before headers' do
+      subject.call('PATH_INFO' => '/info.txt', 'HTTP_ACCEPT' => 'application/json')
+      expect(subject.env['api.format']).to eq(:txt)
+    end
+  end
+
+  context 'accept header detection' do
+    it 'detects from the Accept header' do
+      subject.call('PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/xml')
+      expect(subject.env['api.format']).to eq(:xml)
+    end
+
+    it 'looks for case-indifferent headers' do
+      subject.call('PATH_INFO' => '/info', 'http_accept' => 'application/xml')
+      expect(subject.env['api.format']).to eq(:xml)
+    end
+
+    it 'uses quality rankings to determine formats' do
+      subject.call('PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/json; q=0.3,application/xml; q=1.0')
+      expect(subject.env['api.format']).to eq(:xml)
+      subject.call('PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/json; q=1.0,application/xml; q=0.3')
+      expect(subject.env['api.format']).to eq(:json)
+    end
+
+    it 'handles quality rankings mixed with nothing' do
+      subject.call('PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/json,application/xml; q=1.0')
+      expect(subject.env['api.format']).to eq(:xml)
+    end
+
+    it 'parses headers with other attributes' do
+      subject.call('PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/json; abc=2.3; q=1.0,application/xml; q=0.7')
+      expect(subject.env['api.format']).to eq(:json)
+    end
+
+    it 'parses headers with vendor and api version' do
+      subject.call('PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/vnd.test-v1+xml')
+      expect(subject.env['api.format']).to eq(:xml)
+    end
+
+    it 'parses headers with symbols as hash keys' do
+      subject.call('PATH_INFO' => '/info', 'http_accept' => 'application/xml', system_time: '091293')
+      expect(subject.env[:system_time]).to eq('091293')
+    end
+  end
+
+  context 'content-type' do
+    it 'is set for json' do
+      _, headers, = subject.call('PATH_INFO' => '/info.json')
+      expect(headers['Content-type']).to eq('application/json')
+    end
+    it 'is set for xml' do
+      _, headers, = subject.call('PATH_INFO' => '/info.xml')
+      expect(headers['Content-type']).to eq('application/xml')
+    end
+    it 'is set for txt' do
+      _, headers, = subject.call('PATH_INFO' => '/info.txt')
+      expect(headers['Content-type']).to eq('text/plain')
+    end
+    it 'is set for custom' do
+      subject.options[:content_types] = {}
+      subject.options[:content_types][:custom] = 'application/x-custom'
+      _, headers, = subject.call('PATH_INFO' => '/info.custom')
+      expect(headers['Content-type']).to eq('application/x-custom')
+    end
+  end
+
+  context 'format' do
+    it 'uses custom formatter' do
+      subject.options[:content_types] = {}
+      subject.options[:content_types][:custom] = "don't care"
+      subject.options[:formatters][:custom] = ->(_obj, _env) { 'CUSTOM FORMAT' }
+      _, _, body = subject.call('PATH_INFO' => '/info.custom')
+      expect(body.body).to eq(['CUSTOM FORMAT'])
+    end
+    it 'uses default json formatter' do
+      @body = ['blah']
+      _, _, body = subject.call('PATH_INFO' => '/info.json')
+      expect(body.body).to eq(['["blah"]'])
+    end
+    it 'uses custom json formatter' do
+      subject.options[:formatters][:json] = ->(_obj, _env) { 'CUSTOM JSON FORMAT' }
+      _, _, body = subject.call('PATH_INFO' => '/info.json')
+      expect(body.body).to eq(['CUSTOM JSON FORMAT'])
+    end
+  end
+
+  context 'input' do
+    %w(POST PATCH PUT DELETE).each do |method|
+      ['application/json', 'application/json; charset=utf-8'].each do |content_type|
+        context content_type do
+          it 'parses the body from #{method} and copies values into rack.request.form_hash' do
+            io = StringIO.new('{"is_boolean":true,"string":"thing"}')
+            subject.call(
+              'PATH_INFO' => '/info',
+              'REQUEST_METHOD' => method,
+              'CONTENT_TYPE' => content_type,
+              'rack.input' => io,
+              'CONTENT_LENGTH' => io.length
+            )
+            expect(subject.env['rack.request.form_hash']['is_boolean']).to be true
+            expect(subject.env['rack.request.form_hash']['string']).to eq('thing')
+          end
+        end
+      end
+      it "parses the chunked body from #{method} and copies values into rack.request.from_hash" do
+        io = StringIO.new('{"is_boolean":true,"string":"thing"}')
+        subject.call(
+          'PATH_INFO' => '/infol',
+          'REQUEST_METHOD' => method,
+          'CONTENT_TYPE' => 'application/json',
+          'rack.input' => io,
+          'HTTP_TRANSFER_ENCODING' => 'chunked'
+        )
+        expect(subject.env['rack.request.form_hash']['is_boolean']).to be true
+        expect(subject.env['rack.request.form_hash']['string']).to eq('thing')
+      end
+      it 'rewinds IO' do
+        io = StringIO.new('{"is_boolean":true,"string":"thing"}')
+        io.read
+        subject.call(
+          'PATH_INFO' => '/infol',
+          'REQUEST_METHOD' => method,
+          'CONTENT_TYPE' => 'application/json',
+          'rack.input' => io,
+          'HTTP_TRANSFER_ENCODING' => 'chunked'
+        )
+        expect(subject.env['rack.request.form_hash']['is_boolean']).to be true
+        expect(subject.env['rack.request.form_hash']['string']).to eq('thing')
+      end
+      it 'parses the body from an xml #{method} and copies values into rack.request.from_hash' do
+        io = StringIO.new('<thing><name>Test</name></thing>')
+        subject.call(
+          'PATH_INFO' => '/info.xml',
+          'REQUEST_METHOD' => method,
+          'CONTENT_TYPE' => 'application/xml',
+          'rack.input' => io,
+          'CONTENT_LENGTH' => io.length
+        )
+        expect(subject.env['rack.request.form_hash']['thing']['name']).to eq('Test')
+      end
+      [Rack::Request::FORM_DATA_MEDIA_TYPES, Rack::Request::PARSEABLE_DATA_MEDIA_TYPES].flatten.each do |content_type|
+        it "ignores #{content_type}" do
+          io = StringIO.new('name=Other+Test+Thing')
+          subject.call(
+            'PATH_INFO' => '/info',
+            'REQUEST_METHOD' => method,
+            'CONTENT_TYPE' => content_type,
+            'rack.input' => io,
+            'CONTENT_LENGTH' => io.length
+          )
+          expect(subject.env['rack.request.form_hash']).to be_nil
+        end
+      end
+    end
+  end
+end
diff --git a/spec/grape/middleware/globals_spec.rb b/spec/grape/middleware/globals_spec.rb
new file mode 100644
index 0000000..d63c289
--- /dev/null
+++ b/spec/grape/middleware/globals_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+
+describe Grape::Middleware::Globals do
+  subject { Grape::Middleware::Globals.new(blank_app) }
+  before { allow(subject).to receive(:dup).and_return(subject) }
+
+  let(:blank_app) { ->(_env) { [200, {}, 'Hi there.'] } }
+
+  it 'calls through to the app' do
+    expect(subject.call({})).to eq([200, {}, 'Hi there.'])
+  end
+
+  context 'environment' do
+    it 'should set the grape.request environment' do
+      subject.call({})
+      expect(subject.env['grape.request']).to be_a(Grape::Request)
+    end
+    it 'should set the grape.request.headers environment' do
+      subject.call({})
+      expect(subject.env['grape.request.headers']).to be_a(Hash)
+    end
+    it 'should set the grape.request.params environment' do
+      subject.call('QUERY_STRING' => 'test=1', 'rack.input' => StringIO.new)
+      expect(subject.env['grape.request.params']).to be_a(Hash)
+    end
+  end
+end
diff --git a/spec/grape/middleware/versioner/accept_version_header_spec.rb b/spec/grape/middleware/versioner/accept_version_header_spec.rb
new file mode 100644
index 0000000..d428826
--- /dev/null
+++ b/spec/grape/middleware/versioner/accept_version_header_spec.rb
@@ -0,0 +1,121 @@
+require 'spec_helper'
+
+describe Grape::Middleware::Versioner::AcceptVersionHeader do
+  let(:app) { ->(env) { [200, env, env] } }
+  subject { Grape::Middleware::Versioner::AcceptVersionHeader.new(app, @options || {}) }
+
+  before do
+    @options = {
+      version_options: {
+        using: :accept_version_header
+      }
+    }
+  end
+
+  context 'api.version' do
+    before do
+      @options[:versions] = ['v1']
+    end
+
+    it 'is set' do
+      status, _, env = subject.call('HTTP_ACCEPT_VERSION' => 'v1')
+      expect(env['api.version']).to eql 'v1'
+      expect(status).to eq(200)
+    end
+
+    it 'is set if format provided' do
+      status, _, env = subject.call('HTTP_ACCEPT_VERSION' => 'v1')
+      expect(env['api.version']).to eql 'v1'
+      expect(status).to eq(200)
+    end
+
+    it 'fails with 406 Not Acceptable if version is not supported' do
+      expect do
+        subject.call('HTTP_ACCEPT_VERSION' => 'v2').last
+      end.to throw_symbol(
+        :error,
+        status: 406,
+        headers: { 'X-Cascade' => 'pass' },
+        message: 'The requested version is not supported.'
+      )
+    end
+  end
+
+  it 'succeeds if :strict is not set' do
+    expect(subject.call('HTTP_ACCEPT_VERSION' => '').first).to eq(200)
+    expect(subject.call({}).first).to eq(200)
+  end
+
+  it 'succeeds if :strict is set to false' do
+    @options[:version_options][:strict] = false
+    expect(subject.call('HTTP_ACCEPT_VERSION' => '').first).to eq(200)
+    expect(subject.call({}).first).to eq(200)
+  end
+
+  context 'when :strict is set' do
+    before do
+      @options[:versions] = ['v1']
+      @options[:version_options][:strict] = true
+    end
+
+    it 'fails with 406 Not Acceptable if header is not set' do
+      expect do
+        subject.call({}).last
+      end.to throw_symbol(
+        :error,
+        status: 406,
+        headers: { 'X-Cascade' => 'pass' },
+        message: 'Accept-Version header must be set.'
+      )
+    end
+
+    it 'fails with 406 Not Acceptable if header is empty' do
+      expect do
+        subject.call('HTTP_ACCEPT_VERSION' => '').last
+      end.to throw_symbol(
+        :error,
+        status: 406,
+        headers: { 'X-Cascade' => 'pass' },
+        message: 'Accept-Version header must be set.'
+      )
+    end
+
+    it 'succeeds if proper header is set' do
+      expect(subject.call('HTTP_ACCEPT_VERSION' => 'v1').first).to eq(200)
+    end
+  end
+
+  context 'when :strict and :cascade=>false are set' do
+    before do
+      @options[:versions] = ['v1']
+      @options[:version_options][:strict] = true
+      @options[:version_options][:cascade] = false
+    end
+
+    it 'fails with 406 Not Acceptable if header is not set' do
+      expect do
+        subject.call({}).last
+      end.to throw_symbol(
+        :error,
+        status: 406,
+        headers: {},
+        message: 'Accept-Version header must be set.'
+      )
+    end
+
+    it 'fails with 406 Not Acceptable if header is empty' do
+      expect do
+        subject.call('HTTP_ACCEPT_VERSION' => '').last
+      end.to throw_symbol(
+        :error,
+        status: 406,
+        headers: {},
+        message: 'Accept-Version header must be set.'
+      )
+    end
+
+    it 'succeeds if proper header is set' do
+      expect(subject.call('HTTP_ACCEPT_VERSION' => 'v1').first).to eq(200)
+    end
+  end
+end
diff --git a/spec/grape/middleware/versioner/header_spec.rb b/spec/grape/middleware/versioner/header_spec.rb
new file mode 100644
index 0000000..c3a23c6
--- /dev/null
+++ b/spec/grape/middleware/versioner/header_spec.rb
@@ -0,0 +1,280 @@
+require 'spec_helper'
+
+describe Grape::Middleware::Versioner::Header do
+  let(:app) { ->(env) { [200, env, env] } }
+  subject { Grape::Middleware::Versioner::Header.new(app, @options || {}) }
+
+  before do
+    @options = {
+      version_options: {
+        using: :header,
+        vendor: 'vendor'
+      }
+    }
+  end
+
+  context 'api.type and api.subtype' do
+    it 'sets type and subtype to first choice of content type if no preference given' do
+      status, _, env = subject.call('HTTP_ACCEPT' => '*/*')
+      expect(env['api.type']).to eql 'application'
+      expect(env['api.subtype']).to eql 'vnd.vendor+xml'
+      expect(status).to eq(200)
+    end
+
+    it 'sets preferred type' do
+      status, _, env = subject.call('HTTP_ACCEPT' => 'application/*')
+      expect(env['api.type']).to eql 'application'
+      expect(env['api.subtype']).to eql 'vnd.vendor+xml'
+      expect(status).to eq(200)
+    end
+
+    it 'sets preferred type and subtype' do
+      status, _, env = subject.call('HTTP_ACCEPT' => 'text/plain')
+      expect(env['api.type']).to eql 'text'
+      expect(env['api.subtype']).to eql 'plain'
+      expect(status).to eq(200)
+    end
+  end
+
+  context 'api.format' do
+    it 'is set' do
+      status, _, env = subject.call('HTTP_ACCEPT' => 'application/vnd.vendor+json')
+      expect(env['api.format']).to eql 'json'
+      expect(status).to eq(200)
+    end
+
+    it 'is nil if not provided' do
+      status, _, env = subject.call('HTTP_ACCEPT' => 'application/vnd.vendor')
+      expect(env['api.format']).to eql nil
+      expect(status).to eq(200)
+    end
+
+    ['v1', :v1].each do |version|
+      context 'when version is set to #{version{ ' do
+        before do
+          @options[:versions] = [version]
+        end
+
+        it 'is set' do
+          status, _, env =  subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v1+json')
+          expect(env['api.format']).to eql 'json'
+          expect(status).to eq(200)
+        end
+
+        it 'is nil if not provided' do
+          status, _, env =  subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v1')
+          expect(env['api.format']).to eql nil
+          expect(status).to eq(200)
+        end
+      end
+    end
+  end
+
+  context 'api.vendor' do
+    it 'is set' do
+      status, _, env =  subject.call('HTTP_ACCEPT' => 'application/vnd.vendor')
+      expect(env['api.vendor']).to eql 'vendor'
+      expect(status).to eq(200)
+    end
+
+    it 'is set if format provided' do
+      status, _, env =  subject.call('HTTP_ACCEPT' => 'application/vnd.vendor+json')
+      expect(env['api.vendor']).to eql 'vendor'
+      expect(status).to eq(200)
+    end
+
+    it 'fails with 406 Not Acceptable if vendor is invalid' do
+      expect { subject.call('HTTP_ACCEPT' => 'application/vnd.othervendor+json').last }
+        .to raise_exception do |exception|
+          expect(exception).to be_a(Grape::Exceptions::InvalidAcceptHeader)
+          expect(exception.headers).to eql('X-Cascade' => 'pass')
+          expect(exception.status).to eql 406
+          expect(exception.message).to include 'API vendor or version not found'
+        end
+    end
+
+    context 'when version is set' do
+      before do
+        @options[:versions] = ['v1']
+      end
+
+      it 'is set' do
+        status, _, env =  subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v1')
+        expect(env['api.vendor']).to eql 'vendor'
+        expect(status).to eq(200)
+      end
+
+      it 'is set if format provided' do
+        status, _, env =  subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v1+json')
+        expect(env['api.vendor']).to eql 'vendor'
+        expect(status).to eq(200)
+      end
+
+      it 'fails with 406 Not Acceptable if vendor is invalid' do
+        expect { subject.call('HTTP_ACCEPT' => 'application/vnd.othervendor-v1+json').last }
+          .to raise_exception do |exception|
+            expect(exception).to be_a(Grape::Exceptions::InvalidAcceptHeader)
+            expect(exception.headers).to eql('X-Cascade' => 'pass')
+            expect(exception.status).to eql 406
+            expect(exception.message).to include('API vendor or version not found')
+          end
+      end
+    end
+  end
+
+  context 'api.version' do
+    before do
+      @options[:versions] = ['v1']
+    end
+
+    it 'is set' do
+      status, _, env =  subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v1')
+      expect(env['api.version']).to eql 'v1'
+      expect(status).to eq(200)
+    end
+
+    it 'is set if format provided' do
+      status, _, env =  subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v1+json')
+      expect(env['api.version']).to eql 'v1'
+      expect(status).to eq(200)
+    end
+
+    it 'fails with 406 Not Acceptable if version is invalid' do
+      expect { subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v2+json').last }.to raise_exception do |exception|
+        expect(exception).to be_a(Grape::Exceptions::InvalidAcceptHeader)
+        expect(exception.headers).to eql('X-Cascade' => 'pass')
+        expect(exception.status).to eql 406
+        expect(exception.message).to include('API vendor or version not found')
+      end
+    end
+  end
+
+  it 'succeeds if :strict is not set' do
+    expect(subject.call('HTTP_ACCEPT' => '').first).to eq(200)
+    expect(subject.call({}).first).to eq(200)
+  end
+
+  it 'succeeds if :strict is set to false' do
+    @options[:version_options][:strict] = false
+    expect(subject.call('HTTP_ACCEPT' => '').first).to eq(200)
+    expect(subject.call({}).first).to eq(200)
+  end
+
+  context 'when :strict is set' do
+    before do
+      @options[:versions] = ['v1']
+      @options[:version_options][:strict] = true
+    end
+
+    it 'fails with 406 Not Acceptable if header is not set' do
+      expect { subject.call({}).last }.to raise_exception do |exception|
+        expect(exception).to be_a(Grape::Exceptions::InvalidAcceptHeader)
+        expect(exception.headers).to eql('X-Cascade' => 'pass')
+        expect(exception.status).to eql 406
+        expect(exception.message).to include('Accept header must be set.')
+      end
+    end
+
+    it 'fails with 406 Not Acceptable if header is empty' do
+      expect { subject.call('HTTP_ACCEPT' => '').last }.to raise_exception do |exception|
+        expect(exception).to be_a(Grape::Exceptions::InvalidAcceptHeader)
+        expect(exception.headers).to eql('X-Cascade' => 'pass')
+        expect(exception.status).to eql 406
+        expect(exception.message).to include('Accept header must be set.')
+      end
+    end
+
+    it 'fails with 406 Not Acceptable if type is a range' do
+      expect { subject.call('HTTP_ACCEPT' => '*/*').last }.to raise_exception do |exception|
+        expect(exception).to be_a(Grape::Exceptions::InvalidAcceptHeader)
+        expect(exception.headers).to eql('X-Cascade' => 'pass')
+        expect(exception.status).to eql 406
+        expect(exception.message).to include('Accept header must not contain ranges ("*").')
+      end
+    end
+
+    it 'fails with 406 Not Acceptable if subtype is a range' do
+      expect { subject.call('HTTP_ACCEPT' => 'application/*').last }.to raise_exception do |exception|
+        expect(exception).to be_a(Grape::Exceptions::InvalidAcceptHeader)
+        expect(exception.headers).to eql('X-Cascade' => 'pass')
+        expect(exception.status).to eql 406
+        expect(exception.message).to include('Accept header must not contain ranges ("*").')
+      end
+    end
+
+    it 'succeeds if proper header is set' do
+      expect(subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v1+json').first).to eq(200)
+    end
+  end
+
+  context 'when :strict and :cascade=>false are set' do
+    before do
+      @options[:versions] = ['v1']
+      @options[:version_options][:strict] = true
+      @options[:version_options][:cascade] = false
+    end
+
+    it 'fails with 406 Not Acceptable if header is not set' do
+      expect { subject.call({}).last }.to raise_exception do |exception|
+        expect(exception).to be_a(Grape::Exceptions::InvalidAcceptHeader)
+        expect(exception.headers).to eql({})
+        expect(exception.status).to eql 406
+        expect(exception.message).to include('Accept header must be set.')
+      end
+    end
+
+    it 'fails with 406 Not Acceptable if header is empty' do
+      expect { subject.call('HTTP_ACCEPT' => '').last }.to raise_exception do |exception|
+        expect(exception).to be_a(Grape::Exceptions::InvalidAcceptHeader)
+        expect(exception.headers).to eql({})
+        expect(exception.status).to eql 406
+        expect(exception.message).to include('Accept header must be set.')
+      end
+    end
+
+    it 'fails with 406 Not Acceptable if type is a range' do
+      expect { subject.call('HTTP_ACCEPT' => '*/*').last }.to raise_exception do |exception|
+        expect(exception).to be_a(Grape::Exceptions::InvalidAcceptHeader)
+        expect(exception.headers).to eql({})
+        expect(exception.status).to eql 406
+        expect(exception.message).to include('Accept header must not contain ranges ("*").')
+      end
+    end
+
+    it 'fails with 406 Not Acceptable if subtype is a range' do
+      expect { subject.call('HTTP_ACCEPT' => 'application/*').last }.to raise_exception do |exception|
+        expect(exception).to be_a(Grape::Exceptions::InvalidAcceptHeader)
+        expect(exception.headers).to eql({})
+        expect(exception.status).to eql 406
+        expect(exception.message).to include('Accept header must not contain ranges ("*").')
+      end
+    end
+
+    it 'succeeds if proper header is set' do
+      expect(subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v1+json').first).to eq(200)
+    end
+  end
+
+  context 'when multiple versions are specified' do
+    before do
+      @options[:versions] = %w(v1 v2)
+    end
+
+    it 'succeeds with v1' do
+      expect(subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v1+json').first).to eq(200)
+    end
+
+    it 'succeeds with v2' do
+      expect(subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v2+json').first).to eq(200)
+    end
+
+    it 'fails with another version' do
+      expect { subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v3+json') }.to raise_exception do |exception|
+        expect(exception).to be_a(Grape::Exceptions::InvalidAcceptHeader)
+        expect(exception.headers).to eql('X-Cascade' => 'pass')
+        expect(exception.status).to eql 406
+        expect(exception.message).to include('API vendor or version not found')
+      end
+    end
+  end
+end
diff --git a/spec/grape/middleware/versioner/param_spec.rb b/spec/grape/middleware/versioner/param_spec.rb
new file mode 100644
index 0000000..66e1236
--- /dev/null
+++ b/spec/grape/middleware/versioner/param_spec.rb
@@ -0,0 +1,56 @@
+require 'spec_helper'
+
+describe Grape::Middleware::Versioner::Param do
+  let(:app) { ->(env) { [200, env, env['api.version']] } }
+  subject { Grape::Middleware::Versioner::Param.new(app, @options || {}) }
+
+  it 'sets the API version based on the default param (apiver)' do
+    env = Rack::MockRequest.env_for('/awesome', params: { 'apiver' => 'v1' })
+    expect(subject.call(env)[1]['api.version']).to eq('v1')
+  end
+
+  it 'cuts (only) the version out of the params' do
+    env = Rack::MockRequest.env_for('/awesome', params: { 'apiver' => 'v1', 'other_param' => '5' })
+    env['rack.request.query_hash'] = Rack::Utils.parse_nested_query(env['QUERY_STRING'])
+    expect(subject.call(env)[1]['rack.request.query_hash']['apiver']).to be_nil
+    expect(subject.call(env)[1]['rack.request.query_hash']['other_param']).to eq('5')
+  end
+
+  it 'provides a nil version if no version is given' do
+    env = Rack::MockRequest.env_for('/')
+    expect(subject.call(env).last).to be_nil
+  end
+
+  context 'with specified parameter name' do
+    before { @options = { parameter: 'v' } }
+    it 'sets the API version based on the custom parameter name' do
+      env = Rack::MockRequest.env_for('/awesome', params: { 'v' => 'v1' })
+      expect(subject.call(env)[1]['api.version']).to eq('v1')
+    end
+    it 'does not set the API version based on the default param' do
+      env = Rack::MockRequest.env_for('/awesome', params: { 'apiver' => 'v1' })
+      expect(subject.call(env)[1]['api.version']).to be_nil
+    end
+  end
+
+  context 'with specified versions' do
+    before { @options = { versions: %w(v1 v2) } }
+    it 'throws an error if a non-allowed version is specified' do
+      env = Rack::MockRequest.env_for('/awesome', params: { 'apiver' => 'v3' })
+      expect(catch(:error) { subject.call(env) }[:status]).to eq(404)
+    end
+    it 'allows versions that have been specified' do
+      env = Rack::MockRequest.env_for('/awesome', params: { 'apiver' => 'v1' })
+      expect(subject.call(env)[1]['api.version']).to eq('v1')
+    end
+  end
+
+  it 'returns a 200 when no version is set (matches the first version found)' do
+    @options = {
+      versions: ['v1'],
+      version_options: { using: :header }
+    }
+    env = Rack::MockRequest.env_for('/awesome', params: {})
+    expect(subject.call(env).first).to eq(200)
+  end
+end
diff --git a/spec/grape/middleware/versioner/path_spec.rb b/spec/grape/middleware/versioner/path_spec.rb
new file mode 100644
index 0000000..821bc77
--- /dev/null
+++ b/spec/grape/middleware/versioner/path_spec.rb
@@ -0,0 +1,43 @@
+require 'spec_helper'
+
+describe Grape::Middleware::Versioner::Path do
+  let(:app) { ->(env) { [200, env, env['api.version']] } }
+  subject { Grape::Middleware::Versioner::Path.new(app, @options || {}) }
+
+  it 'sets the API version based on the first path' do
+    expect(subject.call('PATH_INFO' => '/v1/awesome').last).to eq('v1')
+  end
+
+  it 'does not cut the version out of the path' do
+    expect(subject.call('PATH_INFO' => '/v1/awesome')[1]['PATH_INFO']).to eq('/v1/awesome')
+  end
+
+  it 'provides a nil version if no path is given' do
+    expect(subject.call('PATH_INFO' => '/').last).to be_nil
+  end
+
+  context 'with a pattern' do
+    before { @options = { pattern: /v./i } }
+    it 'sets the version if it matches' do
+      expect(subject.call('PATH_INFO' => '/v1/awesome').last).to eq('v1')
+    end
+
+    it 'ignores the version if it fails to match' do
+      expect(subject.call('PATH_INFO' => '/awesome/radical').last).to be_nil
+    end
+  end
+
+  [%w(v1 v2), [:v1, :v2], [:v1, 'v2'], ['v1', :v2]].each do |versions|
+    context 'with specified versions as #{versions}' do
+      before { @options = { versions: versions } }
+
+      it 'throws an error if a non-allowed version is specified' do
+        expect(catch(:error) { subject.call('PATH_INFO' => '/v3/awesome') }[:status]).to eq(404)
+      end
+
+      it 'allows versions that have been specified' do
+        expect(subject.call('PATH_INFO' => '/v1/asoasd').last).to eq('v1')
+      end
+    end
+  end
+end
diff --git a/spec/grape/middleware/versioner_spec.rb b/spec/grape/middleware/versioner_spec.rb
new file mode 100644
index 0000000..d3f84cd
--- /dev/null
+++ b/spec/grape/middleware/versioner_spec.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+describe Grape::Middleware::Versioner do
+  let(:klass) { Grape::Middleware::Versioner }
+
+  it 'recognizes :path' do
+    expect(klass.using(:path)).to eq(Grape::Middleware::Versioner::Path)
+  end
+
+  it 'recognizes :header' do
+    expect(klass.using(:header)).to eq(Grape::Middleware::Versioner::Header)
+  end
+
+  it 'recognizes :param' do
+    expect(klass.using(:param)).to eq(Grape::Middleware::Versioner::Param)
+  end
+
+  it 'recognizes :accept_version_header' do
+    expect(klass.using(:accept_version_header)).to eq(Grape::Middleware::Versioner::AcceptVersionHeader)
+  end
+end
diff --git a/spec/grape/path_spec.rb b/spec/grape/path_spec.rb
new file mode 100644
index 0000000..f2f93a0
--- /dev/null
+++ b/spec/grape/path_spec.rb
@@ -0,0 +1,252 @@
+require 'spec_helper'
+
+module Grape
+  describe Path do
+    describe '#initialize' do
+      it 'remembers the path' do
+        path = Path.new('/:id', anything, anything)
+        expect(path.raw_path).to eql('/:id')
+      end
+
+      it 'remembers the namespace' do
+        path = Path.new(anything, '/users', anything)
+        expect(path.namespace).to eql('/users')
+      end
+
+      it 'remebers the settings' do
+        path = Path.new(anything, anything, foo: 'bar')
+        expect(path.settings).to eql(foo: 'bar')
+      end
+    end
+
+    describe '#mount_path' do
+      it 'is nil when no mount path setting exists' do
+        path = Path.new(anything, anything, {})
+        expect(path.mount_path).to be_nil
+      end
+
+      it 'is nil when the mount path is nil' do
+        path = Path.new(anything, anything, mount_path: nil)
+        expect(path.mount_path).to be_nil
+      end
+
+      it 'splits the mount path' do
+        path = Path.new(anything, anything, mount_path: %w(foo bar))
+        expect(path.mount_path).to eql(%w(foo bar))
+      end
+    end
+
+    describe '#root_prefix' do
+      it 'is nil when no root prefix setting exists' do
+        path = Path.new(anything, anything, {})
+        expect(path.root_prefix).to be_nil
+      end
+
+      it 'is nil when the mount path is nil' do
+        path = Path.new(anything, anything, root_prefix: nil)
+        expect(path.root_prefix).to be_nil
+      end
+
+      it 'splits the mount path' do
+        path = Path.new(anything, anything, root_prefix: 'hello/world')
+        expect(path.root_prefix).to eql(%w(hello world))
+      end
+    end
+
+    describe '#uses_path_versioning?' do
+      it 'is false when the version setting is nil' do
+        path = Path.new(anything, anything, version: nil)
+        expect(path.uses_path_versioning?).to be false
+      end
+
+      it 'is false when the version option is header' do
+        path = Path.new(
+          anything,
+          anything,
+          version: 'v1',
+          version_options: { using: :header }
+        )
+
+        expect(path.uses_path_versioning?).to be false
+      end
+
+      it 'is true when the version option is path' do
+        path = Path.new(
+          anything,
+          anything,
+          version: 'v1',
+          version_options: { using: :path }
+        )
+
+        expect(path.uses_path_versioning?).to be true
+      end
+    end
+
+    describe '#has_namespace?' do
+      it 'is false when the namespace is nil' do
+        path = Path.new(anything, nil, anything)
+        expect(path).not_to have_namespace
+      end
+
+      it 'is false when the namespace starts with whitespace' do
+        path = Path.new(anything, ' /foo', anything)
+        expect(path).not_to have_namespace
+      end
+
+      it 'is false when the namespace is the root path' do
+        path = Path.new(anything, '/', anything)
+        expect(path).not_to have_namespace
+      end
+
+      it 'is true otherwise' do
+        path = Path.new(anything, '/world', anything)
+        expect(path).to have_namespace
+      end
+    end
+
+    describe '#has_path?' do
+      it 'is false when the path is nil' do
+        path = Path.new(nil, anything, anything)
+        expect(path).not_to have_path
+      end
+
+      it 'is false when the path starts with whitespace' do
+        path = Path.new(' /foo', anything, anything)
+        expect(path).not_to have_path
+      end
+
+      it 'is false when the path is the root path' do
+        path = Path.new('/', anything, anything)
+        expect(path).not_to have_path
+      end
+
+      it 'is true otherwise' do
+        path = Path.new('/hello', anything, anything)
+        expect(path).to have_path
+      end
+    end
+
+    describe '#path' do
+      context 'mount_path' do
+        it 'is not included when it is nil' do
+          path = Path.new(nil, nil, mount_path: '/foo/bar')
+          expect(path.path).to eql '/foo/bar'
+        end
+
+        it 'is included when it is not nil' do
+          path = Path.new(nil, nil, {})
+          expect(path.path).to eql('/')
+        end
+      end
+
+      context 'root_prefix' do
+        it 'is not included when it is nil' do
+          path = Path.new(nil, nil, {})
+          expect(path.path).to eql('/')
+        end
+
+        it 'is included after the mount path' do
+          path = Path.new(
+            nil,
+            nil,
+            mount_path: '/foo',
+            root_prefix: '/hello'
+          )
+
+          expect(path.path).to eql('/foo/hello')
+        end
+      end
+
+      it 'uses the namespace after the mount path and root prefix' do
+        path = Path.new(
+          nil,
+          'namespace',
+          mount_path: '/foo',
+          root_prefix: '/hello'
+        )
+
+        expect(path.path).to eql('/foo/hello/namespace')
+      end
+
+      it 'uses the raw path after the namespace' do
+        path = Path.new(
+          'raw_path',
+          'namespace',
+          mount_path: '/foo',
+          root_prefix: '/hello'
+        )
+
+        expect(path.path).to eql('/foo/hello/namespace/raw_path')
+      end
+    end
+
+    describe '#suffix' do
+      context 'when using a specific format' do
+        it 'accepts specified format' do
+          path = Path.new(nil, nil, {})
+          allow(path).to receive(:uses_specific_format?) { true }
+          allow(path).to receive(:settings) { { format: :json } }
+
+          expect(path.suffix).to eql('(.json)')
+        end
+      end
+
+      context 'when path versioning is used' do
+        it "includes a '/'" do
+          path = Path.new(nil, nil, {})
+          allow(path).to receive(:uses_specific_format?) { false }
+          allow(path).to receive(:uses_path_versioning?) { true }
+
+          expect(path.suffix).to eql('(/.:format)')
+        end
+      end
+
+      context 'when path versioning is not used' do
+        it "does not include a '/' when the path has a namespace" do
+          path = Path.new(nil, 'namespace', {})
+          allow(path).to receive(:uses_specific_format?) { false }
+          allow(path).to receive(:uses_path_versioning?) { true }
+
+          expect(path.suffix).to eql('(.:format)')
+        end
+
+        it "does not include a '/' when the path has a path" do
+          path = Path.new('/path', nil, {})
+          allow(path).to receive(:uses_specific_format?) { false }
+          allow(path).to receive(:uses_path_versioning?) { true }
+
+          expect(path.suffix).to eql('(.:format)')
+        end
+
+        it "includes a '/' otherwise" do
+          path = Path.new(nil, nil, {})
+          allow(path).to receive(:uses_specific_format?) { false }
+          allow(path).to receive(:uses_path_versioning?) { true }
+
+          expect(path.suffix).to eql('(/.:format)')
+        end
+      end
+    end
+
+    describe '#path_with_suffix' do
+      it 'combines the path and suffix' do
+        path = Path.new(nil, nil, {})
+        allow(path).to receive(:path) { '/the/path' }
+        allow(path).to receive(:suffix) { 'suffix' }
+
+        expect(path.path_with_suffix).to eql('/the/pathsuffix')
+      end
+
+      context 'when using a specific format' do
+        it 'might have a suffix with specified format' do
+          path = Path.new(nil, nil, {})
+          allow(path).to receive(:path) { '/the/path' }
+          allow(path).to receive(:uses_specific_format?) { true }
+          allow(path).to receive(:settings) { { format: :json } }
+
+          expect(path.path_with_suffix).to eql('/the/path(.json)')
+        end
+      end
+    end
+  end
+end
diff --git a/spec/grape/presenters/presenter_spec.rb b/spec/grape/presenters/presenter_spec.rb
new file mode 100644
index 0000000..e974165
--- /dev/null
+++ b/spec/grape/presenters/presenter_spec.rb
@@ -0,0 +1,70 @@
+require 'spec_helper'
+
+module Grape
+  module Presenters
+    module InsideRouteSpec
+      class Dummy
+        include Grape::DSL::InsideRoute
+
+        attr_reader :env, :request, :new_settings
+
+        def initialize
+          @env = {}
+          @header = {}
+          @new_settings = { namespace_inheritable: {}, namespace_stackable: {} }
+        end
+      end
+    end
+
+    describe Presenter do
+      describe 'represent' do
+        let(:object_mock) do
+          Object.new
+        end
+
+        it 'represent object' do
+          expect(Presenter.represent(object_mock)).to eq object_mock
+        end
+      end
+
+      subject { InsideRouteSpec::Dummy.new }
+
+      describe 'present' do
+        let(:hash_mock) do
+          { key: :value }
+        end
+
+        describe 'instance' do
+          before do
+            subject.present hash_mock, with: Grape::Presenters::Presenter
+          end
+          it 'presents dummy hash' do
+            expect(subject.body).to eq hash_mock
+          end
+        end
+
+        describe 'multiple presenter' do
+          let(:hash_mock1) do
+            { key1: :value1 }
+          end
+
+          let(:hash_mock2) do
+            { key2: :value2 }
+          end
+
+          describe 'instance' do
+            before do
+              subject.present hash_mock1, with: Grape::Presenters::Presenter
+              subject.present hash_mock2, with: Grape::Presenters::Presenter
+            end
+
+            it 'presents both dummy presenter' do
+              expect(subject.body[:key1]).to eq hash_mock1[:key1]
+              expect(subject.body[:key2]).to eq hash_mock2[:key2]
+            end
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/spec/grape/util/inheritable_setting_spec.rb b/spec/grape/util/inheritable_setting_spec.rb
new file mode 100644
index 0000000..ed54def
--- /dev/null
+++ b/spec/grape/util/inheritable_setting_spec.rb
@@ -0,0 +1,217 @@
+require 'spec_helper'
+module Grape
+  module Util
+    describe InheritableSetting do
+      before :each do
+        InheritableSetting.reset_global!
+      end
+
+      let(:parent) do
+        Grape::Util::InheritableSetting.new.tap do |settings|
+          settings.global[:global_thing] = :global_foo_bar
+          settings.namespace[:namespace_thing] = :namespace_foo_bar
+          settings.namespace_inheritable[:namespace_inheritable_thing] = :namespace_inheritable_foo_bar
+          settings.namespace_stackable[:namespace_stackable_thing] = :namespace_stackable_foo_bar
+          settings.route[:route_thing] = :route_foo_bar
+        end
+      end
+
+      let(:other_parent) do
+        Grape::Util::InheritableSetting.new.tap do |settings|
+          settings.namespace[:namespace_thing] = :namespace_foo_bar_other
+          settings.namespace_inheritable[:namespace_inheritable_thing] = :namespace_inheritable_foo_bar_other
+          settings.namespace_stackable[:namespace_stackable_thing] = :namespace_stackable_foo_bar_other
+          settings.route[:route_thing] = :route_foo_bar_other
+        end
+      end
+
+      before :each do
+        subject.inherit_from parent
+      end
+
+      describe '#global' do
+        it 'sets a global value' do
+          subject.global[:some_thing] = :foo_bar
+          expect(subject.global[:some_thing]).to eq :foo_bar
+          subject.global[:some_thing] = :foo_bar_next
+          expect(subject.global[:some_thing]).to eq :foo_bar_next
+        end
+
+        it 'sets the global inherited values' do
+          expect(subject.global[:global_thing]).to eq :global_foo_bar
+        end
+
+        it 'overrides global values' do
+          subject.global[:global_thing] = :global_new_foo_bar
+          expect(parent.global[:global_thing]).to eq :global_new_foo_bar
+        end
+
+        it 'should handle different parents' do
+          subject.global[:global_thing] = :global_new_foo_bar
+
+          subject.inherit_from other_parent
+
+          expect(parent.global[:global_thing]).to eq :global_new_foo_bar
+          expect(other_parent.global[:global_thing]).to eq :global_new_foo_bar
+        end
+      end
+
+      describe '#api_class' do
+        it 'is specific to the class' do
+          subject.api_class[:some_thing] = :foo_bar
+          parent.api_class[:some_thing] = :some_thing
+
+          expect(subject.api_class[:some_thing]).to eq :foo_bar
+          expect(parent.api_class[:some_thing]).to eq :some_thing
+        end
+      end
+
+      describe '#namespace' do
+        it 'sets a value until the end of a namespace' do
+          subject.namespace[:some_thing] = :foo_bar
+          expect(subject.namespace[:some_thing]).to eq :foo_bar
+        end
+
+        it 'uses new values when a new namespace starts' do
+          subject.namespace[:namespace_thing] = :new_namespace_foo_bar
+          expect(subject.namespace[:namespace_thing]).to eq :new_namespace_foo_bar
+
+          expect(parent.namespace[:namespace_thing]).to eq :namespace_foo_bar
+        end
+      end
+
+      describe '#namespace_inheritable' do
+        it 'works with inheritable values' do
+          expect(subject.namespace_inheritable[:namespace_inheritable_thing]).to eq :namespace_inheritable_foo_bar
+        end
+
+        it 'should handle different parents' do
+          expect(subject.namespace_inheritable[:namespace_inheritable_thing]).to eq :namespace_inheritable_foo_bar
+
+          subject.inherit_from other_parent
+
+          expect(subject.namespace_inheritable[:namespace_inheritable_thing]).to eq :namespace_inheritable_foo_bar_other
+
+          subject.inherit_from parent
+
+          expect(subject.namespace_inheritable[:namespace_inheritable_thing]).to eq :namespace_inheritable_foo_bar
+
+          subject.inherit_from other_parent
+
+          subject.namespace_inheritable[:namespace_inheritable_thing] = :my_thing
+
+          expect(subject.namespace_inheritable[:namespace_inheritable_thing]).to eq :my_thing
+
+          subject.inherit_from parent
+
+          expect(subject.namespace_inheritable[:namespace_inheritable_thing]).to eq :my_thing
+        end
+      end
+
+      describe '#namespace_stackable' do
+        it 'works with stackable values' do
+          expect(subject.namespace_stackable[:namespace_stackable_thing]).to eq [:namespace_stackable_foo_bar]
+
+          subject.inherit_from other_parent
+
+          expect(subject.namespace_stackable[:namespace_stackable_thing]).to eq [:namespace_stackable_foo_bar_other]
+        end
+      end
+
+      describe '#route' do
+        it 'sets a value until the next route' do
+          subject.route[:some_thing] = :foo_bar
+          expect(subject.route[:some_thing]).to eq :foo_bar
+
+          subject.route_end
+
+          expect(subject.route[:some_thing]).to be_nil
+        end
+
+        it 'works with route values' do
+          expect(subject.route[:route_thing]).to eq :route_foo_bar
+        end
+      end
+
+      describe '#api_class' do
+        it 'is specific to the class' do
+          subject.api_class[:some_thing] = :foo_bar
+          expect(subject.api_class[:some_thing]).to eq :foo_bar
+        end
+      end
+
+      describe '#inherit_from' do
+        it 'notifies clones' do
+          new_settings = subject.point_in_time_copy
+          expect(new_settings).to receive(:inherit_from).with(other_parent)
+
+          subject.inherit_from other_parent
+        end
+      end
+
+      describe '#point_in_time_copy' do
+        let!(:cloned_obj) { subject.point_in_time_copy }
+
+        it 'resets point_in_time_copies' do
+          expect(cloned_obj.point_in_time_copies).to be_empty
+        end
+
+        it 'decouples namespace values' do
+          subject.namespace[:namespace_thing] = :namespace_foo_bar
+
+          cloned_obj.namespace[:namespace_thing] = :new_namespace_foo_bar
+          expect(subject.namespace[:namespace_thing]).to eq :namespace_foo_bar
+        end
+
+        it 'decouples namespace inheritable values' do
+          expect(cloned_obj.namespace_inheritable[:namespace_inheritable_thing]).to eq :namespace_inheritable_foo_bar
+
+          subject.namespace_inheritable[:namespace_inheritable_thing] = :my_thing
+          expect(subject.namespace_inheritable[:namespace_inheritable_thing]).to eq :my_thing
+
+          expect(cloned_obj.namespace_inheritable[:namespace_inheritable_thing]).to eq :namespace_inheritable_foo_bar
+
+          cloned_obj.namespace_inheritable[:namespace_inheritable_thing] = :my_cloned_thing
+          expect(cloned_obj.namespace_inheritable[:namespace_inheritable_thing]).to eq :my_cloned_thing
+          expect(subject.namespace_inheritable[:namespace_inheritable_thing]).to eq :my_thing
+        end
+
+        it 'decouples namespace stackable values' do
+          expect(cloned_obj.namespace_stackable[:namespace_stackable_thing]).to eq [:namespace_stackable_foo_bar]
+
+          subject.namespace_stackable[:namespace_stackable_thing] = :other_thing
+          expect(subject.namespace_stackable[:namespace_stackable_thing]).to eq [:namespace_stackable_foo_bar, :other_thing]
+          expect(cloned_obj.namespace_stackable[:namespace_stackable_thing]).to eq [:namespace_stackable_foo_bar]
+        end
+
+        it 'decouples route values' do
+          expect(cloned_obj.route[:route_thing]).to eq :route_foo_bar
+
+          subject.route[:route_thing] = :new_route_foo_bar
+          expect(cloned_obj.route[:route_thing]).to eq :route_foo_bar
+        end
+
+        it 'adds itself to original as clone' do
+          expect(subject.point_in_time_copies).to include(cloned_obj)
+        end
+      end
+
+      describe '#to_hash' do
+        it 'return all settings as a hash' do
+          subject.global[:global_thing] = :global_foo_bar
+          subject.namespace[:namespace_thing] = :namespace_foo_bar
+          subject.namespace_inheritable[:namespace_inheritable_thing] = :namespace_inheritable_foo_bar
+          subject.namespace_stackable[:namespace_stackable_thing] = [:namespace_stackable_foo_bar]
+          subject.route[:route_thing] = :route_foo_bar
+
+          expect(subject.to_hash).to include(global: { global_thing: :global_foo_bar })
+          expect(subject.to_hash).to include(namespace: { namespace_thing: :namespace_foo_bar })
+          expect(subject.to_hash).to include(namespace_inheritable: {
+                                               namespace_inheritable_thing: :namespace_inheritable_foo_bar })
+          expect(subject.to_hash).to include(namespace_stackable: { namespace_stackable_thing: [:namespace_stackable_foo_bar, [:namespace_stackable_foo_bar]] })
+          expect(subject.to_hash).to include(route: { route_thing: :route_foo_bar })
+        end
+      end
+    end
+  end
+end
diff --git a/spec/grape/util/inheritable_values_spec.rb b/spec/grape/util/inheritable_values_spec.rb
new file mode 100644
index 0000000..9df6514
--- /dev/null
+++ b/spec/grape/util/inheritable_values_spec.rb
@@ -0,0 +1,63 @@
+require 'spec_helper'
+module Grape
+  module Util
+    describe InheritableValues do
+      let(:parent) { InheritableValues.new }
+      subject { InheritableValues.new(parent) }
+
+      describe '#delete' do
+        it 'deletes a key' do
+          subject[:some_thing] = :new_foo_bar
+          subject.delete :some_thing
+          expect(subject[:some_thing]).to be_nil
+        end
+
+        it 'does not delete parent values' do
+          parent[:some_thing] = :foo
+          subject[:some_thing] = :new_foo_bar
+          subject.delete :some_thing
+          expect(subject[:some_thing]).to eq :foo
+        end
+      end
+
+      describe '#[]' do
+        it 'returns a value' do
+          subject[:some_thing] = :foo
+          expect(subject[:some_thing]).to eq :foo
+        end
+
+        it 'returns parent value when no value is set' do
+          parent[:some_thing] = :foo
+          expect(subject[:some_thing]).to eq :foo
+        end
+
+        it 'overwrites parent value with the current one' do
+          parent[:some_thing] = :foo
+          subject[:some_thing] = :foo_bar
+          expect(subject[:some_thing]).to eq :foo_bar
+        end
+
+        it 'parent values are not changed' do
+          parent[:some_thing] = :foo
+          subject[:some_thing] = :foo_bar
+          expect(parent[:some_thing]).to eq :foo
+        end
+      end
+
+      describe '#[]=' do
+        it 'sets a value' do
+          subject[:some_thing] = :foo
+          expect(subject[:some_thing]).to eq :foo
+        end
+      end
+
+      describe '#to_hash' do
+        it 'returns a Hash representation' do
+          parent[:some_thing] = :foo
+          subject[:some_thing_more] = :foo_bar
+          expect(subject.to_hash).to eq(some_thing: :foo, some_thing_more: :foo_bar)
+        end
+      end
+    end
+  end
+end
diff --git a/spec/grape/util/stackable_values_spec.rb b/spec/grape/util/stackable_values_spec.rb
new file mode 100644
index 0000000..acf3943
--- /dev/null
+++ b/spec/grape/util/stackable_values_spec.rb
@@ -0,0 +1,115 @@
+require 'spec_helper'
+module Grape
+  module Util
+    describe StackableValues do
+      let(:parent) { StackableValues.new }
+      subject { StackableValues.new(parent) }
+
+      describe '#keys' do
+        it 'returns all key' do
+          subject[:some_thing] = :foo_bar
+          subject[:some_thing_else] = :foo_bar
+          expect(subject.keys).to eq [:some_thing, :some_thing_else].sort
+        end
+
+        it 'returns merged keys with parent' do
+          parent[:some_thing] = :foo
+          parent[:some_thing_else] = :foo
+
+          subject[:some_thing] = :foo_bar
+          subject[:some_thing_more] = :foo_bar
+
+          expect(subject.keys).to eq [:some_thing, :some_thing_else, :some_thing_more].sort
+        end
+      end
+
+      describe '#delete' do
+        it 'deletes a key' do
+          subject[:some_thing] = :new_foo_bar
+          subject.delete :some_thing
+          expect(subject[:some_thing]).to eq []
+        end
+
+        it 'does not delete parent values' do
+          parent[:some_thing] = :foo
+          subject[:some_thing] = :new_foo_bar
+          subject.delete :some_thing
+          expect(subject[:some_thing]).to eq [:foo]
+        end
+      end
+
+      describe '#[]' do
+        it 'returns an array of values' do
+          subject[:some_thing] = :foo
+          expect(subject[:some_thing]).to eq [:foo]
+        end
+
+        it 'returns parent value when no value is set' do
+          parent[:some_thing] = :foo
+          expect(subject[:some_thing]).to eq [:foo]
+        end
+
+        it 'combines parent and actual values' do
+          parent[:some_thing] = :foo
+          subject[:some_thing] = :foo_bar
+          expect(subject[:some_thing]).to eq [:foo, :foo_bar]
+        end
+
+        it 'parent values are not changed' do
+          parent[:some_thing] = :foo
+          subject[:some_thing] = :foo_bar
+          expect(parent[:some_thing]).to eq [:foo]
+        end
+      end
+
+      describe '#[]=' do
+        it 'sets a value' do
+          subject[:some_thing] = :foo
+          expect(subject[:some_thing]).to eq [:foo]
+        end
+
+        it 'pushes further values' do
+          subject[:some_thing] = :foo
+          subject[:some_thing] = :bar
+          expect(subject[:some_thing]).to eq [:foo, :bar]
+        end
+
+        it 'can handle array values' do
+          subject[:some_thing] = :foo
+          subject[:some_thing] = [:bar, :more]
+          expect(subject[:some_thing]).to eq [:foo, [:bar, :more]]
+
+          parent[:some_thing_else] = [:foo, :bar]
+          subject[:some_thing_else] = [:some, :bar, :foo]
+
+          expect(subject[:some_thing_else]).to eq [[:foo, :bar], [:some, :bar, :foo]]
+        end
+      end
+
+      describe '#to_hash' do
+        it 'returns a Hash representation' do
+          parent[:some_thing] = :foo
+          subject[:some_thing] = [:bar, :more]
+          subject[:some_thing_more] = :foo_bar
+          expect(subject.to_hash).to eq(some_thing: [:foo, [:bar, :more]], some_thing_more: [:foo_bar])
+        end
+      end
+
+      describe '#clone' do
+        let(:obj_cloned) { subject.clone }
+        it 'copies all values' do
+          parent = StackableValues.new
+          child = StackableValues.new parent
+          grandchild = StackableValues.new child
+
+          parent[:some_thing] = :foo
+          child[:some_thing] = [:bar, :more]
+          grandchild[:some_thing] = :grand_foo_bar
+          grandchild[:some_thing_more] = :foo_bar
+
+          expect(grandchild.clone.to_hash).to eq(some_thing: [:foo, [:bar, :more], :grand_foo_bar], some_thing_more: [:foo_bar])
+        end
+      end
+    end
+  end
+end
diff --git a/spec/grape/util/strict_hash_configuration_spec.rb b/spec/grape/util/strict_hash_configuration_spec.rb
new file mode 100644
index 0000000..4da9eb3
--- /dev/null
+++ b/spec/grape/util/strict_hash_configuration_spec.rb
@@ -0,0 +1,38 @@
+require 'spec_helper'
+module Grape
+  module Util
+    describe 'StrictHashConfiguration' do
+      subject do
+        Class.new do
+          include Grape::Util::StrictHashConfiguration.module(:config1, :config2, config3: [:config4], config5: [config6: [:config7, :config8]])
+        end
+      end
+
+      it 'set nested configs' do
+        subject.configure do
+          config1 'alpha'
+          config2 'beta'
+
+          config3 do
+            config4 'gamma'
+          end
+
+          local_var = 8
+
+          config5 do
+            config6 do
+              config7 7
+              config8 local_var
+            end
+          end
+        end
+
+        expect(subject.settings).to eq(config1: 'alpha',
+                                       config2: 'beta',
+                                       config3: { config4: 'gamma' },
+                                       config5: { config6: { config7: 7, config8: 8 } }
+                                      )
+      end
+    end
+  end
+end
diff --git a/spec/grape/validations/attributes_iterator_spec.rb b/spec/grape/validations/attributes_iterator_spec.rb
new file mode 100644
index 0000000..7a8e4ec
--- /dev/null
+++ b/spec/grape/validations/attributes_iterator_spec.rb
@@ -0,0 +1,4 @@
+require 'spec_helper'
+
+describe Grape::Validations::AttributesIterator do
+end
diff --git a/spec/grape/validations/params_scope_spec.rb b/spec/grape/validations/params_scope_spec.rb
new file mode 100644
index 0000000..1adee78
--- /dev/null
+++ b/spec/grape/validations/params_scope_spec.rb
@@ -0,0 +1,245 @@
+require 'spec_helper'
+
+describe Grape::Validations::ParamsScope do
+  subject do
+    Class.new(Grape::API)
+  end
+
+  def app
+    subject
+  end
+
+  context 'setting a default' do
+    let(:documentation) { subject.routes.first.route_params }
+
+    context 'when the default value is truthy' do
+      before do
+        subject.params do
+          optional :int, type: Integer, default: 42
+        end
+        subject.get
+      end
+
+      it 'adds documentation about the default value' do
+        expect(documentation).to have_key('int')
+        expect(documentation['int']).to have_key(:default)
+        expect(documentation['int'][:default]).to eq(42)
+      end
+    end
+
+    context 'when the default value is false' do
+      before do
+        subject.params do
+          optional :bool, type: Virtus::Attribute::Boolean, default: false
+        end
+        subject.get
+      end
+
+      it 'adds documentation about the default value' do
+        expect(documentation).to have_key('bool')
+        expect(documentation['bool']).to have_key(:default)
+        expect(documentation['bool'][:default]).to eq(false)
+      end
+    end
+
+    context 'when the default value is nil' do
+      before do
+        subject.params do
+          optional :object, type: Object, default: nil
+        end
+        subject.get
+      end
+
+      it 'adds documentation about the default value' do
+        expect(documentation).to have_key('object')
+        expect(documentation['object']).to have_key(:default)
+        expect(documentation['object'][:default]).to eq(nil)
+      end
+    end
+  end
+
+  context 'without a default' do
+    before do
+      subject.params do
+        optional :object, type: Object
+      end
+      subject.get
+    end
+
+    it 'does not add documentation for the default value' do
+      documentation = subject.routes.first.route_params
+      expect(documentation).to have_key('object')
+      expect(documentation['object']).not_to have_key(:default)
+    end
+  end
+
+  context 'setting description' do
+    [:desc, :description].each do |description_type|
+      it "allows setting #{description_type}" do
+        subject.params do
+          requires :int, type: Integer, description_type => 'My very nice integer'
+        end
+        subject.get '/single' do
+          'int works'
+        end
+        get '/single', int: 420
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('int works')
+      end
+    end
+  end
+
+  context 'array without coerce type explicitly given' do
+    it 'sets the type based on first element' do
+      subject.params do
+        requires :periods, type: Array, values: -> { %w(day month) }
+      end
+      subject.get('/required') { 'required works' }
+
+      get '/required', periods: %w(day month)
+      expect(last_response.status).to eq(200)
+      expect(last_response.body).to eq('required works')
+    end
+
+    it 'fails to call API without Array type' do
+      subject.params do
+        requires :periods, type: Array, values: -> { %w(day month) }
+      end
+      subject.get('/required') { 'required works' }
+
+      get '/required', periods: 'day'
+      expect(last_response.status).to eq(400)
+      expect(last_response.body).to eq('periods is invalid')
+    end
+
+    it 'raises exception when values are of different type' do
+      expect do
+        subject.params { requires :numbers, type: Array, values: [1, 'definitely not a number', 3] }
+      end.to raise_error Grape::Exceptions::IncompatibleOptionValues
+    end
+
+    it 'raises exception when range values have different endpoint types' do
+      expect do
+        subject.params { requires :numbers, type: Array, values: 0.0..10 }
+      end.to raise_error Grape::Exceptions::IncompatibleOptionValues
+    end
+  end
+
+  context 'with range values' do
+    context "when left range endpoint isn't #kind_of? the type" do
+      it 'raises exception' do
+        expect do
+          subject.params { requires :latitude, type: Integer, values: -90.0..90 }
+        end.to raise_error Grape::Exceptions::IncompatibleOptionValues
+      end
+    end
+
+    context "when right range endpoint isn't #kind_of? the type" do
+      it 'raises exception' do
+        expect do
+          subject.params { requires :latitude, type: Integer, values: -90..90.0 }
+        end.to raise_error Grape::Exceptions::IncompatibleOptionValues
+      end
+    end
+
+    context 'when both range endpoints are #kind_of? the type' do
+      it 'accepts values in the range' do
+        subject.params do
+          requires :letter, type: String, values: 'a'..'z'
+        end
+        subject.get('/letter') { params[:letter] }
+
+        get '/letter', letter: 'j'
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('j')
+      end
+
+      it 'rejects values outside the range' do
+        subject.params do
+          requires :letter, type: String, values: 'a'..'z'
+        end
+        subject.get('/letter') { params[:letter] }
+
+        get '/letter', letter: 'J'
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('letter does not have a valid value')
+      end
+    end
+  end
+
+  context 'parameters in group' do
+    it 'errors when no type is provided' do
+      expect do
+        subject.params do
+          group :a do
+            requires :b
+          end
+        end
+      end.to raise_error Grape::Exceptions::MissingGroupTypeError
+
+      expect do
+        subject.params do
+          optional :a do
+            requires :b
+          end
+        end
+      end.to raise_error Grape::Exceptions::MissingGroupTypeError
+    end
+
+    it 'allows Hash as type' do
+      subject.params do
+        group :a, type: Hash do
+          requires :b
+        end
+      end
+      subject.get('/group') { 'group works' }
+      get '/group', a: { b: true }
+      expect(last_response.status).to eq(200)
+      expect(last_response.body).to eq('group works')
+
+      subject.params do
+        optional :a, type: Hash do
+          requires :b
+        end
+      end
+      get '/optional_type_hash'
+    end
+
+    it 'allows Array as type' do
+      subject.params do
+        group :a, type: Array do
+          requires :b
+        end
+      end
+      subject.get('/group') { 'group works' }
+      get '/group', a: [{ b: true }]
+      expect(last_response.status).to eq(200)
+      expect(last_response.body).to eq('group works')
+
+      subject.params do
+        optional :a, type: Array do
+          requires :b
+        end
+      end
+      get '/optional_type_array'
+    end
+
+    it 'errors with an unsupported type' do
+      expect do
+        subject.params do
+          group :a, type: Set do
+            requires :b
+          end
+        end
+      end.to raise_error Grape::Exceptions::UnsupportedGroupTypeError
+
+      expect do
+        subject.params do
+          optional :a, type: Set do
+            requires :b
+          end
+        end
+      end.to raise_error Grape::Exceptions::UnsupportedGroupTypeError
+    end
+  end
+end
diff --git a/spec/grape/validations/validators/all_or_none_spec.rb b/spec/grape/validations/validators/all_or_none_spec.rb
new file mode 100644
index 0000000..0e6e9b4
--- /dev/null
+++ b/spec/grape/validations/validators/all_or_none_spec.rb
@@ -0,0 +1,60 @@
+require 'spec_helper'
+
+describe Grape::Validations::AllOrNoneOfValidator do
+  describe '#validate!' do
+    let(:scope) do
+      Struct.new(:opts) do
+        def params(arg)
+          arg
+        end
+
+        def required?; end
+      end
+    end
+    let(:all_or_none_params) { [:beer, :wine, :grapefruit] }
+    let(:validator) { described_class.new(all_or_none_params, {}, false, scope.new) }
+
+    context 'when all restricted params are present' do
+      let(:params) { { beer: true, wine: true, grapefruit: true } }
+
+      it 'does not raise a validation exception' do
+        expect(validator.validate!(params)).to eql params
+      end
+
+      context 'mixed with other params' do
+        let(:mixed_params) { params.merge!(other: true, andanother: true) }
+
+        it 'does not raise a validation exception' do
+          expect(validator.validate!(mixed_params)).to eql mixed_params
+        end
+      end
+    end
+
+    context 'when none of the restricted params is selected' do
+      let(:params) { { somethingelse: true } }
+
+      it 'does not raise a validation exception' do
+        expect(validator.validate!(params)).to eql params
+      end
+    end
+
+    context 'when only a subset of restricted params are present' do
+      let(:params) { { beer: true, grapefruit: true } }
+
+      it 'raises a validation exception' do
+        expect do
+          validator.validate! params
+        end.to raise_error(Grape::Exceptions::Validation)
+      end
+      context 'mixed with other params' do
+        let(:mixed_params) { params.merge!(other: true, andanother: true) }
+
+        it 'raise a validation exception' do
+          expect do
+            validator.validate! params
+          end.to raise_error(Grape::Exceptions::Validation)
+        end
+      end
+    end
+  end
+end
diff --git a/spec/grape/validations/validators/allow_blank_spec.rb b/spec/grape/validations/validators/allow_blank_spec.rb
new file mode 100644
index 0000000..a400913
--- /dev/null
+++ b/spec/grape/validations/validators/allow_blank_spec.rb
@@ -0,0 +1,282 @@
+require 'spec_helper'
+
+describe Grape::Validations::AllowBlankValidator do
+  module ValidationsSpec
+    module AllowBlankValidatorSpec
+      class API < Grape::API
+        default_format :json
+
+        params do
+          requires :name, allow_blank: false
+        end
+        get
+
+        params do
+          optional :name, allow_blank: false
+        end
+        get '/disallow_blank_optional_param'
+
+        params do
+          requires :name, allow_blank: true
+        end
+        get '/allow_blank'
+
+        params do
+          requires :val, type: DateTime, allow_blank: true
+        end
+        get '/allow_datetime_blank'
+
+        params do
+          requires :val, type: DateTime, allow_blank: false
+        end
+        get '/disallow_datetime_blank'
+
+        params do
+          requires :val, type: DateTime
+        end
+        get '/default_allow_datetime_blank'
+
+        params do
+          requires :val, type: Date, allow_blank: true
+        end
+        get '/allow_date_blank'
+
+        params do
+          requires :val, type: Integer, allow_blank: true
+        end
+        get '/allow_integer_blank'
+
+        params do
+          requires :val, type: Float, allow_blank: true
+        end
+        get '/allow_float_blank'
+
+        params do
+          requires :val, type: Fixnum, allow_blank: true
+        end
+        get '/allow_fixnum_blank'
+
+        params do
+          requires :val, type: Symbol, allow_blank: true
+        end
+        get '/allow_symbol_blank'
+
+        params do
+          requires :val, type: Boolean, allow_blank: true
+        end
+        get '/allow_boolean_blank'
+
+        params do
+          requires :val, type: Boolean, allow_blank: false
+        end
+        get '/disallow_boolean_blank'
+
+        params do
+          optional :user, type: Hash do
+            requires :name, allow_blank: false
+          end
+        end
+        get '/disallow_blank_required_param_in_an_optional_group'
+
+        params do
+          optional :user, type: Hash do
+            requires :name, type: Date, allow_blank: true
+          end
+        end
+        get '/allow_blank_date_param_in_an_optional_group'
+
+        params do
+          optional :user, type: Hash do
+            optional :name, allow_blank: false
+            requires :age
+          end
+        end
+        get '/disallow_blank_optional_param_in_an_optional_group'
+
+        params do
+          requires :user, type: Hash do
+            requires :name, allow_blank: false
+          end
+        end
+        get '/disallow_blank_required_param_in_a_required_group'
+
+        params do
+          requires :user, type: Hash do
+            requires :name, allow_blank: false
+          end
+        end
+        get '/disallow_string_value_in_a_required_hash_group'
+
+        params do
+          requires :user, type: Hash do
+            optional :name, allow_blank: false
+          end
+        end
+        get '/disallow_blank_optional_param_in_a_required_group'
+
+        params do
+          optional :user, type: Hash do
+            optional :name, allow_blank: false
+          end
+        end
+        get '/disallow_string_value_in_an_optional_hash_group'
+      end
+    end
+  end
+
+  def app
+    ValidationsSpec::AllowBlankValidatorSpec::API
+  end
+
+  context 'invalid input' do
+    it 'refuses empty string' do
+      get '/', name: ''
+      expect(last_response.status).to eq(400)
+
+      get '/disallow_datetime_blank', val: ''
+      expect(last_response.status).to eq(400)
+    end
+
+    it 'refuses only whitespaces' do
+      get '/', name: '   '
+      expect(last_response.status).to eq(400)
+
+      get '/', name: "  \n "
+      expect(last_response.status).to eq(400)
+
+      get '/', name: "\n"
+      expect(last_response.status).to eq(400)
+    end
+
+    it 'refuses nil' do
+      get '/', name: nil
+      expect(last_response.status).to eq(400)
+    end
+  end
+
+  context 'valid input' do
+    it 'accepts valid input' do
+      get '/', name: 'bob'
+      expect(last_response.status).to eq(200)
+    end
+
+    it 'accepts empty input when allow_blank is false' do
+      get '/allow_blank', name: ''
+      expect(last_response.status).to eq(200)
+    end
+
+    it 'accepts empty input' do
+      get '/default_allow_datetime_blank', val: ''
+      expect(last_response.status).to eq(200)
+    end
+
+    it 'accepts empty when datetime allow_blank' do
+      get '/allow_datetime_blank', val: ''
+      expect(last_response.status).to eq(200)
+    end
+
+    it 'accepts empty when date allow_blank' do
+      get '/allow_date_blank', val: ''
+      expect(last_response.status).to eq(200)
+    end
+
+    context 'allow_blank when Numeric' do
+      it 'accepts empty when integer allow_blank' do
+        get '/allow_integer_blank', val: ''
+        expect(last_response.status).to eq(200)
+      end
+
+      it 'accepts empty when float allow_blank' do
+        get '/allow_float_blank', val: ''
+        expect(last_response.status).to eq(200)
+      end
+
+      it 'accepts empty when fixnum allow_blank' do
+        get '/allow_fixnum_blank', val: ''
+        expect(last_response.status).to eq(200)
+      end
+    end
+
+    it 'accepts empty when symbol allow_blank' do
+      get '/allow_symbol_blank', val: ''
+      expect(last_response.status).to eq(200)
+    end
+
+    it 'accepts empty when boolean allow_blank' do
+      get '/allow_boolean_blank', val: ''
+      expect(last_response.status).to eq(200)
+    end
+
+    it 'accepts false when boolean allow_blank' do
+      get '/disallow_boolean_blank', val: false
+      expect(last_response.status).to eq(200)
+    end
+  end
+
+  context 'in an optional group' do
+    context 'as a required param' do
+      it 'accepts a missing group, even with a disallwed blank param' do
+        get '/disallow_blank_required_param_in_an_optional_group'
+        expect(last_response.status).to eq(200)
+      end
+
+      it 'accepts a nested missing date value' do
+        get '/allow_blank_date_param_in_an_optional_group', user: { name: '' }
+        expect(last_response.status).to eq(200)
+      end
+
+      it 'refuses a blank value in an existing group' do
+        get '/disallow_blank_required_param_in_an_optional_group', user: { name: '' }
+        expect(last_response.status).to eq(400)
+      end
+    end
+
+    context 'as an optional param' do
+      it 'accepts a missing group, even with a disallwed blank param' do
+        get '/disallow_blank_optional_param_in_an_optional_group'
+        expect(last_response.status).to eq(200)
+      end
+
+      it 'accepts a nested missing optional value' do
+        get '/disallow_blank_optional_param_in_an_optional_group', user: { age: '29' }
+        expect(last_response.status).to eq(200)
+      end
+
+      it 'refuses a blank existing value in an existing scope' do
+        get '/disallow_blank_optional_param_in_an_optional_group', user: { age: '29', name: '' }
+        expect(last_response.status).to eq(400)
+      end
+    end
+  end
+
+  context 'in a required group' do
+    context 'as a required param' do
+      it 'refuses a blank value in a required existing group' do
+        get '/disallow_blank_required_param_in_a_required_group', user: { name: '' }
+        expect(last_response.status).to eq(400)
+      end
+
+      it 'refuses a string value in a required hash group' do
+        get '/disallow_string_value_in_a_required_hash_group', user: ''
+        expect(last_response.status).to eq(400)
+      end
+    end
+
+    context 'as an optional param' do
+      it 'accepts a nested missing value' do
+        get '/disallow_blank_optional_param_in_a_required_group', user: { age: '29' }
+        expect(last_response.status).to eq(200)
+      end
+
+      it 'refuses a blank existing value in an existing scope' do
+        get '/disallow_blank_optional_param_in_a_required_group', user: { age: '29', name: '' }
+        expect(last_response.status).to eq(400)
+      end
+
+      it 'refuses a string value in an optional hash group' do
+        get '/disallow_string_value_in_an_optional_hash_group', user: ''
+        expect(last_response.status).to eq(400)
+      end
+    end
+  end
+end
diff --git a/spec/grape/validations/validators/at_least_one_of_spec.rb b/spec/grape/validations/validators/at_least_one_of_spec.rb
new file mode 100644
index 0000000..6a0a05f
--- /dev/null
+++ b/spec/grape/validations/validators/at_least_one_of_spec.rb
@@ -0,0 +1,67 @@
+require 'spec_helper'
+
+describe Grape::Validations::AtLeastOneOfValidator do
+  describe '#validate!' do
+    let(:scope) do
+      Struct.new(:opts) do
+        def params(arg)
+          arg
+        end
+
+        def required?; end
+      end
+    end
+    let(:at_least_one_of_params) { [:beer, :wine, :grapefruit] }
+    let(:validator) { described_class.new(at_least_one_of_params, {}, false, scope.new) }
+
+    context 'when all restricted params are present' do
+      let(:params) { { beer: true, wine: true, grapefruit: true } }
+
+      it 'does not raise a validation exception' do
+        expect(validator.validate!(params)).to eql params
+      end
+
+      context 'mixed with other params' do
+        let(:mixed_params) { params.merge!(other: true, andanother: true) }
+
+        it 'does not raise a validation exception' do
+          expect(validator.validate!(mixed_params)).to eql mixed_params
+        end
+      end
+    end
+
+    context 'when a subset of restricted params are present' do
+      let(:params) { { beer: true, grapefruit: true } }
+
+      it 'does not raise a validation exception' do
+        expect(validator.validate!(params)).to eql params
+      end
+    end
+
+    context 'when params keys come as strings' do
+      let(:params) { { 'beer' => true, 'grapefruit' => true } }
+
+      it 'does not raise a validation exception' do
+        expect(validator.validate!(params)).to eql params
+      end
+    end
+
+    context 'when none of the restricted params is selected' do
+      let(:params) { { somethingelse: true } }
+
+      it 'raises a validation exception' do
+        expect do
+          validator.validate! params
+        end.to raise_error(Grape::Exceptions::Validation)
+      end
+    end
+
+    context 'when exactly one of the restricted params is selected' do
+      let(:params) { { beer: true, somethingelse: true } }
+
+      it 'does not raise a validation exception' do
+        expect(validator.validate!(params)).to eql params
+      end
+    end
+  end
+end
diff --git a/spec/grape/validations/validators/coerce_spec.rb b/spec/grape/validations/validators/coerce_spec.rb
new file mode 100644
index 0000000..756fe4f
--- /dev/null
+++ b/spec/grape/validations/validators/coerce_spec.rb
@@ -0,0 +1,235 @@
+# encoding: utf-8
+require 'spec_helper'
+
+describe Grape::Validations::CoerceValidator do
+  subject do
+    Class.new(Grape::API)
+  end
+
+  def app
+    subject
+  end
+
+  describe 'coerce' do
+    context 'i18n' do
+      after :each do
+        I18n.locale = :en
+      end
+
+      it 'i18n error on malformed input' do
+        I18n.load_path << File.expand_path('../zh-CN.yml', __FILE__)
+        I18n.reload!
+        I18n.locale = 'zh-CN'.to_sym
+        subject.params do
+          requires :age, type: Integer
+        end
+        subject.get '/single' do
+          'int works'
+        end
+
+        get '/single', age: '43a'
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('年龄格式不正确')
+      end
+
+      it 'gives an english fallback error when default locale message is blank' do
+        I18n.locale = 'pt-BR'.to_sym
+        subject.params do
+          requires :age, type: Integer
+        end
+        subject.get '/single' do
+          'int works'
+        end
+
+        get '/single', age: '43a'
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('age is invalid')
+      end
+    end
+
+    it 'error on malformed input' do
+      subject.params do
+        requires :int, type: Integer
+      end
+      subject.get '/single' do
+        'int works'
+      end
+
+      get '/single', int: '43a'
+      expect(last_response.status).to eq(400)
+      expect(last_response.body).to eq('int is invalid')
+
+      get '/single', int: '43'
+      expect(last_response.status).to eq(200)
+      expect(last_response.body).to eq('int works')
+    end
+
+    it 'error on malformed input (Array)' do
+      subject.params do
+        requires :ids, type: Array[Integer]
+      end
+      subject.get '/array' do
+        'array int works'
+      end
+
+      get 'array', ids: %w(1 2 az)
+      expect(last_response.status).to eq(400)
+      expect(last_response.body).to eq('ids is invalid')
+
+      get 'array', ids: %w(1 2 890)
+      expect(last_response.status).to eq(200)
+      expect(last_response.body).to eq('array int works')
+    end
+
+    context 'complex objects' do
+      module CoerceValidatorSpec
+        class User
+          include Virtus.model
+          attribute :id, Integer
+          attribute :name, String
+        end
+      end
+
+      it 'error on malformed input for complex objects' do
+        subject.params do
+          requires :user, type: CoerceValidatorSpec::User
+        end
+        subject.get '/user' do
+          'complex works'
+        end
+
+        get '/user', user: '32'
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('user is invalid')
+
+        get '/user', user: { id: 32, name: 'Bob' }
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('complex works')
+      end
+    end
+
+    context 'coerces' do
+      it 'Integer' do
+        subject.params do
+          requires :int, coerce: Integer
+        end
+        subject.get '/int' do
+          params[:int].class
+        end
+
+        get '/int', int: '45'
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('Fixnum')
+      end
+
+      context 'Array' do
+        it 'Array of Integers' do
+          subject.params do
+            requires :arry, coerce: Array[Integer]
+          end
+          subject.get '/array' do
+            params[:arry][0].class
+          end
+
+          get '/array', arry: %w(1 2 3)
+          expect(last_response.status).to eq(200)
+          expect(last_response.body).to eq('Fixnum')
+        end
+
+        it 'Array of Bools' do
+          subject.params do
+            requires :arry, coerce: Array[Virtus::Attribute::Boolean]
+          end
+          subject.get '/array' do
+            params[:arry][0].class
+          end
+
+          get 'array', arry: [1, 0]
+          expect(last_response.status).to eq(200)
+          expect(last_response.body).to eq('TrueClass')
+        end
+      end
+
+      context 'Set' do
+        it 'Set of Integers' do
+          subject.params do
+            requires :set, coerce: Set[Integer]
+          end
+          subject.get '/set' do
+            params[:set].first.class
+          end
+
+          get '/set', set: Set.new([1, 2, 3, 4]).to_a
+          expect(last_response.status).to eq(200)
+          expect(last_response.body).to eq('Fixnum')
+        end
+
+        it 'Set of Bools' do
+          subject.params do
+            requires :set, coerce: Set[Virtus::Attribute::Boolean]
+          end
+          subject.get '/set' do
+            params[:set].first.class
+          end
+
+          get '/set', set: Set.new([1, 0]).to_a
+          expect(last_response.status).to eq(200)
+          expect(last_response.body).to eq('TrueClass')
+        end
+      end
+
+      it 'Bool' do
+        subject.params do
+          requires :bool, coerce: Virtus::Attribute::Boolean
+        end
+        subject.get '/bool' do
+          params[:bool].class
+        end
+
+        get '/bool', bool: 1
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('TrueClass')
+
+        get '/bool', bool: 0
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('FalseClass')
+
+        get '/bool', bool: 'false'
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('FalseClass')
+
+        get '/bool', bool: 'true'
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('TrueClass')
+      end
+
+      it 'file' do
+        subject.params do
+          requires :file, coerce: Rack::Multipart::UploadedFile
+        end
+        subject.post '/upload' do
+          params[:file].filename
+        end
+
+        post '/upload', file: Rack::Test::UploadedFile.new(__FILE__)
+        expect(last_response.status).to eq(201)
+        expect(last_response.body).to eq(File.basename(__FILE__).to_s)
+      end
+
+      it 'Nests integers' do
+        subject.params do
+          requires :integers, type: Hash do
+            requires :int, coerce: Integer
+          end
+        end
+        subject.get '/int' do
+          params[:integers][:int].class
+        end
+
+        get '/int', integers: { int: '45' }
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('Fixnum')
+      end
+    end
+  end
+end
diff --git a/spec/grape/validations/validators/default_spec.rb b/spec/grape/validations/validators/default_spec.rb
new file mode 100644
index 0000000..746a9ad
--- /dev/null
+++ b/spec/grape/validations/validators/default_spec.rb
@@ -0,0 +1,211 @@
+require 'spec_helper'
+
+describe Grape::Validations::DefaultValidator do
+  module ValidationsSpec
+    module DefaultValidatorSpec
+      class API < Grape::API
+        default_format :json
+
+        params do
+          optional :id
+          optional :type, default: 'default-type'
+        end
+        get '/' do
+          { id: params[:id], type: params[:type] }
+        end
+
+        params do
+          optional :type1, default: 'default-type1'
+          optional :type2, default: 'default-type2'
+        end
+        get '/user' do
+          { type1: params[:type1], type2: params[:type2] }
+        end
+
+        params do
+          requires :id
+          optional :type1, default: 'default-type1'
+          optional :type2, default: 'default-type2'
+        end
+
+        get '/message' do
+          { id: params[:id], type1: params[:type1], type2: params[:type2] }
+        end
+
+        params do
+          optional :random, default: -> { Random.rand }
+          optional :not_random, default: Random.rand
+        end
+        get '/numbers' do
+          { random_number: params[:random], non_random_number: params[:non_random_number] }
+        end
+
+        params do
+          optional :array, type: Array do
+            requires :name
+            optional :with_default, default: 'default'
+          end
+        end
+        get '/array' do
+          { array: params[:array] }
+        end
+      end
+    end
+  end
+
+  def app
+    ValidationsSpec::DefaultValidatorSpec::API
+  end
+
+  it 'set default value for optional param' do
+    get('/')
+    expect(last_response.status).to eq(200)
+    expect(last_response.body).to eq({ id: nil, type: 'default-type' }.to_json)
+  end
+
+  it 'set default values for optional params' do
+    get('/user')
+    expect(last_response.status).to eq(200)
+    expect(last_response.body).to eq({ type1: 'default-type1', type2: 'default-type2' }.to_json)
+  end
+
+  it 'set default values for missing params in the request' do
+    get('/user?type2=value2')
+    expect(last_response.status).to eq(200)
+    expect(last_response.body).to eq({ type1: 'default-type1', type2: 'value2' }.to_json)
+  end
+
+  it 'set default values for optional params and allow to use required fields in the same time' do
+    get('/message?id=1')
+    expect(last_response.status).to eq(200)
+    expect(last_response.body).to eq({ id: '1', type1: 'default-type1', type2: 'default-type2' }.to_json)
+  end
+
+  it 'sets lambda based defaults at the time of call' do
+    get('/numbers')
+    expect(last_response.status).to eq(200)
+    before = JSON.parse(last_response.body)
+    get('/numbers')
+    expect(last_response.status).to eq(200)
+    after = JSON.parse(last_response.body)
+
+    expect(before['non_random_number']).to eq(after['non_random_number'])
+    expect(before['random_number']).not_to eq(after['random_number'])
+  end
+
+  it 'sets default values for grouped arrays' do
+    get('/array?array[][name]=name&array[][name]=name2&array[][with_default]=bar2')
+    expect(last_response.status).to eq(200)
+    expect(last_response.body).to eq({ array: [{ name: 'name', with_default: 'default' }, { name: 'name2', with_default: 'bar2' }] }.to_json)
+  end
+
+  context 'optional group with defaults' do
+    subject do
+      Class.new(Grape::API) do
+        default_format :json
+      end
+    end
+
+    def app
+      subject
+    end
+
+    context 'optional array without default value includes optional param with default value' do
+      before do
+        subject.params do
+          optional :optional_array, type: Array do
+            optional :foo_in_optional_array, default: 'bar'
+          end
+        end
+        subject.post '/optional_array' do
+          { optional_array: params[:optional_array] }
+        end
+      end
+
+      it 'returns nil for optional array if param is not provided' do
+        post '/optional_array'
+        expect(last_response.status).to eq(201)
+        expect(last_response.body).to eq({ optional_array: nil }.to_json)
+      end
+    end
+
+    context 'optional array with default value includes optional param with default value' do
+      before do
+        subject.params do
+          optional :optional_array_with_default, type: Array, default: [] do
+            optional :foo_in_optional_array, default: 'bar'
+          end
+        end
+        subject.post '/optional_array_with_default' do
+          { optional_array_with_default: params[:optional_array_with_default] }
+        end
+      end
+
+      it 'sets default value for optional array if param is not provided' do
+        post '/optional_array_with_default'
+        expect(last_response.status).to eq(201)
+        expect(last_response.body).to eq({ optional_array_with_default: [] }.to_json)
+      end
+    end
+
+    context 'optional hash without default value includes optional param with default value' do
+      before do
+        subject.params do
+          optional :optional_hash_without_default, type: Hash do
+            optional :foo_in_optional_hash, default: 'bar'
+          end
+        end
+        subject.post '/optional_hash_without_default' do
+          { optional_hash_without_default: params[:optional_hash_without_default] }
+        end
+      end
+
+      it 'returns nil for optional hash if param is not provided' do
+        post '/optional_hash_without_default'
+        expect(last_response.status).to eq(201)
+        expect(last_response.body).to eq({ optional_hash_without_default: nil }.to_json)
+      end
+    end
+
+    context 'optional hash with default value includes optional param with default value' do
+      before do
+        subject.params do
+          optional :optional_hash_with_default, type: Hash, default: {} do
+            optional :foo_in_optional_hash, default: 'bar'
+          end
+        end
+        subject.post '/optional_hash_with_default_empty_hash' do
+          { optional_hash_with_default: params[:optional_hash_with_default] }
+        end
+
+        subject.params do
+          optional :optional_hash_with_default, type: Hash, default: { foo_in_optional_hash: 'parent_default' } do
+            optional :some_param
+            optional :foo_in_optional_hash, default: 'own_default'
+          end
+        end
+        subject.post '/optional_hash_with_default_inner_params' do
+          { foo_in_optional_hash: params[:optional_hash_with_default][:foo_in_optional_hash] }
+        end
+      end
+
+      it 'sets default value for optional hash if param is not provided' do
+        post '/optional_hash_with_default_empty_hash'
+        expect(last_response.status).to eq(201)
+        expect(last_response.body).to eq({ optional_hash_with_default: {} }.to_json)
+      end
+
+      it 'sets default value from parent defaults for inner param if parent param is not provided' do
+        post '/optional_hash_with_default_inner_params'
+        expect(last_response.status).to eq(201)
+        expect(last_response.body).to eq({ foo_in_optional_hash: 'parent_default' }.to_json)
+      end
+
+      it 'sets own default value for inner param if parent param is provided' do
+        post '/optional_hash_with_default_inner_params', optional_hash_with_default: { some_param: 'param' }
+        expect(last_response.status).to eq(201)
+        expect(last_response.body).to eq({ foo_in_optional_hash: 'own_default' }.to_json)
+      end
+    end
+  end
+end
diff --git a/spec/grape/validations/validators/exactly_one_of_spec.rb b/spec/grape/validations/validators/exactly_one_of_spec.rb
new file mode 100644
index 0000000..166bb06
--- /dev/null
+++ b/spec/grape/validations/validators/exactly_one_of_spec.rb
@@ -0,0 +1,75 @@
+require 'spec_helper'
+
+describe Grape::Validations::ExactlyOneOfValidator do
+  describe '#validate!' do
+    let(:scope) do
+      Struct.new(:opts) do
+        def params(arg)
+          arg
+        end
+
+        def required?; end
+      end
+    end
+    let(:exactly_one_of_params) { [:beer, :wine, :grapefruit] }
+    let(:validator) { described_class.new(exactly_one_of_params, {}, false, scope.new) }
+
+    context 'when all restricted params are present' do
+      let(:params) { { beer: true, wine: true, grapefruit: true } }
+
+      it 'raises a validation exception' do
+        expect do
+          validator.validate! params
+        end.to raise_error(Grape::Exceptions::Validation)
+      end
+
+      context 'mixed with other params' do
+        let(:mixed_params) { params.merge!(other: true, andanother: true) }
+
+        it 'still raises a validation exception' do
+          expect do
+            validator.validate! mixed_params
+          end.to raise_error(Grape::Exceptions::Validation)
+        end
+      end
+    end
+
+    context 'when a subset of restricted params are present' do
+      let(:params) { { beer: true, grapefruit: true } }
+
+      it 'raises a validation exception' do
+        expect do
+          validator.validate! params
+        end.to raise_error(Grape::Exceptions::Validation)
+      end
+    end
+
+    context 'when params keys come as strings' do
+      let(:params) { { 'beer' => true, 'grapefruit' => true } }
+
+      it 'raises a validation exception' do
+        expect do
+          validator.validate! params
+        end.to raise_error(Grape::Exceptions::Validation)
+      end
+    end
+
+    context 'when none of the restricted params is selected' do
+      let(:params) { { somethingelse: true } }
+
+      it 'raises a validation exception' do
+        expect do
+          validator.validate! params
+        end.to raise_error(Grape::Exceptions::Validation)
+      end
+    end
+
+    context 'when exactly one of the restricted params is selected' do
+      let(:params) { { beer: true, somethingelse: true } }
+
+      it 'does not raise a validation exception' do
+        expect(validator.validate!(params)).to eql params
+      end
+    end
+  end
+end
diff --git a/spec/grape/validations/validators/mutual_exclusion_spec.rb b/spec/grape/validations/validators/mutual_exclusion_spec.rb
new file mode 100644
index 0000000..f067780
--- /dev/null
+++ b/spec/grape/validations/validators/mutual_exclusion_spec.rb
@@ -0,0 +1,63 @@
+require 'spec_helper'
+
+describe Grape::Validations::MutualExclusionValidator do
+  describe '#validate!' do
+    let(:scope) do
+      Struct.new(:opts) do
+        def params(arg)
+          arg
+        end
+      end
+    end
+    let(:mutually_exclusive_params) { [:beer, :wine, :grapefruit] }
+    let(:validator) { described_class.new(mutually_exclusive_params, {}, false, scope.new) }
+
+    context 'when all mutually exclusive params are present' do
+      let(:params) { { beer: true, wine: true, grapefruit: true } }
+
+      it 'raises a validation exception' do
+        expect do
+          validator.validate! params
+        end.to raise_error(Grape::Exceptions::Validation)
+      end
+
+      context 'mixed with other params' do
+        let(:mixed_params) { params.merge!(other: true, andanother: true) }
+
+        it 'still raises a validation exception' do
+          expect do
+            validator.validate! mixed_params
+          end.to raise_error(Grape::Exceptions::Validation)
+        end
+      end
+    end
+
+    context 'when a subset of mutually exclusive params are present' do
+      let(:params) { { beer: true, grapefruit: true } }
+
+      it 'raises a validation exception' do
+        expect do
+          validator.validate! params
+        end.to raise_error(Grape::Exceptions::Validation)
+      end
+    end
+
+    context 'when params keys come as strings' do
+      let(:params) { { 'beer' => true, 'grapefruit' => true } }
+
+      it 'raises a validation exception' do
+        expect do
+          validator.validate! params
+        end.to raise_error(Grape::Exceptions::Validation)
+      end
+    end
+
+    context 'when no mutually exclusive params are present' do
+      let(:params) { { beer: true, somethingelse: true } }
+
+      it 'params' do
+        expect(validator.validate!(params)).to eql params
+      end
+    end
+  end
+end
diff --git a/spec/grape/validations/validators/presence_spec.rb b/spec/grape/validations/validators/presence_spec.rb
new file mode 100644
index 0000000..980bec1
--- /dev/null
+++ b/spec/grape/validations/validators/presence_spec.rb
@@ -0,0 +1,217 @@
+require 'spec_helper'
+
+describe Grape::Validations::PresenceValidator do
+  subject do
+    Class.new(Grape::API) do
+      format :json
+    end
+  end
+  def app
+    subject
+  end
+
+  context 'without validation' do
+    before do
+      subject.resource :bacons do
+        get do
+          'All the bacon'
+        end
+      end
+    end
+    it 'does not validate for any params' do
+      get '/bacons'
+      expect(last_response.status).to eq(200)
+      expect(last_response.body).to eq('All the bacon'.to_json)
+    end
+  end
+
+  context 'with a required regexp parameter supplied in the POST body' do
+    before do
+      subject.format :json
+      subject.params do
+        requires :id, regexp: /^[0-9]+$/
+      end
+      subject.post do
+        { ret: params[:id] }
+      end
+    end
+    it 'validates id' do
+      post '/'
+      expect(last_response.status).to eq(400)
+      expect(last_response.body).to eq('{"error":"id is missing"}')
+
+      io = StringIO.new('{"id" : "a56b"}')
+      post '/', {}, 'rack.input' => io, 'CONTENT_TYPE' => 'application/json', 'CONTENT_LENGTH' => io.length
+      expect(last_response.body).to eq('{"error":"id is invalid"}')
+      expect(last_response.status).to eq(400)
+
+      io = StringIO.new('{"id" : 56}')
+      post '/', {}, 'rack.input' => io, 'CONTENT_TYPE' => 'application/json', 'CONTENT_LENGTH' => io.length
+      expect(last_response.body).to eq('{"ret":56}')
+      expect(last_response.status).to eq(201)
+    end
+  end
+
+  context 'with a required non-empty string' do
+    before do
+      subject.params do
+        requires :email, type: String, allow_blank: false, regexp: /^\S+$/
+      end
+      subject.get do
+        'Hello'
+      end
+    end
+    it 'requires when missing' do
+      get '/'
+      expect(last_response.status).to eq(400)
+      expect(last_response.body).to eq('{"error":"email is missing, email is empty"}')
+    end
+    it 'requires when empty' do
+      get '/', email: ''
+      expect(last_response.status).to eq(400)
+      expect(last_response.body).to eq('{"error":"email is empty, email is invalid"}')
+    end
+    it 'valid when set' do
+      get '/', email: 'bob at example.com'
+      expect(last_response.status).to eq(200)
+      expect(last_response.body).to eq('Hello'.to_json)
+    end
+  end
+
+  context 'with required parameters and no type' do
+    before do
+      subject.params do
+        requires :name, :company
+      end
+      subject.get do
+        'Hello'
+      end
+    end
+    it 'validates name, company' do
+      get '/'
+      expect(last_response.status).to eq(400)
+      expect(last_response.body).to eq('{"error":"name is missing"}')
+
+      get '/', name: 'Bob'
+      expect(last_response.status).to eq(400)
+      expect(last_response.body).to eq('{"error":"company is missing"}')
+
+      get '/', name: 'Bob', company: 'TestCorp'
+      expect(last_response.status).to eq(200)
+      expect(last_response.body).to eq('Hello'.to_json)
+    end
+  end
+
+  context 'with nested parameters' do
+    before do
+      subject.params do
+        requires :user, type: Hash do
+          requires :first_name
+          requires :last_name
+        end
+      end
+      subject.get '/nested' do
+        'Nested'
+      end
+    end
+    it 'validates nested parameters' do
+      get '/nested'
+      expect(last_response.status).to eq(400)
+      expect(last_response.body).to eq('{"error":"user is missing, user[first_name] is missing, user[last_name] is missing"}')
+
+      get '/nested', user: { first_name: 'Billy' }
+      expect(last_response.status).to eq(400)
+      expect(last_response.body).to eq('{"error":"user[last_name] is missing"}')
+
+      get '/nested', user: { first_name: 'Billy', last_name: 'Bob' }
+      expect(last_response.status).to eq(200)
+      expect(last_response.body).to eq('Nested'.to_json)
+    end
+  end
+
+  context 'with triply nested required parameters' do
+    before do
+      subject.params do
+        requires :admin, type: Hash do
+          requires :admin_name
+          requires :super, type: Hash do
+            requires :user, type: Hash do
+              requires :first_name
+              requires :last_name
+            end
+          end
+        end
+      end
+      subject.get '/nested_triple' do
+        'Nested triple'
+      end
+    end
+    it 'validates triple nested parameters' do
+      get '/nested_triple'
+      expect(last_response.status).to eq(400)
+      expect(last_response.body).to include '{"error":"admin is missing'
+
+      get '/nested_triple', user: { first_name: 'Billy' }
+      expect(last_response.status).to eq(400)
+      expect(last_response.body).to include '{"error":"admin is missing'
+
+      get '/nested_triple', admin: { super: { first_name: 'Billy' } }
+      expect(last_response.status).to eq(400)
+      expect(last_response.body).to eq('{"error":"admin[admin_name] is missing, admin[super][user] is missing, admin[super][user][first_name] is missing, admin[super][user][last_name] is missing"}')
+
+      get '/nested_triple', super: { user: { first_name: 'Billy', last_name: 'Bob' } }
+      expect(last_response.status).to eq(400)
+      expect(last_response.body).to include '{"error":"admin is missing'
+
+      get '/nested_triple', admin: { super: { user: { first_name: 'Billy' } } }
+      expect(last_response.status).to eq(400)
+      expect(last_response.body).to eq('{"error":"admin[admin_name] is missing, admin[super][user][last_name] is missing"}')
+
+      get '/nested_triple', admin: { admin_name: 'admin', super: { user: { first_name: 'Billy' } } }
+      expect(last_response.status).to eq(400)
+      expect(last_response.body).to eq('{"error":"admin[super][user][last_name] is missing"}')
+
+      get '/nested_triple', admin: { admin_name: 'admin', super: { user: { first_name: 'Billy', last_name: 'Bob' } } }
+      expect(last_response.status).to eq(200)
+      expect(last_response.body).to eq('Nested triple'.to_json)
+    end
+  end
+
+  context 'with reused parameter documentation once required and once optional' do
+    before do
+      docs = { name: { type: String, desc: 'some name' } }
+
+      subject.params do
+        requires :all, using: docs
+      end
+      subject.get '/required' do
+        'Hello required'
+      end
+
+      subject.params do
+        optional :all, using: docs
+      end
+      subject.get '/optional' do
+        'Hello optional'
+      end
+    end
+    it 'works with required' do
+      get '/required'
+      expect(last_response.status).to eq(400)
+      expect(last_response.body).to eq('{"error":"name is missing"}')
+
+      get '/required', name: 'Bob'
+      expect(last_response.status).to eq(200)
+      expect(last_response.body).to eq('Hello required'.to_json)
+    end
+    it 'works with optional' do
+      get '/optional'
+      expect(last_response.status).to eq(200)
+      expect(last_response.body).to eq('Hello optional'.to_json)
+
+      get '/optional', name: 'Bob'
+      expect(last_response.status).to eq(200)
+      expect(last_response.body).to eq('Hello optional'.to_json)
+    end
+  end
+end
diff --git a/spec/grape/validations/validators/regexp_spec.rb b/spec/grape/validations/validators/regexp_spec.rb
new file mode 100644
index 0000000..5c5b353
--- /dev/null
+++ b/spec/grape/validations/validators/regexp_spec.rb
@@ -0,0 +1,43 @@
+require 'spec_helper'
+
+describe Grape::Validations::RegexpValidator do
+  module ValidationsSpec
+    module RegexpValidatorSpec
+      class API < Grape::API
+        default_format :json
+
+        params do
+          requires :name, regexp: /^[a-z]+$/
+        end
+        get do
+        end
+      end
+    end
+  end
+
+  def app
+    ValidationsSpec::RegexpValidatorSpec::API
+  end
+
+  context 'invalid input' do
+    it 'refuses inapppopriate' do
+      get '/', name: 'invalid name'
+      expect(last_response.status).to eq(400)
+    end
+
+    it 'refuses empty' do
+      get '/', name: ''
+      expect(last_response.status).to eq(400)
+    end
+  end
+
+  it 'accepts nil' do
+    get '/', name: nil
+    expect(last_response.status).to eq(200)
+  end
+
+  it 'accepts valid input' do
+    get '/', name: 'bob'
+    expect(last_response.status).to eq(200)
+  end
+end
diff --git a/spec/grape/validations/validators/values_spec.rb b/spec/grape/validations/validators/values_spec.rb
new file mode 100644
index 0000000..a10fc32
--- /dev/null
+++ b/spec/grape/validations/validators/values_spec.rb
@@ -0,0 +1,268 @@
+require 'spec_helper'
+
+describe Grape::Validations::ValuesValidator do
+  class ValuesModel
+    DEFAULT_VALUES = ['valid-type1', 'valid-type2', 'valid-type3']
+    class << self
+      def values
+        @values ||= []
+        [DEFAULT_VALUES + @values].flatten.uniq
+      end
+
+      def add_value(value)
+        @values ||= []
+        @values << value
+      end
+    end
+  end
+
+  module ValidationsSpec
+    module ValuesValidatorSpec
+      class API < Grape::API
+        default_format :json
+
+        params do
+          requires :type, values: ValuesModel.values
+        end
+        get '/' do
+          { type: params[:type] }
+        end
+
+        params do
+          optional :type, values: ValuesModel.values, default: 'valid-type2'
+        end
+        get '/default/valid' do
+          { type: params[:type] }
+        end
+
+        params do
+          optional :type, values: -> { ValuesModel.values }, default: 'valid-type2'
+        end
+        get '/lambda' do
+          { type: params[:type] }
+        end
+
+        params do
+          optional :type, values: ValuesModel.values, default: -> { ValuesModel.values.sample }
+        end
+        get '/default_lambda' do
+          { type: params[:type] }
+        end
+
+        params do
+          optional :type, values: -> { ValuesModel.values }, default: -> { ValuesModel.values.sample }
+        end
+        get '/default_and_values_lambda' do
+          { type: params[:type] }
+        end
+
+        params do
+          requires :type, type: Integer, desc: 'An integer', values: [10, 11], default: 10
+        end
+        get '/values/coercion' do
+          { type: params[:type] }
+        end
+
+        params do
+          requires :type, type: Array[Integer], desc: 'An integer', values: [10, 11], default: 10
+        end
+        get '/values/array_coercion' do
+          { type: params[:type] }
+        end
+
+        params do
+          optional :optional, type: Array do
+            requires :type, values: %w(a b)
+          end
+        end
+        get '/optional_with_required_values'
+      end
+    end
+  end
+
+  def app
+    ValidationsSpec::ValuesValidatorSpec::API
+  end
+
+  it 'allows a valid value for a parameter' do
+    get('/', type: 'valid-type1')
+    expect(last_response.status).to eq 200
+    expect(last_response.body).to eq({ type: 'valid-type1' }.to_json)
+  end
+
+  it 'does not allow an invalid value for a parameter' do
+    get('/', type: 'invalid-type')
+    expect(last_response.status).to eq 400
+    expect(last_response.body).to eq({ error: 'type does not have a valid value' }.to_json)
+  end
+
+  context 'nil value for a parameter' do
+    it 'does not allow for root params scope' do
+      get('/', type: nil)
+      expect(last_response.status).to eq 400
+      expect(last_response.body).to eq({ error: 'type does not have a valid value' }.to_json)
+    end
+
+    it 'allows for a required param in child scope' do
+      get('/optional_with_required_values')
+      expect(last_response.status).to eq 200
+    end
+  end
+
+  it 'allows a valid default value' do
+    get('/default/valid')
+    expect(last_response.status).to eq 200
+    expect(last_response.body).to eq({ type: 'valid-type2' }.to_json)
+  end
+
+  it 'allows a proc for values' do
+    get('/lambda', type: 'valid-type1')
+    expect(last_response.status).to eq 200
+    expect(last_response.body).to eq({ type: 'valid-type1' }.to_json)
+  end
+
+  it 'does not validate updated values without proc' do
+    ValuesModel.add_value('valid-type4')
+
+    get('/', type: 'valid-type4')
+    expect(last_response.status).to eq 400
+    expect(last_response.body).to eq({ error: 'type does not have a valid value' }.to_json)
+  end
+
+  it 'validates against values in a proc' do
+    ValuesModel.add_value('valid-type4')
+
+    get('/lambda', type: 'valid-type4')
+    expect(last_response.status).to eq 200
+    expect(last_response.body).to eq({ type: 'valid-type4' }.to_json)
+  end
+
+  it 'does not allow an invalid value for a parameter using lambda' do
+    get('/lambda', type: 'invalid-type')
+    expect(last_response.status).to eq 400
+    expect(last_response.body).to eq({ error: 'type does not have a valid value' }.to_json)
+  end
+
+  it 'validates default value from proc' do
+    get('/default_lambda')
+    expect(last_response.status).to eq 200
+  end
+
+  it 'validates default value from proc against values in a proc' do
+    get('/default_and_values_lambda')
+    expect(last_response.status).to eq 200
+  end
+
+  it 'raises IncompatibleOptionValues on an invalid default value from proc' do
+    subject = Class.new(Grape::API)
+    expect do
+      subject.params { optional :type, values: ['valid-type1', 'valid-type2', 'valid-type3'], default: ValuesModel.values.sample + '_invalid' }
+    end.to raise_error Grape::Exceptions::IncompatibleOptionValues
+  end
+
+  it 'raises IncompatibleOptionValues on an invalid default value' do
+    subject = Class.new(Grape::API)
+    expect do
+      subject.params { optional :type, values: ['valid-type1', 'valid-type2', 'valid-type3'], default: 'invalid-type' }
+    end.to raise_error Grape::Exceptions::IncompatibleOptionValues
+  end
+
+  it 'raises IncompatibleOptionValues when type is incompatible with values array' do
+    subject = Class.new(Grape::API)
+    expect do
+      subject.params { optional :type, values: ['valid-type1', 'valid-type2', 'valid-type3'], type: Symbol }
+    end.to raise_error Grape::Exceptions::IncompatibleOptionValues
+  end
+
+  it 'allows values to be a kind of the coerced type not just an instance of it' do
+    get('/values/coercion', type: 10)
+    expect(last_response.status).to eq 200
+    expect(last_response.body).to eq({ type: 10 }.to_json)
+  end
+
+  it 'allows values to be a kind of the coerced type in an array' do
+    get('/values/array_coercion', type: [10])
+    expect(last_response.status).to eq 200
+    expect(last_response.body).to eq({ type: [10] }.to_json)
+  end
+
+  it 'raises IncompatibleOptionValues when values contains a value that is not a kind of the type' do
+    subject = Class.new(Grape::API)
+    expect do
+      subject.params { requires :type, values: [10.5, 11], type: Integer }
+    end.to raise_error Grape::Exceptions::IncompatibleOptionValues
+  end
+
+  context 'with a lambda values' do
+    subject do
+      Class.new(Grape::API) do
+        params do
+          optional :type, type: String, values: -> { [SecureRandom.uuid] }, default: -> { SecureRandom.uuid }
+        end
+        get '/random_values'
+      end
+    end
+
+    def app
+      subject
+    end
+
+    before do
+      expect(SecureRandom).to receive(:uuid).and_return('foo').once
+    end
+
+    it 'only evaluates values dynamically with each request' do
+      get '/random_values', type: 'foo'
+      expect(last_response.status).to eq 200
+    end
+
+    it 'chooses default' do
+      get '/random_values'
+      expect(last_response.status).to eq 200
+    end
+  end
+
+  context 'with a range of values' do
+    subject(:app) do
+      Class.new(Grape::API) do
+        params do
+          optional :value, type: Float, values: 0.0..10.0
+        end
+        get '/value' do
+          { value: params[:value] }.to_json
+        end
+
+        params do
+          optional :values, type: Array[Float], values: 0.0..10.0
+        end
+        get '/values' do
+          { values: params[:values] }.to_json
+        end
+      end
+    end
+
+    it 'allows a single value inside of the range' do
+      get('/value', value: 5.2)
+      expect(last_response.status).to eq 200
+      expect(last_response.body).to eq({ value: 5.2 }.to_json)
+    end
+
+    it 'allows an array of values inside of the range' do
+      get('/values', values: [8.6, 7.5, 3, 0.9])
+      expect(last_response.status).to eq 200
+      expect(last_response.body).to eq({ values: [8.6, 7.5, 3.0, 0.9] }.to_json)
+    end
+
+    it 'rejects a single value outside the range' do
+      get('/value', value: 'a')
+      expect(last_response.status).to eq 400
+      expect(last_response.body).to eq('value is invalid, value does not have a valid value')
+    end
+
+    it 'rejects an array of values if any of them are outside the range' do
+      get('/values', values: [8.6, 75, 3, 0.9])
+      expect(last_response.status).to eq 400
+      expect(last_response.body).to eq('values does not have a valid value')
+    end
+  end
+end
diff --git a/spec/grape/validations/validators/zh-CN.yml b/spec/grape/validations/validators/zh-CN.yml
new file mode 100644
index 0000000..39f6bbd
--- /dev/null
+++ b/spec/grape/validations/validators/zh-CN.yml
@@ -0,0 +1,10 @@
+zh-CN:
+  grape:
+    errors:
+      format: ! '%{attributes}%{message}'
+      attributes:
+        age: 年龄
+      messages:
+        coerce: '格式不正确'
+        presence: '请填写'
+        regexp: '格式不正确'
diff --git a/spec/grape/validations_spec.rb b/spec/grape/validations_spec.rb
new file mode 100644
index 0000000..5786ae1
--- /dev/null
+++ b/spec/grape/validations_spec.rb
@@ -0,0 +1,1378 @@
+require 'spec_helper'
+
+describe Grape::Validations do
+  subject { Class.new(Grape::API) }
+
+  def app
+    subject
+  end
+
+  describe 'params' do
+    context 'optional' do
+      it 'validates when params is present' do
+        subject.params do
+          optional :a_number, regexp: /^[0-9]+$/
+        end
+        subject.get '/optional' do
+          'optional works!'
+        end
+
+        get '/optional', a_number: 'string'
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('a_number is invalid')
+
+        get '/optional', a_number: 45
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('optional works!')
+      end
+
+      it "doesn't validate when param not present" do
+        subject.params do
+          optional :a_number, regexp: /^[0-9]+$/
+        end
+        subject.get '/optional' do
+          'optional works!'
+        end
+
+        get '/optional'
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('optional works!')
+      end
+
+      it 'adds to declared parameters' do
+        subject.params do
+          optional :some_param
+        end
+        expect(subject.route_setting(:declared_params)).to eq([:some_param])
+      end
+    end
+
+    context 'optional using Grape::Entity documentation' do
+      def define_optional_using
+        documentation = { field_a: { type: String }, field_b: { type: String } }
+        subject.params do
+          optional :all, using: documentation
+        end
+      end
+      before do
+        define_optional_using
+        subject.get '/optional' do
+          'optional with using works'
+        end
+      end
+
+      it 'adds entity documentation to declared params' do
+        define_optional_using
+        expect(subject.route_setting(:declared_params)).to eq([:field_a, :field_b])
+      end
+
+      it 'works when field_a and field_b are not present' do
+        get '/optional'
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('optional with using works')
+      end
+
+      it 'works when field_a is present' do
+        get '/optional', field_a: 'woof'
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('optional with using works')
+      end
+
+      it 'works when field_b is present' do
+        get '/optional', field_b: 'woof'
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('optional with using works')
+      end
+    end
+
+    context 'required' do
+      before do
+        subject.params do
+          requires :key, type: String
+        end
+        subject.get('/required') { 'required works' }
+        subject.put('/required') { { key: params[:key] }.to_json }
+      end
+
+      it 'errors when param not present' do
+        get '/required'
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('key is missing')
+      end
+
+      it "doesn't throw a missing param when param is present" do
+        get '/required', key: 'cool'
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('required works')
+      end
+
+      it 'adds to declared parameters' do
+        subject.params do
+          requires :some_param
+        end
+        expect(subject.route_setting(:declared_params)).to eq([:some_param])
+      end
+
+      it 'works when required field is present but nil' do
+        put '/required', { key: nil }.to_json, 'CONTENT_TYPE' => 'application/json'
+        expect(last_response.status).to eq(200)
+        expect(JSON.parse(last_response.body)).to eq('key' => nil)
+      end
+    end
+
+    context 'requires :all using Grape::Entity documentation' do
+      def define_requires_all
+        documentation = {
+          required_field: { type: String },
+          optional_field: { type: String }
+        }
+        subject.params do
+          requires :all, except: :optional_field, using: documentation
+        end
+      end
+      before do
+        define_requires_all
+        subject.get '/required' do
+          'required works'
+        end
+      end
+
+      it 'adds entity documentation to declared params' do
+        define_requires_all
+        expect(subject.route_setting(:declared_params)).to eq([:required_field, :optional_field])
+      end
+
+      it 'errors when required_field is not present' do
+        get '/required'
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('required_field is missing')
+      end
+
+      it 'works when required_field is present' do
+        get '/required', required_field: 'woof'
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('required works')
+      end
+    end
+
+    context 'requires :none using Grape::Entity documentation' do
+      def define_requires_none
+        documentation = {
+          required_field: { type: String },
+          optional_field: { type: String }
+        }
+        subject.params do
+          requires :none, except: :required_field, using: documentation
+        end
+      end
+      before do
+        define_requires_none
+        subject.get '/required' do
+          'required works'
+        end
+      end
+
+      it 'adds entity documentation to declared params' do
+        define_requires_none
+        expect(subject.route_setting(:declared_params)).to eq([:required_field, :optional_field])
+      end
+
+      it 'errors when required_field is not present' do
+        get '/required'
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('required_field is missing')
+      end
+
+      it 'works when required_field is present' do
+        get '/required', required_field: 'woof'
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('required works')
+      end
+    end
+
+    context 'requires :all or :none but except a non-existent field using Grape::Entity documentation' do
+      context 'requires :all' do
+        def define_requires_all
+          documentation = {
+            required_field: { type: String },
+            optional_field: { type: String }
+          }
+          subject.params do
+            requires :all, except: :non_existent_field, using: documentation
+          end
+        end
+
+        it 'adds only the entity documentation to declared params, nothing more' do
+          define_requires_all
+          expect(subject.route_setting(:declared_params)).to eq([:required_field, :optional_field])
+        end
+      end
+
+      context 'requires :none' do
+        def define_requires_none
+          documentation = {
+            required_field: { type: String },
+            optional_field: { type: String }
+          }
+          subject.params do
+            requires :none, except: :non_existent_field, using: documentation
+          end
+        end
+
+        it 'adds only the entity documentation to declared params, nothing more' do
+          expect { define_requires_none }.to raise_error(ArgumentError)
+        end
+      end
+    end
+
+    context 'required with an Array block' do
+      before do
+        subject.params do
+          requires :items, type: Array do
+            requires :key
+          end
+        end
+        subject.get('/required') { 'required works' }
+        subject.put('/required') { { items: params[:items] }.to_json }
+      end
+
+      it 'errors when param not present' do
+        get '/required'
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('items is missing')
+      end
+
+      it 'errors when param is not an Array' do
+        get '/required', items: 'hello'
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('items is invalid, items[key] is missing')
+
+        get '/required', items: { key: 'foo' }
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('items is invalid')
+      end
+
+      it "doesn't throw a missing param when param is present" do
+        get '/required', items: [{ key: 'hello' }, { key: 'world' }]
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('required works')
+      end
+
+      it "doesn't throw a missing param when param is present but empty" do
+        put '/required', { items: [] }.to_json, 'CONTENT_TYPE' => 'application/json'
+        expect(last_response.status).to eq(200)
+        expect(JSON.parse(last_response.body)).to eq('items' => [])
+      end
+
+      it 'adds to declared parameters' do
+        subject.params do
+          requires :items, type: Array do
+            requires :key
+          end
+        end
+        expect(subject.route_setting(:declared_params)).to eq([items: [:key]])
+      end
+    end
+
+    context 'required with a Hash block' do
+      before do
+        subject.params do
+          requires :items, type: Hash do
+            requires :key
+          end
+        end
+        subject.get '/required' do
+          'required works'
+        end
+      end
+
+      it 'errors when param not present' do
+        get '/required'
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('items is missing, items[key] is missing')
+      end
+
+      it 'errors when param is not a Hash' do
+        get '/required', items: 'hello'
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('items is invalid, items[key] is missing')
+
+        get '/required', items: [{ key: 'foo' }]
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('items is invalid')
+      end
+
+      it "doesn't throw a missing param when param is present" do
+        get '/required', items: { key: 'hello' }
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('required works')
+      end
+
+      it 'adds to declared parameters' do
+        subject.params do
+          requires :items, type: Array do
+            requires :key
+          end
+        end
+        expect(subject.route_setting(:declared_params)).to eq([items: [:key]])
+      end
+    end
+
+    context 'group' do
+      before do
+        subject.params do
+          group :items, type: Array do
+            requires :key
+          end
+        end
+        subject.get '/required' do
+          'required works'
+        end
+      end
+
+      it 'errors when param not present' do
+        get '/required'
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('items is missing')
+      end
+
+      it "doesn't throw a missing param when param is present" do
+        get '/required', items: [key: 'hello', key: 'world']
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('required works')
+      end
+
+      it 'adds to declared parameters' do
+        subject.params do
+          group :items, type: Array do
+            requires :key
+          end
+        end
+        expect(subject.route_setting(:declared_params)).to eq([items: [:key]])
+      end
+    end
+
+    context 'group params with nested params which has a type' do
+      let(:invalid_items) { { items: '' } }
+
+      before do
+        subject.params do
+          optional :items, type: Array do
+            optional :key1, type: String
+            optional :key2, type: String
+          end
+        end
+        subject.post '/group_with_nested' do
+          'group with nested works'
+        end
+      end
+
+      it 'errors when group param is invalid'do
+        post '/group_with_nested', items: invalid_items
+        expect(last_response.status).to eq(400)
+      end
+    end
+
+    context 'custom validator for a Hash' do
+      module DateRangeValidations
+        class DateRangeValidator < Grape::Validations::Base
+          def validate_param!(attr_name, params)
+            unless params[attr_name][:from] <= params[attr_name][:to]
+              fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: "'from' must be lower or equal to 'to'"
+            end
+          end
+        end
+      end
+
+      before do
+        subject.params do
+          optional :date_range, date_range: true, type: Hash do
+            requires :from, type: Integer
+            requires :to, type: Integer
+          end
+        end
+        subject.get('/optional') do
+          'optional works'
+        end
+        subject.params do
+          requires :date_range, date_range: true, type: Hash do
+            requires :from, type: Integer
+            requires :to, type: Integer
+          end
+        end
+        subject.get('/required') do
+          'required works'
+        end
+      end
+
+      context 'which is optional' do
+        it "doesn't throw an error if the validation passes" do
+          get '/optional', date_range: { from: 1, to: 2 }
+          expect(last_response.status).to eq(200)
+        end
+
+        it 'errors if the validation fails' do
+          get '/optional', date_range: { from: 2, to: 1 }
+          expect(last_response.status).to eq(400)
+        end
+      end
+
+      context 'which is required' do
+        it "doesn't throw an error if the validation passes" do
+          get '/required', date_range: { from: 1, to: 2 }
+          expect(last_response.status).to eq(200)
+        end
+
+        it 'errors if the validation fails' do
+          get '/required', date_range: { from: 2, to: 1 }
+          expect(last_response.status).to eq(400)
+        end
+      end
+    end
+
+    context 'validation within arrays' do
+      before do
+        subject.params do
+          group :children, type: Array do
+            requires :name
+            group :parents, type: Array do
+              requires :name
+            end
+          end
+        end
+        subject.get '/within_array' do
+          'within array works'
+        end
+      end
+
+      it 'can handle new scopes within child elements' do
+        get '/within_array', children: [
+          { name: 'John', parents: [{ name: 'Jane' }, { name: 'Bob' }] },
+          { name: 'Joe', parents: [{ name: 'Josie' }] }
+        ]
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('within array works')
+      end
+
+      it 'errors when a parameter is not present' do
+        get '/within_array', children: [
+          { name: 'Jim', parents: [{}] },
+          { name: 'Job', parents: [{ name: 'Joy' }] }
+        ]
+        # NOTE: with body parameters in json or XML or similar this
+        # should actually fail with: children[parents][name] is missing.
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('children[parents] is missing')
+      end
+
+      it 'safely handles empty arrays and blank parameters' do
+        # NOTE: with body parameters in json or XML or similar this
+        # should actually return 200, since an empty array is valid.
+        get '/within_array', children: []
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('children is missing')
+        get '/within_array', children: [name: 'Jay']
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('children[parents] is missing')
+      end
+
+      it 'errors when param is not an Array' do
+        # NOTE: would be nicer if these just returned 'children is invalid'
+        get '/within_array', children: 'hello'
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('children is invalid, children[name] is missing, children[parents] is missing, children[parents] is invalid, children[parents][name] is missing')
+
+        get '/within_array', children: { name: 'foo' }
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('children is invalid, children[parents] is missing')
+
+        get '/within_array', children: [name: 'Jay', parents: { name: 'Fred' }]
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('children[parents] is invalid')
+      end
+    end
+
+    context 'with block param' do
+      before do
+        subject.params do
+          requires :planets, type: Array do
+            requires :name
+          end
+        end
+        subject.get '/req' do
+          'within array works'
+        end
+        subject.put '/req' do
+          ''
+        end
+
+        subject.params do
+          group :stars, type: Array do
+            requires :name
+          end
+        end
+        subject.get '/grp' do
+          'within array works'
+        end
+        subject.put '/grp' do
+          ''
+        end
+
+        subject.params do
+          requires :name
+          optional :moons, type: Array do
+            requires :name
+          end
+        end
+        subject.get '/opt' do
+          'within array works'
+        end
+        subject.put '/opt' do
+          ''
+        end
+      end
+
+      it 'requires defaults to Array type' do
+        get '/req', planets: 'Jupiter, Saturn'
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('planets is invalid, planets[name] is missing')
+
+        get '/req', planets: { name: 'Jupiter' }
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('planets is invalid')
+
+        get '/req', planets: [{ name: 'Venus' }, { name: 'Mars' }]
+        expect(last_response.status).to eq(200)
+
+        put_with_json '/req', planets: []
+        expect(last_response.status).to eq(200)
+      end
+
+      it 'optional defaults to Array type' do
+        get '/opt', name: 'Jupiter', moons: 'Europa, Ganymede'
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('moons is invalid, moons[name] is missing')
+
+        get '/opt', name: 'Jupiter', moons: { name: 'Ganymede' }
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('moons is invalid')
+
+        get '/opt', name: 'Jupiter', moons: [{ name: 'Io' }, { name: 'Callisto' }]
+        expect(last_response.status).to eq(200)
+
+        put_with_json '/opt', name: 'Venus'
+        expect(last_response.status).to eq(200)
+
+        put_with_json '/opt', name: 'Mercury', moons: []
+        expect(last_response.status).to eq(200)
+      end
+
+      it 'group defaults to Array type' do
+        get '/grp', stars: 'Sun'
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('stars is invalid, stars[name] is missing')
+
+        get '/grp', stars: { name: 'Sun' }
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('stars is invalid')
+
+        get '/grp', stars: [{ name: 'Sun' }]
+        expect(last_response.status).to eq(200)
+
+        put_with_json '/grp', stars: []
+        expect(last_response.status).to eq(200)
+      end
+    end
+
+    context 'validation within arrays with JSON' do
+      before do
+        subject.params do
+          group :children, type: Array do
+            requires :name
+            group :parents, type: Array do
+              requires :name
+            end
+          end
+        end
+        subject.put '/within_array' do
+          'within array works'
+        end
+      end
+
+      it 'can handle new scopes within child elements' do
+        put_with_json '/within_array', children: [
+          { name: 'John', parents: [{ name: 'Jane' }, { name: 'Bob' }] },
+          { name: 'Joe', parents: [{ name: 'Josie' }] }
+        ]
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('within array works')
+      end
+
+      it 'errors when a parameter is not present' do
+        put_with_json '/within_array', children: [
+          { name: 'Jim', parents: [{}] },
+          { name: 'Job', parents: [{ name: 'Joy' }] }
+        ]
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('children[parents][name] is missing')
+      end
+
+      it 'safely handles empty arrays and blank parameters' do
+        put_with_json '/within_array', children: []
+        expect(last_response.status).to eq(200)
+        put_with_json '/within_array', children: [name: 'Jay']
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('children[parents] is missing')
+      end
+    end
+
+    context 'optional with an Array block' do
+      before do
+        subject.params do
+          optional :items, type: Array do
+            requires :key
+          end
+        end
+        subject.get '/optional_group' do
+          'optional group works'
+        end
+      end
+
+      it "doesn't throw a missing param when the group isn't present" do
+        get '/optional_group'
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('optional group works')
+      end
+
+      it "doesn't throw a missing param when both group and param are given" do
+        get '/optional_group', items: [{ key: 'foo' }]
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('optional group works')
+      end
+
+      it 'errors when group is present, but required param is not' do
+        get '/optional_group', items: [{ not_key: 'foo' }]
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('items[key] is missing')
+      end
+
+      it "errors when param is present but isn't an Array" do
+        get '/optional_group', items: 'hello'
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('items is invalid, items[key] is missing')
+
+        get '/optional_group', items: { key: 'foo' }
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('items is invalid')
+      end
+
+      it 'adds to declared parameters' do
+        subject.params do
+          optional :items, type: Array do
+            requires :key
+          end
+        end
+        expect(subject.route_setting(:declared_params)).to eq([items: [:key]])
+      end
+    end
+
+    context 'nested optional Array blocks' do
+      before do
+        subject.params do
+          optional :items, type: Array do
+            requires :key
+            optional(:optional_subitems, type: Array) { requires :value }
+            requires(:required_subitems, type: Array) { requires :value }
+          end
+        end
+        subject.get('/nested_optional_group') { 'nested optional group works' }
+      end
+
+      it 'does no internal validations if the outer group is blank' do
+        get '/nested_optional_group'
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('nested optional group works')
+      end
+
+      it 'does internal validations if the outer group is present' do
+        get '/nested_optional_group', items: [{ key: 'foo' }]
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('items[required_subitems] is missing')
+
+        get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }] }]
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('nested optional group works')
+      end
+
+      it 'handles deep nesting' do
+        get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }], optional_subitems: [{ not_value: 'baz' }] }]
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('items[optional_subitems][value] is missing')
+
+        get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }], optional_subitems: [{ value: 'baz' }] }]
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('nested optional group works')
+      end
+
+      it 'handles validation within arrays' do
+        get '/nested_optional_group', items: [{ key: 'foo' }]
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('items[required_subitems] is missing')
+
+        get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }] }]
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('nested optional group works')
+
+        get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }], optional_subitems: [{ not_value: 'baz' }] }]
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to eq('items[optional_subitems][value] is missing')
+      end
+
+      it 'adds to declared parameters' do
+        subject.params do
+          optional :items, type: Array do
+            requires :key
+            optional(:optional_subitems, type: Array) { requires :value }
+            requires(:required_subitems, type: Array) { requires :value }
+          end
+        end
+        expect(subject.route_setting(:declared_params)).to eq([items: [:key, { optional_subitems: [:value] }, { required_subitems: [:value] }]])
+      end
+    end
+
+    context 'multiple validation errors' do
+      before do
+        subject.params do
+          requires :yolo
+          requires :swag
+        end
+        subject.get '/two_required' do
+          'two required works'
+        end
+      end
+
+      it 'throws the validation errors' do
+        get '/two_required'
+        expect(last_response.status).to eq(400)
+        expect(last_response.body).to match(/yolo is missing/)
+        expect(last_response.body).to match(/swag is missing/)
+      end
+    end
+
+    context 'custom validation' do
+      module CustomValidations
+        class Customvalidator < Grape::Validations::Base
+          def validate_param!(attr_name, params)
+            unless params[attr_name] == 'im custom'
+              fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: 'is not custom!'
+            end
+          end
+        end
+      end
+
+      context 'when using optional with a custom validator' do
+        before do
+          subject.params do
+            optional :custom, customvalidator: true
+          end
+          subject.get '/optional_custom' do
+            'optional with custom works!'
+          end
+        end
+
+        it 'validates when param is present' do
+          get '/optional_custom', custom: 'im custom'
+          expect(last_response.status).to eq(200)
+          expect(last_response.body).to eq('optional with custom works!')
+
+          get '/optional_custom', custom: 'im wrong'
+          expect(last_response.status).to eq(400)
+          expect(last_response.body).to eq('custom is not custom!')
+        end
+
+        it "skips validation when parameter isn't present" do
+          get '/optional_custom'
+          expect(last_response.status).to eq(200)
+          expect(last_response.body).to eq('optional with custom works!')
+        end
+
+        it 'validates with custom validator when param present and incorrect type' do
+          subject.params do
+            optional :custom, type: String, customvalidator: true
+          end
+
+          get '/optional_custom', custom: 123
+          expect(last_response.status).to eq(400)
+          expect(last_response.body).to eq('custom is not custom!')
+        end
+      end
+
+      context 'when using requires with a custom validator' do
+        before do
+          subject.params do
+            requires :custom, customvalidator: true
+          end
+          subject.get '/required_custom' do
+            'required with custom works!'
+          end
+        end
+
+        it 'validates when param is present' do
+          get '/required_custom', custom: 'im wrong, validate me'
+          expect(last_response.status).to eq(400)
+          expect(last_response.body).to eq('custom is not custom!')
+
+          get '/required_custom', custom: 'im custom'
+          expect(last_response.status).to eq(200)
+          expect(last_response.body).to eq('required with custom works!')
+        end
+
+        it 'validates when param is not present' do
+          get '/required_custom'
+          expect(last_response.status).to eq(400)
+          expect(last_response.body).to eq('custom is missing, custom is not custom!')
+        end
+
+        context 'nested namespaces' do
+          before do
+            subject.params do
+              requires :custom, customvalidator: true
+            end
+            subject.namespace 'nested' do
+              get 'one' do
+                'validation failed'
+              end
+              namespace 'nested' do
+                get 'two' do
+                  'validation failed'
+                end
+              end
+            end
+            subject.namespace 'peer' do
+              get 'one' do
+                'no validation required'
+              end
+              namespace 'nested' do
+                get 'two' do
+                  'no validation required'
+                end
+              end
+            end
+
+            subject.namespace 'unrelated' do
+              params do
+                requires :name
+              end
+              get 'one' do
+                'validation required'
+              end
+
+              namespace 'double' do
+                get 'two' do
+                  'no validation required'
+                end
+              end
+            end
+          end
+
+          specify 'the parent namespace uses the validator' do
+            get '/nested/one', custom: 'im wrong, validate me'
+            expect(last_response.status).to eq(400)
+            expect(last_response.body).to eq('custom is not custom!')
+          end
+
+          specify 'the nested namespace inherits the custom validator' do
+            get '/nested/nested/two', custom: 'im wrong, validate me'
+            expect(last_response.status).to eq(400)
+            expect(last_response.body).to eq('custom is not custom!')
+          end
+
+          specify 'peer namespaces does not have the validator' do
+            get '/peer/one', custom: 'im not validated'
+            expect(last_response.status).to eq(200)
+            expect(last_response.body).to eq('no validation required')
+          end
+
+          specify 'namespaces nested in peers should also not have the validator' do
+            get '/peer/nested/two', custom: 'im not validated'
+            expect(last_response.status).to eq(200)
+            expect(last_response.body).to eq('no validation required')
+          end
+
+          specify 'when nested, specifying a route should clear out the validations for deeper nested params' do
+            get '/unrelated/one'
+            expect(last_response.status).to eq(400)
+            get '/unrelated/double/two'
+            expect(last_response.status).to eq(200)
+          end
+        end
+      end
+
+      context 'when using options on param' do
+        module CustomValidations
+          class CustomvalidatorWithOptions < Grape::Validations::Base
+            def validate_param!(attr_name, params)
+              unless params[attr_name] == @option[:text]
+                fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: @option[:error_message]
+              end
+            end
+          end
+        end
+
+        before do
+          subject.params do
+            optional :custom, customvalidator_with_options: { text: 'im custom with options', error_message: 'is not custom with options!' }
+          end
+          subject.get '/optional_custom' do
+            'optional with custom works!'
+          end
+        end
+
+        it 'validates param with custom validator with options' do
+          get '/optional_custom', custom: 'im custom with options'
+          expect(last_response.status).to eq(200)
+          expect(last_response.body).to eq('optional with custom works!')
+
+          get '/optional_custom', custom: 'im wrong'
+          expect(last_response.status).to eq(400)
+          expect(last_response.body).to eq('custom is not custom with options!')
+        end
+      end
+    end # end custom validation
+
+    context 'named' do
+      context 'can be defined' do
+        it 'in helpers' do
+          subject.helpers do
+            params :pagination do
+            end
+          end
+        end
+
+        it 'in helper module which kind of Grape::DSL::Helpers::BaseHelper' do
+          module SharedParams
+            extend Grape::DSL::Helpers::BaseHelper
+            params :pagination do
+            end
+          end
+          subject.helpers SharedParams
+        end
+      end
+
+      context 'can be included in usual params' do
+        before do
+          module SharedParams
+            extend Grape::DSL::Helpers::BaseHelper
+            params :period do
+              optional :start_date
+              optional :end_date
+            end
+          end
+          subject.helpers SharedParams
+
+          subject.helpers do
+            params :pagination do
+              optional :page, type: Integer
+              optional :per_page, type: Integer
+            end
+          end
+        end
+
+        it 'by #use' do
+          subject.params do
+            use :pagination
+          end
+          expect(subject.route_setting(:declared_params)).to eq [:page, :per_page]
+        end
+
+        it 'by #use with multiple params' do
+          subject.params do
+            use :pagination, :period
+          end
+          expect(subject.route_setting(:declared_params)).to eq [:page, :per_page, :start_date, :end_date]
+        end
+      end
+
+      context 'with block' do
+        before do
+          subject.helpers do
+            params :order do |options|
+              optional :order, type: Symbol, values: [:asc, :desc], default: options[:default_order]
+              optional :order_by, type: Symbol, values: options[:order_by], default: options[:default_order_by]
+            end
+          end
+          subject.format :json
+          subject.params do
+            use :order, default_order: :asc, order_by: [:name, :created_at], default_order_by: :created_at
+          end
+          subject.get '/order' do
+            {
+              order: params[:order],
+              order_by: params[:order_by]
+            }
+          end
+        end
+        it 'returns defaults' do
+          get '/order'
+          expect(last_response.status).to eq(200)
+          expect(last_response.body).to eq({ order: :asc, order_by: :created_at }.to_json)
+        end
+        it 'overrides default value for order' do
+          get '/order?order=desc'
+          expect(last_response.status).to eq(200)
+          expect(last_response.body).to eq({ order: :desc, order_by: :created_at }.to_json)
+        end
+        it 'overrides default value for order_by' do
+          get '/order?order_by=name'
+          expect(last_response.status).to eq(200)
+          expect(last_response.body).to eq({ order: :asc, order_by: :name }.to_json)
+        end
+        it 'fails with invalid value' do
+          get '/order?order=invalid'
+          expect(last_response.status).to eq(400)
+          expect(last_response.body).to eq('{"error":"order does not have a valid value"}')
+        end
+      end
+    end
+
+    context 'documentation' do
+      it 'can be included with a hash' do
+        documentation = { example: 'Joe' }
+
+        subject.params do
+          requires 'first_name', documentation: documentation
+        end
+        subject.get '/' do
+        end
+
+        expect(subject.routes.first.route_params['first_name'][:documentation]).to eq(documentation)
+      end
+    end
+
+    context 'mutually exclusive' do
+      context 'optional params' do
+        it 'errors when two or more are present' do
+          subject.params do
+            optional :beer
+            optional :wine
+            optional :juice
+            mutually_exclusive :beer, :wine, :juice
+          end
+          subject.get '/mutually_exclusive' do
+            'mutually_exclusive works!'
+          end
+
+          get '/mutually_exclusive', beer: 'string', wine: 'anotherstring'
+          expect(last_response.status).to eq(400)
+          expect(last_response.body).to eq 'beer, wine are mutually exclusive'
+        end
+      end
+
+      context 'more than one set of mutually exclusive params' do
+        it 'errors for all sets' do
+          subject.params do
+            optional :beer
+            optional :wine
+            mutually_exclusive :beer, :wine
+            optional :nested, type: Hash do
+              optional :scotch
+              optional :aquavit
+              mutually_exclusive :scotch, :aquavit
+            end
+            optional :nested2, type: Array do
+              optional :scotch2
+              optional :aquavit2
+              mutually_exclusive :scotch2, :aquavit2
+            end
+          end
+          subject.get '/mutually_exclusive' do
+            'mutually_exclusive works!'
+          end
+
+          get '/mutually_exclusive', beer: 'true', wine: 'true', nested: { scotch: 'true', aquavit: 'true' }, nested2: [{ scotch2: 'true' }, { scotch2: 'true', aquavit2: 'true' }]
+          expect(last_response.status).to eq(400)
+          expect(last_response.body).to eq 'beer, wine are mutually exclusive, scotch, aquavit are mutually exclusive, scotch2, aquavit2 are mutually exclusive'
+        end
+      end
+
+      context 'in a group' do
+        it 'works when only one from the set is present' do
+          subject.params do
+            group :drink, type: Hash do
+              optional :wine
+              optional :beer
+              optional :juice
+
+              mutually_exclusive :beer, :wine, :juice
+            end
+          end
+          subject.get '/mutually_exclusive_group' do
+            'mutually_exclusive_group works!'
+          end
+
+          get '/mutually_exclusive_group', drink: { beer: 'true' }
+          expect(last_response.status).to eq(200)
+        end
+
+        it 'errors when more than one from the set is present' do
+          subject.params do
+            group :drink, type: Hash do
+              optional :wine
+              optional :beer
+              optional :juice
+
+              mutually_exclusive :beer, :wine, :juice
+            end
+          end
+          subject.get '/mutually_exclusive_group' do
+            'mutually_exclusive_group works!'
+          end
+
+          get '/mutually_exclusive_group', drink: { beer: 'true', juice: 'true', wine: 'true' }
+          expect(last_response.status).to eq(400)
+        end
+      end
+
+      context 'mutually exclusive params inside Hash group' do
+        it 'invalidates if request param is invalid type' do
+          subject.params do
+            optional :wine, type: Hash do
+              optional :grape
+              optional :country
+              mutually_exclusive :grape, :country
+            end
+          end
+          subject.post '/mutually_exclusive' do
+            'mutually_exclusive works!'
+          end
+
+          post '/mutually_exclusive', wine: '2015 sauvignon'
+          expect(last_response.status).to eq(400)
+          expect(last_response.body).to eq 'wine is invalid'
+        end
+      end
+    end
+
+    context 'exactly one of' do
+      context 'params' do
+        before :each do
+          subject.params do
+            optional :beer
+            optional :wine
+            optional :juice
+            exactly_one_of :beer, :wine, :juice
+          end
+          subject.get '/exactly_one_of' do
+            'exactly_one_of works!'
+          end
+        end
+
+        it 'errors when none are present' do
+          get '/exactly_one_of'
+          expect(last_response.status).to eq(400)
+          expect(last_response.body).to eq 'beer, wine, juice are missing, exactly one parameter must be provided'
+        end
+
+        it 'succeeds when one is present' do
+          get '/exactly_one_of', beer: 'string'
+          expect(last_response.status).to eq(200)
+          expect(last_response.body).to eq 'exactly_one_of works!'
+        end
+
+        it 'errors when two or more are present' do
+          get '/exactly_one_of', beer: 'string', wine: 'anotherstring'
+          expect(last_response.status).to eq(400)
+          expect(last_response.body).to eq 'beer, wine are mutually exclusive'
+        end
+      end
+
+      context 'nested params' do
+        before :each do
+          subject.params do
+            requires :nested, type: Hash do
+              optional :beer_nested
+              optional :wine_nested
+              optional :juice_nested
+              exactly_one_of :beer_nested, :wine_nested, :juice_nested
+            end
+            optional :nested2, type: Array do
+              optional :beer_nested2
+              optional :wine_nested2
+              optional :juice_nested2
+              exactly_one_of :beer_nested2, :wine_nested2, :juice_nested2
+            end
+          end
+          subject.get '/exactly_one_of_nested' do
+            'exactly_one_of works!'
+          end
+        end
+
+        it 'errors when none are present' do
+          get '/exactly_one_of_nested'
+          expect(last_response.status).to eq(400)
+          expect(last_response.body).to eq 'nested is missing, beer_nested, wine_nested, juice_nested are missing, exactly one parameter must be provided'
+        end
+
+        it 'succeeds when one is present' do
+          get '/exactly_one_of_nested', nested: { beer_nested: 'string' }
+          expect(last_response.status).to eq(200)
+          expect(last_response.body).to eq 'exactly_one_of works!'
+        end
+
+        it 'errors when two or more are present' do
+          get '/exactly_one_of_nested', nested: { beer_nested: 'string' }, nested2: [{ beer_nested2: 'string', wine_nested2: 'anotherstring' }]
+          expect(last_response.status).to eq(400)
+          expect(last_response.body).to eq 'beer_nested2, wine_nested2 are mutually exclusive'
+        end
+      end
+    end
+
+    context 'at least one of' do
+      context 'params' do
+        before :each do
+          subject.params do
+            optional :beer
+            optional :wine
+            optional :juice
+            at_least_one_of :beer, :wine, :juice
+          end
+          subject.get '/at_least_one_of' do
+            'at_least_one_of works!'
+          end
+        end
+
+        it 'errors when none are present' do
+          get '/at_least_one_of'
+          expect(last_response.status).to eq(400)
+          expect(last_response.body).to eq 'beer, wine, juice are missing, at least one parameter must be provided'
+        end
+
+        it 'does not error when one is present' do
+          get '/at_least_one_of', beer: 'string'
+          expect(last_response.status).to eq(200)
+          expect(last_response.body).to eq 'at_least_one_of works!'
+        end
+
+        it 'does not error when two are present' do
+          get '/at_least_one_of', beer: 'string', wine: 'string'
+          expect(last_response.status).to eq(200)
+          expect(last_response.body).to eq 'at_least_one_of works!'
+        end
+      end
+
+      context 'nested params' do
+        before :each do
+          subject.params do
+            requires :nested, type: Hash do
+              optional :beer_nested
+              optional :wine_nested
+              optional :juice_nested
+              at_least_one_of :beer_nested, :wine_nested, :juice_nested
+            end
+            optional :nested2, type: Array do
+              optional :beer_nested2
+              optional :wine_nested2
+              optional :juice_nested2
+              at_least_one_of :beer_nested2, :wine_nested2, :juice_nested2
+            end
+          end
+          subject.get '/at_least_one_of_nested' do
+            'at_least_one_of works!'
+          end
+        end
+
+        it 'errors when none are present' do
+          get '/at_least_one_of_nested'
+          expect(last_response.status).to eq(400)
+          expect(last_response.body).to eq 'nested is missing, beer_nested, wine_nested, juice_nested are missing, at least one parameter must be provided'
+        end
+
+        it 'does not error when one is present' do
+          get '/at_least_one_of_nested', nested: { beer_nested: 'string' }, nested2: [{ beer_nested2: 'string' }]
+          expect(last_response.status).to eq(200)
+          expect(last_response.body).to eq 'at_least_one_of works!'
+        end
+
+        it 'does not error when two are present' do
+          get '/at_least_one_of_nested', nested: { beer_nested: 'string', wine_nested: 'string' }, nested2: [{ beer_nested2: 'string', wine_nested2: 'string' }]
+          expect(last_response.status).to eq(200)
+          expect(last_response.body).to eq 'at_least_one_of works!'
+        end
+      end
+    end
+
+    context 'in a group' do
+      it 'works when only one from the set is present' do
+        subject.params do
+          group :drink, type: Hash do
+            optional :wine
+            optional :beer
+            optional :juice
+
+            exactly_one_of :beer, :wine, :juice
+          end
+        end
+        subject.get '/exactly_one_of_group' do
+          'exactly_one_of_group works!'
+        end
+
+        get '/exactly_one_of_group', drink: { beer: 'true' }
+        expect(last_response.status).to eq(200)
+      end
+
+      it 'errors when no parameter from the set is present' do
+        subject.params do
+          group :drink, type: Hash do
+            optional :wine
+            optional :beer
+            optional :juice
+
+            exactly_one_of :beer, :wine, :juice
+          end
+        end
+        subject.get '/exactly_one_of_group' do
+          'exactly_one_of_group works!'
+        end
+
+        get '/exactly_one_of_group', drink: {}
+        expect(last_response.status).to eq(400)
+      end
+
+      it 'errors when more than one from the set is present' do
+        subject.params do
+          group :drink, type: Hash do
+            optional :wine
+            optional :beer
+            optional :juice
+
+            exactly_one_of :beer, :wine, :juice
+          end
+        end
+        subject.get '/exactly_one_of_group' do
+          'exactly_one_of_group works!'
+        end
+
+        get '/exactly_one_of_group', drink: { beer: 'true', juice: 'true', wine: 'true' }
+        expect(last_response.status).to eq(400)
+      end
+
+      it 'does not falsely think the param is there if it is provided outside the block' do
+        subject.params do
+          group :drink, type: Hash do
+            optional :wine
+            optional :beer
+            optional :juice
+
+            exactly_one_of :beer, :wine, :juice
+          end
+        end
+        subject.get '/exactly_one_of_group' do
+          'exactly_one_of_group works!'
+        end
+
+        get '/exactly_one_of_group', drink: { foo: 'bar' }, beer: 'true'
+        expect(last_response.status).to eq(400)
+      end
+    end
+  end
+end
diff --git a/spec/shared/versioning_examples.rb b/spec/shared/versioning_examples.rb
new file mode 100644
index 0000000..eca477e
--- /dev/null
+++ b/spec/shared/versioning_examples.rb
@@ -0,0 +1,152 @@
+shared_examples_for 'versioning' do
+  it 'sets the API version' do
+    subject.format :txt
+    subject.version 'v1', macro_options
+    subject.get :hello do
+      "Version: #{request.env['api.version'] }"
+    end
+    versioned_get '/hello', 'v1', macro_options
+    expect(last_response.body).to eql 'Version: v1'
+  end
+
+  it 'adds the prefix before the API version' do
+    subject.format :txt
+    subject.prefix 'api'
+    subject.version 'v1', macro_options
+    subject.get :hello do
+      "Version: #{request.env['api.version'] }"
+    end
+    versioned_get '/hello', 'v1', macro_options.merge(prefix: 'api')
+    expect(last_response.body).to eql 'Version: v1'
+  end
+
+  it 'is able to specify version as a nesting' do
+    subject.version 'v2', macro_options
+    subject.get '/awesome' do
+      'Radical'
+    end
+
+    subject.version 'v1', macro_options do
+      get '/legacy' do
+        'Totally'
+      end
+    end
+
+    versioned_get '/awesome', 'v1', macro_options
+    expect(last_response.status).to eql 404
+
+    versioned_get '/awesome', 'v2', macro_options
+    expect(last_response.status).to eql 200
+    versioned_get '/legacy', 'v1', macro_options
+    expect(last_response.status).to eql 200
+    versioned_get '/legacy', 'v2', macro_options
+    expect(last_response.status).to eql 404
+  end
+
+  it 'is able to specify multiple versions' do
+    subject.version 'v1', 'v2', macro_options
+    subject.get 'awesome' do
+      'I exist'
+    end
+
+    versioned_get '/awesome', 'v1', macro_options
+    expect(last_response.status).to eql 200
+    versioned_get '/awesome', 'v2', macro_options
+    expect(last_response.status).to eql 200
+    versioned_get '/awesome', 'v3', macro_options
+    expect(last_response.status).to eql 404
+  end
+
+  context 'with different versions for the same endpoint' do
+    context 'without a prefix' do
+      it 'allows the same endpoint to be implemented' do
+        subject.format :txt
+        subject.version 'v2', macro_options
+        subject.get 'version' do
+          request.env['api.version']
+        end
+
+        subject.version 'v1', macro_options do
+          get 'version' do
+            'version ' + request.env['api.version']
+          end
+        end
+
+        versioned_get '/version', 'v2', macro_options
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('v2')
+        versioned_get '/version', 'v1', macro_options
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('version v1')
+      end
+    end
+
+    context 'with a prefix' do
+      it 'allows the same endpoint to be implemented' do
+        subject.format :txt
+        subject.prefix 'api'
+        subject.version 'v2', macro_options
+        subject.get 'version' do
+          request.env['api.version']
+        end
+
+        subject.version 'v1', macro_options do
+          get 'version' do
+            'version ' + request.env['api.version']
+          end
+        end
+
+        versioned_get '/version', 'v1', macro_options.merge(prefix: subject.prefix)
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('version v1')
+
+        versioned_get '/version', 'v2', macro_options.merge(prefix: subject.prefix)
+        expect(last_response.status).to eq(200)
+        expect(last_response.body).to eq('v2')
+      end
+    end
+  end
+
+  context 'with before block defined within a version block' do
+    it 'calls before block that is defined within the version block' do
+      subject.format :txt
+      subject.prefix 'api'
+      subject.version 'v2', macro_options do
+        before do
+          @output ||= 'v2-'
+        end
+        get 'version' do
+          @output += 'version'
+        end
+      end
+
+      subject.version 'v1', macro_options do
+        before do
+          @output ||= 'v1-'
+        end
+        get 'version' do
+          @output += 'version'
+        end
+      end
+
+      versioned_get '/version', 'v1', macro_options.merge(prefix: subject.prefix)
+      expect(last_response.status).to eq(200)
+      expect(last_response.body).to eq('v1-version')
+
+      versioned_get '/version', 'v2', macro_options.merge(prefix: subject.prefix)
+      expect(last_response.status).to eq(200)
+      expect(last_response.body).to eq('v2-version')
+    end
+  end
+
+  it 'does not overwrite version parameter with API version' do
+    subject.format :txt
+    subject.version 'v1', macro_options
+    subject.params { requires :version }
+    subject.get :api_version_with_version_param do
+      params[:version]
+    end
+    versioned_get '/api_version_with_version_param?version=1', 'v1', macro_options
+    expect(last_response.body).to eql '1'
+  end
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
new file mode 100644
index 0000000..c6cd2d0
--- /dev/null
+++ b/spec/spec_helper.rb
@@ -0,0 +1,29 @@
+$LOAD_PATH.unshift(File.dirname(__FILE__))
+$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
+$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'support'))
+
+require 'grape'
+
+require 'rubygems'
+require 'bundler'
+Bundler.setup :default, :test
+
+require 'json'
+require 'rack/test'
+require 'base64'
+require 'cookiejar'
+require 'json'
+require 'mime/types'
+
+Dir["#{File.dirname(__FILE__)}/support/*.rb"].each do |file|
+  require file
+end
+
+I18n.enforce_available_locales = false
+
+RSpec.configure do |config|
+  config.include Rack::Test::Methods
+  config.raise_errors_for_deprecations!
+
+  config.before(:each) { Grape::Util::InheritableSetting.reset_global! }
+end
diff --git a/spec/support/basic_auth_encode_helpers.rb b/spec/support/basic_auth_encode_helpers.rb
new file mode 100644
index 0000000..a5ab555
--- /dev/null
+++ b/spec/support/basic_auth_encode_helpers.rb
@@ -0,0 +1,3 @@
+def encode_basic_auth(username, password)
+  'Basic ' + Base64.encode64("#{username}:#{password}")
+end
diff --git a/spec/support/content_type_helpers.rb b/spec/support/content_type_helpers.rb
new file mode 100644
index 0000000..801b315
--- /dev/null
+++ b/spec/support/content_type_helpers.rb
@@ -0,0 +1,11 @@
+module ContentTypeHelpers
+  %w(put patch post delete).each do |method|
+    define_method :"#{method}_with_json" do |uri, params = {}, env = {}, &block|
+      params = params.to_json
+      env['CONTENT_TYPE'] ||= 'application/json'
+      send(method, uri, params, env, &block)
+    end
+  end
+end
+
+include(ContentTypeHelpers)
diff --git a/spec/support/endpoint_faker.rb b/spec/support/endpoint_faker.rb
new file mode 100644
index 0000000..2d9c82f
--- /dev/null
+++ b/spec/support/endpoint_faker.rb
@@ -0,0 +1,23 @@
+module Spec
+  module Support
+    class EndpointFaker
+      class FakerAPI < Grape::API
+        get '/' do
+        end
+      end
+
+      def initialize(app, endpoint = FakerAPI.endpoints.first)
+        @app = app
+        @endpoint = endpoint
+      end
+
+      def call(env)
+        @endpoint.instance_exec do
+          @request = Grape::Request.new(env.dup)
+        end
+
+        @app.call(env.merge('api.endpoint' => @endpoint))
+      end
+    end
+  end
+end
diff --git a/spec/support/file_streamer.rb b/spec/support/file_streamer.rb
new file mode 100644
index 0000000..8a9f24d
--- /dev/null
+++ b/spec/support/file_streamer.rb
@@ -0,0 +1,11 @@
+class FileStreamer
+  def initialize(file_path)
+    @file_path = file_path
+  end
+
+  def each(&blk)
+    File.open(@file_path, 'rb') do |file|
+      file.each(10, &blk)
+    end
+  end
+end
diff --git a/spec/support/versioned_helpers.rb b/spec/support/versioned_helpers.rb
new file mode 100644
index 0000000..9f0e4c1
--- /dev/null
+++ b/spec/support/versioned_helpers.rb
@@ -0,0 +1,50 @@
+# Versioning
+
+# Returns the path with options[:version] prefixed if options[:using] is :path.
+# Returns normal path otherwise.
+def versioned_path(options = {})
+  case options[:using]
+  when :path
+    File.join('/', options[:prefix] || '', options[:version], options[:path])
+  when :param
+    File.join('/', options[:prefix] || '', options[:path])
+  when :header
+    File.join('/', options[:prefix] || '', options[:path])
+  when :accept_version_header
+    File.join('/', options[:prefix] || '', options[:path])
+  else
+    fail ArgumentError.new("unknown versioning strategy: #{options[:using]}")
+  end
+end
+
+def versioned_headers(options)
+  case options[:using]
+  when :path
+    {}  # no-op
+  when :param
+    {}  # no-op
+  when :header
+    {
+      'HTTP_ACCEPT' => [
+        "application/vnd.#{options[:vendor] }-#{options[:version] }",
+        options[:format]
+      ].compact.join('+')
+    }
+  when :accept_version_header
+    {
+      'HTTP_ACCEPT_VERSION' => "#{options[:version] }"
+    }
+  else
+    fail ArgumentError.new("unknown versioning strategy: #{options[:using]}")
+  end
+end
+
+def versioned_get(path, version_name, version_options = {})
+  path    = versioned_path(version_options.merge(version: version_name, path: path))
+  headers = versioned_headers(version_options.merge(version: version_name))
+  params = {}
+  if version_options[:using] == :param
+    params = { version_options[:parameter] => version_name }
+  end
+  get path, params, headers
+end

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-ruby-extras/ruby-grape.git



More information about the Pkg-ruby-extras-commits mailing list