diff --git a/lib/active_resource.rb b/lib/active_resource.rb index 8e4f34ecf4..07323690db 100644 --- a/lib/active_resource.rb +++ b/lib/active_resource.rb @@ -41,6 +41,7 @@ module ActiveResource autoload :CustomMethods autoload :Formats autoload :HttpMock + autoload :Rescuable autoload :Schema autoload :Singleton autoload :InheritingHash diff --git a/lib/active_resource/base.rb b/lib/active_resource/base.rb index 29376f081d..2b71c9431b 100644 --- a/lib/active_resource/base.rb +++ b/lib/active_resource/base.rb @@ -1443,6 +1443,8 @@ def encode(options = {}) # my_branch.name # => "Wilson Road" def reload self.load(self.class.find(to_param, params: @prefix_options).attributes, false, true) + rescue => exception + rescue_with_handler(exception) || raise end # A method to manually load attributes from a \hash. Recursively loads collections of @@ -1725,7 +1727,7 @@ class Base include ActiveModel::Conversion include ActiveModel::Serializers::JSON include ActiveModel::Serializers::Xml - include ActiveResource::Reflection + include ActiveResource::Reflection, ActiveResource::Rescuable end ActiveSupport.run_load_hooks(:active_resource, Base) diff --git a/lib/active_resource/rescuable.rb b/lib/active_resource/rescuable.rb new file mode 100644 index 0000000000..f7c2ba8244 --- /dev/null +++ b/lib/active_resource/rescuable.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module ActiveResource + # = Active Resource \Rescuable + # + # Provides + # {rescue_from}[rdoc-ref:ActiveSupport::Rescuable::ClassMethods#rescue_from] + # for resources. Wraps calls over the network to handle configured errors. + module Rescuable + extend ActiveSupport::Concern + + included do + include ActiveSupport::Rescuable + + around_save :handle_exceptions + around_destroy :handle_exceptions + end + + private + def handle_exceptions + yield + rescue => exception + rescue_with_handler(exception) || raise + end + end +end diff --git a/test/cases/rescuable_test.rb b/test/cases/rescuable_test.rb new file mode 100644 index 0000000000..c50b271208 --- /dev/null +++ b/test/cases/rescuable_test.rb @@ -0,0 +1,83 @@ +# frozen_string_literal: true + +require "abstract_unit" +require "fixtures/person" + +class RescuableTest < ActiveSupport::TestCase + class Rescuable < ActiveResource::Base + class Error < StandardError + end + + self.site = "http://37s.sunrise.i:3000" + + rescue_from ActiveResource::ResourceNotFound, with: :rescue_not_found + rescue_from ActiveResource::UnauthorizedAccess, with: :rescue_unauthorized + rescue_from ActiveResource::BadRequest, with: ->(*) { raise Error, "Bad Request" } + + schema do + attribute :not_found, :boolean + attribute :unauthorized, :boolean + end + + def rescue_not_found + self.not_found = true + end + + def rescue_unauthorized + self.unauthorized = true + end + end + + def test_rescue_from_catches_exceptions_raised_during_reload + ActiveResource::HttpMock.respond_to.get "/rescuables/1.json", {}, nil, 404 + resource = Rescuable.new({ id: 1 }, true) + + assert_nothing_raised { resource.reload } + + assert_predicate resource, :not_found? + end + + def test_rescue_from_catches_exceptions_raised_during_create + ActiveResource::HttpMock.respond_to.post "/rescuables.json", {}, nil, 401 + resource = Rescuable.new + + assert_nothing_raised { resource.save! } + + assert_predicate resource, :unauthorized? + end + + def test_rescue_from_catches_exceptions_raised_during_destroy + ActiveResource::HttpMock.respond_to.delete "/rescuables/1.json", {}, nil, 401 + resource = Rescuable.new({ id: 1 }, true) + + assert_nothing_raised { resource.destroy } + + assert_predicate resource, :unauthorized? + end + + def test_rescue_from_catches_exceptions_raised_during_save + ActiveResource::HttpMock.respond_to.put "/rescuables/1.json", {}, nil, 401 + resource = Rescuable.new({ id: 1 }, true) + + assert_nothing_raised { resource.save! } + + assert_predicate resource, :unauthorized? + end + + def test_rescue_from_catches_exceptions_raised_during_update + ActiveResource::HttpMock.respond_to.put "/rescuables/1.json", {}, nil, 401 + resource = Rescuable.new({ id: 1 }, true) + + assert_nothing_raised { resource.update_attributes(saved: true) } + + assert_predicate resource, :unauthorized? + end + + def test_rescue_from_re_raises_exceptions_raised_during_save + ActiveResource::HttpMock.respond_to.post "/rescuables.json", {}, {}, 400 + + assert_raises Rescuable::Error, match: "Bad Request" do + Rescuable.create! + end + end +end