Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add custom error formatter #76

Merged
merged 15 commits into from
Oct 20, 2017
46 changes: 37 additions & 9 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,48 @@
# This configuration was generated by `rubocop --auto-gen-config`
# on 2015-01-13 18:47:14 -0500 using RuboCop version 0.28.0.
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2017-10-09 10:22:52 -0500 using RuboCop version 0.41.2.
# 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: 25
# Configuration parameters: AllowURI, URISchemes.
# Offense count: 1
Metrics/AbcSize:
Max: 20
Max: 18

# Offense count: 7
# Offense count: 1
Metrics/CyclomaticComplexity:
Max: 9

# Offense count: 1
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes.
# URISchemes: http, https
Metrics/LineLength:
Max: 87

# Offense count: 1
# Configuration parameters: CountComments.
Metrics/MethodLength:
Max: 18

# Offense count: 1
Metrics/PerceivedComplexity:
Max: 11

# Offense count: 5
Style/Documentation:
Enabled: false
Exclude:
- 'spec/**/*'
- 'test/**/*'
- 'lib/grape-active_model_serializers/endpoint_extension.rb'
- 'lib/grape-active_model_serializers/error_formatter.rb'
- 'lib/grape-active_model_serializers/formatter.rb'
- 'lib/grape-active_model_serializers/options_builder.rb'
- 'lib/grape-active_model_serializers/serializer_resolver.rb'

# Offense count: 2
# Configuration parameters: Exclude.
# Configuration parameters: ExpectMatchingDefinition, Regex, IgnoreExecutableScripts.
Style/FileName:
Enabled: false
Exclude:
- 'lib/grape-active_model_serializers.rb'
- 'spec/grape-active_model_serializers_spec.rb'
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

### 1.5.2 (Next)

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

### 1.5.1 (April 25, 2017)
Expand Down
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ source 'https://rubygems.org'

gemspec

case version = ENV['GRAPE_VERSION'] || '~> 0.10.0'
case version = ENV['GRAPE_VERSION'] || '~> 1.0.0'
when 'HEAD'
gem 'grape', github: 'intridea/grape'
else
Expand Down
1 change: 1 addition & 0 deletions lib/grape-active_model_serializers.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require 'active_model_serializers'
require 'grape'
require 'grape-active_model_serializers/endpoint_extension'
require 'grape-active_model_serializers/error_formatter'
require 'grape-active_model_serializers/formatter'
require 'grape-active_model_serializers/serializer_resolver'
require 'grape-active_model_serializers/options_builder'
Expand Down
53 changes: 53 additions & 0 deletions lib/grape-active_model_serializers/error_formatter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
module Grape
module ErrorFormatter
module ActiveModelSerializers
extend Base

class << self
def call(message, backtrace, options = {}, env = nil, original_exception = nil)
message = present(message, env) if respond_to?(:present)
message = wrap_message(message)

rescue_options = options[:rescue_options] || {}
if rescue_options[:backtrace] && backtrace && !backtrace.empty?
message = message.merge(backtrace: backtrace)
end
if rescue_options[:original_exception] && original_exception
message = message
.merge(original_exception: original_exception.inspect)
end
if ::Grape.const_defined? :Json
::Grape::Json.dump(message)
else
::MultiJson.dump(message)
end
end

private

def wrap_message(message)
if active_model?(message)
::ActiveModelSerializers::SerializableResource.new(
message,
serializer: ActiveModel::Serializer::ErrorSerializer
).as_json
elsif ok_to_pass_through?(message)
message
else
{ error: message }
end
end

def active_model?(message)
message.respond_to?(:errors) &&
message.errors.is_a?(ActiveModel::Errors)
end

def ok_to_pass_through?(message)
message.is_a?(Exceptions::ValidationErrors) ||
message.is_a?(Hash)
end
end
end
end
end
92 changes: 92 additions & 0 deletions spec/grape-active_model_serializers/error_formatter_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
require 'spec_helper'
require 'grape-active_model_serializers/error_formatter'

describe Grape::ErrorFormatter::ActiveModelSerializers do
subject { Grape::ErrorFormatter::ActiveModelSerializers }
let(:backtrace) { ['Line:1'] }
let(:options) { Hash.new }
let(:env) { { 'api.endpoint' => app.endpoints.first } }
let(:original_exception) { StandardError.new('oh noes!') }

let(:app) {
Class.new(Grape::API) do |app|
app.format :json
app.formatter :jsonapi, Grape::Formatter::ActiveModelSerializers
app.error_formatter :jsonapi, Grape::ErrorFormatter::ActiveModelSerializers

app.namespace('space') do |ns|
ns.get('/', root: false) do
error!(message)
end
end
end
}
let(:foo) {
Class.new {
include ActiveModel::Model

attr_accessor :name

def initialize(attributes = {})
super
errors.add(:name, 'We don\'t like bears')
end
}
}

before do
ActiveModel::Serializer.config.adapter = :json_api
end

after do
ActiveModel::Serializer.config.adapter = :json
end

describe '#call' do
context 'message is an activemodel' do
let(:message) {
foo.new(name: 'bar')
}
it 'formats the error' do
result = subject
.call(message, backtrace, options, env, original_exception)
json_hash = JSON.parse(result)

expected_result = {
'errors' => [
{
'source' => {
'pointer' => '/data/attributes/name'
},
'detail' => 'We don\'t like bears'
}
]
}

expect(json_hash == expected_result).to eq(true)
end
end

context 'message is hash like' do
let(:message) { { 'errors' => ['error'] } }
it 'passes the message through' do
result = subject
.call(message, backtrace, options, env, original_exception)
json_hash = JSON.parse(result)

expect(json_hash == message).to eq(true)
end
end

context 'message is text' do
let(:message) { 'error' }
it 'wraps the error' do
result = subject
.call(message, backtrace, options, env, original_exception)
json_hash = JSON.parse(result)

expect(json_hash == { 'error' => message }).to eq(true)
end
end
end
end