Skip to content

Commit

Permalink
Merge 8df7235 into 2633f74
Browse files Browse the repository at this point in the history
  • Loading branch information
myxoh committed Feb 21, 2019
2 parents 2633f74 + 8df7235 commit b90da75
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 7 deletions.
4 changes: 2 additions & 2 deletions .rubocop_todo.yml
Expand Up @@ -38,7 +38,7 @@ Metrics/BlockLength:
# Offense count: 9
# Configuration parameters: CountComments.
Metrics/ClassLength:
Max: 295
Max: 297

# Offense count: 31
Metrics/CyclomaticComplexity:
Expand All @@ -53,7 +53,7 @@ Metrics/LineLength:
# Offense count: 57
# Configuration parameters: CountComments.
Metrics/MethodLength:
Max: 33
Max: 34

# Offense count: 12
# Configuration parameters: CountComments.
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -3,6 +3,7 @@
#### Features

* Your contribution here.
* [#1864](https://github.com/ruby-grape/grape/pull/1864): Adds `ensure_block` on the API - [@myxoh](https://github.com/myxoh).

#### Fixes

Expand Down
11 changes: 9 additions & 2 deletions README.md
Expand Up @@ -59,7 +59,7 @@
- [Nested mutually_exclusive, exactly_one_of, at_least_one_of, all_or_none_of](#nested-mutually_exclusive-exactly_one_of-at_least_one_of-all_or_none_of)
- [Namespace Validation and Coercion](#namespace-validation-and-coercion)
- [Custom Validators](#custom-validators)
- [Validation Errors](#validation-errors)
- [Validation Errors and Rescuing](#validation-errors-and-rescuing)
- [I18n](#i18n)
- [Custom Validation messages](#custom-validation-messages)
- [presence, allow_blank, values, regexp](#presence-allow_blank-values-regexp)
Expand Down Expand Up @@ -1603,7 +1603,7 @@ end

Every validation will have it's own instance of the validator, which means that the validator can have a state.

### Validation Errors
### Validation Errors and Rescuing

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`.

Expand Down Expand Up @@ -1657,6 +1657,13 @@ params do
end
```

You can ensure a block of code runs after every request (including failures) with `ensure`:
```ruby
ensure_block do
This.block(of: code) # Will definetely run after every request
end
```

### I18n

Grape supports I18n for parameter-related error messages, but will fallback to English if
Expand Down
5 changes: 5 additions & 0 deletions lib/grape/dsl/request_response.rb
Expand Up @@ -131,6 +131,11 @@ def rescue_from(*args, &block)
namespace_stackable(:rescue_options, options)
end

def ensure_block(&ensured_block)
namespace_inheritable(:ensured, true)
namespace_inheritable(:ensured_block, ensured_block)
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.
Expand Down
4 changes: 3 additions & 1 deletion lib/grape/endpoint.rb
Expand Up @@ -291,7 +291,9 @@ def build_stack(helpers)
rescue_options: namespace_stackable_with_hash(:rescue_options) || {},
rescue_handlers: namespace_reverse_stackable_with_hash(:rescue_handlers) || {},
base_only_rescue_handlers: namespace_stackable_with_hash(:base_only_rescue_handlers) || {},
all_rescue_handler: namespace_inheritable(:all_rescue_handler)
all_rescue_handler: namespace_inheritable(:all_rescue_handler),
ensured: namespace_inheritable(:ensured),
ensured_block: namespace_inheritable(:ensured_block)

stack.concat namespace_stackable(:middleware)

Expand Down
15 changes: 13 additions & 2 deletions lib/grape/middleware/error.rb
Expand Up @@ -21,7 +21,9 @@ def default_options
},
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
all_rescue_handler: nil, # rescue handler block to rescue from all exceptions
ensured: false,
ensured_block: nil # This should always be present if ensured is present
}
end

Expand All @@ -32,7 +34,6 @@ def initialize(app, **options)

def call!(env)
@env = env

begin
error_response(catch(:error) do
return @app.call(@env)
Expand All @@ -46,9 +47,19 @@ def call!(env)
raise

run_rescue_handler(handler, error)
ensure
execute_ensured_block! if should_ensure?
end
end

def should_ensure?
options[:ensured]
end

def execute_ensured_block!
options[:ensured_block].call
end

def error!(message, status = options[:default_status], headers = {}, backtrace = [], original_exception = nil)
headers = headers.reverse_merge(Grape::Http::Headers::CONTENT_TYPE => content_type)
rack_response(format_message(message, backtrace, original_exception), status, headers)
Expand Down
61 changes: 61 additions & 0 deletions spec/grape/api_spec.rb
Expand Up @@ -1628,6 +1628,67 @@ def three
end
end

describe '.ensure_block' do
let!(:code) { { has_executed: false } }

context 'when the ensure block has no exceptions' do
before do
code_to_execute = code
subject.ensure_block do
code_to_execute[:has_executed] = true
end
end

context 'when no API call is made' do
it 'has not executed the ensure code' do
expect(code[:has_executed]).to be false
end
end

context 'when no errors occurs' do
before do
subject.get '/no_exceptions' do
'success'
end
end

it 'executes the ensure code' do
get '/no_exceptions'
expect(last_response.body).to eq 'success'
expect(code[:has_executed]).to be true
end
end

context 'when an unhandled occurs inside the API call' do
before do
subject.get '/unhandled_exception' do
raise StandardError
end
end

it 'executes the ensure code' do
expect { get '/unhandled_exception' }.to raise_error StandardError
expect(code[:has_executed]).to be true
end
end

context 'when a handled error occurs inside the API call' do
before do
subject.rescue_from(StandardError) { error! 'handled' }
subject.get '/handled_exception' do
raise StandardError
end
end

it 'executes the ensure code' do
get '/handled_exception'
expect(code[:has_executed]).to be true
expect(last_response.body).to eq 'handled'
end
end
end
end

describe '.rescue_from' do
it 'does not rescue errors when rescue_from is not set' do
subject.get '/exception' do
Expand Down

0 comments on commit b90da75

Please sign in to comment.