Removes the monkey-patch for Marshal.load
Marshal.load autoloads out of the box with Zeitwerk. See https://github.com/fxn/zeitwerk/blob/master/test/lib/zeitwerk/test_marshal.rb for similar coverage.
This commit is contained in:
parent
8da6ba9cae
commit
6d38553b09
|
@ -1,6 +1,5 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "active_support/core_ext/marshal"
|
|
||||||
require "active_support/core_ext/file/atomic"
|
require "active_support/core_ext/file/atomic"
|
||||||
require "active_support/core_ext/string/conversions"
|
require "active_support/core_ext/string/conversions"
|
||||||
require "uri/common"
|
require "uri/common"
|
||||||
|
|
|
@ -8,7 +8,6 @@ rescue LoadError => e
|
||||||
end
|
end
|
||||||
|
|
||||||
require "active_support/core_ext/enumerable"
|
require "active_support/core_ext/enumerable"
|
||||||
require "active_support/core_ext/marshal"
|
|
||||||
require "active_support/core_ext/array/extract_options"
|
require "active_support/core_ext/array/extract_options"
|
||||||
|
|
||||||
module ActiveSupport
|
module ActiveSupport
|
||||||
|
|
|
@ -16,7 +16,6 @@ rescue LoadError
|
||||||
end
|
end
|
||||||
|
|
||||||
require "active_support/digest"
|
require "active_support/digest"
|
||||||
require "active_support/core_ext/marshal"
|
|
||||||
|
|
||||||
module ActiveSupport
|
module ActiveSupport
|
||||||
module Cache
|
module Cache
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require "active_support/core_ext/string/inflections"
|
|
||||||
|
|
||||||
module ActiveSupport
|
|
||||||
module MarshalWithAutoloading # :nodoc:
|
|
||||||
def load(source, proc = nil)
|
|
||||||
super(source, proc)
|
|
||||||
rescue ArgumentError, NameError => exc
|
|
||||||
if exc.message.match(%r|undefined class/module (.+?)(?:::)?\z|)
|
|
||||||
# try loading the class/module
|
|
||||||
loaded = $1.constantize
|
|
||||||
|
|
||||||
raise unless $1 == loaded.name
|
|
||||||
|
|
||||||
# if it is an IO we need to go back to read the object
|
|
||||||
source.rewind if source.respond_to?(:rewind)
|
|
||||||
retry
|
|
||||||
else
|
|
||||||
raise exc
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Marshal.singleton_class.prepend(ActiveSupport::MarshalWithAutoloading)
|
|
|
@ -1,6 +1,5 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "behaviors/autoloading_cache_behavior"
|
|
||||||
require_relative "behaviors/cache_delete_matched_behavior"
|
require_relative "behaviors/cache_delete_matched_behavior"
|
||||||
require_relative "behaviors/cache_increment_decrement_behavior"
|
require_relative "behaviors/cache_increment_decrement_behavior"
|
||||||
require_relative "behaviors/cache_instrumentation_behavior"
|
require_relative "behaviors/cache_instrumentation_behavior"
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require_relative "../../dependencies_test_helpers"
|
|
||||||
|
|
||||||
module AutoloadingCacheBehavior
|
|
||||||
include DependenciesTestHelpers
|
|
||||||
|
|
||||||
def test_simple_autoloading
|
|
||||||
with_autoloading_fixtures do
|
|
||||||
@cache.write("foo", EM.new)
|
|
||||||
end
|
|
||||||
|
|
||||||
remove_constants(:EM)
|
|
||||||
ActiveSupport::Dependencies.clear
|
|
||||||
|
|
||||||
with_autoloading_fixtures do
|
|
||||||
assert_kind_of EM, @cache.read("foo")
|
|
||||||
end
|
|
||||||
|
|
||||||
remove_constants(:EM)
|
|
||||||
ActiveSupport::Dependencies.clear
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_two_classes_autoloading
|
|
||||||
with_autoloading_fixtures do
|
|
||||||
@cache.write("foo", [EM.new, ClassFolder.new])
|
|
||||||
end
|
|
||||||
|
|
||||||
remove_constants(:EM, :ClassFolder)
|
|
||||||
ActiveSupport::Dependencies.clear
|
|
||||||
|
|
||||||
with_autoloading_fixtures do
|
|
||||||
loaded = @cache.read("foo")
|
|
||||||
assert_kind_of Array, loaded
|
|
||||||
assert_equal 2, loaded.size
|
|
||||||
assert_kind_of EM, loaded[0]
|
|
||||||
assert_kind_of ClassFolder, loaded[1]
|
|
||||||
end
|
|
||||||
|
|
||||||
remove_constants(:EM, :ClassFolder)
|
|
||||||
ActiveSupport::Dependencies.clear
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -36,7 +36,6 @@ class FileStoreTest < ActiveSupport::TestCase
|
||||||
include CacheDeleteMatchedBehavior
|
include CacheDeleteMatchedBehavior
|
||||||
include CacheIncrementDecrementBehavior
|
include CacheIncrementDecrementBehavior
|
||||||
include CacheInstrumentationBehavior
|
include CacheInstrumentationBehavior
|
||||||
include AutoloadingCacheBehavior
|
|
||||||
|
|
||||||
def test_clear
|
def test_clear
|
||||||
gitkeep = File.join(cache_dir, ".gitkeep")
|
gitkeep = File.join(cache_dir, ".gitkeep")
|
||||||
|
|
|
@ -55,7 +55,6 @@ class MemCacheStoreTest < ActiveSupport::TestCase
|
||||||
include CacheIncrementDecrementBehavior
|
include CacheIncrementDecrementBehavior
|
||||||
include CacheInstrumentationBehavior
|
include CacheInstrumentationBehavior
|
||||||
include EncodedKeyCacheBehavior
|
include EncodedKeyCacheBehavior
|
||||||
include AutoloadingCacheBehavior
|
|
||||||
include ConnectionPoolBehavior
|
include ConnectionPoolBehavior
|
||||||
include FailureSafetyBehavior
|
include FailureSafetyBehavior
|
||||||
|
|
||||||
|
|
|
@ -136,7 +136,6 @@ module ActiveSupport::Cache::RedisCacheStoreTests
|
||||||
include LocalCacheBehavior
|
include LocalCacheBehavior
|
||||||
include CacheIncrementDecrementBehavior
|
include CacheIncrementDecrementBehavior
|
||||||
include CacheInstrumentationBehavior
|
include CacheInstrumentationBehavior
|
||||||
include AutoloadingCacheBehavior
|
|
||||||
include EncodedKeyCacheBehavior
|
include EncodedKeyCacheBehavior
|
||||||
|
|
||||||
def test_fetch_multi_uses_redis_mget
|
def test_fetch_multi_uses_redis_mget
|
||||||
|
|
|
@ -1,165 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require_relative "../abstract_unit"
|
|
||||||
require "active_support/core_ext/marshal"
|
|
||||||
require_relative "../dependencies_test_helpers"
|
|
||||||
|
|
||||||
class MarshalTest < ActiveSupport::TestCase
|
|
||||||
include ActiveSupport::Testing::Isolation
|
|
||||||
include DependenciesTestHelpers
|
|
||||||
|
|
||||||
def teardown
|
|
||||||
ActiveSupport::Dependencies.clear
|
|
||||||
remove_constants(:EM, :ClassFolder)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "that Marshal#load still works" do
|
|
||||||
sanity_data = ["test", [1, 2, 3], { a: [1, 2, 3] }, ActiveSupport::TestCase]
|
|
||||||
sanity_data.each do |obj|
|
|
||||||
dumped = Marshal.dump(obj)
|
|
||||||
assert_equal Marshal.method(:load).super_method.call(dumped), Marshal.load(dumped)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
test "that Marshal#load still works when passed a proc" do
|
|
||||||
example_string = "test"
|
|
||||||
|
|
||||||
example_proc = Proc.new do |o|
|
|
||||||
if o.is_a?(String)
|
|
||||||
o.capitalize!
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
dumped = Marshal.dump(example_string)
|
|
||||||
assert_equal Marshal.load(dumped, example_proc), "Test"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "that a missing class is autoloaded from string" do
|
|
||||||
dumped = nil
|
|
||||||
with_autoloading_fixtures do
|
|
||||||
dumped = Marshal.dump(EM.new)
|
|
||||||
end
|
|
||||||
|
|
||||||
remove_constants(:EM)
|
|
||||||
ActiveSupport::Dependencies.clear
|
|
||||||
|
|
||||||
with_autoloading_fixtures do
|
|
||||||
object = nil
|
|
||||||
assert_nothing_raised do
|
|
||||||
object = Marshal.load(dumped)
|
|
||||||
end
|
|
||||||
|
|
||||||
assert_kind_of EM, object
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
test "that classes in sub modules work" do
|
|
||||||
dumped = nil
|
|
||||||
with_autoloading_fixtures do
|
|
||||||
dumped = Marshal.dump(ClassFolder::ClassFolderSubclass.new)
|
|
||||||
end
|
|
||||||
|
|
||||||
remove_constants(:ClassFolder)
|
|
||||||
ActiveSupport::Dependencies.clear
|
|
||||||
|
|
||||||
with_autoloading_fixtures do
|
|
||||||
object = nil
|
|
||||||
assert_nothing_raised do
|
|
||||||
object = Marshal.load(dumped)
|
|
||||||
end
|
|
||||||
|
|
||||||
assert_kind_of ClassFolder::ClassFolderSubclass, object
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
test "that more than one missing class is autoloaded" do
|
|
||||||
dumped = nil
|
|
||||||
with_autoloading_fixtures do
|
|
||||||
dumped = Marshal.dump([EM.new, ClassFolder.new])
|
|
||||||
end
|
|
||||||
|
|
||||||
remove_constants(:EM, :ClassFolder)
|
|
||||||
ActiveSupport::Dependencies.clear
|
|
||||||
|
|
||||||
with_autoloading_fixtures do
|
|
||||||
loaded = Marshal.load(dumped)
|
|
||||||
assert_equal 2, loaded.size
|
|
||||||
assert_kind_of EM, loaded[0]
|
|
||||||
assert_kind_of ClassFolder, loaded[1]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
test "when one constant resolves to another" do
|
|
||||||
class Parent; C = Class.new; end
|
|
||||||
class Child < Parent; C = Class.new; end
|
|
||||||
|
|
||||||
dump = Marshal.dump(Child::C.new)
|
|
||||||
|
|
||||||
Child.send(:remove_const, :C)
|
|
||||||
|
|
||||||
assert_raise(ArgumentError) { Marshal.load(dump) }
|
|
||||||
end
|
|
||||||
|
|
||||||
test "that a real missing class is causing an exception" do
|
|
||||||
dumped = nil
|
|
||||||
with_autoloading_fixtures do
|
|
||||||
dumped = Marshal.dump(EM.new)
|
|
||||||
end
|
|
||||||
|
|
||||||
remove_constants(:EM)
|
|
||||||
ActiveSupport::Dependencies.clear
|
|
||||||
|
|
||||||
assert_raise(NameError) do
|
|
||||||
Marshal.load(dumped)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
test "when first class is autoloaded and second not" do
|
|
||||||
dumped = nil
|
|
||||||
class SomeClass
|
|
||||||
end
|
|
||||||
|
|
||||||
with_autoloading_fixtures do
|
|
||||||
dumped = Marshal.dump([EM.new, SomeClass.new])
|
|
||||||
end
|
|
||||||
|
|
||||||
remove_constants(:EM)
|
|
||||||
self.class.send(:remove_const, :SomeClass)
|
|
||||||
ActiveSupport::Dependencies.clear
|
|
||||||
|
|
||||||
with_autoloading_fixtures do
|
|
||||||
assert_raise(NameError) do
|
|
||||||
Marshal.load(dumped)
|
|
||||||
end
|
|
||||||
|
|
||||||
assert_nothing_raised do
|
|
||||||
EM.new
|
|
||||||
end
|
|
||||||
|
|
||||||
assert_raise(NameError, "We expected SomeClass to not be loaded but it is!") do
|
|
||||||
SomeClass.new
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
test "loading classes from files trigger autoloading" do
|
|
||||||
Tempfile.open("object_serializer_test") do |f|
|
|
||||||
with_autoloading_fixtures do
|
|
||||||
Marshal.dump(EM.new, f)
|
|
||||||
end
|
|
||||||
|
|
||||||
f.rewind
|
|
||||||
remove_constants(:EM)
|
|
||||||
ActiveSupport::Dependencies.clear
|
|
||||||
|
|
||||||
with_autoloading_fixtures do
|
|
||||||
object = nil
|
|
||||||
assert_nothing_raised do
|
|
||||||
object = Marshal.load(f)
|
|
||||||
end
|
|
||||||
|
|
||||||
assert_kind_of EM, object
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -4000,25 +4000,6 @@ NOTE: Defined in `active_support/core_ext/file/atomic.rb`.
|
||||||
|
|
||||||
[File.atomic_write]: https://api.rubyonrails.org/classes/File.html#method-c-atomic_write
|
[File.atomic_write]: https://api.rubyonrails.org/classes/File.html#method-c-atomic_write
|
||||||
|
|
||||||
Extensions to `Marshal`
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
### `load`
|
|
||||||
|
|
||||||
Active Support adds constant autoloading support to `load`.
|
|
||||||
|
|
||||||
For example, the file cache store deserializes this way:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
File.open(file_name) { |f| Marshal.load(f) }
|
|
||||||
```
|
|
||||||
|
|
||||||
If the cached data refers to a constant that is unknown at that point, the autoloading mechanism is triggered and if it succeeds the deserialization is retried transparently.
|
|
||||||
|
|
||||||
WARNING. If the argument is an `IO` it needs to respond to `rewind` to be able to retry. Regular files respond to `rewind`.
|
|
||||||
|
|
||||||
NOTE: Defined in `active_support/core_ext/marshal.rb`.
|
|
||||||
|
|
||||||
Extensions to `NameError`
|
Extensions to `NameError`
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue