human_error 1.13.4 → 2.0.0beta2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4ec632d4d98534bf56ffb31036e08757bc571329
4
- data.tar.gz: 0ccf6401fb434ed5439bd950cea70e19538c25a5
3
+ metadata.gz: 38bd2094b46916777e66209fd09752aec12782c6
4
+ data.tar.gz: 5e8539f5cf3898079f28f3a6014aca66f7d09d79
5
5
  SHA512:
6
- metadata.gz: 659545cf72c6db3d1cea151d9516d00c5c6c4d9e27bde9403cf352dfda4591a838476cb435f81344d5525ba8436032969a329c24cbfd7db95ad93b582dc25d9c
7
- data.tar.gz: 9f54d8085cdf37151658b1ac6112b4c5b2da2057f8707251443ea29f9525d889b415247dcbf738ec954840bd1b3842a7061a4cd0efba0e62273a535307026fdb
6
+ metadata.gz: 7395626c161f3012d42c7cf0de4d934bf77cf34447bb192d4b06f004efc80e11dfbd6f910cb7b1efb4a5c53759f4a114e5f535242d32073a3e2714eb83cea386
7
+ data.tar.gz: b1dbf433d0e8725ef3ac2192dc2bb6afc8e3bc49078eb43fd5309334959d0ac76b58fa41f1b9426189309304c4ca391686c32d9a62ea207f2654c9c51a74aa50
@@ -10,6 +10,25 @@ class AssociationError < RequestError
10
10
  :association_id,
11
11
  :attributes
12
12
 
13
+ def self.convert(original_error, overrides = {})
14
+ initialization_parameters = {}
15
+
16
+ case original_error.class.name
17
+ when 'ActiveRecord::InvalidForeignKey'
18
+ message_info_pattern = /DETAIL: Key \((.*)_id\)=\(([a-f0-9\-]+)\)/
19
+ message_info = original_error.
20
+ message.
21
+ match(message_info_pattern)[1..-1]
22
+
23
+ initialization_parameters = {
24
+ association_name: message_info[0],
25
+ association_id: message_info[1],
26
+ }
27
+ end
28
+
29
+ new(initialization_parameters.merge(overrides))
30
+ end
31
+
13
32
  def http_status
14
33
  422
15
34
  end
@@ -6,6 +6,26 @@ module Errors
6
6
  class ResourceNotFoundError < RequestError
7
7
  include CrudError
8
8
 
9
+ def self.convert(original_error, overrides = {})
10
+ initialization_parameters = {}
11
+
12
+ case original_error.class.name
13
+ when 'ActiveRecord::RecordNotFound'
14
+ initialization_parameters = {
15
+ resource_id: case original_error.message
16
+ when /\ACouldn't find .* without an ID\z/
17
+ []
18
+ when /\ACouldn't find .* with \'.*\'=([a-f0-9\-]+)/
19
+ [Regexp.last_match(1)]
20
+ when /\ACouldn't find all .* with \'.*\': ((?:[a-f0-9\-]+(?:, )?)+)/
21
+ Array(Regexp.last_match(1).split(', '))
22
+ end,
23
+ }
24
+ end
25
+
26
+ new(initialization_parameters.merge(overrides))
27
+ end
28
+
9
29
  def http_status
10
30
  404
11
31
  end
@@ -9,6 +9,22 @@ class ResourcePersistenceError < RequestError
9
9
  attr_accessor :errors,
10
10
  :attributes
11
11
 
12
+ def self.convert(original_error, overrides = {})
13
+ initialization_parameters = {}
14
+
15
+ case original_error.class.name
16
+ when 'ActiveRecord::RecordInvalid',
17
+ 'ActiveRecord::RecordNotSaved'
18
+
19
+ initialization_parameters = {
20
+ attributes: original_error.record.attributes,
21
+ errors: original_error.record.errors.full_messages,
22
+ }
23
+ end
24
+
25
+ new(initialization_parameters.merge(overrides))
26
+ end
27
+
12
28
  def http_status
13
29
  422
14
30
  end
@@ -6,49 +6,41 @@ class HumanError
6
6
  module RescuableResource
7
7
  module ClassMethods
8
8
  # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Style/GuardClause
9
- def rescue_resource(_resource_name, from:, via:)
10
- lookup_library = via
9
+ def rescue_resource(resource_name, from:, via:)
10
+ resource_class_name = resource_name.classify
11
+ lookup_library = via
11
12
 
12
13
  if from.include? 'persistence'
13
- rescue_from HumanError::Errors::ResourcePersistenceError do |e|
14
- error = lookup_library.fetch(
15
- 'ResourcePersistenceError',
16
- resource_name: e.resource_name,
17
- attributes: e.attributes,
18
- errors: e.errors,
19
- action: action_name)
20
-
21
- render json: error,
22
- status: error.http_status
14
+ rescue_from 'ActiveRecord::RecordInvalid',
15
+ 'ActiveRecord::RecordNotSaved' do |exception|
16
+ human_error = lookup_library.convert(exception,
17
+ resource_name: resource_class_name,
18
+ action: action_name)
19
+
20
+ render json: human_error,
21
+ status: human_error.http_status
23
22
  end
24
23
  end
25
24
 
26
25
  if from.include? 'not_found'
27
- rescue_from HumanError::Errors::ResourceNotFoundError do |e|
28
- resource_id = e.resource_id.is_a?(Array) ? e.resource_id : [e.resource_id]
26
+ rescue_from 'ActiveRecord::RecordNotFound' do |exception|
27
+ human_error = lookup_library.convert(exception,
28
+ resource_name: resource_class_name,
29
+ action: action_name)
29
30
 
30
- error = lookup_library.fetch(
31
- 'ResourceNotFoundError',
32
- resource_name: e.resource_name,
33
- resource_id: resource_id,
34
- action: action_name)
35
-
36
- render json: error,
37
- status: error.http_status
31
+ render json: human_error,
32
+ status: human_error.http_status
38
33
  end
39
34
  end
40
35
 
41
36
  if from.include? 'association'
42
- rescue_from HumanError::Errors::AssociationError do |e|
43
- error = lookup_library.fetch(
44
- 'AssociationError',
45
- resource_name: e.resource_name,
46
- association_name: e.association_name,
47
- association_id: e.association_id,
48
- attributes: e.attributes)
49
-
50
- render json: error,
51
- status: error.http_status
37
+ rescue_from 'ActiveRecord::InvalidForeignKey' do |exception|
38
+ human_error = lookup_library.convert(exception,
39
+ resource_name: resource_class_name,
40
+ action: action_name)
41
+
42
+ render json: human_error,
43
+ status: human_error.http_status
52
44
  end
53
45
  end
54
46
  end
@@ -1,3 +1,3 @@
1
1
  class HumanError
2
- VERSION = '1.13.4'
2
+ VERSION = '2.0.0beta2'
3
3
  end
data/lib/human_error.rb CHANGED
@@ -11,9 +11,19 @@ class HumanError
11
11
  yield configuration if block_given?
12
12
  end
13
13
 
14
- def fetch(error_type, **args)
15
- Object.const_get("HumanError::Errors::#{error_type}").
16
- new(configuration.to_h.merge(**args))
14
+ def fetch(error_type, **_args)
15
+ Object.const_get("HumanError::Errors::#{error_type}")
16
+ end
17
+
18
+ def convert(original_error, overrides = {})
19
+ overrides = configuration.to_h.merge(overrides)
20
+
21
+ case original_error.class.name
22
+ when 'ActiveRecord::InvalidForeignKey'
23
+ fetch('AssociationError').convert(original_error, overrides)
24
+ when 'ActiveRecord::RecordNotFound'
25
+ fetch('ResourceNotFoundError').convert(original_error, overrides)
26
+ end
17
27
  end
18
28
 
19
29
  def raise(error_type, **args)
@@ -1,20 +1,29 @@
1
1
  require 'rspectacular'
2
2
  require 'human_error/errors/crud_errors/association_error'
3
+ require 'active_record/errors'
3
4
 
4
5
  class HumanError
5
6
  module Errors
6
7
  describe AssociationError do
7
- let(:error) { AssociationError.new }
8
+ let(:foreign_key_error) do
9
+ ActiveRecord::InvalidForeignKey.new('DETAIL: Key (resource_id)=(123)')
10
+ end
8
11
 
9
12
  it 'has a status of 422' do
13
+ error = AssociationError.new
14
+
10
15
  expect(error.http_status).to eql 422
11
16
  end
12
17
 
13
18
  it 'has a code of 1009' do
19
+ error = AssociationError.new
20
+
14
21
  expect(error.code).to eql 1009
15
22
  end
16
23
 
17
24
  it 'has a knowledgebase article ID of 1234567890' do
25
+ error = AssociationError.new
26
+
18
27
  expect(error.knowledgebase_article_id).to eql '1234567890'
19
28
  end
20
29
 
@@ -46,6 +55,22 @@ describe AssociationError do
46
55
  expect(error.friendly_message).to eql 'Sorry! There was a problem when we tried to ' \
47
56
  'set the black leather trenchcoat on that Neo.'
48
57
  end
58
+
59
+ it 'can convert an "ActiveRecord::InvalidForeignKey"' do
60
+ error = AssociationError.convert(foreign_key_error)
61
+
62
+ expect(error.resource_name).to eql nil
63
+ expect(error.association_name).to eql 'resource'
64
+ expect(error.association_id).to eql '123'
65
+ end
66
+
67
+ it 'can convert an "ActiveRecord::InvalidForeignKey" while overriding attributes' do
68
+ error = AssociationError.convert(foreign_key_error, resource_name: 'my_resource')
69
+
70
+ expect(error.resource_name).to eql 'my_resource'
71
+ expect(error.association_name).to eql 'resource'
72
+ expect(error.association_id).to eql '123'
73
+ end
49
74
  end
50
75
  end
51
76
  end
@@ -1,20 +1,25 @@
1
1
  require 'rspectacular'
2
2
  require 'human_error/errors/crud_errors/resource_not_found_error'
3
+ require 'active_record/errors'
3
4
 
4
5
  class HumanError
5
6
  module Errors
6
7
  describe ResourceNotFoundError do
7
- let(:error) { ResourceNotFoundError.new }
8
-
9
8
  it 'has a status of 404' do
9
+ error = ResourceNotFoundError.new
10
+
10
11
  expect(error.http_status).to eql 404
11
12
  end
12
13
 
13
14
  it 'has a code of 1005' do
15
+ error = ResourceNotFoundError.new
16
+
14
17
  expect(error.code).to eql 1005
15
18
  end
16
19
 
17
20
  it 'has a knowledgebase article ID of 1234567890' do
21
+ error = ResourceNotFoundError.new
22
+
18
23
  expect(error.knowledgebase_article_id).to eql '1234567890'
19
24
  end
20
25
 
@@ -49,6 +54,43 @@ describe ResourceNotFoundError do
49
54
  expect(error.friendly_message).to eql 'Sorry! The black leather trenchcoat you ' \
50
55
  'tried to bullet time does not exist.'
51
56
  end
57
+
58
+ it 'can convert an "ActiveRecord::RecordNotFound" with no IDs' do
59
+ record_not_found_error = ActiveRecord::RecordNotFound.new \
60
+ "Couldn't find resource without an ID"
61
+ error = ResourceNotFoundError.convert(record_not_found_error)
62
+
63
+ expect(error.resource_name).to eql nil
64
+ expect(error.resource_id).to eql []
65
+ end
66
+
67
+ it 'can convert an "ActiveRecord::RecordNotFound" with one ID' do
68
+ record_not_found_error = ActiveRecord::RecordNotFound.new \
69
+ "Couldn't find resource with 'id'=123"
70
+ error = ResourceNotFoundError.convert(record_not_found_error)
71
+
72
+ expect(error.resource_name).to eql nil
73
+ expect(error.resource_id).to eql %w{123}
74
+ end
75
+
76
+ it 'can convert an "ActiveRecord::RecordNotFound" with multiple IDs' do
77
+ record_not_found_error = ActiveRecord::RecordNotFound.new \
78
+ "Couldn't find all resource with 'id': 123, 456, 789"
79
+ error = ResourceNotFoundError.convert(record_not_found_error)
80
+
81
+ expect(error.resource_name).to eql nil
82
+ expect(error.resource_id).to eql %w{123 456 789}
83
+ end
84
+
85
+ it 'can convert an "ActiveRecord::RecordNotFound" while overriding attributes' do
86
+ record_not_found_error = ActiveRecord::RecordNotFound.new \
87
+ "Couldn't find resource with 'id'=123"
88
+ error = ResourceNotFoundError.convert(record_not_found_error,
89
+ resource_name: 'my_resource')
90
+
91
+ expect(error.resource_name).to eql 'my_resource'
92
+ expect(error.resource_id).to eql %w{123}
93
+ end
52
94
  end
53
95
  end
54
96
  end
@@ -1,20 +1,43 @@
1
1
  require 'rspectacular'
2
2
  require 'human_error/errors/crud_errors/resource_persistence_error'
3
+ require 'active_support'
4
+ require 'active_model'
5
+ require 'active_record/errors'
6
+ require 'active_record/validations'
7
+
8
+ class HumanErrorTestModel
9
+ include ActiveModel::Model
10
+ include ActiveModel::AttributeMethods
11
+
12
+ attr_accessor :some_attribute
13
+
14
+ validates_presence_of :some_attribute
15
+
16
+ def attributes
17
+ {
18
+ 'some_attribute' => @some_attribute,
19
+ }
20
+ end
21
+ end
3
22
 
4
23
  class HumanError
5
24
  module Errors
6
25
  describe ResourcePersistenceError do
7
- let(:error) { ResourcePersistenceError.new }
8
-
9
26
  it 'has a status of 422' do
27
+ error = ResourcePersistenceError.new
28
+
10
29
  expect(error.http_status).to eql 422
11
30
  end
12
31
 
13
32
  it 'has a code of 1006' do
33
+ error = ResourcePersistenceError.new
34
+
14
35
  expect(error.code).to eql 1006
15
36
  end
16
37
 
17
38
  it 'has a knowledgebase article ID of 1234567890' do
39
+ error = ResourcePersistenceError.new
40
+
18
41
  expect(error.knowledgebase_article_id).to eql '1234567890'
19
42
  end
20
43
 
@@ -42,6 +65,28 @@ describe ResourcePersistenceError do
42
65
  expect(error.friendly_message).to eql 'Sorry! We had a problem when tried to ' \
43
66
  'bullet time that black leather trenchcoat.'
44
67
  end
68
+
69
+ it 'can convert an "ActiveRecord::RecordNotSaved"' do
70
+ record = HumanErrorTestModel.new
71
+ record.valid?
72
+ resource_persistence_error = ActiveRecord::RecordNotSaved.new('message', record)
73
+ error = ResourcePersistenceError.
74
+ convert(resource_persistence_error)
75
+
76
+ expect(error.attributes).to eql('some_attribute' => nil)
77
+ expect(error.errors).to eql ["Some attribute can't be blank"]
78
+ end
79
+
80
+ it 'can convert an "ActiveRecord::RecordInvalid"' do
81
+ record = HumanErrorTestModel.new
82
+ record.valid?
83
+ resource_persistence_error = ActiveRecord::RecordInvalid.new(record)
84
+ error = ResourcePersistenceError.
85
+ convert(resource_persistence_error)
86
+
87
+ expect(error.attributes).to eql('some_attribute' => nil)
88
+ expect(error.errors).to eql ["Some attribute can't be blank"]
89
+ end
45
90
  end
46
91
  end
47
92
  end
@@ -1,7 +1,12 @@
1
1
  require 'rspectacular'
2
2
  require 'human_error'
3
+ require 'active_record/errors'
3
4
 
4
5
  describe HumanError do
6
+ let(:original_error) do
7
+ ActiveRecord::RecordNotFound.new("Couldn't find resource with 'id'=3")
8
+ end
9
+
5
10
  it 'can create an instance of HumanError' do
6
11
  expect(HumanError.new).to be_a HumanError
7
12
  end
@@ -17,7 +22,7 @@ describe HumanError do
17
22
  it 'can lookup errors' do
18
23
  human_error = HumanError.new
19
24
 
20
- expect(human_error.fetch('RequestError')).to be_a HumanError::Errors::RequestError
25
+ expect(human_error.fetch('RequestError')).to eql HumanError::Errors::RequestError
21
26
  end
22
27
 
23
28
  it 'can lookup errors based on the local configuration' do
@@ -25,7 +30,7 @@ describe HumanError do
25
30
  config.api_version = 'foo'
26
31
  end
27
32
 
28
- fetched_error = human_error.fetch('RequestError')
33
+ fetched_error = human_error.convert(original_error)
29
34
 
30
35
  expect(fetched_error.api_version).to eql 'foo'
31
36
  end
@@ -41,7 +46,7 @@ describe HumanError do
41
46
  config.api_version = 'foo'
42
47
  end
43
48
 
44
- fetched_error = human_error.fetch('RequestError')
49
+ fetched_error = human_error.convert(original_error)
45
50
 
46
51
  expect(fetched_error.api_version).to eql 'foo'
47
52
  end
@@ -53,7 +58,7 @@ describe HumanError do
53
58
  config.api_version = 'foo'
54
59
  end
55
60
 
56
- fetched_error = human_error.fetch('RequestError', api_version: 'bar')
61
+ fetched_error = human_error.convert(original_error, api_version: 'bar')
57
62
 
58
63
  expect(fetched_error.api_version).to eql 'bar'
59
64
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: human_error
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.13.4
4
+ version: 2.0.0beta2
5
5
  platform: ruby
6
6
  authors:
7
7
  - jfelchner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-26 00:00:00.000000000 Z
11
+ date: 2015-04-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -39,19 +39,19 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0.50'
41
41
  - !ruby/object:Gem::Dependency
42
- name: codeclimate-test-reporter
42
+ name: activerecord
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 0.3.0
47
+ version: '4.1'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 0.3.0
54
+ version: '4.1'
55
55
  description: ''
56
56
  email: accounts+git@thekompanee.com
57
57
  executables: []
@@ -76,7 +76,6 @@ files:
76
76
  - lib/human_error/errors/crud_errors/resource_persistence_error.rb
77
77
  - lib/human_error/errors/request_error.rb
78
78
  - lib/human_error/knowledgebase_id_directory.rb
79
- - lib/human_error/persistable.rb
80
79
  - lib/human_error/rescuable_resource.rb
81
80
  - lib/human_error/utilities/string.rb
82
81
  - lib/human_error/version.rb
@@ -106,9 +105,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
106
105
  version: '0'
107
106
  required_rubygems_version: !ruby/object:Gem::Requirement
108
107
  requirements:
109
- - - ">="
108
+ - - ">"
110
109
  - !ruby/object:Gem::Version
111
- version: '0'
110
+ version: 1.3.1
112
111
  requirements: []
113
112
  rubyforge_project:
114
113
  rubygems_version: 2.4.6
@@ -1,56 +0,0 @@
1
- class HumanError
2
- module Persistable
3
- module ClassMethods
4
- def find(*ids)
5
- super
6
- rescue ActiveRecord::RecordNotFound => e
7
- ids = case e.message
8
- when /\ACouldn't find .* without an ID\z/
9
- []
10
- when /\ACouldn't find .* with \'.*\'=([a-f0-9\-]+)/
11
- [Regexp.last_match(1)]
12
- when /\ACouldn't find all .* with \'.*\': ((?:[a-f0-9\-]+(?:, )?)+)/
13
- Regexp.last_match(1).split(', ')
14
- end
15
-
16
- raise HumanError::Errors::ResourceNotFoundError.new(
17
- resource_name: Persistable.human_error_resource_name(self),
18
- resource_id: ids)
19
- end
20
- end
21
-
22
- def save!(*args)
23
- super
24
- rescue ActiveRecord::InvalidForeignKey => e
25
- association_info_pattern = /DETAIL: Key \((.*)_id\)=\(([a-f0-9\-]+)\)/
26
- association_name, association_id = e.message.
27
- match(association_info_pattern) \
28
- [1..-1]
29
-
30
- raise HumanError::Errors::AssociationError.new(
31
- resource_name: Persistable.human_error_resource_name(self.class),
32
- association_name: association_name,
33
- association_id: association_id,
34
- attributes: attributes)
35
- rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotSaved
36
- raise HumanError::Errors::ResourcePersistenceError.new(
37
- resource_name: Persistable.human_error_resource_name(self.class),
38
- attributes: attributes,
39
- errors: errors.full_messages)
40
- end
41
-
42
- def self.human_error_resource_name(klass)
43
- last_part_of_class_name = /::(\w+)\z/
44
-
45
- klass.
46
- name[last_part_of_class_name, 1].
47
- underscore.
48
- humanize.
49
- downcase
50
- end
51
-
52
- def self.included(base)
53
- base.extend ClassMethods
54
- end
55
- end
56
- end