restful_error 1.0.2 → 1.0.3

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
  SHA256:
3
- metadata.gz: d9c9179f83c80510bc4d7a61c63f3bb73ec7767853a601063f3f407c07e8c12f
4
- data.tar.gz: c3b69c8c5aeb55ebcff66c10825af68d4fec44f93af18e9588ae06edaff71542
3
+ metadata.gz: 4744dd451535b3d85e7ce8b47a76abee9fa33970a72d6748e553f89bb830e9e5
4
+ data.tar.gz: 6cfc09fbf86294c2453a3effeb1537f83a9c8fef8bed444f24916e3f45c2d470
5
5
  SHA512:
6
- metadata.gz: 93c65efd5ece3f8dffc6760086b17ac682ec7577583023deeedcb45565593d8c31cc9cb465a0de9bbc2c5c8de640655793e6e194138e7ff5423d7055b6c3f561
7
- data.tar.gz: '09e8c789c9d89598f66cb00d3b21e8a526e5908c006893f5e920d81be07fb32f7d61d2745a18c5611bd9071dde261884f26f5a4d875422a762d54079ec6c04f3'
6
+ metadata.gz: 6d339f3e39861bfdc9bf64fc983d9b03e15708f4323d3f9987da1ae70c7bc973b8cfecb7cc7568a1078957f7eef6ba9a8e1f5c533f50a734b2fcc0005f5c2170
7
+ data.tar.gz: cbbf4ee06b69b396f79cd351c2659458422090d927af7584f31eab4e8e4fe1bcc4019cb21b90f2695ddbfe2683199afd412a4f6bad11ddf61365cb182a26f55f
@@ -12,6 +12,7 @@ module RestfulError
12
12
  word.gsub!(/(?<=[A-Z])(?=[A-Z][a-z])|(?<=[a-z\d])(?=[A-Z])/, "_")
13
13
  word.tr!("-", "_")
14
14
  word.downcase!
15
+ word
15
16
  end
16
17
 
17
18
  def camelize(word_)
@@ -21,6 +22,7 @@ module RestfulError
21
22
  word.sub!(/^[a-z\d]*/) { ::Regexp.last_match(0).capitalize }
22
23
  word.gsub!(%r{(?:_|(/))([a-z\d]*)}) { "#{Regexp.last_match(1)}#{Regexp.last_match(2).capitalize}" }
23
24
  word.gsub!("/", "::")
25
+ word
24
26
  end
25
27
  end
26
28
  end
@@ -10,18 +10,14 @@ module RestfulError
10
10
 
11
11
  def show
12
12
  @exception = request.env["action_dispatch.exception"]
13
- status = Status.new(request.path_info[1..].to_i)
13
+ code = request.path_info[1..].to_i
14
+ status = RestfulError.build_status_from_symbol_or_code(code)
14
15
  @status_code = status.code
15
16
  @reason_phrase = status.reason_phrase
16
- @message = @exception.try(:response_message)
17
- unless @message
18
- class_name = @exception.class.name
19
- class_key = RestfulError::Inflector.underscore(class_name)
20
- @message = I18n.t class_key, default: [ status.symbol, @reason_phrase ], scope: :restful_error
21
- end
17
+ @response_message = @exception.try(:response_message) || RestfulError.localized_phrase(@exception.class.name, status) || nil
22
18
 
23
19
  self.status = status.code
24
- render "restful_error/show"
20
+ render "restful_error/show", formats: request.format.symbol
25
21
  end
26
22
  end
27
23
 
@@ -4,34 +4,34 @@ require "restful_error/inflector"
4
4
  require "rack/utils"
5
5
 
6
6
  module RestfulError
7
+ Status = Data.define(:code, :reason_phrase, :symbol, :const_name)
8
+
7
9
  STATUS_CODE_TO_SYMBOL = Rack::Utils::SYMBOL_TO_STATUS_CODE.invert
8
- def self.code_from(code_or_sym_or_const_name)
9
- case code_or_sym_or_const_name
10
+
11
+ def self.build_status_from_const(const_sym)
12
+ const_name = const_sym.to_s
13
+ return unless /[A-Z]/.match?(const_name[0])
14
+ symbol = RestfulError::Inflector.underscore(const_name).to_sym
15
+ build_status_from_symbol_or_code(symbol)
16
+ end
17
+ def self.build_status_from_symbol_or_code(code_or_sym)
18
+ case code_or_sym
10
19
  when Integer
11
- code_or_sym_or_const_name
20
+ code = code_or_sym
21
+ symbol = STATUS_CODE_TO_SYMBOL[code]
22
+ const_name = Inflector.camelize(symbol.to_s)
23
+ reason_phrase = Rack::Utils::HTTP_STATUS_CODES[code]
24
+ Status.new(code:, symbol:, const_name:, reason_phrase:)
12
25
  when Symbol
13
- str = code_or_sym_or_const_name.to_s
14
- sym = /[A-Z]/.match?(str[0]) ? RestfulError::Inflector.underscore(str).to_sym : code_or_sym_or_const_name
15
26
  begin
16
- Rack::Utils.status_code(sym)
27
+ build_status_from_symbol_or_code Rack::Utils.status_code(code_or_sym)
17
28
  rescue ArgumentError
18
29
  nil
19
30
  end
20
31
  when /\A\d{3}\z/
21
- code_from(code_or_sym_or_const_name.to_i)
32
+ build_status_from_symbol_or_code(code_or_sym.to_i)
22
33
  else
23
- raise ArgumentError, "Invalid argument: #{code_or_sym_or_const_name.inspect}"
24
- end
25
- end
26
-
27
- Status = Data.define(:code, :reason_phrase, :symbol, :const_name) do
28
- def initialize(code:)
29
- reason_phrase = Rack::Utils::HTTP_STATUS_CODES[code]
30
- raise ArgumentError, "Invalid status code: #{code}" unless reason_phrase
31
-
32
- symbol = STATUS_CODE_TO_SYMBOL[code]
33
- const_name = Inflector.camelize(symbol.to_s)
34
- super(code:, reason_phrase:, symbol:, const_name:)
34
+ raise ArgumentError, "Invalid argument: #{code_or_sym.inspect}"
35
35
  end
36
36
  end
37
37
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RestfulError
4
- VERSION = "1.0.2"
4
+ VERSION = "1.0.3"
5
5
  end
data/lib/restful_error.rb CHANGED
@@ -3,43 +3,62 @@
3
3
  require "rack/utils"
4
4
  require "restful_error/railtie" if defined? ActionController
5
5
  require "restful_error/status"
6
-
7
- I18n.load_path += Dir["#{File.expand_path("../config/locales")}/*.yml"] if defined? I18n
6
+ require "restful_error/version"
8
7
 
9
8
  module RestfulError
10
- class BaseError < StandardError; end
11
-
12
9
  module Helper
13
10
  def restful
14
- raise NotImplementedError, "http_status must be implemented by including class" unless respond_to?(:http_status)
11
+ @restful ||= begin
12
+ raise NotImplementedError, "http_status must be implemented by including class" unless respond_to?(:http_status)
13
+ RestfulError.build_status_from_symbol_or_code(http_status)
14
+ end
15
+ end
16
+ def response_message
17
+ return @response_message unless @response_message.nil?
18
+ @response_message = RestfulError.localized_phrase(self.class.name, restful)
19
+ end
20
+ end
15
21
 
16
- @restful ||= Status.new(RestfulError.code_from(http_status))
22
+ class BaseError < StandardError
23
+ include RestfulError::Helper
24
+ def initialize(message = nil)
25
+ @response_message = message
26
+ super
17
27
  end
18
28
  end
19
29
 
20
30
  @cache = {}
21
31
  class << self
22
32
  def [](code_like)
23
- code = RestfulError.code_from(code_like)
24
- @cache[code] ||= build_error_class_for(code)
33
+ status = RestfulError.build_status_from_symbol_or_code(code_like)
34
+ @cache[status.code] ||= build_error_class_for(status)
25
35
  end
26
36
 
27
37
  def const_missing(const_name)
28
- code = RestfulError.code_from(const_name)
29
- return super unless code
38
+ status = RestfulError.build_status_from_const(const_name)
39
+ return super unless status
40
+
41
+ @cache[status.code] ||= build_error_class_for(status)
42
+ end
30
43
 
31
- @cache[code] ||= build_error_class_for(code)
44
+ def init_i18n
45
+ return if @init_i18n
46
+ I18n.load_path += Dir["#{File.expand_path("./config/locales")}/*.yml"]
47
+ @init_i18n = true
48
+ end
49
+ def localized_phrase(class_name, status)
50
+ return false unless defined?(I18n)
51
+ init_i18n
52
+ class_key = RestfulError::Inflector.underscore(class_name)
53
+ I18n.t class_key, default: [status.symbol, false], scope: :restful_error
32
54
  end
33
55
 
34
56
  private
35
57
 
36
- def build_error_class_for(code)
37
- status = Status.new(code)
38
- message = defined?(I18n) ? I18n.t(status.symbol, default: status.reason_phrase, scope: :restful_error) : status.reason_phrase
58
+ def build_error_class_for(status)
39
59
  klass = Class.new(BaseError) do
40
60
  define_method(:http_status) { status.code }
41
61
  define_method(:restful) { status }
42
- define_method(:message) { message }
43
62
  end
44
63
  const_set(status.const_name, klass)
45
64
  if defined? ActionDispatch::ExceptionWrapper
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_controller"
4
+ require "i18n"
5
+ require "spec_helper"
6
+ require "restful_error/railtie" # require again for run after restful_error_spec
7
+
8
+ RSpec.describe RestfulError::ExceptionsController do
9
+ include Rack::Test::Methods
10
+ def app = RestfulError.exceptions_app
11
+
12
+ shared_context "json" do
13
+ let(:request) { get "/#{status_code}", {}, 'HTTP_ACCEPT' => 'application/json' }
14
+ let(:json) { request; JSON.parse(last_response.body) }
15
+ end
16
+
17
+ before do
18
+ env "action_dispatch.exception", exception
19
+ end
20
+ describe RestfulError[404] do
21
+ let(:status_code) { 404 }
22
+ include_context "json" do
23
+ context 'default message' do
24
+ let(:exception) { described_class.new }
25
+ it do
26
+ expect(json).to eq({status_code: 404, reason_phrase: "Not Found", response_message: 'Page not found'}.stringify_keys)
27
+ expect(last_response.status).to eq status_code
28
+ end
29
+ end
30
+ context 'custom message' do
31
+ let(:exception) { described_class.new("custom message") }
32
+ it do
33
+ expect(json).to eq({status_code:, reason_phrase: "Not Found", response_message: "custom message"}.stringify_keys)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ context ActionController::RoutingError do
39
+ let(:exception) { described_class.new("no route") }
40
+ let(:status_code) { 404 }
41
+ include_context "json" do
42
+ it do
43
+ expect(json).to eq({status_code:, reason_phrase: "Not Found", response_message: 'Requested resource is not found'}.stringify_keys)
44
+ expect(last_response.status).to eq status_code
45
+ end
46
+ end
47
+ end
48
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: restful_error
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - kuboon
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-01-30 00:00:00.000000000 Z
11
+ date: 2025-01-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -39,15 +39,13 @@ extensions: []
39
39
  extra_rdoc_files: []
40
40
  files:
41
41
  - lib/restful_error.rb
42
- - lib/restful_error/engine.rb
43
42
  - lib/restful_error/inflector.rb
44
43
  - lib/restful_error/railtie.rb
45
44
  - lib/restful_error/status.rb
46
45
  - lib/restful_error/version.rb
47
- - spec/rack_spec.rb
48
- - spec/railtie_spec.rb
49
46
  - spec/restful_error_spec.rb
50
47
  - spec/spec_helper.rb
48
+ - spec/with_rails/exceptions_app_spec.rb
51
49
  homepage: https://github.com/kuboon/restful_error
52
50
  licenses:
53
51
  - MIT
@@ -1,9 +0,0 @@
1
- begin
2
- require 'rails'
3
- module RestfulError #:nodoc:
4
- class Engine < ::Rails::Engine #:nodoc:
5
- end
6
- end
7
- rescue LoadError
8
- #do nothing
9
- end
data/spec/rack_spec.rb DELETED
@@ -1,20 +0,0 @@
1
- require 'action_controller'
2
- require 'spec_helper'
3
-
4
- describe 'RestfulError' do
5
- describe RestfulError::ExceptionsController do
6
- include Rack::Test::Methods
7
- def app
8
- RestfulError::ExceptionsController.action(:show)
9
- end
10
- describe '404' do
11
- before do
12
- env "action_dispatch.exception", RestfulError[404].new
13
- end
14
- it do
15
- get '/404'
16
- expect(last_response.status).to eq 404
17
- end
18
- end
19
- end
20
- end
data/spec/railtie_spec.rb DELETED
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "action_controller"
4
- require "spec_helper"
5
-
6
- RSpec.describe "RestfulError.exceptions_app" do
7
- let(:app) { RestfulError.exceptions_app }
8
- let(:env) { {} }
9
- let(:request) { Rack::MockRequest.new(app) }
10
-
11
- it "renders 404" do
12
- env["action_dispatch.exception"] = ActionController::RoutingError.new("Not Found")
13
- env["PATH_INFO"] = "/404"
14
- response = request.get("/404", env)
15
- expect(response.status).to eq 404
16
- expect(response.body).to include "Not Found"
17
- end
18
- end