Skip to content

Commit 2222851

Browse files
author
Pedro Belo
committed
change error interface: read user message from locales
1 parent 4a83438 commit 2222851

File tree

5 files changed

+98
-100
lines changed

5 files changed

+98
-100
lines changed

lib/pliny/errors.rb

Lines changed: 46 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,61 @@
1+
require "i18n"
2+
13
module Pliny
24
module Errors
35
class Error < StandardError
4-
attr_accessor :id, :metadata
56

6-
def self.render(error)
7-
headers = { "Content-Type" => "application/json; charset=utf-8" }
8-
data = { id: error.id, message: error.message }.merge(error.metadata)
9-
[error.status, headers, [MultiJson.encode(data)]]
7+
class << self
8+
attr_accessor :error_class_id, :error_class_status
9+
10+
def render(error)
11+
headers = { "Content-Type" => "application/json; charset=utf-8" }
12+
data = { id: error.id, message: error.user_message }.merge(error.metadata)
13+
[error.status, headers, [MultiJson.encode(data)]]
14+
end
1015
end
1116

12-
def initialize(message, id: nil, metadata: {})
13-
@id = id
17+
attr_accessor :id, :status, :metadata, :user_message
18+
19+
def initialize(id=nil, metadata: {})
20+
@id = (id || self.class.error_class_id).to_sym
21+
@status = self.class.error_class_status
1422
@metadata = metadata
15-
super(message)
23+
@user_message = I18n.t("errors.#{@id}")
24+
super(@id.to_s)
1625
end
1726
end
1827

19-
class HTTPStatusError < Error
20-
attr_accessor :status
21-
22-
def initialize(message=nil, options={})
23-
meta = Pliny::Errors::META[self.class]
24-
message ||= "#{meta[1]}."
25-
options[:id] ||= meta[1].downcase.tr(' ', '_').to_sym
26-
@status = options.delete(:status) || meta[0]
27-
super(message, options)
28+
def self.MakeError(status, id)
29+
Class.new(Pliny::Errors::Error) do
30+
@error_class_id = id
31+
@error_class_status = status
2832
end
2933
end
3034

31-
class BadRequest < HTTPStatusError; end # 400
32-
class Unauthorized < HTTPStatusError; end # 401
33-
class PaymentRequired < HTTPStatusError; end # 402
34-
class Forbidden < HTTPStatusError; end # 403
35-
class NotFound < HTTPStatusError; end # 404
36-
class MethodNotAllowed < HTTPStatusError; end # 405
37-
class NotAcceptable < HTTPStatusError; end # 406
38-
class ProxyAuthenticationRequired < HTTPStatusError; end # 407
39-
class RequestTimeout < HTTPStatusError; end # 408
40-
class Conflict < HTTPStatusError; end # 409
41-
class Gone < HTTPStatusError; end # 410
42-
class LengthRequired < HTTPStatusError; end # 411
43-
class PreconditionFailed < HTTPStatusError; end # 412
44-
class RequestEntityTooLarge < HTTPStatusError; end # 413
45-
class RequestURITooLong < HTTPStatusError; end # 414
46-
class UnsupportedMediaType < HTTPStatusError; end # 415
47-
class RequestedRangeNotSatisfiable < HTTPStatusError; end # 416
48-
class ExpectationFailed < HTTPStatusError; end # 417
49-
class UnprocessableEntity < HTTPStatusError; end # 422
50-
class TooManyRequests < HTTPStatusError; end # 429
51-
class InternalServerError < HTTPStatusError; end # 500
52-
class NotImplemented < HTTPStatusError; end # 501
53-
class BadGateway < HTTPStatusError; end # 502
54-
class ServiceUnavailable < HTTPStatusError; end # 503
55-
class GatewayTimeout < HTTPStatusError; end # 504
56-
57-
# Messages for nicer exceptions, from rfc2616
58-
META = {
59-
BadRequest => [400, 'Bad request'],
60-
Unauthorized => [401, 'Unauthorized'],
61-
PaymentRequired => [402, 'Payment required'],
62-
Forbidden => [403, 'Forbidden'],
63-
NotFound => [404, 'Not found'],
64-
MethodNotAllowed => [405, 'Method not allowed'],
65-
NotAcceptable => [406, 'Not acceptable'],
66-
ProxyAuthenticationRequired => [407, 'Proxy authentication required'],
67-
RequestTimeout => [408, 'Request timeout'],
68-
Conflict => [409, 'Conflict'],
69-
Gone => [410, 'Gone'],
70-
LengthRequired => [411, 'Length required'],
71-
PreconditionFailed => [412, 'Precondition failed'],
72-
RequestEntityTooLarge => [413, 'Request entity too large'],
73-
RequestURITooLong => [414, 'Request-URI too long'],
74-
UnsupportedMediaType => [415, 'Unsupported media type'],
75-
RequestedRangeNotSatisfiable => [416, 'Requested range not satisfiable'],
76-
ExpectationFailed => [417, 'Expectation failed'],
77-
UnprocessableEntity => [422, 'Unprocessable entity'],
78-
TooManyRequests => [429, 'Too many requests'],
79-
InternalServerError => [500, 'Internal server error'],
80-
NotImplemented => [501, 'Not implemented'],
81-
BadGateway => [502, 'Bad gateway'],
82-
ServiceUnavailable => [503, 'Service unavailable'],
83-
GatewayTimeout => [504, 'Gateway timeout'],
84-
}.freeze
35+
BadRequest = MakeError(400, :bad_request)
36+
Unauthorized = MakeError(401, :unauthorized)
37+
PaymentRequired = MakeError(402, :payment_required)
38+
Forbidden = MakeError(403, :forbidden)
39+
NotFound = MakeError(404, :not_found)
40+
MethodNotAllowed = MakeError(405, :method_not_allowed)
41+
NotAcceptable = MakeError(406, :not_acceptable)
42+
ProxyAuthenticationRequired = MakeError(407, :proxy_authentication_required)
43+
RequestTimeout = MakeError(408, :request_timeout)
44+
Conflict = MakeError(409, :conflict)
45+
Gone = MakeError(410, :gone)
46+
LengthRequired = MakeError(411, :length_required)
47+
PreconditionFailed = MakeError(412, :precondition_failed)
48+
RequestEntityTooLarge = MakeError(413, :request_entity_too_large)
49+
RequestURITooLong = MakeError(414, :request_uri_too_long)
50+
UnsupportedMediaType = MakeError(415, :unsupported_media_type)
51+
RequestedRangeNotSatisfiable = MakeError(416, :requested_range_not_satisfiable)
52+
ExpectationFailed = MakeError(417, :expectation_failed)
53+
UnprocessableEntity = MakeError(422, :unprocessable_entity)
54+
TooManyRequests = MakeError(429, :too_many_requests)
55+
InternalServerError = MakeError(500, :internal_server_error)
56+
NotImplemented = MakeError(501, :not_implemented)
57+
BadGateway = MakeError(502, :bad_gateway)
58+
ServiceUnavailable = MakeError(503, :service_unavailable)
59+
GatewayTimeout = MakeError(504, :gateway_timeout)
8560
end
8661
end

lib/template/config/locales/en.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,27 @@
11
en:
2+
errors:
3+
bad_gateway: Bad gateway
4+
bad_request: Bad request
5+
conflict: Conflict
6+
expectation_failed: Expectation failed
7+
forbidden: Forbidden
8+
gateway_timeout: Gateway timed out
9+
gone: Gone
10+
internal_server_error: Internal server error
11+
length_required: Length required
12+
method_not_allowed: Method not allowed
13+
not_acceptable: Not acceptable
14+
not_found: Not found
15+
not_implemented: Not implemented
16+
payment_required: Payment required
17+
proxy_authentication_required: Proxy authentication required
18+
precondition_failed: Precondition failed
19+
request_entity_too_large: Request entity too large
20+
request_timeout: Request timed out
21+
request_uri_too_long: Requrest URI too long
22+
requested_range_not_satisfiable: Requested range not satisfiable
23+
service_unavailable: Service unavailable
24+
too_many_requests: Too many requests
25+
unauthorized: Unauthorized
26+
unprocessable_entity: Unprocessable entity
27+
unsupported_media_type: Unsupported media type

pliny.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Gem::Specification.new do |gem|
1616
gem.files = %x{ git ls-files }.split("\n").select { |d| d =~ %r{^(License|README|bin/|data/|ext/|lib/|spec/|test/)} }
1717

1818
gem.add_dependency "activesupport", "~> 4.1", ">= 4.1.0"
19+
gem.add_dependency "i18n", "~> 0.7", ">= 0.7"
1920
gem.add_dependency "multi_json", "~> 1.9", ">= 1.9.3"
2021
gem.add_dependency "prmd", "~> 0.7.0"
2122
gem.add_dependency "sinatra", "~> 1.4", ">= 1.4.5"

spec/errors_spec.rb

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,32 @@
11
require "spec_helper"
22

33
describe Pliny::Errors do
4-
describe Pliny::Errors::Error do
5-
it "takes a message" do
6-
e = Pliny::Errors::Error.new("Fail.")
7-
assert_equal "Fail.", e.message
8-
end
9-
it "takes an identifier" do
10-
e = Pliny::Errors::Error.new(nil, id: :fail)
11-
assert_equal :fail, e.id
12-
end
4+
it "bundles classes to represent the different status codes" do
5+
error = Pliny::Errors::BadRequest.new
6+
assert_equal :bad_request, error.id
7+
assert_equal 400, error.status
8+
assert_equal "Bad request", error.user_message
139

14-
it "takes metadata" do
15-
meta = { resource: "artists" }
16-
e = Pliny::Errors::Error.new(nil, metadata: meta)
17-
assert_equal meta, e.metadata
18-
end
10+
error = Pliny::Errors::InternalServerError.new
11+
assert_equal :internal_server_error, error.id
12+
assert_equal 500, error.status
13+
assert_equal "Internal server error", error.user_message
1914
end
2015

21-
describe Pliny::Errors::HTTPStatusError do
22-
it "includes an HTTP error that will take generic parameters" do
23-
e = Pliny::Errors::HTTPStatusError.new("error", id: :foo, status: 499)
24-
assert_equal :foo, e.id
25-
assert_equal 499, e.status
26-
assert_equal "error", e.message
27-
end
16+
it "keeps the error id stored as the internal message" do
17+
error = Pliny::Errors::BadRequest.new
18+
assert_equal "bad_request", error.message
19+
end
20+
21+
it "takes a custom id" do
22+
error = Pliny::Errors::BadRequest.new(:invalid_json)
23+
assert_equal :invalid_json, error.id
24+
assert_equal 400, error.status
25+
end
2826

29-
it "includes pre-defined HTTP error templates" do
30-
e = Pliny::Errors::NotFound.new
31-
assert_equal "Not found.", e.message
32-
assert_equal :not_found, e.id
33-
assert_equal 404, e.status
34-
end
27+
it "takes optional metadata" do
28+
metadata = { foo: "bar" }
29+
error = Pliny::Errors::BadRequest.new(:invalid_json, metadata: metadata)
30+
assert_equal metadata, error.metadata
3531
end
3632
end

spec/middleware/rescue_errors_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def app
2323
assert_equal 503, last_response.status
2424
error_json = MultiJson.decode(last_response.body)
2525
assert_equal "service_unavailable", error_json["id"]
26-
assert_equal "Service unavailable.", error_json["message"]
26+
assert_equal "Service unavailable", error_json["message"]
2727
end
2828

2929
it "intercepts exceptions and renders" do
@@ -32,7 +32,7 @@ def app
3232
assert_equal 500, last_response.status
3333
error_json = MultiJson.decode(last_response.body)
3434
assert_equal "internal_server_error", error_json["id"]
35-
assert_equal "Internal server error.", error_json["message"]
35+
assert_equal "Internal server error", error_json["message"]
3636
end
3737

3838
it "raises given the raise option" do

0 commit comments

Comments
 (0)