graphiti_errors 1.0.alpha.2

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.
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: []