diff --git a/lib/active_resource.rb b/lib/active_resource.rb index 729ad530d6..239d1d2d73 100644 --- a/lib/active_resource.rb +++ b/lib/active_resource.rb @@ -39,6 +39,7 @@ module ActiveResource autoload :HttpMock autoload :Schema autoload :Singleton + autoload :InheritingHash autoload :Validations autoload :Collection end diff --git a/lib/active_resource/base.rb b/lib/active_resource/base.rb index 5a8de2bcf9..8004982a58 100644 --- a/lib/active_resource/base.rb +++ b/lib/active_resource/base.rb @@ -697,12 +697,11 @@ def connection(refresh = false) end def headers - headers_state = self._headers || {} - if superclass != Object - self._headers = superclass.headers.merge(headers_state) - else - headers_state - end + self._headers ||= if superclass != Object + InheritingHash.new(superclass.headers) + else + {} + end end attr_writer :element_name diff --git a/lib/active_resource/inheriting_hash.rb b/lib/active_resource/inheriting_hash.rb new file mode 100644 index 0000000000..4975d9b319 --- /dev/null +++ b/lib/active_resource/inheriting_hash.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module ActiveResource + class InheritingHash < Hash + def initialize(parent_hash = {}) + # Default hash value must be nil, which allows fallback lookup on parent hash + super(nil) + @parent_hash = parent_hash + end + + def [](key) + super || @parent_hash[key] + end + end +end diff --git a/test/cases/base_test.rb b/test/cases/base_test.rb index a5292dfcf2..f14d541c2a 100644 --- a/test/cases/base_test.rb +++ b/test/cases/base_test.rb @@ -706,6 +706,32 @@ def test_header_inheritance_should_not_leak_upstream assert_nil fruit.headers["key2"] end + def test_header_inheritance_can_override_upstream + fruit = Class.new(ActiveResource::Base) + apple = Class.new(fruit) + fruit.site = "http://market" + + fruit.headers["key"] = "fruit-value" + assert_equal "fruit-value", apple.headers["key"] + + apple.headers["key"] = "apple-value" + assert_equal "apple-value", apple.headers["key"] + assert_equal "fruit-value", fruit.headers["key"] + end + + + def test_header_inheritance_should_not_override_upstream_on_read + fruit = Class.new(ActiveResource::Base) + apple = Class.new(fruit) + fruit.site = "http://market" + + fruit.headers["key"] = "value" + assert_equal "value", apple.headers["key"] + + fruit.headers["key"] = "new-value" + assert_equal "new-value", apple.headers["key"] + end + def test_header_should_be_copied_to_main_thread_if_not_defined fruit = Class.new(ActiveResource::Base)