grape 0.14.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of grape might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +32 -4
- data/Gemfile.lock +13 -13
- data/README.md +290 -12
- data/UPGRADING.md +68 -1
- data/gemfiles/rails_3.gemfile +1 -1
- data/lib/grape.rb +8 -2
- data/lib/grape/api.rb +40 -34
- data/lib/grape/dsl/configuration.rb +2 -115
- data/lib/grape/dsl/desc.rb +101 -0
- data/lib/grape/dsl/headers.rb +16 -0
- data/lib/grape/dsl/helpers.rb +5 -9
- data/lib/grape/dsl/inside_route.rb +3 -11
- data/lib/grape/dsl/logger.rb +20 -0
- data/lib/grape/dsl/parameters.rb +12 -10
- data/lib/grape/dsl/request_response.rb +17 -4
- data/lib/grape/dsl/routing.rb +24 -7
- data/lib/grape/dsl/settings.rb +8 -2
- data/lib/grape/endpoint.rb +30 -26
- data/lib/grape/error_formatter.rb +31 -0
- data/lib/grape/error_formatter/base.rb +0 -28
- data/lib/grape/error_formatter/json.rb +13 -2
- data/lib/grape/error_formatter/txt.rb +3 -1
- data/lib/grape/error_formatter/xml.rb +3 -1
- data/lib/grape/exceptions/base.rb +11 -4
- data/lib/grape/exceptions/incompatible_option_values.rb +1 -1
- data/lib/grape/exceptions/invalid_accept_header.rb +1 -1
- data/lib/grape/exceptions/invalid_formatter.rb +1 -1
- data/lib/grape/exceptions/invalid_message_body.rb +1 -1
- data/lib/grape/exceptions/invalid_version_header.rb +1 -1
- data/lib/grape/exceptions/invalid_versioner_option.rb +1 -1
- data/lib/grape/exceptions/invalid_with_option_for_represent.rb +1 -1
- data/lib/grape/exceptions/method_not_allowed.rb +10 -0
- data/lib/grape/exceptions/missing_group_type.rb +1 -1
- data/lib/grape/exceptions/missing_mime_type.rb +1 -1
- data/lib/grape/exceptions/missing_option.rb +1 -1
- data/lib/grape/exceptions/missing_vendor_option.rb +1 -1
- data/lib/grape/exceptions/unknown_options.rb +1 -1
- data/lib/grape/exceptions/unknown_parameter.rb +1 -1
- data/lib/grape/exceptions/unknown_validator.rb +1 -1
- data/lib/grape/exceptions/unsupported_group_type.rb +1 -1
- data/lib/grape/exceptions/validation.rb +2 -1
- data/lib/grape/formatter.rb +31 -0
- data/lib/grape/middleware/base.rb +28 -2
- data/lib/grape/middleware/error.rb +24 -1
- data/lib/grape/middleware/formatter.rb +4 -3
- data/lib/grape/middleware/versioner/param.rb +13 -2
- data/lib/grape/parser.rb +29 -0
- data/lib/grape/util/sendfile_response.rb +19 -0
- data/lib/grape/util/strict_hash_configuration.rb +1 -1
- data/lib/grape/validations/params_scope.rb +39 -9
- data/lib/grape/validations/types.rb +16 -0
- data/lib/grape/validations/validators/all_or_none.rb +1 -1
- data/lib/grape/validations/validators/allow_blank.rb +2 -2
- data/lib/grape/validations/validators/at_least_one_of.rb +1 -1
- data/lib/grape/validations/validators/base.rb +26 -0
- data/lib/grape/validations/validators/coerce.rb +16 -14
- data/lib/grape/validations/validators/default.rb +1 -1
- data/lib/grape/validations/validators/exactly_one_of.rb +10 -1
- data/lib/grape/validations/validators/mutual_exclusion.rb +1 -1
- data/lib/grape/validations/validators/presence.rb +1 -1
- data/lib/grape/validations/validators/regexp.rb +2 -2
- data/lib/grape/validations/validators/values.rb +2 -2
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api/custom_validations_spec.rb +156 -21
- data/spec/grape/api/namespace_parameters_in_route_spec.rb +38 -0
- data/spec/grape/api/optional_parameters_in_route_spec.rb +43 -0
- data/spec/grape/api/required_parameters_in_route_spec.rb +37 -0
- data/spec/grape/api_spec.rb +118 -60
- data/spec/grape/dsl/configuration_spec.rb +0 -75
- data/spec/grape/dsl/desc_spec.rb +77 -0
- data/spec/grape/dsl/headers_spec.rb +32 -0
- data/spec/grape/dsl/inside_route_spec.rb +0 -18
- data/spec/grape/dsl/logger_spec.rb +26 -0
- data/spec/grape/dsl/parameters_spec.rb +13 -7
- data/spec/grape/dsl/request_response_spec.rb +17 -3
- data/spec/grape/dsl/routing_spec.rb +8 -1
- data/spec/grape/dsl/settings_spec.rb +42 -0
- data/spec/grape/endpoint_spec.rb +60 -9
- data/spec/grape/exceptions/validation_errors_spec.rb +2 -2
- data/spec/grape/exceptions/validation_spec.rb +7 -0
- data/spec/grape/integration/rack_sendfile_spec.rb +44 -0
- data/spec/grape/middleware/base_spec.rb +100 -0
- data/spec/grape/middleware/exception_spec.rb +1 -2
- data/spec/grape/middleware/formatter_spec.rb +12 -2
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +1 -1
- data/spec/grape/middleware/versioner/header_spec.rb +11 -1
- data/spec/grape/middleware/versioner/param_spec.rb +105 -1
- data/spec/grape/validations/params_scope_spec.rb +77 -0
- data/spec/grape/validations/validators/allow_blank_spec.rb +277 -0
- data/spec/grape/validations/validators/coerce_spec.rb +91 -0
- data/spec/grape/validations/validators/default_spec.rb +6 -0
- data/spec/grape/validations/validators/presence_spec.rb +27 -0
- data/spec/grape/validations/validators/regexp_spec.rb +36 -0
- data/spec/grape/validations/validators/values_spec.rb +44 -0
- data/spec/grape/validations_spec.rb +149 -4
- data/spec/spec_helper.rb +1 -0
- metadata +26 -5
- data/lib/grape/formatter/base.rb +0 -31
- data/lib/grape/parser/base.rb +0 -29
- data/pkg/grape-0.13.0.gem +0 -0
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 6cfff6fd29d73a9f0c990999725d77521054eaed
         | 
| 4 | 
            +
              data.tar.gz: 66ad4e087e432fe5c5d57641db83e5a78730d110
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 3949ea88f5dd3f57167aaf201789ac4619a334d778cba2b2c8e335a96060d76aa3f218234f5cc2597a2aea23d37bf29baed46505997fe4f7498231734af1b304
         | 
| 7 | 
            +
              data.tar.gz: 0c903f1ba5a4d5f1cd4c75c9fd6fb10ef55aefe18543810aa81d8d14f3571133429aee42555972618f6f1608d83dbd66b3cf71df25758634770bc03902ea32a9
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -1,12 +1,40 @@ | |
| 1 | 
            -
            0. | 
| 1 | 
            +
            0.15.0 (3/8/2016)
         | 
| 2 | 
            +
            =================
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            #### Features
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            * [#1227](https://github.com/ruby-grape/grape/pull/1227): Store `message_key` on `Grape::Exceptions::Validation` - [@stjhimy](https://github.com/sthimy).
         | 
| 7 | 
            +
            * [#1232](https://github.com/ruby-grape/grape/pull/1232): Helpers are now available inside `rescue_from` - [@namusyaka](https://github.com/namusyaka).
         | 
| 8 | 
            +
            * [#1237](https://github.com/ruby-grape/grape/pull/1237): Allow multiple parameters in `given`, which behaves as if the scopes were nested in the inputted order - [@ochagata](https://github.com/ochagata).
         | 
| 9 | 
            +
            * [#1238](https://github.com/ruby-grape/grape/pull/1238): Call `after` of middleware on error - [@namusyaka](https://github.com/namusyaka).
         | 
| 10 | 
            +
            * [#1243](https://github.com/ruby-grape/grape/pull/1243): Add `header` support for middleware - [@namusyaka](https://github.com/namusyaka).
         | 
| 11 | 
            +
            * [#1252](https://github.com/ruby-grape/grape/pull/1252): Allow default to be a subset or equal to allowed values without raising IncompatibleOptionValues - [@jeradphelps](https://github.com/jeradphelps).
         | 
| 12 | 
            +
            * [#1255](https://github.com/ruby-grape/grape/pull/1255): Allow param type definition in `route_param` - [@namusyaka](https://github.com/namusyaka).
         | 
| 13 | 
            +
            * [#1257](https://github.com/ruby-grape/grape/pull/1257): Allow Proc, Symbol or String in `rescue_from with: ...` - [@namusyaka](https://github.com/namusyaka).
         | 
| 14 | 
            +
            * [#1280](https://github.com/ruby-grape/grape/pull/1280): Support `Rack::Sendfile` middleware - [@lfidnl](https://github.com/lfidnl).
         | 
| 15 | 
            +
            * [#1285](https://github.com/ruby-grape/grape/pull/1285): Add a warning for errors appearing in `after` callbacks - [@gregormelhorn](https://github.com/gregormelhorn).
         | 
| 16 | 
            +
            * [#1295](https://github.com/ruby-grape/grape/pull/1295): Add custom validation messages for parameter exceptions - [@railsmith](https://github.com/railsmith).
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            #### Fixes
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            * [#1216](https://github.com/ruby-grape/grape/pull/1142): Fix JSON error response when calling `error!` with non-Strings - [@jrforrest](https://github.com/jrforrest).
         | 
| 21 | 
            +
            * [#1225](https://github.com/ruby-grape/grape/pull/1225): Fix `given` with nested params not returning correct declared params - [@JanStevens](https://github.com/JanStevens).
         | 
| 22 | 
            +
            * [#1249](https://github.com/ruby-grape/grape/pull/1249): Don't fail even if invalid type value is passed to default validator - [@namusyaka](https://github.com/namusyaka).
         | 
| 23 | 
            +
            * [#1266](https://github.com/ruby-grape/grape/pull/1266): Fix `Allow` header including `OPTIONS` when `do_not_route_options!` is active - [@arempe93](https://github.com/arempe93).
         | 
| 24 | 
            +
            * [#1270](https://github.com/ruby-grape/grape/pull/1270): Fix `param` versioning with a custom parameter - [@wshatch](https://github.com/wshatch).
         | 
| 25 | 
            +
            * [#1282](https://github.com/ruby-grape/grape/pull/1282): Fix specs circular dependency - [@304](https://github.com/304).
         | 
| 26 | 
            +
            * [#1283](https://github.com/ruby-grape/grape/pull/1283): Fix 500 error for xml format when method is not allowed - [@304](https://github.com/304).
         | 
| 27 | 
            +
            * [#1197](https://github.com/ruby-grape/grape/pull/1290): Fix using JSON and Array[JSON] as groups when parameter is optional - [@lukeivers](https://github.com/lukeivers).
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            0.14.0 (12/07/2015)
         | 
| 2 30 | 
             
            ===================
         | 
| 3 31 |  | 
| 4 32 | 
             
            #### Features
         | 
| 5 33 |  | 
| 6 34 | 
             
            * [#1218](https://github.com/ruby-grape/grape/pull/1218): Provide array index context in errors - [@towanda](https://github.com/towanda).
         | 
| 7 35 | 
             
            * [#1196](https://github.com/ruby-grape/grape/pull/1196): Allow multiple `before_each` blocks - [@huynhquancam](https://github.com/huynhquancam).
         | 
| 8 | 
            -
            * [#1190](https://github.com/ruby-grape/grape/ | 
| 9 | 
            -
            * [#1188](https://github.com/ruby-grape/grape/ | 
| 36 | 
            +
            * [#1190](https://github.com/ruby-grape/grape/pull/1190): Bypass formatting for statuses with no entity-body - [@tylerdooling](https://github.com/tylerdooling).
         | 
| 37 | 
            +
            * [#1188](https://github.com/ruby-grape/grape/pull/1188): Allow parameters with more than one type - [@dslh](https://github.com/dslh).
         | 
| 10 38 | 
             
            * [#1179](https://github.com/ruby-grape/grape/pull/1179): Allow all RFC6838 valid characters in header vendor - [@suan](https://github.com/suan).
         | 
| 11 39 | 
             
            * [#1170](https://github.com/ruby-grape/grape/pull/1170): Allow dashes and periods in header vendor - [@suan](https://github.com/suan).
         | 
| 12 40 | 
             
            * [#1167](https://github.com/ruby-grape/grape/pull/1167): Convenience wrapper `type: File` for validating multipart file parameters - [@dslh](https://github.com/dslh).
         | 
| @@ -23,7 +51,7 @@ | |
| 23 51 | 
             
            * [#1142](https://github.com/ruby-grape/grape/pull/1142): Makes #declared unavailable to before filters - [@jrforrest](https://github.com/jrforrest).
         | 
| 24 52 | 
             
            * [#1114](https://github.com/ruby-grape/grape/pull/1114): Fix regression which broke identical endpoints with different versions - [@suan](https://github.com/suan).
         | 
| 25 53 | 
             
            * [#1109](https://github.com/ruby-grape/grape/pull/1109): Memoize Virtus attribute and fix memory leak - [@marshall-lee](https://github.com/marshall-lee).
         | 
| 26 | 
            -
            * [#1101](https://github.com/ | 
| 54 | 
            +
            * [#1101](https://github.com/ruby-grape/grape/pull/1101): Fix: Incorrect media-type `Accept` header now correctly returns 406 with `strict: true` - [@elliotlarson](https://github.com/elliotlarson).
         | 
| 27 55 | 
             
            * [#1108](https://github.com/ruby-grape/grape/pull/1039): Raise a warning when `desc` is called with options hash and block - [@rngtng](https://github.com/rngtng).
         | 
| 28 56 |  | 
| 29 57 | 
             
            0.13.0 (8/10/2015)
         | 
    
        data/Gemfile.lock
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            PATH
         | 
| 2 2 | 
             
              remote: .
         | 
| 3 3 | 
             
              specs:
         | 
| 4 | 
            -
                grape (0. | 
| 4 | 
            +
                grape (0.15.0)
         | 
| 5 5 | 
             
                  activesupport
         | 
| 6 6 | 
             
                  builder
         | 
| 7 7 | 
             
                  hashie (>= 2.1.0)
         | 
| @@ -15,7 +15,7 @@ PATH | |
| 15 15 | 
             
            GEM
         | 
| 16 16 | 
             
              remote: https://rubygems.org/
         | 
| 17 17 | 
             
              specs:
         | 
| 18 | 
            -
                activesupport (4.2.5)
         | 
| 18 | 
            +
                activesupport (4.2.5.1)
         | 
| 19 19 | 
             
                  i18n (~> 0.7)
         | 
| 20 20 | 
             
                  json (~> 1.7, >= 1.7.7)
         | 
| 21 21 | 
             
                  minitest (~> 5.1)
         | 
| @@ -25,7 +25,7 @@ GEM | |
| 25 25 | 
             
                  bundler
         | 
| 26 26 | 
             
                  rake
         | 
| 27 27 | 
             
                  thor (>= 0.14.0)
         | 
| 28 | 
            -
                ast (2. | 
| 28 | 
            +
                ast (2.2.0)
         | 
| 29 29 | 
             
                astrolabe (1.3.1)
         | 
| 30 30 | 
             
                  parser (~> 2.2)
         | 
| 31 31 | 
             
                axiom-types (0.1.1)
         | 
| @@ -45,7 +45,7 @@ GEM | |
| 45 45 | 
             
                ffi (1.9.10)
         | 
| 46 46 | 
             
                formatador (0.2.5)
         | 
| 47 47 | 
             
                git-version-bump (0.15.1)
         | 
| 48 | 
            -
                grape-entity (0. | 
| 48 | 
            +
                grape-entity (0.5.0)
         | 
| 49 49 | 
             
                  activesupport
         | 
| 50 50 | 
             
                  multi_json (>= 1.3.2)
         | 
| 51 51 | 
             
                guard (2.13.0)
         | 
| @@ -67,24 +67,24 @@ GEM | |
| 67 67 | 
             
                  rubocop (~> 0.20)
         | 
| 68 68 | 
             
                hashie (3.4.3)
         | 
| 69 69 | 
             
                i18n (0.7.0)
         | 
| 70 | 
            -
                ice_nine (0.11. | 
| 70 | 
            +
                ice_nine (0.11.2)
         | 
| 71 71 | 
             
                json (1.8.3)
         | 
| 72 72 | 
             
                listen (3.0.5)
         | 
| 73 73 | 
             
                  rb-fsevent (>= 0.9.3)
         | 
| 74 74 | 
             
                  rb-inotify (>= 0.9)
         | 
| 75 | 
            -
                lumberjack (1.0. | 
| 75 | 
            +
                lumberjack (1.0.10)
         | 
| 76 76 | 
             
                maruku (0.7.2)
         | 
| 77 77 | 
             
                method_source (0.8.2)
         | 
| 78 78 | 
             
                mime-types (2.99)
         | 
| 79 | 
            -
                minitest (5.8. | 
| 79 | 
            +
                minitest (5.8.4)
         | 
| 80 80 | 
             
                multi_json (1.11.2)
         | 
| 81 81 | 
             
                multi_xml (0.5.5)
         | 
| 82 82 | 
             
                nenv (0.2.0)
         | 
| 83 83 | 
             
                notiffany (0.0.8)
         | 
| 84 84 | 
             
                  nenv (~> 0.1)
         | 
| 85 85 | 
             
                  shellany (~> 0.0)
         | 
| 86 | 
            -
                parser (2. | 
| 87 | 
            -
                  ast ( | 
| 86 | 
            +
                parser (2.3.0.6)
         | 
| 87 | 
            +
                  ast (~> 2.2)
         | 
| 88 88 | 
             
                powerpack (0.1.1)
         | 
| 89 89 | 
             
                pry (0.10.3)
         | 
| 90 90 | 
             
                  coderay (~> 1.1.0)
         | 
| @@ -100,9 +100,9 @@ GEM | |
| 100 100 | 
             
                  rack (>= 1.0.0)
         | 
| 101 101 | 
             
                rack-test (0.6.3)
         | 
| 102 102 | 
             
                  rack (>= 1.0)
         | 
| 103 | 
            -
                rainbow (2. | 
| 104 | 
            -
                rake (10. | 
| 105 | 
            -
                rb-fsevent (0.9. | 
| 103 | 
            +
                rainbow (2.1.0)
         | 
| 104 | 
            +
                rake (10.5.0)
         | 
| 105 | 
            +
                rb-fsevent (0.9.7)
         | 
| 106 106 | 
             
                rb-inotify (0.9.5)
         | 
| 107 107 | 
             
                  ffi (>= 0.5.0)
         | 
| 108 108 | 
             
                rspec (3.4.0)
         | 
| @@ -114,7 +114,7 @@ GEM | |
| 114 114 | 
             
                rspec-expectations (3.4.0)
         | 
| 115 115 | 
             
                  diff-lcs (>= 1.2.0, < 2.0)
         | 
| 116 116 | 
             
                  rspec-support (~> 3.4.0)
         | 
| 117 | 
            -
                rspec-mocks (3.4. | 
| 117 | 
            +
                rspec-mocks (3.4.1)
         | 
| 118 118 | 
             
                  diff-lcs (>= 1.2.0, < 2.0)
         | 
| 119 119 | 
             
                  rspec-support (~> 3.4.0)
         | 
| 120 120 | 
             
                rspec-support (3.4.1)
         | 
    
        data/README.md
    CHANGED
    
    | @@ -41,6 +41,7 @@ | |
| 41 41 | 
             
              - [Custom Validators](#custom-validators)
         | 
| 42 42 | 
             
              - [Validation Errors](#validation-errors)
         | 
| 43 43 | 
             
              - [I18n](#i18n)
         | 
| 44 | 
            +
              - [Custom Validation Messages](#custom-validation-messages)
         | 
| 44 45 | 
             
            - [Headers](#headers)
         | 
| 45 46 | 
             
            - [Routes](#routes)
         | 
| 46 47 | 
             
            - [Helpers](#helpers)
         | 
| @@ -73,6 +74,7 @@ | |
| 73 74 | 
             
            - [Before and After](#before-and-after)
         | 
| 74 75 | 
             
            - [Anchoring](#anchoring)
         | 
| 75 76 | 
             
            - [Using Custom Middleware](#using-custom-middleware)
         | 
| 77 | 
            +
              - [Grape Middleware](#grape-middleware)
         | 
| 76 78 | 
             
              - [Rails Middleware](#rails-middleware)
         | 
| 77 79 | 
             
              - [Remote IP](#remote-ip)
         | 
| 78 80 | 
             
            - [Writing Tests](#writing-tests)
         | 
| @@ -99,7 +101,7 @@ content negotiation, versioning and much more. | |
| 99 101 |  | 
| 100 102 | 
             
            ## Stable Release
         | 
| 101 103 |  | 
| 102 | 
            -
            You're reading the documentation for the stable release of  | 
| 104 | 
            +
            You're reading the documentation for the stable release of Grape, 0.15.0.
         | 
| 103 105 | 
             
            Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
         | 
| 104 106 |  | 
| 105 107 | 
             
            ## Project Resources
         | 
| @@ -600,10 +602,10 @@ It also works on nested hashes: | |
| 600 602 | 
             
            format :json
         | 
| 601 603 |  | 
| 602 604 | 
             
            params do
         | 
| 603 | 
            -
              requires :user, : | 
| 605 | 
            +
              requires :user, type: Hash do
         | 
| 604 606 | 
             
                requires :first_name, type: String
         | 
| 605 607 | 
             
                optional :last_name, type: String
         | 
| 606 | 
            -
                requires :address, : | 
| 608 | 
            +
                requires :address, type: Hash do
         | 
| 607 609 | 
             
                  requires :city, type: String
         | 
| 608 610 | 
             
                  optional :region, type: String
         | 
| 609 611 | 
             
                end
         | 
| @@ -1146,6 +1148,19 @@ namespace :statuses do | |
| 1146 1148 | 
             
            end
         | 
| 1147 1149 | 
             
            ```
         | 
| 1148 1150 |  | 
| 1151 | 
            +
            You can also define a route parameter type by passing to `route_param`'s options.
         | 
| 1152 | 
            +
             | 
| 1153 | 
            +
            ```ruby
         | 
| 1154 | 
            +
            namespace :arithmetic do
         | 
| 1155 | 
            +
              route_param :n, type: Integer do
         | 
| 1156 | 
            +
                desc 'Returns in power'
         | 
| 1157 | 
            +
                get 'power' do
         | 
| 1158 | 
            +
                  params[:n] ** params[:n]
         | 
| 1159 | 
            +
                end
         | 
| 1160 | 
            +
              end
         | 
| 1161 | 
            +
            end
         | 
| 1162 | 
            +
            ```
         | 
| 1163 | 
            +
             | 
| 1149 1164 | 
             
            ### Custom Validators
         | 
| 1150 1165 |  | 
| 1151 1166 | 
             
            ```ruby
         | 
| @@ -1182,6 +1197,35 @@ params do | |
| 1182 1197 | 
             
            end
         | 
| 1183 1198 | 
             
            ```
         | 
| 1184 1199 |  | 
| 1200 | 
            +
            You can also create custom validation that use request to validate the attribute. For example if you want to have parameters that are available to only admins, you can do the following.
         | 
| 1201 | 
            +
             | 
| 1202 | 
            +
            ```ruby
         | 
| 1203 | 
            +
            class Admin < Grape::Validations::Base
         | 
| 1204 | 
            +
              def validate(request)
         | 
| 1205 | 
            +
                # return if the param we are checking was not in request
         | 
| 1206 | 
            +
                # @attrs is a list containing the attribute we are currently validating
         | 
| 1207 | 
            +
                # in our sample case this method once will get called with
         | 
| 1208 | 
            +
                # @attrs being [:admin_field] and once with @attrs being [:admin_false_field]
         | 
| 1209 | 
            +
                return unless request.params.key? @attrs.first
         | 
| 1210 | 
            +
                # check if admin flag is set to true
         | 
| 1211 | 
            +
                return unless @option
         | 
| 1212 | 
            +
                # check if user is admin or not
         | 
| 1213 | 
            +
                # as an example get a token from request and check if it's admin or not
         | 
| 1214 | 
            +
                fail Grape::Exceptions::Validation, params: @attrs, message: 'Can not set admin-only field.' unless request.headers['X-Access-Token'] == 'admin'
         | 
| 1215 | 
            +
              end
         | 
| 1216 | 
            +
            end
         | 
| 1217 | 
            +
            ```
         | 
| 1218 | 
            +
             | 
| 1219 | 
            +
            And use it in your endpoint definition as:
         | 
| 1220 | 
            +
             | 
| 1221 | 
            +
            ```ruby
         | 
| 1222 | 
            +
            params do
         | 
| 1223 | 
            +
              optional :admin_field, type: String, admin: true
         | 
| 1224 | 
            +
              optional :non_admin_field, type: String
         | 
| 1225 | 
            +
              optional :admin_false_field, type: String, admin: false
         | 
| 1226 | 
            +
            end
         | 
| 1227 | 
            +
            ```
         | 
| 1228 | 
            +
             | 
| 1185 1229 | 
             
            ### Validation Errors
         | 
| 1186 1230 |  | 
| 1187 1231 | 
             
            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`.
         | 
| @@ -1222,6 +1266,119 @@ end | |
| 1222 1266 | 
             
            Grape supports I18n for parameter-related error messages, but will fallback to English if
         | 
| 1223 1267 | 
             
            translations for the default locale have not been provided. See [en.yml](lib/grape/locale/en.yml) for message keys.
         | 
| 1224 1268 |  | 
| 1269 | 
            +
            ### Custom Validation messages
         | 
| 1270 | 
            +
             | 
| 1271 | 
            +
            Grape supports custom validation messages for parameter-related and coerce-related error messages.
         | 
| 1272 | 
            +
             | 
| 1273 | 
            +
            #### `presence`, `allow_blank`, `values`, `regexp`
         | 
| 1274 | 
            +
             | 
| 1275 | 
            +
            ```ruby
         | 
| 1276 | 
            +
            params do
         | 
| 1277 | 
            +
              requires :name, values: { value: 1..10, message: 'not in range from 1 to 10' }, allow_blank: { value: false, message: 'cannot be blank' }, regexp: { value: /^[a-z]+$/, message: 'format is invalid' }, message: 'is required'
         | 
| 1278 | 
            +
            end
         | 
| 1279 | 
            +
            ```
         | 
| 1280 | 
            +
            #### `all_or_none_of`
         | 
| 1281 | 
            +
             | 
| 1282 | 
            +
            ```ruby
         | 
| 1283 | 
            +
            params do
         | 
| 1284 | 
            +
              optional :beer
         | 
| 1285 | 
            +
              optional :wine
         | 
| 1286 | 
            +
              optional :juice
         | 
| 1287 | 
            +
              all_or_none_of :beer, :wine, :juice, message: "all params are required or none is required"
         | 
| 1288 | 
            +
            end
         | 
| 1289 | 
            +
            ```
         | 
| 1290 | 
            +
             | 
| 1291 | 
            +
            #### `mutually_exclusive`
         | 
| 1292 | 
            +
             | 
| 1293 | 
            +
            ```ruby
         | 
| 1294 | 
            +
            params do
         | 
| 1295 | 
            +
              optional :beer
         | 
| 1296 | 
            +
              optional :wine
         | 
| 1297 | 
            +
              optional :juice
         | 
| 1298 | 
            +
              mutually_exclusive :beer, :wine, :juice, message: "are mutually exclusive cannot pass both params"
         | 
| 1299 | 
            +
            end
         | 
| 1300 | 
            +
            ```
         | 
| 1301 | 
            +
            #### `exactly_one_of`
         | 
| 1302 | 
            +
             | 
| 1303 | 
            +
            ```ruby
         | 
| 1304 | 
            +
            params do
         | 
| 1305 | 
            +
              optional :beer
         | 
| 1306 | 
            +
              optional :wine
         | 
| 1307 | 
            +
              optional :juice
         | 
| 1308 | 
            +
              exactly_one_of :beer, :wine, :juice, message: {exactly_one: "are missing, exactly one parameter is required", mutual_exclusion: "are mutually exclusive, exactly one parameter is required"}
         | 
| 1309 | 
            +
            end
         | 
| 1310 | 
            +
            ```
         | 
| 1311 | 
            +
            #### `at_least_one_of`
         | 
| 1312 | 
            +
             | 
| 1313 | 
            +
            ```ruby
         | 
| 1314 | 
            +
            params do
         | 
| 1315 | 
            +
              optional :beer
         | 
| 1316 | 
            +
              optional :wine
         | 
| 1317 | 
            +
              optional :juice
         | 
| 1318 | 
            +
              at_least_one_of :beer, :wine, :juice, message: "are missing, please specify at least one param"
         | 
| 1319 | 
            +
            end
         | 
| 1320 | 
            +
            ```
         | 
| 1321 | 
            +
            #### `Coerce`
         | 
| 1322 | 
            +
             | 
| 1323 | 
            +
            ```ruby
         | 
| 1324 | 
            +
            params do
         | 
| 1325 | 
            +
              requires :int, type: {value: Integer, message: "type cast is invalid" }
         | 
| 1326 | 
            +
            end
         | 
| 1327 | 
            +
            ```
         | 
| 1328 | 
            +
            #### `With Lambdas`
         | 
| 1329 | 
            +
             | 
| 1330 | 
            +
            ```ruby
         | 
| 1331 | 
            +
            params do
         | 
| 1332 | 
            +
              requires :name, values: { value: -> { (1..10).to_a }, message: 'not in range from 1 to 10' }
         | 
| 1333 | 
            +
            end
         | 
| 1334 | 
            +
            ```
         | 
| 1335 | 
            +
            #### `Pass symbols for i18n translations`
         | 
| 1336 | 
            +
             | 
| 1337 | 
            +
            You can pass a symbol if you want i18n translations for your custom validation messages.
         | 
| 1338 | 
            +
             | 
| 1339 | 
            +
            ```ruby
         | 
| 1340 | 
            +
            params do
         | 
| 1341 | 
            +
              requires :name, message: :name_required
         | 
| 1342 | 
            +
            end
         | 
| 1343 | 
            +
            ```
         | 
| 1344 | 
            +
            ```ruby
         | 
| 1345 | 
            +
            # en.yml
         | 
| 1346 | 
            +
             | 
| 1347 | 
            +
            en:
         | 
| 1348 | 
            +
              grape:
         | 
| 1349 | 
            +
                errors:
         | 
| 1350 | 
            +
                  format: ! '%{attributes} %{message}'
         | 
| 1351 | 
            +
                  messages:
         | 
| 1352 | 
            +
                    name_required: 'must be present'
         | 
| 1353 | 
            +
            ```
         | 
| 1354 | 
            +
             | 
| 1355 | 
            +
            #### `Overriding attribute names`
         | 
| 1356 | 
            +
             | 
| 1357 | 
            +
            You can also override attribute names.
         | 
| 1358 | 
            +
             | 
| 1359 | 
            +
            ```ruby
         | 
| 1360 | 
            +
            # en.yml
         | 
| 1361 | 
            +
             | 
| 1362 | 
            +
            en:
         | 
| 1363 | 
            +
              grape:
         | 
| 1364 | 
            +
                errors:
         | 
| 1365 | 
            +
                  format: ! '%{attributes} %{message}'
         | 
| 1366 | 
            +
                  messages:
         | 
| 1367 | 
            +
                    name_required: 'must be present'
         | 
| 1368 | 
            +
                  attributes:
         | 
| 1369 | 
            +
                    name: 'Oops! Name'
         | 
| 1370 | 
            +
            ```
         | 
| 1371 | 
            +
            Will produce 'Oops! Name must be present'
         | 
| 1372 | 
            +
             | 
| 1373 | 
            +
            #### `With Default`
         | 
| 1374 | 
            +
             | 
| 1375 | 
            +
            You cannot set a custom message option for Default as it requires interpolation `%{option1}: %{value1} is incompatible with %{option2}: %{value2}`. You can change the default error message for Default by changing the `incompatible_option_values` message key inside [en.yml](lib/grape/locale/en.yml)
         | 
| 1376 | 
            +
             | 
| 1377 | 
            +
            ```ruby
         | 
| 1378 | 
            +
            params do
         | 
| 1379 | 
            +
              requires :name, values: { value: -> { (1..10).to_a }, message: 'not in range from 1 to 10' }, default: 5
         | 
| 1380 | 
            +
            end
         | 
| 1381 | 
            +
            ```
         | 
| 1225 1382 | 
             
            ## Headers
         | 
| 1226 1383 |  | 
| 1227 1384 | 
             
            Request headers are available through the `headers` helper or from `env` in their original form.
         | 
| @@ -1542,6 +1699,12 @@ You can abort the execution of an API method by raising errors with `error!`. | |
| 1542 1699 | 
             
            error! 'Access Denied', 401
         | 
| 1543 1700 | 
             
            ```
         | 
| 1544 1701 |  | 
| 1702 | 
            +
            Anything that responds to `#to_s` can be given as a first argument to `error!`.
         | 
| 1703 | 
            +
             | 
| 1704 | 
            +
            ```ruby
         | 
| 1705 | 
            +
            error! :not_found, 404
         | 
| 1706 | 
            +
            ```
         | 
| 1707 | 
            +
             | 
| 1545 1708 | 
             
            You can also return JSON formatted objects by raising error! and passing a hash
         | 
| 1546 1709 | 
             
            instead of a message.
         | 
| 1547 1710 |  | 
| @@ -1562,7 +1725,7 @@ end | |
| 1562 1725 |  | 
| 1563 1726 | 
             
            The following example specifies the entity to use in the `http_codes` definition.
         | 
| 1564 1727 |  | 
| 1565 | 
            -
            ```
         | 
| 1728 | 
            +
            ```ruby
         | 
| 1566 1729 | 
             
            desc 'My Route' do
         | 
| 1567 1730 | 
             
             failure [[408, 'Unauthorized', API::Error]]
         | 
| 1568 1731 | 
             
            end
         | 
| @@ -1731,8 +1894,41 @@ rescue_from RuntimeError, rescue_subclasses: false do |e| | |
| 1731 1894 | 
             
            end
         | 
| 1732 1895 | 
             
            ```
         | 
| 1733 1896 |  | 
| 1897 | 
            +
            Helpers are also available inside `rescue_from`.
         | 
| 1898 | 
            +
             | 
| 1899 | 
            +
            ```ruby
         | 
| 1900 | 
            +
            class Twitter::API < Grape::API
         | 
| 1901 | 
            +
              format :json
         | 
| 1902 | 
            +
              helpers do
         | 
| 1903 | 
            +
                def server_error!
         | 
| 1904 | 
            +
                  error!({ error: 'Server error.' }, 500, { 'Content-Type' => 'text/error' })
         | 
| 1905 | 
            +
                end
         | 
| 1906 | 
            +
              end
         | 
| 1907 | 
            +
             | 
| 1908 | 
            +
              rescue_from :all do |e|
         | 
| 1909 | 
            +
                server_error!
         | 
| 1910 | 
            +
              end
         | 
| 1911 | 
            +
            end
         | 
| 1912 | 
            +
            ```
         | 
| 1913 | 
            +
             | 
| 1734 1914 | 
             
            The `rescue_from` block must return a `Rack::Response` object, call `error!` or re-raise an exception.
         | 
| 1735 1915 |  | 
| 1916 | 
            +
            The `with` keyword is available as `rescue_from` options, it can be passed method name or Proc object.
         | 
| 1917 | 
            +
             | 
| 1918 | 
            +
            ```ruby
         | 
| 1919 | 
            +
            class Twitter::API < Grape::API
         | 
| 1920 | 
            +
              format :json
         | 
| 1921 | 
            +
              helpers do
         | 
| 1922 | 
            +
                def server_error!
         | 
| 1923 | 
            +
                  error!({ error: 'Server error.' }, 500, { 'Content-Type' => 'text/error' })
         | 
| 1924 | 
            +
                end
         | 
| 1925 | 
            +
              end
         | 
| 1926 | 
            +
             | 
| 1927 | 
            +
              rescue_from :all,          with: :server_error!
         | 
| 1928 | 
            +
              rescue_from ArgumentError, with: -> { Rack::Response.new('rescued with a method', 400) }
         | 
| 1929 | 
            +
            end
         | 
| 1930 | 
            +
            ```
         | 
| 1931 | 
            +
             | 
| 1736 1932 | 
             
            #### Unrescuable Exceptions
         | 
| 1737 1933 |  | 
| 1738 1934 | 
             
            `Grape::Exceptions::InvalidVersionHeader`, which is raised when the version in the request header doesn't match the currently evaluated version for the endpoint, will _never_ be rescued from a `rescue_from` block (even a `rescue_from :all`) This is because Grape relies on Rack to catch that error and try the next versioned-route for cases where there exist identical Grape endpoints with different versions.
         | 
| @@ -1801,7 +1997,7 @@ class API < Grape::API | |
| 1801 1997 | 
             
            end
         | 
| 1802 1998 | 
             
            ```
         | 
| 1803 1999 |  | 
| 1804 | 
            -
            For similar to Rails request logging try the [grape_logging](https://github.com/aserafin/grape_logging)  | 
| 2000 | 
            +
            For similar to Rails request logging try the [grape_logging](https://github.com/aserafin/grape_logging) or [grape-middleware-logger](https://github.com/ridiculous/grape-middleware-logger) gems.
         | 
| 1805 2001 |  | 
| 1806 2002 | 
             
            ## API Formats
         | 
| 1807 2003 |  | 
| @@ -1885,7 +2081,7 @@ class Twitter::API < Grape::API | |
| 1885 2081 | 
             
                filename = params[:file][:filename]
         | 
| 1886 2082 | 
             
                content_type MIME::Types.type_for(filename)[0].to_s
         | 
| 1887 2083 | 
             
                env['api.format'] = :binary # there's no formatter for :binary, data will be returned "as is"
         | 
| 1888 | 
            -
                header 'Content-Disposition', "attachment; filename*=UTF-8''#{ | 
| 2084 | 
            +
                header 'Content-Disposition', "attachment; filename*=UTF-8''#{CGI.escape(filename)}"
         | 
| 1889 2085 | 
             
                params[:file][:tempfile].read
         | 
| 1890 2086 | 
             
              end
         | 
| 1891 2087 | 
             
            end
         | 
| @@ -2274,6 +2470,30 @@ class API < Grape::API | |
| 2274 2470 | 
             
            end
         | 
| 2275 2471 | 
             
            ```
         | 
| 2276 2472 |  | 
| 2473 | 
            +
            If you want to take advantage of `Rack::Sendfile`, which intercepts responses whose body is
         | 
| 2474 | 
            +
            being served from a file and replaces it with a server specific X-Sendfile header, specify `to_path`
         | 
| 2475 | 
            +
            method in your file streamer class which returns path of served file:
         | 
| 2476 | 
            +
             | 
| 2477 | 
            +
            ```ruby
         | 
| 2478 | 
            +
            class FileStreamer
         | 
| 2479 | 
            +
              # ...
         | 
| 2480 | 
            +
             | 
| 2481 | 
            +
              def to_path
         | 
| 2482 | 
            +
                @file_path
         | 
| 2483 | 
            +
              end
         | 
| 2484 | 
            +
             | 
| 2485 | 
            +
              # ...
         | 
| 2486 | 
            +
            end
         | 
| 2487 | 
            +
            ```
         | 
| 2488 | 
            +
             | 
| 2489 | 
            +
            Note: don't forget turn on `Rack::Sendfile` middleware in your API:
         | 
| 2490 | 
            +
             | 
| 2491 | 
            +
            ```ruby
         | 
| 2492 | 
            +
            class API < Grape::API
         | 
| 2493 | 
            +
              use Rack::Sendfile
         | 
| 2494 | 
            +
            end
         | 
| 2495 | 
            +
            ```
         | 
| 2496 | 
            +
             | 
| 2277 2497 | 
             
            ## Authentication
         | 
| 2278 2498 |  | 
| 2279 2499 | 
             
            ### Basic and Digest Auth
         | 
| @@ -2399,7 +2619,9 @@ Before and after callbacks execute in the following order: | |
| 2399 2619 |  | 
| 2400 2620 | 
             
            Steps 4, 5 and 6 only happen if validation succeeds.
         | 
| 2401 2621 |  | 
| 2402 | 
            -
             | 
| 2622 | 
            +
            #### Examples
         | 
| 2623 | 
            +
             | 
| 2624 | 
            +
            Using a simple `before` block to set a header
         | 
| 2403 2625 |  | 
| 2404 2626 | 
             
            ```ruby
         | 
| 2405 2627 | 
             
            before do
         | 
| @@ -2407,7 +2629,9 @@ before do | |
| 2407 2629 | 
             
            end
         | 
| 2408 2630 | 
             
            ```
         | 
| 2409 2631 |  | 
| 2410 | 
            -
             | 
| 2632 | 
            +
            **Namespaces**
         | 
| 2633 | 
            +
             | 
| 2634 | 
            +
            Callbacks apply to each API call within and below the current namespace:
         | 
| 2411 2635 |  | 
| 2412 2636 | 
             
            ```ruby
         | 
| 2413 2637 | 
             
            class MyAPI < Grape::API
         | 
| @@ -2441,8 +2665,7 @@ GET /foo        # 'root - foo - blah' | |
| 2441 2665 | 
             
            GET /foo/bar    # 'root - foo - bar - blah'
         | 
| 2442 2666 | 
             
            ```
         | 
| 2443 2667 |  | 
| 2444 | 
            -
            Params on a `namespace` (or  | 
| 2445 | 
            -
            `before_validation` or `after_validation`:
         | 
| 2668 | 
            +
            Params on a `namespace` (or whichever alias you are using) will also be available when using `before_validation` or `after_validation`:
         | 
| 2446 2669 |  | 
| 2447 2670 | 
             
            ```ruby
         | 
| 2448 2671 | 
             
            class MyAPI < Grape::API
         | 
| @@ -2469,6 +2692,8 @@ GET /123        # 'Fixnum' | |
| 2469 2692 | 
             
            GET /foo        # 400 error - 'blah is invalid'
         | 
| 2470 2693 | 
             
            ```
         | 
| 2471 2694 |  | 
| 2695 | 
            +
            **Versioning**
         | 
| 2696 | 
            +
             | 
| 2472 2697 | 
             
            When a callback is defined within a version block, it's only called for the routes defined in that block.
         | 
| 2473 2698 |  | 
| 2474 2699 | 
             
            ```ruby
         | 
| @@ -2502,6 +2727,33 @@ GET /foo/v1       # 'v1-hello' | |
| 2502 2727 | 
             
            GET /foo/v2       # 'v2-hello'
         | 
| 2503 2728 | 
             
            ```
         | 
| 2504 2729 |  | 
| 2730 | 
            +
            **Altering Responses**
         | 
| 2731 | 
            +
             | 
| 2732 | 
            +
            Using `present` in any callback allows you to add data to a response:
         | 
| 2733 | 
            +
             | 
| 2734 | 
            +
            ```ruby
         | 
| 2735 | 
            +
            class MyAPI < Grape::API
         | 
| 2736 | 
            +
              format :json
         | 
| 2737 | 
            +
             | 
| 2738 | 
            +
              after_validation do
         | 
| 2739 | 
            +
                present :name, params[:name] if params[:name]
         | 
| 2740 | 
            +
              end
         | 
| 2741 | 
            +
             | 
| 2742 | 
            +
              get '/greeting' do
         | 
| 2743 | 
            +
                present :greeting, 'Hello!'
         | 
| 2744 | 
            +
              end
         | 
| 2745 | 
            +
            end
         | 
| 2746 | 
            +
            ```
         | 
| 2747 | 
            +
             | 
| 2748 | 
            +
            The behaviour is then:
         | 
| 2749 | 
            +
             | 
| 2750 | 
            +
            ```bash
         | 
| 2751 | 
            +
            GET /greeting              # {"greeting":"Hello!"}
         | 
| 2752 | 
            +
            GET /greeting?name=Alan    # {"name":"Alan","greeting":"Hello!"}
         | 
| 2753 | 
            +
            ```
         | 
| 2754 | 
            +
             | 
| 2755 | 
            +
            Instead of altering a response, you can also terminate and rewrite it from any callback using `error!`, including `after`. This will cause all subsequent steps in the process to not be called. **This includes the actual api call and any callbacks**
         | 
| 2756 | 
            +
             | 
| 2505 2757 | 
             
            ## Anchoring
         | 
| 2506 2758 |  | 
| 2507 2759 | 
             
            Grape by default anchors all request paths, which means that the request URL
         | 
| @@ -2533,6 +2785,32 @@ part. | |
| 2533 2785 |  | 
| 2534 2786 | 
             
            ## Using Custom Middleware
         | 
| 2535 2787 |  | 
| 2788 | 
            +
            ### Grape Middleware
         | 
| 2789 | 
            +
             | 
| 2790 | 
            +
            You can make a custom middleware by using `Grape::Middleware::Base`.
         | 
| 2791 | 
            +
            It's inherited from some grape official middlewares in fact.
         | 
| 2792 | 
            +
             | 
| 2793 | 
            +
            For example, you can write a middleware to log application exception.
         | 
| 2794 | 
            +
             | 
| 2795 | 
            +
            ```ruby
         | 
| 2796 | 
            +
            class LoggingError < Grape::Middleware::Base
         | 
| 2797 | 
            +
              def after
         | 
| 2798 | 
            +
                return unless @app_response && @app_response[0] == 500
         | 
| 2799 | 
            +
                env['rack.logger'].error("Raised error on #{env['PATH_INFO']}")
         | 
| 2800 | 
            +
              end
         | 
| 2801 | 
            +
            end
         | 
| 2802 | 
            +
            ```
         | 
| 2803 | 
            +
             | 
| 2804 | 
            +
            Your middleware can overwrite application response as follows, except error case.
         | 
| 2805 | 
            +
             | 
| 2806 | 
            +
            ```ruby
         | 
| 2807 | 
            +
            class Overwriter < Grape::Middleware::Base
         | 
| 2808 | 
            +
              def after
         | 
| 2809 | 
            +
                [200, { 'Content-Type' => 'text/plain' }, ['Overwrited.']]
         | 
| 2810 | 
            +
              end
         | 
| 2811 | 
            +
            end
         | 
| 2812 | 
            +
            ```
         | 
| 2813 | 
            +
             | 
| 2536 2814 | 
             
            ### Rails Middleware
         | 
| 2537 2815 |  | 
| 2538 2816 | 
             
            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.
         | 
| @@ -2683,7 +2961,7 @@ end | |
| 2683 2961 | 
             
            ```
         | 
| 2684 2962 |  | 
| 2685 2963 | 
             
            In Rails, HTTP request tests would go into the `spec/requests` group. You may want your API code to go into
         | 
| 2686 | 
            -
            `app/api` - you can match that layout under `spec` by adding the following in `spec/ | 
| 2964 | 
            +
            `app/api` - you can match that layout under `spec` by adding the following in `spec/rails_helper.rb`.
         | 
| 2687 2965 |  | 
| 2688 2966 | 
             
            ```ruby
         | 
| 2689 2967 | 
             
            RSpec.configure do |config|
         | 
| @@ -2734,7 +3012,7 @@ describe 'an endpoint that needs helpers stubbed' do | |
| 2734 3012 | 
             
                Grape::Endpoint.before_each nil
         | 
| 2735 3013 | 
             
              end
         | 
| 2736 3014 |  | 
| 2737 | 
            -
              it ' | 
| 3015 | 
            +
              it 'stubs the helper' do
         | 
| 2738 3016 | 
             
                # ...
         | 
| 2739 3017 | 
             
              end
         | 
| 2740 3018 | 
             
            end
         |