graphiti_errors 1.0.alpha.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0e0ac02ec824fd98eac0e01f515350539d30a421
4
+ data.tar.gz: 1a8375c9d25fe7929d478fcbdf16c48a0de53aac
5
+ SHA512:
6
+ metadata.gz: e16a9a55cb1675d9d359b1041f1f14cd80d269372501e8c57c0f14f3f8cd15f5130590cdd86fea5a20a2620848138b6f125f7111b0318e2104dcdf7e30dbec6e
7
+ data.tar.gz: abdae9dc497b6dd703e6f230d78538c8b3cfef5dc952bb9acd1f527ee5f44d701b5f8365109174598f4b0093ffc873e887725c2362ac678766e9ececd49cf2cc
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.2.2
data/.travis.yml ADDED
@@ -0,0 +1,20 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.3
4
+ before_install: gem install bundler -v 1.11.2
5
+
6
+ script: "bundle exec rake"
7
+
8
+ install: bundle install --retry=3 --jobs=3
9
+
10
+ gemfile:
11
+ - gemfiles/rails_4.gemfile
12
+ - gemfiles/rails_5.gemfile
13
+
14
+ deploy:
15
+ provider: rubygems
16
+ api_key: $RUBYGEMS_API_KEY
17
+ gem: graphiti_errors
18
+ on:
19
+ tags: true
20
+ repo: graphiti-api/graphiti_errors
data/Appraisals ADDED
@@ -0,0 +1,7 @@
1
+ appraise "rails-4" do
2
+ gem "rails", "~> 4.1"
3
+ end
4
+
5
+ appraise "rails-5" do
6
+ gem "rails", "~> 5.0"
7
+ end
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in graphiti_errors.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'activemodel'
8
+ gem 'appraisal'
9
+ gem 'pry'
10
+ gem 'pry-byebug'
11
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Lee Richmond
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # JsonapiErrorable
2
+
3
+ [![Build Status](https://travis-ci.org/jsonapi-suite/jsonapi_errorable.svg?branch=master)](https://travis-ci.org/jsonapi-suite/jsonapi_errorable)
4
+
5
+ Error handling patterns for jsonapi.org-compatible APIs.
6
+
7
+ [View official documentation](http://jsonapi-suite.github.io/jsonapi_errorable)
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+ require "appraisal"
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ if !ENV["APPRAISAL_INITIALIZED"] && !ENV["TRAVIS"]
8
+ task :default => :appraisal
9
+ else
10
+ task :default => :spec
11
+ end
data/bin/appraisal ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+ #
4
+ # This file was generated by Bundler.
5
+ #
6
+ # The application 'appraisal' is installed as part of a gem, and
7
+ # this file is here to facilitate running it.
8
+ #
9
+
10
+ require "pathname"
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
12
+ Pathname.new(__FILE__).realpath)
13
+
14
+ require "rubygems"
15
+ require "bundler/setup"
16
+
17
+ load Gem.bin_path("appraisal", "appraisal")
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "graphiti_errors"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/rspec ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+ #
4
+ # This file was generated by Bundler.
5
+ #
6
+ # The application 'rspec' is installed as part of a gem, and
7
+ # this file is here to facilitate running it.
8
+ #
9
+
10
+ require "pathname"
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
12
+ Pathname.new(__FILE__).realpath)
13
+
14
+ require "rubygems"
15
+ require "bundler/setup"
16
+
17
+ load Gem.bin_path("rspec-core", "rspec")
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,13 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 4.1"
6
+
7
+ group :test do
8
+ gem "appraisal"
9
+ gem "pry"
10
+ gem "pry-byebug"
11
+ end
12
+
13
+ gemspec :path => "../"
@@ -0,0 +1,160 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ jsonapi_errorable (0.8.0)
5
+ jsonapi-serializable (~> 0.1)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ actionmailer (4.2.6)
11
+ actionpack (= 4.2.6)
12
+ actionview (= 4.2.6)
13
+ activejob (= 4.2.6)
14
+ mail (~> 2.5, >= 2.5.4)
15
+ rails-dom-testing (~> 1.0, >= 1.0.5)
16
+ actionpack (4.2.6)
17
+ actionview (= 4.2.6)
18
+ activesupport (= 4.2.6)
19
+ rack (~> 1.6)
20
+ rack-test (~> 0.6.2)
21
+ rails-dom-testing (~> 1.0, >= 1.0.5)
22
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
23
+ actionview (4.2.6)
24
+ activesupport (= 4.2.6)
25
+ builder (~> 3.1)
26
+ erubis (~> 2.7.0)
27
+ rails-dom-testing (~> 1.0, >= 1.0.5)
28
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
29
+ activejob (4.2.6)
30
+ activesupport (= 4.2.6)
31
+ globalid (>= 0.3.0)
32
+ activemodel (4.2.6)
33
+ activesupport (= 4.2.6)
34
+ builder (~> 3.1)
35
+ activerecord (4.2.6)
36
+ activemodel (= 4.2.6)
37
+ activesupport (= 4.2.6)
38
+ arel (~> 6.0)
39
+ activesupport (4.2.6)
40
+ i18n (~> 0.7)
41
+ json (~> 1.7, >= 1.7.7)
42
+ minitest (~> 5.1)
43
+ thread_safe (~> 0.3, >= 0.3.4)
44
+ tzinfo (~> 1.1)
45
+ appraisal (2.1.0)
46
+ bundler
47
+ rake
48
+ thor (>= 0.14.0)
49
+ arel (6.0.3)
50
+ builder (3.2.2)
51
+ byebug (9.0.5)
52
+ coderay (1.1.1)
53
+ concurrent-ruby (1.0.2)
54
+ diff-lcs (1.2.5)
55
+ erubis (2.7.0)
56
+ globalid (0.3.7)
57
+ activesupport (>= 4.1.0)
58
+ i18n (0.7.0)
59
+ json (1.8.3)
60
+ jsonapi-renderer (0.2.0)
61
+ jsonapi-serializable (0.3.0)
62
+ jsonapi-renderer (~> 0.2.0)
63
+ jsonapi_spec_helpers (0.2.0)
64
+ loofah (2.0.3)
65
+ nokogiri (>= 1.5.9)
66
+ mail (2.6.4)
67
+ mime-types (>= 1.16, < 4)
68
+ method_source (0.8.2)
69
+ mime-types (3.1)
70
+ mime-types-data (~> 3.2015)
71
+ mime-types-data (3.2016.0521)
72
+ mini_portile2 (2.1.0)
73
+ minitest (5.9.0)
74
+ nokogiri (1.6.8)
75
+ mini_portile2 (~> 2.1.0)
76
+ pkg-config (~> 1.1.7)
77
+ pkg-config (1.1.7)
78
+ pry (0.10.4)
79
+ coderay (~> 1.1.0)
80
+ method_source (~> 0.8.1)
81
+ slop (~> 3.4)
82
+ pry-byebug (3.4.0)
83
+ byebug (~> 9.0)
84
+ pry (~> 0.10)
85
+ rack (1.6.4)
86
+ rack-test (0.6.3)
87
+ rack (>= 1.0)
88
+ rails (4.2.6)
89
+ actionmailer (= 4.2.6)
90
+ actionpack (= 4.2.6)
91
+ actionview (= 4.2.6)
92
+ activejob (= 4.2.6)
93
+ activemodel (= 4.2.6)
94
+ activerecord (= 4.2.6)
95
+ activesupport (= 4.2.6)
96
+ bundler (>= 1.3.0, < 2.0)
97
+ railties (= 4.2.6)
98
+ sprockets-rails
99
+ rails-deprecated_sanitizer (1.0.3)
100
+ activesupport (>= 4.2.0.alpha)
101
+ rails-dom-testing (1.0.7)
102
+ activesupport (>= 4.2.0.beta, < 5.0)
103
+ nokogiri (~> 1.6.0)
104
+ rails-deprecated_sanitizer (>= 1.0.1)
105
+ rails-html-sanitizer (1.0.3)
106
+ loofah (~> 2.0)
107
+ railties (4.2.6)
108
+ actionpack (= 4.2.6)
109
+ activesupport (= 4.2.6)
110
+ rake (>= 0.8.7)
111
+ thor (>= 0.18.1, < 2.0)
112
+ rake (10.5.0)
113
+ rspec-core (3.5.3)
114
+ rspec-support (~> 3.5.0)
115
+ rspec-expectations (3.5.0)
116
+ diff-lcs (>= 1.2.0, < 2.0)
117
+ rspec-support (~> 3.5.0)
118
+ rspec-mocks (3.5.0)
119
+ diff-lcs (>= 1.2.0, < 2.0)
120
+ rspec-support (~> 3.5.0)
121
+ rspec-rails (3.5.1)
122
+ actionpack (>= 3.0)
123
+ activesupport (>= 3.0)
124
+ railties (>= 3.0)
125
+ rspec-core (~> 3.5.0)
126
+ rspec-expectations (~> 3.5.0)
127
+ rspec-mocks (~> 3.5.0)
128
+ rspec-support (~> 3.5.0)
129
+ rspec-support (3.5.0)
130
+ slop (3.6.0)
131
+ sprockets (3.7.0)
132
+ concurrent-ruby (~> 1.0)
133
+ rack (> 1, < 3)
134
+ sprockets-rails (3.2.0)
135
+ actionpack (>= 4.0)
136
+ activesupport (>= 4.0)
137
+ sprockets (>= 3.0.0)
138
+ sqlite3 (1.3.11)
139
+ thor (0.19.1)
140
+ thread_safe (0.3.5)
141
+ tzinfo (1.2.2)
142
+ thread_safe (~> 0.1)
143
+
144
+ PLATFORMS
145
+ ruby
146
+
147
+ DEPENDENCIES
148
+ appraisal
149
+ bundler (~> 1.11)
150
+ jsonapi_errorable!
151
+ jsonapi_spec_helpers
152
+ pry
153
+ pry-byebug
154
+ rails (~> 4.1)
155
+ rake (~> 10.0)
156
+ rspec-rails (~> 3.0)
157
+ sqlite3
158
+
159
+ BUNDLED WITH
160
+ 1.15.4
@@ -0,0 +1,13 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 5.0"
6
+
7
+ group :test do
8
+ gem "appraisal"
9
+ gem "pry"
10
+ gem "pry-byebug"
11
+ end
12
+
13
+ gemspec :path => "../"
@@ -0,0 +1,164 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ jsonapi_errorable (0.8.0)
5
+ jsonapi-serializable (~> 0.1)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ actioncable (5.0.0.1)
11
+ actionpack (= 5.0.0.1)
12
+ nio4r (~> 1.2)
13
+ websocket-driver (~> 0.6.1)
14
+ actionmailer (5.0.0.1)
15
+ actionpack (= 5.0.0.1)
16
+ actionview (= 5.0.0.1)
17
+ activejob (= 5.0.0.1)
18
+ mail (~> 2.5, >= 2.5.4)
19
+ rails-dom-testing (~> 2.0)
20
+ actionpack (5.0.0.1)
21
+ actionview (= 5.0.0.1)
22
+ activesupport (= 5.0.0.1)
23
+ rack (~> 2.0)
24
+ rack-test (~> 0.6.3)
25
+ rails-dom-testing (~> 2.0)
26
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
27
+ actionview (5.0.0.1)
28
+ activesupport (= 5.0.0.1)
29
+ builder (~> 3.1)
30
+ erubis (~> 2.7.0)
31
+ rails-dom-testing (~> 2.0)
32
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
33
+ activejob (5.0.0.1)
34
+ activesupport (= 5.0.0.1)
35
+ globalid (>= 0.3.6)
36
+ activemodel (5.0.0.1)
37
+ activesupport (= 5.0.0.1)
38
+ activerecord (5.0.0.1)
39
+ activemodel (= 5.0.0.1)
40
+ activesupport (= 5.0.0.1)
41
+ arel (~> 7.0)
42
+ activesupport (5.0.0.1)
43
+ concurrent-ruby (~> 1.0, >= 1.0.2)
44
+ i18n (~> 0.7)
45
+ minitest (~> 5.1)
46
+ tzinfo (~> 1.1)
47
+ appraisal (2.1.0)
48
+ bundler
49
+ rake
50
+ thor (>= 0.14.0)
51
+ arel (7.1.1)
52
+ builder (3.2.2)
53
+ byebug (9.0.5)
54
+ coderay (1.1.1)
55
+ concurrent-ruby (1.0.2)
56
+ diff-lcs (1.2.5)
57
+ erubis (2.7.0)
58
+ globalid (0.3.7)
59
+ activesupport (>= 4.1.0)
60
+ i18n (0.7.0)
61
+ jsonapi-renderer (0.2.0)
62
+ jsonapi-serializable (0.3.0)
63
+ jsonapi-renderer (~> 0.2.0)
64
+ jsonapi_spec_helpers (0.2.0)
65
+ loofah (2.0.3)
66
+ nokogiri (>= 1.5.9)
67
+ mail (2.6.4)
68
+ mime-types (>= 1.16, < 4)
69
+ method_source (0.8.2)
70
+ mime-types (3.1)
71
+ mime-types-data (~> 3.2015)
72
+ mime-types-data (3.2016.0521)
73
+ mini_portile2 (2.1.0)
74
+ minitest (5.9.0)
75
+ nio4r (1.2.1)
76
+ nokogiri (1.6.8)
77
+ mini_portile2 (~> 2.1.0)
78
+ pkg-config (~> 1.1.7)
79
+ pkg-config (1.1.7)
80
+ pry (0.10.4)
81
+ coderay (~> 1.1.0)
82
+ method_source (~> 0.8.1)
83
+ slop (~> 3.4)
84
+ pry-byebug (3.4.0)
85
+ byebug (~> 9.0)
86
+ pry (~> 0.10)
87
+ rack (2.0.1)
88
+ rack-test (0.6.3)
89
+ rack (>= 1.0)
90
+ rails (5.0.0.1)
91
+ actioncable (= 5.0.0.1)
92
+ actionmailer (= 5.0.0.1)
93
+ actionpack (= 5.0.0.1)
94
+ actionview (= 5.0.0.1)
95
+ activejob (= 5.0.0.1)
96
+ activemodel (= 5.0.0.1)
97
+ activerecord (= 5.0.0.1)
98
+ activesupport (= 5.0.0.1)
99
+ bundler (>= 1.3.0, < 2.0)
100
+ railties (= 5.0.0.1)
101
+ sprockets-rails (>= 2.0.0)
102
+ rails-dom-testing (2.0.1)
103
+ activesupport (>= 4.2.0, < 6.0)
104
+ nokogiri (~> 1.6.0)
105
+ rails-html-sanitizer (1.0.3)
106
+ loofah (~> 2.0)
107
+ railties (5.0.0.1)
108
+ actionpack (= 5.0.0.1)
109
+ activesupport (= 5.0.0.1)
110
+ method_source
111
+ rake (>= 0.8.7)
112
+ thor (>= 0.18.1, < 2.0)
113
+ rake (10.5.0)
114
+ rspec-core (3.5.3)
115
+ rspec-support (~> 3.5.0)
116
+ rspec-expectations (3.5.0)
117
+ diff-lcs (>= 1.2.0, < 2.0)
118
+ rspec-support (~> 3.5.0)
119
+ rspec-mocks (3.5.0)
120
+ diff-lcs (>= 1.2.0, < 2.0)
121
+ rspec-support (~> 3.5.0)
122
+ rspec-rails (3.5.1)
123
+ actionpack (>= 3.0)
124
+ activesupport (>= 3.0)
125
+ railties (>= 3.0)
126
+ rspec-core (~> 3.5.0)
127
+ rspec-expectations (~> 3.5.0)
128
+ rspec-mocks (~> 3.5.0)
129
+ rspec-support (~> 3.5.0)
130
+ rspec-support (3.5.0)
131
+ slop (3.6.0)
132
+ sprockets (3.7.0)
133
+ concurrent-ruby (~> 1.0)
134
+ rack (> 1, < 3)
135
+ sprockets-rails (3.2.0)
136
+ actionpack (>= 4.0)
137
+ activesupport (>= 4.0)
138
+ sprockets (>= 3.0.0)
139
+ sqlite3 (1.3.11)
140
+ thor (0.19.1)
141
+ thread_safe (0.3.5)
142
+ tzinfo (1.2.2)
143
+ thread_safe (~> 0.1)
144
+ websocket-driver (0.6.4)
145
+ websocket-extensions (>= 0.1.0)
146
+ websocket-extensions (0.1.2)
147
+
148
+ PLATFORMS
149
+ ruby
150
+
151
+ DEPENDENCIES
152
+ appraisal
153
+ bundler (~> 1.11)
154
+ jsonapi_errorable!
155
+ jsonapi_spec_helpers
156
+ pry
157
+ pry-byebug
158
+ rails (~> 5.0)
159
+ rake (~> 10.0)
160
+ rspec-rails (~> 3.0)
161
+ sqlite3
162
+
163
+ BUNDLED WITH
164
+ 1.15.4
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'graphiti_errors/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "graphiti_errors"
8
+ spec.version = GraphitiErrors::VERSION
9
+ spec.authors = ["Lee Richmond"]
10
+ spec.email = ["lrichmond1@bloomberg.net"]
11
+
12
+ spec.summary = %q{Error-handling patterns for JSONAPIs}
13
+ spec.description = %q{Handles application errors and model validations}
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'jsonapi-serializable', '~> 0.1'
22
+
23
+ # Rails is added in Appraisals
24
+ spec.add_development_dependency "bundler", "~> 1.11"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "rspec-rails", "~> 3.0"
27
+ spec.add_development_dependency "sqlite3"
28
+ spec.add_development_dependency "graphiti_spec_helpers"
29
+ end
@@ -0,0 +1,95 @@
1
+ module GraphitiErrors
2
+ class ExceptionHandler
3
+ attr_accessor :show_raw_error
4
+
5
+ def initialize(options = {})
6
+ @status = options[:status]
7
+ @title = options[:title]
8
+ @message = options[:message]
9
+ @meta = options[:meta]
10
+ @log = options[:log]
11
+ end
12
+
13
+ def status_code(error)
14
+ @status || 500
15
+ end
16
+
17
+ def error_code(error)
18
+ status_code = status_code(error)
19
+ Rack::Utils::SYMBOL_TO_STATUS_CODE.invert[status_code]
20
+ end
21
+
22
+ def backtrace_cleaner
23
+ defined?(Rails) ? Rails.backtrace_cleaner : nil
24
+ end
25
+
26
+ def title
27
+ @title || 'Error'
28
+ end
29
+
30
+ def detail(error)
31
+ if @message == true
32
+ error.message
33
+ else
34
+ @message ? @message.call(error) : default_detail
35
+ end
36
+ end
37
+
38
+ def meta(error)
39
+ {}.tap do |meta_payload|
40
+ if @meta.respond_to?(:call)
41
+ meta_payload.merge!(@meta.call(error))
42
+ end
43
+
44
+ if show_raw_error
45
+ meta_payload[:__raw_error__] = {
46
+ message: error.message,
47
+ backtrace: error.backtrace
48
+ }
49
+ end
50
+ end
51
+ end
52
+
53
+ def error_payload(error)
54
+ {
55
+ errors: [
56
+ code: error_code(error),
57
+ status: status_code(error).to_s,
58
+ title: title,
59
+ detail: detail(error),
60
+ meta: meta(error)
61
+ ]
62
+ }
63
+ end
64
+
65
+ def log?
66
+ @log != false
67
+ end
68
+
69
+ def log(error)
70
+ return unless log?
71
+ backtrace = error.backtrace
72
+
73
+ if cleaner = backtrace_cleaner
74
+ backtrace = cleaner.clean(backtrace)
75
+ end
76
+
77
+ log_error(error, backtrace)
78
+ end
79
+
80
+ private
81
+
82
+ def log_error(e, backtrace)
83
+ logger.error "\033[31mERROR: #{e.class}: #{e.message}\033[0m"
84
+ logger.error "\033[31m#{backtrace.join("\n")}\033[0m"
85
+ end
86
+
87
+ def logger
88
+ GraphitiErrors.logger
89
+ end
90
+
91
+ def default_detail
92
+ "We've notified our engineers and hope to address this issue shortly."
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,151 @@
1
+ module GraphitiErrors
2
+ module Serializers
3
+ class Validation
4
+ attr_reader :object
5
+
6
+ def initialize(object, relationship_payloads = {}, relationship_meta = {})
7
+ @object = object
8
+ @relationship_payloads = relationship_payloads
9
+ @relationship_meta = relationship_meta
10
+ end
11
+
12
+ def attribute_errors
13
+ [].tap do |errors|
14
+ each_error do |attribute, message, code|
15
+ error = {
16
+ code: 'unprocessable_entity',
17
+ status: '422',
18
+ title: 'Validation Error',
19
+ detail: detail_for(attribute, message),
20
+ source: { pointer: pointer_for(object, attribute) },
21
+ meta: meta_for(attribute, message, code, @relationship_meta)
22
+ }
23
+
24
+ errors << error
25
+ end
26
+ end
27
+ end
28
+
29
+ def errors
30
+ return [] unless object.respond_to?(:errors)
31
+
32
+ all_errors = attribute_errors
33
+ all_errors |= relationship_errors(@relationship_payloads)
34
+ all_errors
35
+ end
36
+
37
+ private
38
+
39
+ def each_error
40
+ object.errors.messages.each_pair do |attribute, messages|
41
+ details = if Rails::VERSION::MAJOR >= 5
42
+ object.errors.details.find { |k,v| k == attribute }[1]
43
+ end
44
+
45
+ messages.each_with_index do |message, index|
46
+ code = details[index][:error] if details
47
+ yield attribute, message, code
48
+ end
49
+ end
50
+ end
51
+
52
+ def relationship?(name)
53
+ relationship_names = []
54
+ if activerecord?
55
+ relationship_names = object.class
56
+ .reflect_on_all_associations.map(&:name)
57
+ elsif object.respond_to?(:relationship_names)
58
+ relationship_names = object.relationship_names
59
+ end
60
+
61
+ relationship_names.include?(name)
62
+ end
63
+
64
+ def attribute?(name)
65
+ object.respond_to?(name)
66
+ end
67
+
68
+ def meta_for(attribute, message, code, relationship_meta)
69
+ meta = {
70
+ attribute: attribute,
71
+ message: message
72
+ }
73
+ meta.merge!(code: code) if Rails::VERSION::MAJOR >= 5
74
+
75
+ unless relationship_meta.empty?
76
+ meta = {
77
+ relationship: meta.merge(relationship_meta)
78
+ }
79
+ end
80
+
81
+ meta
82
+ end
83
+
84
+ def detail_for(attribute, message)
85
+ detail = object.errors.full_message(attribute, message)
86
+ detail = message if attribute.to_s.downcase == 'base'
87
+ detail
88
+ end
89
+
90
+ # @richmolj: Keeping this to support ember-data, but I hate the concept.
91
+ def pointer_for(object, name)
92
+ if relationship?(name)
93
+ "/data/relationships/#{name}"
94
+ elsif attribute?(name)
95
+ "/data/attributes/#{name}"
96
+ elsif name == :base
97
+ nil
98
+ else
99
+ # Probably a nested relation, like post.comments
100
+ "/data/relationships/#{name}"
101
+ end
102
+ end
103
+
104
+ def activerecord?
105
+ object.class.respond_to?(:reflect_on_all_associations)
106
+ end
107
+
108
+ def traverse_relationships(relationship_params)
109
+ return unless relationship_params
110
+
111
+ relationship_params.each_pair do |name, payload|
112
+ relationship_objects = Array(@object.send(name))
113
+
114
+ relationship_objects.each do |relationship_object|
115
+ related_payload = payload
116
+ if payload.is_a?(Array)
117
+ related_payload = payload.find do |p|
118
+ temp_id = relationship_object
119
+ .instance_variable_get(:@_jsonapi_temp_id)
120
+ p[:meta][:temp_id] === temp_id ||
121
+ p[:meta][:id] == relationship_object.id.to_s
122
+ end
123
+ end
124
+
125
+ yield name, relationship_object, related_payload
126
+ relationship_errors(related_payload[:relationships])
127
+ end
128
+ end
129
+ end
130
+
131
+ def relationship_errors(relationship_payloads)
132
+ errors = []
133
+ traverse_relationships(relationship_payloads) do |name, model, payload|
134
+ meta = {}.tap do |hash|
135
+ hash[:name] = name
136
+ hash[:type] = payload[:meta][:jsonapi_type]
137
+ if temp_id = model.instance_variable_get(:@_jsonapi_temp_id)
138
+ hash[:'temp-id'] = temp_id
139
+ else
140
+ hash[:id] = model.id
141
+ end
142
+ end
143
+
144
+ serializer = self.class.new(model, payload[:relationships], meta)
145
+ errors |= serializer.errors
146
+ end
147
+ errors
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,12 @@
1
+ module GraphitiErrors
2
+ module Validatable
3
+ def render_errors_for(record)
4
+ validation = Serializers::Validation.new \
5
+ record, deserialized_params.relationships
6
+
7
+ render \
8
+ json: { errors: validation.errors },
9
+ status: :unprocessable_entity
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ module GraphitiErrors
2
+ VERSION = "1.0.alpha.2"
3
+ end
@@ -0,0 +1,73 @@
1
+ require 'jsonapi/serializable'
2
+
3
+ require 'graphiti_errors/version'
4
+ require 'graphiti_errors/exception_handler'
5
+ require 'graphiti_errors/validatable'
6
+ require 'graphiti_errors/serializers/validation'
7
+
8
+ module GraphitiErrors
9
+ def self.included(klass)
10
+ klass.class_eval do
11
+ class << self
12
+ attr_accessor :_errorable_registry
13
+ end
14
+
15
+ def self.inherited(subklass)
16
+ subklass._errorable_registry = self._errorable_registry.dup
17
+ end
18
+ end
19
+ klass._errorable_registry = {}
20
+ klass.send(:include, Validatable)
21
+ klass.extend ClassMethods
22
+ end
23
+
24
+ def self.disable!
25
+ @enabled = false
26
+ end
27
+
28
+ def self.enable!
29
+ @enabled = true
30
+ end
31
+
32
+ def self.disabled?
33
+ @enabled == false
34
+ end
35
+
36
+ def self.logger
37
+ @logger ||= defined?(Rails) ? Rails.logger : Logger.new($stdout)
38
+ end
39
+
40
+ def self.logger=(logger)
41
+ @logger = logger
42
+ end
43
+
44
+ def handle_exception(e, show_raw_error: false)
45
+ raise e if GraphitiErrors.disabled?
46
+
47
+ exception_klass = self.class._errorable_registry[e.class] || default_exception_handler.new
48
+ exception_klass.show_raw_error = show_raw_error
49
+ exception_klass.log(e)
50
+ json = exception_klass.error_payload(e)
51
+ status = exception_klass.status_code(e)
52
+ render json: json, status: status
53
+ end
54
+
55
+ def default_exception_handler
56
+ self.class.default_exception_handler
57
+ end
58
+
59
+ def registered_exception?(e)
60
+ self.class._errorable_registry.key?(e.class)
61
+ end
62
+
63
+ module ClassMethods
64
+ def register_exception(klass, options = {})
65
+ exception_klass = options[:handler] || default_exception_handler
66
+ self._errorable_registry[klass] = exception_klass.new(options)
67
+ end
68
+
69
+ def default_exception_handler
70
+ GraphitiErrors::ExceptionHandler
71
+ end
72
+ end
73
+ end
metadata ADDED
@@ -0,0 +1,151 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: graphiti_errors
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.alpha.2
5
+ platform: ruby
6
+ authors:
7
+ - Lee Richmond
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-08-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: jsonapi-serializable
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.11'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.11'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec-rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: sqlite3
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: graphiti_spec_helpers
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Handles application errors and model validations
98
+ email:
99
+ - lrichmond1@bloomberg.net
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".gitignore"
105
+ - ".rspec"
106
+ - ".ruby-version"
107
+ - ".travis.yml"
108
+ - Appraisals
109
+ - Gemfile
110
+ - LICENSE.txt
111
+ - README.md
112
+ - Rakefile
113
+ - bin/appraisal
114
+ - bin/console
115
+ - bin/rspec
116
+ - bin/setup
117
+ - gemfiles/rails_4.gemfile
118
+ - gemfiles/rails_4.gemfile.lock
119
+ - gemfiles/rails_5.gemfile
120
+ - gemfiles/rails_5.gemfile.lock
121
+ - graphiti_errors.gemspec
122
+ - lib/graphiti_errors.rb
123
+ - lib/graphiti_errors/exception_handler.rb
124
+ - lib/graphiti_errors/serializers/validation.rb
125
+ - lib/graphiti_errors/validatable.rb
126
+ - lib/graphiti_errors/version.rb
127
+ homepage:
128
+ licenses:
129
+ - MIT
130
+ metadata: {}
131
+ post_install_message:
132
+ rdoc_options: []
133
+ require_paths:
134
+ - lib
135
+ required_ruby_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ required_rubygems_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">"
143
+ - !ruby/object:Gem::Version
144
+ version: 1.3.1
145
+ requirements: []
146
+ rubyforge_project:
147
+ rubygems_version: 2.6.12
148
+ signing_key:
149
+ specification_version: 4
150
+ summary: Error-handling patterns for JSONAPIs
151
+ test_files: []