Skip to content

Commit b265419

Browse files
xndblock
authored andcommitted
add custom error formatter (#76)
1 parent fab4767 commit b265419

File tree

6 files changed

+185
-10
lines changed

6 files changed

+185
-10
lines changed

.rubocop_todo.yml

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,48 @@
1-
# This configuration was generated by `rubocop --auto-gen-config`
2-
# on 2015-01-13 18:47:14 -0500 using RuboCop version 0.28.0.
1+
# This configuration was generated by
2+
# `rubocop --auto-gen-config`
3+
# on 2017-10-09 10:22:52 -0500 using RuboCop version 0.41.2.
34
# The point is for the user to remove these configuration records
45
# one by one as the offenses are removed from the code base.
56
# Note that changes in the inspected code, or installation of new
67
# versions of RuboCop, may require this file to be generated again.
78

8-
# Offense count: 25
9-
# Configuration parameters: AllowURI, URISchemes.
9+
# Offense count: 1
1010
Metrics/AbcSize:
11-
Max: 20
11+
Max: 18
1212

13-
# Offense count: 7
13+
# Offense count: 1
14+
Metrics/CyclomaticComplexity:
15+
Max: 9
16+
17+
# Offense count: 1
18+
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes.
19+
# URISchemes: http, https
20+
Metrics/LineLength:
21+
Max: 87
22+
23+
# Offense count: 1
24+
# Configuration parameters: CountComments.
25+
Metrics/MethodLength:
26+
Max: 18
27+
28+
# Offense count: 1
29+
Metrics/PerceivedComplexity:
30+
Max: 11
31+
32+
# Offense count: 5
1433
Style/Documentation:
15-
Enabled: false
34+
Exclude:
35+
- 'spec/**/*'
36+
- 'test/**/*'
37+
- 'lib/grape-active_model_serializers/endpoint_extension.rb'
38+
- 'lib/grape-active_model_serializers/error_formatter.rb'
39+
- 'lib/grape-active_model_serializers/formatter.rb'
40+
- 'lib/grape-active_model_serializers/options_builder.rb'
41+
- 'lib/grape-active_model_serializers/serializer_resolver.rb'
1642

1743
# Offense count: 2
18-
# Configuration parameters: Exclude.
44+
# Configuration parameters: ExpectMatchingDefinition, Regex, IgnoreExecutableScripts.
1945
Style/FileName:
20-
Enabled: false
46+
Exclude:
47+
- 'lib/grape-active_model_serializers.rb'
48+
- 'spec/grape-active_model_serializers_spec.rb'

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
### 1.5.2 (Next)
44

5+
* [#76](https://github.com/ruby-grape/grape-active_model_serializers/pull/76): Add custom error formatter - [@xn](https://github.com/xn).
56
* Your contribution here.
67

78
### 1.5.1 (April 25, 2017)

Gemfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ source 'https://rubygems.org'
22

33
gemspec
44

5-
case version = ENV['GRAPE_VERSION'] || '~> 0.10.0'
5+
case version = ENV['GRAPE_VERSION'] || '~> 1.0.0'
66
when 'HEAD'
77
gem 'grape', github: 'intridea/grape'
88
else

lib/grape-active_model_serializers.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
require 'active_model_serializers'
22
require 'grape'
33
require 'grape-active_model_serializers/endpoint_extension'
4+
require 'grape-active_model_serializers/error_formatter'
45
require 'grape-active_model_serializers/formatter'
56
require 'grape-active_model_serializers/serializer_resolver'
67
require 'grape-active_model_serializers/options_builder'
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
module Grape
2+
module ErrorFormatter
3+
module ActiveModelSerializers
4+
extend Base
5+
6+
class << self
7+
def call(message, backtrace, options = {}, env = nil, original_exception = nil)
8+
message = present(message, env) if respond_to?(:present)
9+
message = wrap_message(message)
10+
11+
rescue_options = options[:rescue_options] || {}
12+
if rescue_options[:backtrace] && backtrace && !backtrace.empty?
13+
message = message.merge(backtrace: backtrace)
14+
end
15+
if rescue_options[:original_exception] && original_exception
16+
message = message
17+
.merge(original_exception: original_exception.inspect)
18+
end
19+
if ::Grape.const_defined? :Json
20+
::Grape::Json.dump(message)
21+
else
22+
::MultiJson.dump(message)
23+
end
24+
end
25+
26+
private
27+
28+
def wrap_message(message)
29+
if active_model?(message)
30+
::ActiveModelSerializers::SerializableResource.new(
31+
message,
32+
serializer: ActiveModel::Serializer::ErrorSerializer
33+
).as_json
34+
elsif ok_to_pass_through?(message)
35+
message
36+
else
37+
{ error: message }
38+
end
39+
end
40+
41+
def active_model?(message)
42+
message.respond_to?(:errors) &&
43+
message.errors.is_a?(ActiveModel::Errors)
44+
end
45+
46+
def ok_to_pass_through?(message)
47+
message.is_a?(Exceptions::ValidationErrors) ||
48+
message.is_a?(Hash)
49+
end
50+
end
51+
end
52+
end
53+
end
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
require 'spec_helper'
2+
require 'grape-active_model_serializers/error_formatter'
3+
4+
describe Grape::ErrorFormatter::ActiveModelSerializers do
5+
subject { Grape::ErrorFormatter::ActiveModelSerializers }
6+
let(:backtrace) { ['Line:1'] }
7+
let(:options) { Hash.new }
8+
let(:env) { { 'api.endpoint' => app.endpoints.first } }
9+
let(:original_exception) { StandardError.new('oh noes!') }
10+
11+
let(:app) {
12+
Class.new(Grape::API) do |app|
13+
app.format :json
14+
app.formatter :jsonapi, Grape::Formatter::ActiveModelSerializers
15+
app.error_formatter :jsonapi, Grape::ErrorFormatter::ActiveModelSerializers
16+
17+
app.namespace('space') do |ns|
18+
ns.get('/', root: false) do
19+
error!(message)
20+
end
21+
end
22+
end
23+
}
24+
let(:foo) {
25+
Class.new {
26+
include ActiveModel::Model
27+
28+
attr_accessor :name
29+
30+
def initialize(attributes = {})
31+
super
32+
errors.add(:name, 'We don\'t like bears')
33+
end
34+
}
35+
}
36+
37+
before do
38+
ActiveModel::Serializer.config.adapter = :json_api
39+
end
40+
41+
after do
42+
ActiveModel::Serializer.config.adapter = :json
43+
end
44+
45+
describe '#call' do
46+
context 'message is an activemodel' do
47+
let(:message) {
48+
foo.new(name: 'bar')
49+
}
50+
it 'formats the error' do
51+
result = subject
52+
.call(message, backtrace, options, env, original_exception)
53+
json_hash = JSON.parse(result)
54+
55+
expected_result = {
56+
'errors' => [
57+
{
58+
'source' => {
59+
'pointer' => '/data/attributes/name'
60+
},
61+
'detail' => 'We don\'t like bears'
62+
}
63+
]
64+
}
65+
66+
expect(json_hash == expected_result).to eq(true)
67+
end
68+
end
69+
70+
context 'message is hash like' do
71+
let(:message) { { 'errors' => ['error'] } }
72+
it 'passes the message through' do
73+
result = subject
74+
.call(message, backtrace, options, env, original_exception)
75+
json_hash = JSON.parse(result)
76+
77+
expect(json_hash == message).to eq(true)
78+
end
79+
end
80+
81+
context 'message is text' do
82+
let(:message) { 'error' }
83+
it 'wraps the error' do
84+
result = subject
85+
.call(message, backtrace, options, env, original_exception)
86+
json_hash = JSON.parse(result)
87+
88+
expect(json_hash == { 'error' => message }).to eq(true)
89+
end
90+
end
91+
end
92+
end

0 commit comments

Comments
 (0)