json_error 0.1.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +6 -0
- data/README.md +68 -19
- data/bin/console +2 -0
- data/lib/json_error/base.rb +83 -26
- data/lib/json_error/errors/disallowed_property_setting.rb +11 -0
- data/lib/json_error/version.rb +1 -1
- data/lib/json_error.rb +1 -0
- data/spec/fixtures/json_error_base_children.rb +18 -0
- data/spec/fixtures/locales/en.yml +7 -1
- data/spec/json_error/base_children_spec.rb +274 -0
- data/spec/json_error/base_spec.rb +76 -68
- data/spec/json_error/errors/disallowed_property_setting.rb +21 -0
- data/spec/spec_helper.rb +2 -1
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a4322948d35b22110cc1c146b0da8bd3d0f16f94
|
4
|
+
data.tar.gz: 55b445035a7813c5e4d73a12e1c270696aac45b9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e8dcdc4ca99ac2245247d35cbecfd2dc1e5e2ced09fd8fcfa303a2a699bd33868dc03fff3cd99daaef8bb734eb24fe19e2dc8c78e092046d54a3a3251241b4d7
|
7
|
+
data.tar.gz: fc4668dd5daca19f4a389d0b455617b3ae7958bf5461f5d7c2a5f01e207df5c04a951495a1e3adaf0117b250bfb5638af8ffd289d1d300c30f1025ecdd7c46c2
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# JSONError
|
2
2
|
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/json_error.svg)](https://badge.fury.io/rb/json_error)
|
3
4
|
[![Code Climate](https://codeclimate.com/github/buszu/json_error/badges/gpa.svg)](https://codeclimate.com/github/buszu/json_error)
|
4
5
|
|
5
6
|
## Installation
|
@@ -7,46 +8,95 @@
|
|
7
8
|
Add this line to your application's Gemfile:
|
8
9
|
|
9
10
|
```ruby
|
10
|
-
gem 'json_error'
|
11
|
+
gem 'json_error'
|
11
12
|
```
|
12
13
|
|
13
14
|
And then execute:
|
14
15
|
|
15
16
|
$ bundle
|
16
17
|
|
18
|
+
## Introduction
|
19
|
+
|
20
|
+
### What is JSONError gem?
|
21
|
+
It provides parent class with simple DSL to define API errors. It is based on concept, that error is something that went wrong (_what? = key_), in some circumstances (_when? = context_).
|
22
|
+
|
23
|
+
### Why would I use it?
|
24
|
+
To design, manage and maintain API's errors JSON responses with ease.
|
25
|
+
|
26
|
+
### How to do it?
|
27
|
+
Using locale file (e.g. json_errors.en.yml).
|
28
|
+
|
29
|
+
```yml
|
30
|
+
# json_errors.en.yml example
|
31
|
+
en:
|
32
|
+
json_errors:
|
33
|
+
resource_not_found:
|
34
|
+
status: 404
|
35
|
+
message: 'Resource %{name} (%{id}) was not found.'
|
36
|
+
description: 'Blah blah blah.'
|
37
|
+
href: 'https://developers.api.pl/doc/errors/resource_not_found'
|
38
|
+
batmans:
|
39
|
+
resource_not_found:
|
40
|
+
status: 404
|
41
|
+
message: 'Resource Batman (%{id}) was not found.'
|
42
|
+
description: "Cuz I'm Batman"
|
43
|
+
```
|
44
|
+
|
17
45
|
## Usage
|
18
46
|
|
19
|
-
###
|
47
|
+
### Understading JSONError
|
48
|
+
|
49
|
+
Let's take a look at example error class definition.
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
class Error < JSONError.base
|
53
|
+
key :error
|
54
|
+
translated_properties :message, :href
|
55
|
+
custom_properties :details, :some_dependency
|
56
|
+
visible_properties :key, :status, :message, :href, :details
|
57
|
+
end
|
58
|
+
```
|
59
|
+
|
60
|
+
*Key* is the error identifier. It's not obligatory to specify its value here - you can also and pass it to the Error initializer when instantiating error object:
|
61
|
+
```ruby
|
62
|
+
Error.new(key: :error)
|
63
|
+
```
|
64
|
+
however, json errors must have a key. Key is not inherited.
|
65
|
+
|
66
|
+
*Default properties* are properties included to JSON output by default (:key, status: 500). You can change status in locale.
|
67
|
+
|
68
|
+
*Translated properties* are error attributes which values will be loaded from locale. Translated properties can be inherited.
|
69
|
+
|
70
|
+
*Custom properties* are attributes that won't be loaded from locale but passed to JSON output as they stand. It can be for exammple hash of ActiveRecord object validation error messages. Custom properties can be inherited.
|
71
|
+
|
72
|
+
*Visible properties* is optional whitelist for output JSON properties. By default all default, translated and custom properties are included in output. You can use visible_properties method to include only demanded subset. Visible properties are not inherited.
|
73
|
+
|
74
|
+
### Defining JSONError
|
75
|
+
|
76
|
+
Let's introduce two example error classes now. They will be used in further examples.
|
20
77
|
|
21
78
|
```ruby
|
22
79
|
module Errors
|
23
80
|
class ResourceNotFound < JSONError.base
|
24
|
-
|
25
|
-
|
26
|
-
options.merge!(key: :resource_not_found)
|
27
|
-
super(options)
|
28
|
-
end
|
81
|
+
key :resource_not_found
|
82
|
+
translated_properties :message, :href, :description
|
29
83
|
end
|
30
84
|
|
31
85
|
class OtherError < JSONError.base
|
32
|
-
|
33
|
-
|
34
|
-
super - %i(href description)
|
35
|
-
end
|
36
|
-
|
37
|
-
options.merge!(key: :other_error)
|
38
|
-
super(options)
|
86
|
+
key :other_error
|
87
|
+
translated_properties :message
|
39
88
|
end
|
40
89
|
end
|
41
90
|
```
|
42
91
|
|
43
92
|
### Defining JSONError Properties
|
44
93
|
|
45
|
-
|
94
|
+
Just add entries to locale.
|
46
95
|
|
47
96
|
```yml
|
97
|
+
# config/locales/json_errors.en.yml
|
48
98
|
en:
|
49
|
-
|
99
|
+
json_errors:
|
50
100
|
resource_not_found:
|
51
101
|
status: 404
|
52
102
|
message: 'Resource %{name} (%{id}) was not found.'
|
@@ -82,7 +132,6 @@ end
|
|
82
132
|
|
83
133
|
```ruby
|
84
134
|
# Remember to load dependencies
|
85
|
-
|
86
135
|
class Application < Sinatra::Base
|
87
136
|
before { content_type :json }
|
88
137
|
|
@@ -124,7 +173,7 @@ You can specify different contexts for the same error key.
|
|
124
173
|
# In locale
|
125
174
|
|
126
175
|
en:
|
127
|
-
|
176
|
+
json_errors:
|
128
177
|
resource_not_found:
|
129
178
|
status: 404
|
130
179
|
message: 'Resource %{name} (%{id}) was not found.'
|
@@ -157,7 +206,7 @@ Errors::ResourceNotFound.new(context: 'batmans', id: 2)
|
|
157
206
|
|
158
207
|
## Contributing
|
159
208
|
|
160
|
-
|
209
|
+
Questions, propositions, bug reports and pull requests are welcome.
|
161
210
|
|
162
211
|
|
163
212
|
## License
|
data/bin/console
CHANGED
data/lib/json_error/base.rb
CHANGED
@@ -1,43 +1,96 @@
|
|
1
1
|
module JSONError
|
2
2
|
class Base < StandardError
|
3
|
+
DEFAULT_STATUS = 500
|
3
4
|
NULL_TOKEN = '~'
|
4
5
|
|
5
6
|
attr_reader :context, :key, :options
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
super(message)
|
13
|
-
end
|
8
|
+
class << self
|
9
|
+
def inherited(base)
|
10
|
+
base.instance_variable_set(:@translated_properties, translated_properties)
|
11
|
+
base.instance_variable_set(:@custom_properties, custom_properties)
|
12
|
+
end
|
14
13
|
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
def base?
|
15
|
+
self == JSONError.base
|
16
|
+
end
|
18
17
|
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
def disallowed_property_setting!(property)
|
19
|
+
raise(Errors::DisallowedPropertySetting, property: property, class: self)
|
20
|
+
end
|
21
|
+
|
22
|
+
def key(key = nil)
|
23
|
+
return @key if key.nil?
|
24
|
+
disallowed_property_setting!(:key) if base?
|
25
|
+
|
26
|
+
@key ||= key
|
27
|
+
end
|
28
|
+
|
29
|
+
def properties
|
30
|
+
[*default_properties, *translated_properties, *custom_properties]
|
31
|
+
end
|
32
|
+
|
33
|
+
def default_properties
|
34
|
+
%i(status key)
|
35
|
+
end
|
36
|
+
|
37
|
+
def translated_properties(*translated_properties)
|
38
|
+
return @translated_properties if translated_properties.empty?
|
39
|
+
disallowed_property_setting!(:translated_properties) if base?
|
40
|
+
|
41
|
+
translated_properties.each do |property|
|
42
|
+
define_method(property) { translate(property) }
|
43
|
+
end
|
44
|
+
@translated_properties = [*@translated_properties, *translated_properties].uniq
|
45
|
+
end
|
46
|
+
|
47
|
+
def custom_properties(*custom_properties)
|
48
|
+
return @custom_properties if custom_properties.empty?
|
49
|
+
disallowed_property_setting!(:custom_properties) if base?
|
22
50
|
|
23
|
-
|
24
|
-
|
51
|
+
attr_accessor(*custom_properties)
|
52
|
+
@custom_properties = [*@custom_properties, *custom_properties].uniq
|
53
|
+
end
|
54
|
+
|
55
|
+
def visible_properties(*visible_properties)
|
56
|
+
return(@visible_properties || properties) if visible_properties.empty?
|
57
|
+
disallowed_property_setting!(:visible_properties) if base?
|
58
|
+
|
59
|
+
@visible_properties = visible_properties.uniq
|
60
|
+
end
|
25
61
|
end
|
26
62
|
|
27
|
-
def
|
28
|
-
|
63
|
+
def initialize(attrs = {})
|
64
|
+
@key = self.class.key || attrs.fetch(:key)
|
65
|
+
@context = attrs[:context]
|
66
|
+
assign_custom_properties(attrs)
|
67
|
+
assign_options(attrs)
|
68
|
+
super(message)
|
29
69
|
end
|
30
70
|
|
31
71
|
def status
|
32
|
-
translate(:status, default:
|
72
|
+
translate(:status, default: DEFAULT_STATUS)
|
33
73
|
end
|
34
74
|
|
35
75
|
def to_json(*args)
|
36
|
-
|
76
|
+
visible_properties_hash.to_json(*args)
|
37
77
|
end
|
38
78
|
|
39
79
|
private
|
40
80
|
|
81
|
+
def assign_custom_properties(attrs = {})
|
82
|
+
attrs = attrs.dup.delete_if { |k, _v| !custom_properties.include?(k) }
|
83
|
+
custom_properties.each do |property|
|
84
|
+
instance_variable_set("@#{property}", attrs[property])
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def assign_options(attrs = {})
|
89
|
+
reserved_names = %i(key context) + custom_properties
|
90
|
+
@options = attrs.dup.delete_if { |k, _v| reserved_names.include?(k) }
|
91
|
+
options.merge!(default: NULL_TOKEN)
|
92
|
+
end
|
93
|
+
|
41
94
|
def translate(node, translation_options = {})
|
42
95
|
translation_options = options.merge(translation_options)
|
43
96
|
translation = I18n.t("#{translation_path}.#{node}", translation_options)
|
@@ -45,15 +98,19 @@ module JSONError
|
|
45
98
|
end
|
46
99
|
|
47
100
|
def translation_path
|
48
|
-
context.nil? ? "
|
101
|
+
context.nil? ? "json_errors.#{key}" : "json_errors.#{context}.#{key}"
|
102
|
+
end
|
103
|
+
|
104
|
+
def visible_properties_hash
|
105
|
+
Hash[visible_properties.map { |property| [property, send(property)] }]
|
106
|
+
end
|
107
|
+
|
108
|
+
def visible_properties
|
109
|
+
self.class.visible_properties
|
49
110
|
end
|
50
111
|
|
51
|
-
def
|
52
|
-
|
53
|
-
key: key,
|
54
|
-
message: message,
|
55
|
-
description: description,
|
56
|
-
href: href }.delete_if { |k, _v| !self.class.properties.include?(k) }
|
112
|
+
def custom_properties
|
113
|
+
self.class.custom_properties || []
|
57
114
|
end
|
58
115
|
end
|
59
116
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module JSONError
|
2
|
+
module Errors
|
3
|
+
class DisallowedPropertySetting < StandardError
|
4
|
+
def initialize(options = {})
|
5
|
+
property = options.fetch(:property)
|
6
|
+
klass = options.fetch(:class)
|
7
|
+
super("Setting #{property} property is disallowed for #{klass}.")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
data/lib/json_error/version.rb
CHANGED
data/lib/json_error.rb
CHANGED
@@ -0,0 +1,18 @@
|
|
1
|
+
class ResourceNotFound < JSONError.base
|
2
|
+
key :resource_not_found
|
3
|
+
translated_properties :message, :description, :href
|
4
|
+
custom_properties :details
|
5
|
+
end
|
6
|
+
|
7
|
+
class MissingTranslations < JSONError.base
|
8
|
+
key :missing_translations
|
9
|
+
translated_properties :message, :description, :href
|
10
|
+
custom_properties :details
|
11
|
+
visible_properties(*default_properties, :message, :details)
|
12
|
+
end
|
13
|
+
|
14
|
+
class ChildError < MissingTranslations
|
15
|
+
key :child_error
|
16
|
+
translated_properties :additional_translated
|
17
|
+
custom_properties :additional_custom
|
18
|
+
end
|
@@ -1,7 +1,13 @@
|
|
1
1
|
en:
|
2
|
-
|
2
|
+
json_errors:
|
3
3
|
resource_not_found:
|
4
4
|
status: 404
|
5
5
|
message: 'Resource %{name} (%{id}) was not found.'
|
6
6
|
description: 'Blah blah blah.'
|
7
7
|
href: 'https://developers.api.pl/doc/errors/resource_not_found'
|
8
|
+
other:
|
9
|
+
resource_not_found:
|
10
|
+
status: 400
|
11
|
+
message: 'Resource not found.'
|
12
|
+
description: 'Bleh bleh bleh.'
|
13
|
+
href: 'https://developers.api.pl/doc/errors/resource_not_found?context=other'
|
@@ -0,0 +1,274 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ResourceNotFound do
|
4
|
+
context 'as JSONError.base child-class' do
|
5
|
+
describe 'NULL_TOKEN' do
|
6
|
+
subject { described_class::NULL_TOKEN }
|
7
|
+
it { is_expected.to eq('~') }
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '.superclass' do
|
11
|
+
subject { described_class.superclass }
|
12
|
+
it { is_expected.to be(JSONError.base) }
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '.base?' do
|
16
|
+
subject { described_class.base? }
|
17
|
+
it { is_expected.to be_falsey }
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '.key' do
|
21
|
+
context 'when no key was passed' do
|
22
|
+
subject { described_class.key }
|
23
|
+
it { is_expected.to eq(:resource_not_found) }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '.properties' do
|
28
|
+
subject { described_class.properties }
|
29
|
+
let(:properties) do
|
30
|
+
described_class.default_properties + %i(message description href details)
|
31
|
+
end
|
32
|
+
it { is_expected.to eq(properties) }
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '.default_properties' do
|
36
|
+
subject { described_class.default_properties }
|
37
|
+
it { is_expected.to eq(%i(status key)) }
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '.translated_properties' do
|
41
|
+
subject { described_class.translated_properties }
|
42
|
+
context 'when no args were passed' do
|
43
|
+
it { is_expected.to eq(%i(message description href)) }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '.custom_properties' do
|
48
|
+
subject { described_class.custom_properties }
|
49
|
+
|
50
|
+
context 'when no args were passed' do
|
51
|
+
it { is_expected.to eq(%i(details)) }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe '.visible_properties' do
|
56
|
+
subject { described_class.visible_properties }
|
57
|
+
context 'when no args were passed' do
|
58
|
+
it { is_expected.to eq(described_class.properties) }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '.new' do
|
63
|
+
context 'called without :key option' do
|
64
|
+
subject { described_class.new }
|
65
|
+
it { is_expected.to be_a(described_class) }
|
66
|
+
describe '#key' do
|
67
|
+
it { expect(subject.key).to eq(:resource_not_found) }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'called with :key option' do
|
72
|
+
subject { described_class.new(key: :other_key) }
|
73
|
+
it { is_expected.to be_a(described_class) }
|
74
|
+
describe '#key' do
|
75
|
+
it { expect(subject.key).to eq(:resource_not_found) }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe '#status' do
|
81
|
+
context 'for default context' do
|
82
|
+
subject { described_class.new.status }
|
83
|
+
it { is_expected.to eq(404) }
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'for context "other"' do
|
87
|
+
subject { described_class.new(context: 'other').status }
|
88
|
+
it { is_expected.to eq(400) }
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe '#key' do
|
93
|
+
subject { described_class.new.key }
|
94
|
+
it { is_expected.to eq(:resource_not_found) }
|
95
|
+
end
|
96
|
+
|
97
|
+
describe '#message' do
|
98
|
+
context 'for default context' do
|
99
|
+
subject { described_class.new.message }
|
100
|
+
it { is_expected.to eq('Resource %{name} (%{id}) was not found.') }
|
101
|
+
end
|
102
|
+
|
103
|
+
context 'for context "other"' do
|
104
|
+
subject { described_class.new(context: 'other').message }
|
105
|
+
it { is_expected.to eq('Resource not found.') }
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe '#description' do
|
110
|
+
context 'for default context' do
|
111
|
+
subject { described_class.new.description }
|
112
|
+
it { is_expected.to eq('Blah blah blah.') }
|
113
|
+
end
|
114
|
+
|
115
|
+
context 'for context "other"' do
|
116
|
+
subject { described_class.new(context: 'other').description }
|
117
|
+
it { is_expected.to eq('Bleh bleh bleh.') }
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe '#href' do
|
122
|
+
context 'for default context' do
|
123
|
+
subject { described_class.new.href }
|
124
|
+
it do
|
125
|
+
is_expected.to eq('https://developers.api.pl/doc/errors/resource_not_found')
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
context 'for context "other"' do
|
130
|
+
subject { described_class.new(context: 'other').href }
|
131
|
+
it do
|
132
|
+
is_expected.to eq('https://developers.api.pl/doc/errors/resource_not_found?context=other')
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe '#to_json' do
|
138
|
+
let(:parse) { -> (s) { JSON.parse(s) } }
|
139
|
+
let(:error) { described_class.new }
|
140
|
+
subject { error.to_json }
|
141
|
+
|
142
|
+
describe '#keys' do
|
143
|
+
let(:keys) { %w(status key message description href details) }
|
144
|
+
it { expect(parse.call(subject).keys).to eq(keys) }
|
145
|
+
end
|
146
|
+
|
147
|
+
context 'when defined in locale' do
|
148
|
+
it { is_expected.to be_a(String) }
|
149
|
+
describe 'status property' do
|
150
|
+
it { expect(parse.call(subject)['status']).to eq(error.status) }
|
151
|
+
end
|
152
|
+
describe 'key property' do
|
153
|
+
it { expect(parse.call(subject)['key']).to eq(error.key.to_s) }
|
154
|
+
end
|
155
|
+
describe 'message property' do
|
156
|
+
it { expect(parse.call(subject)['message']).to eq(error.message.to_s) }
|
157
|
+
end
|
158
|
+
describe 'description property' do
|
159
|
+
it { expect(parse.call(subject)['description']).to eq(error.description.to_s) }
|
160
|
+
end
|
161
|
+
describe 'href property' do
|
162
|
+
it { expect(parse.call(subject)['href']).to eq(error.href.to_s) }
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
describe MissingTranslations do
|
170
|
+
context 'as JSONError.base child-class' do
|
171
|
+
describe '.visible_properties' do
|
172
|
+
subject { described_class.visible_properties }
|
173
|
+
context 'when no args were passed' do
|
174
|
+
it { is_expected.to eq(%i(status key message details)) }
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
describe '#status' do
|
179
|
+
subject { described_class.new.status }
|
180
|
+
it { is_expected.to eq(500) }
|
181
|
+
end
|
182
|
+
|
183
|
+
describe '#key' do
|
184
|
+
subject { described_class.new.key }
|
185
|
+
it { is_expected.to eq(:missing_translations) }
|
186
|
+
end
|
187
|
+
|
188
|
+
describe '#message' do
|
189
|
+
subject { described_class.new.message }
|
190
|
+
it { is_expected.to be_nil }
|
191
|
+
end
|
192
|
+
|
193
|
+
describe '#description' do
|
194
|
+
subject { described_class.new.description }
|
195
|
+
it { is_expected.to be_nil }
|
196
|
+
end
|
197
|
+
|
198
|
+
describe '#href' do
|
199
|
+
subject { described_class.new.href }
|
200
|
+
it { is_expected.to be_nil }
|
201
|
+
end
|
202
|
+
|
203
|
+
describe '#to_json' do
|
204
|
+
let(:parse) { -> (s) { JSON.parse(s) } }
|
205
|
+
let(:details) { { 'detail' => 'value' } }
|
206
|
+
let(:error) { described_class.new(details: details) }
|
207
|
+
subject { error.to_json }
|
208
|
+
|
209
|
+
describe '#keys' do
|
210
|
+
let(:keys) { %w(status key message details) }
|
211
|
+
it { expect(parse.call(subject).keys).to eq(keys) }
|
212
|
+
end
|
213
|
+
|
214
|
+
context 'when defined in locale' do
|
215
|
+
it { is_expected.to be_a(String) }
|
216
|
+
describe 'status property' do
|
217
|
+
it { expect(parse.call(subject)['status']).to eq(error.status) }
|
218
|
+
end
|
219
|
+
describe 'key property' do
|
220
|
+
it { expect(parse.call(subject)['key']).to eq(error.key.to_s) }
|
221
|
+
end
|
222
|
+
describe 'message property' do
|
223
|
+
it { expect(parse.call(subject)['message']).to be_nil }
|
224
|
+
end
|
225
|
+
describe 'details property' do
|
226
|
+
it { expect(parse.call(subject)['details']).to eq(error.details) }
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
describe ChildError do
|
234
|
+
let(:parent_class) { MissingTranslations }
|
235
|
+
context 'as MissingTranslations child-class' do
|
236
|
+
describe '.visible_properties' do
|
237
|
+
subject { described_class.visible_properties.sort }
|
238
|
+
context 'when no args were passed' do
|
239
|
+
it do
|
240
|
+
is_expected.to eq(
|
241
|
+
[*parent_class.properties, :additional_translated, :additional_custom].sort
|
242
|
+
)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
describe '.translated_properties' do
|
248
|
+
subject { described_class.translated_properties.sort }
|
249
|
+
context 'when no args were passed' do
|
250
|
+
it do
|
251
|
+
is_expected.to eq(
|
252
|
+
[*parent_class.translated_properties, :additional_translated].sort
|
253
|
+
)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
describe '.custom_properties' do
|
259
|
+
subject { described_class.custom_properties.sort }
|
260
|
+
context 'when no args were passed' do
|
261
|
+
it do
|
262
|
+
is_expected.to eq(
|
263
|
+
[*parent_class.custom_properties, :additional_custom].sort
|
264
|
+
)
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
describe '#key' do
|
270
|
+
subject { described_class.new.key }
|
271
|
+
it { is_expected.to eq(:child_error) }
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
@@ -1,6 +1,11 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe JSONError::Base do
|
4
|
+
describe 'DEFAULT_STATUS' do
|
5
|
+
subject { described_class::DEFAULT_STATUS }
|
6
|
+
it { is_expected.to eq(500) }
|
7
|
+
end
|
8
|
+
|
4
9
|
describe 'NULL_TOKEN' do
|
5
10
|
subject { described_class::NULL_TOKEN }
|
6
11
|
it { is_expected.to eq('~') }
|
@@ -11,88 +16,111 @@ describe JSONError::Base do
|
|
11
16
|
it { is_expected.to be(StandardError) }
|
12
17
|
end
|
13
18
|
|
14
|
-
describe '.
|
15
|
-
|
16
|
-
|
17
|
-
|
19
|
+
describe '.base?' do
|
20
|
+
subject { described_class.base? }
|
21
|
+
it { is_expected.to be_truthy }
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '.key' do
|
25
|
+
context 'when no key was passed' do
|
26
|
+
subject { described_class.key }
|
27
|
+
it { is_expected.to be_nil }
|
18
28
|
end
|
19
29
|
|
20
|
-
context '
|
21
|
-
|
22
|
-
|
30
|
+
context 'when key were passed' do
|
31
|
+
let(:error) { JSONError::Errors::DisallowedPropertySetting }
|
32
|
+
subject { -> { described_class.key(:error) } }
|
33
|
+
it { is_expected.to raise_error(error) }
|
23
34
|
end
|
24
35
|
end
|
25
36
|
|
26
|
-
describe '
|
27
|
-
subject {
|
37
|
+
describe '.properties' do
|
38
|
+
subject { described_class.properties }
|
39
|
+
it { is_expected.to eq(described_class.default_properties) }
|
40
|
+
end
|
28
41
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
42
|
+
describe '.default_properties' do
|
43
|
+
subject { described_class.default_properties }
|
44
|
+
it { is_expected.to eq(%i(status key)) }
|
45
|
+
end
|
33
46
|
|
34
|
-
|
35
|
-
|
36
|
-
|
47
|
+
describe '.translated_properties' do
|
48
|
+
subject { described_class.translated_properties(*args) }
|
49
|
+
|
50
|
+
context 'when no args were passed' do
|
51
|
+
let(:args) { [] }
|
52
|
+
it { is_expected.to be_nil }
|
37
53
|
end
|
38
|
-
end
|
39
54
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
55
|
+
context 'when args were passed' do
|
56
|
+
let(:args) { %i(message) }
|
57
|
+
let(:error) { JSONError::Errors::DisallowedPropertySetting }
|
58
|
+
it { expect { subject }.to raise_error(error) }
|
59
|
+
end
|
44
60
|
end
|
45
61
|
|
46
|
-
describe '
|
47
|
-
subject {
|
62
|
+
describe '.custom_properties' do
|
63
|
+
subject { described_class.custom_properties(*args) }
|
48
64
|
|
49
|
-
context 'when
|
50
|
-
let(:
|
65
|
+
context 'when no args were passed' do
|
66
|
+
let(:args) { [] }
|
51
67
|
it { is_expected.to be_nil }
|
52
68
|
end
|
53
69
|
|
54
|
-
context 'when
|
55
|
-
let(:
|
56
|
-
let(:
|
57
|
-
|
58
|
-
described_class.new(key: :resource_not_found, name: name, id: id)
|
59
|
-
end
|
60
|
-
it { is_expected.to eq("Resource #{name} (#{id}) was not found.") }
|
70
|
+
context 'when args were passed' do
|
71
|
+
let(:args) { %i(message) }
|
72
|
+
let(:error) { JSONError::Errors::DisallowedPropertySetting }
|
73
|
+
it { expect { subject }.to raise_error(error) }
|
61
74
|
end
|
62
75
|
end
|
63
76
|
|
64
|
-
describe '
|
65
|
-
subject {
|
77
|
+
describe '.visible_properties' do
|
78
|
+
subject { described_class.visible_properties(*args) }
|
66
79
|
|
67
|
-
context 'when
|
68
|
-
let(:
|
69
|
-
it { is_expected.to
|
80
|
+
context 'when no args were passed' do
|
81
|
+
let(:args) { [] }
|
82
|
+
it { is_expected.to eq(described_class.properties) }
|
70
83
|
end
|
71
84
|
|
72
|
-
context 'when
|
73
|
-
let(:
|
74
|
-
|
85
|
+
context 'when args were passed' do
|
86
|
+
let(:args) { %i(message) }
|
87
|
+
let(:error) { JSONError::Errors::DisallowedPropertySetting }
|
88
|
+
it { expect { subject }.to raise_error(error) }
|
75
89
|
end
|
76
90
|
end
|
77
91
|
|
78
|
-
describe '
|
79
|
-
|
92
|
+
describe '.new' do
|
93
|
+
context 'called without :key option' do
|
94
|
+
subject { -> { described_class.new } }
|
95
|
+
it { is_expected.to raise_error(KeyError) }
|
96
|
+
end
|
97
|
+
|
98
|
+
context 'called with :key option' do
|
99
|
+
subject { described_class.new(key: :resource_not_found) }
|
100
|
+
it { is_expected.to be_a(described_class) }
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe '#status' do
|
105
|
+
subject { error.status }
|
80
106
|
|
81
107
|
context 'when not defined in locale' do
|
82
108
|
let(:error) { described_class.new(key: :other_error) }
|
83
|
-
it { is_expected.to
|
109
|
+
it { is_expected.to eq(500) }
|
84
110
|
end
|
85
111
|
|
86
112
|
context 'when defined in locale' do
|
87
113
|
let(:error) { described_class.new(key: :resource_not_found) }
|
88
|
-
it
|
89
|
-
is_expected.to eq(
|
90
|
-
'https://developers.api.pl/doc/errors/resource_not_found'
|
91
|
-
)
|
92
|
-
end
|
114
|
+
it { is_expected.to eq(404) }
|
93
115
|
end
|
94
116
|
end
|
95
117
|
|
118
|
+
describe '#key' do
|
119
|
+
let(:error) { described_class.new(key: :resource_not_found) }
|
120
|
+
subject { error.key }
|
121
|
+
it { is_expected.to eq(:resource_not_found) }
|
122
|
+
end
|
123
|
+
|
96
124
|
describe '#to_json' do
|
97
125
|
let(:parse) { -> (s) { JSON.parse(s) } }
|
98
126
|
subject { error.to_json }
|
@@ -106,15 +134,6 @@ describe JSONError::Base do
|
|
106
134
|
describe 'key property' do
|
107
135
|
it { expect(parse.call(subject)['key']).to eq(error.key.to_s) }
|
108
136
|
end
|
109
|
-
describe 'message property' do
|
110
|
-
it { expect(parse.call(subject)['message']).to be_nil }
|
111
|
-
end
|
112
|
-
describe 'description property' do
|
113
|
-
it { expect(parse.call(subject)['description']).to be_nil }
|
114
|
-
end
|
115
|
-
describe 'href property' do
|
116
|
-
it { expect(parse.call(subject)['href']).to be_nil }
|
117
|
-
end
|
118
137
|
end
|
119
138
|
|
120
139
|
context 'when defined in locale' do
|
@@ -126,17 +145,6 @@ describe JSONError::Base do
|
|
126
145
|
describe 'key property' do
|
127
146
|
it { expect(parse.call(subject)['key']).to eq(error.key.to_s) }
|
128
147
|
end
|
129
|
-
describe 'message property' do
|
130
|
-
it { expect(parse.call(subject)['message']).to eq(error.message) }
|
131
|
-
end
|
132
|
-
describe 'description property' do
|
133
|
-
it do
|
134
|
-
expect(parse.call(subject)['description']).to eq(error.description)
|
135
|
-
end
|
136
|
-
end
|
137
|
-
describe 'href property' do
|
138
|
-
it { expect(parse.call(subject)['href']).to eq(error.href) }
|
139
|
-
end
|
140
148
|
end
|
141
149
|
end
|
142
150
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe JSONError::Base do
|
4
|
+
describe '.superclass' do
|
5
|
+
subject { described_class.superclass }
|
6
|
+
it { is_expected.to be(StandardError) }
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '.new' do
|
10
|
+
let(:no_option_error) { KeyError }
|
11
|
+
subject { -> (options) { described_class.new(options) } }
|
12
|
+
|
13
|
+
it 'requires :property option' do
|
14
|
+
expect { subject.call({}) }.to raise_error(no_option_error)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'requires :class option' do
|
18
|
+
expect { subject.call(property: :p) }.to raise_error(no_option_error)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: json_error
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Krzysztof Buszewicz
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-10-
|
11
|
+
date: 2015-10-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -100,9 +100,13 @@ files:
|
|
100
100
|
- json_error.gemspec
|
101
101
|
- lib/json_error.rb
|
102
102
|
- lib/json_error/base.rb
|
103
|
+
- lib/json_error/errors/disallowed_property_setting.rb
|
103
104
|
- lib/json_error/version.rb
|
105
|
+
- spec/fixtures/json_error_base_children.rb
|
104
106
|
- spec/fixtures/locales/en.yml
|
107
|
+
- spec/json_error/base_children_spec.rb
|
105
108
|
- spec/json_error/base_spec.rb
|
109
|
+
- spec/json_error/errors/disallowed_property_setting.rb
|
106
110
|
- spec/json_error_spec.rb
|
107
111
|
- spec/spec_helper.rb
|
108
112
|
homepage: https://github.com/buszu/json_error
|
@@ -130,8 +134,11 @@ signing_key:
|
|
130
134
|
specification_version: 4
|
131
135
|
summary: JSON errors base for APIs.
|
132
136
|
test_files:
|
137
|
+
- spec/fixtures/json_error_base_children.rb
|
133
138
|
- spec/fixtures/locales/en.yml
|
139
|
+
- spec/json_error/base_children_spec.rb
|
134
140
|
- spec/json_error/base_spec.rb
|
141
|
+
- spec/json_error/errors/disallowed_property_setting.rb
|
135
142
|
- spec/json_error_spec.rb
|
136
143
|
- spec/spec_helper.rb
|
137
144
|
has_rdoc:
|