memorb 0.1.0
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 +7 -0
- data/.circleci/config.yml +52 -0
- data/.gitignore +4 -0
- data/.rspec +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +7 -0
- data/README.md +258 -0
- data/Rakefile +4 -0
- data/lib/memorb.rb +34 -0
- data/lib/memorb/agent.rb +30 -0
- data/lib/memorb/errors.rb +11 -0
- data/lib/memorb/integration.rb +277 -0
- data/lib/memorb/integrator_class_methods.rb +44 -0
- data/lib/memorb/key_value_store.rb +88 -0
- data/lib/memorb/method_identifier.rb +33 -0
- data/lib/memorb/ruby_compatibility.rb +55 -0
- data/lib/memorb/version.rb +5 -0
- data/memorb.gemspec +27 -0
- data/spec/memorb/agent_spec.rb +45 -0
- data/spec/memorb/integration_spec.rb +529 -0
- data/spec/memorb/integrator_class_methods_spec.rb +142 -0
- data/spec/memorb/key_value_store_spec.rb +91 -0
- data/spec/memorb/method_identifier_spec.rb +84 -0
- data/spec/memorb_spec.rb +198 -0
- data/spec/spec_helper.rb +69 -0
- metadata +111 -0
@@ -0,0 +1,142 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe ::Memorb::IntegratorClassMethods do
|
4
|
+
let(:integrator) { ::Class.new { extend ::Memorb } }
|
5
|
+
let(:integration) { ::Memorb::Integration[integrator] }
|
6
|
+
let(:instance) { integrator.new }
|
7
|
+
|
8
|
+
describe '::memorb' do
|
9
|
+
it 'returns the integration for the integrator' do
|
10
|
+
expect(integrator.memorb).to be(integration)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
describe '::memorb!' do
|
14
|
+
it 'calls register with the same arguments' do
|
15
|
+
spy = double('spy', register: nil)
|
16
|
+
mod = Module.new
|
17
|
+
::Memorb::RubyCompatibility.define_method(mod, :memorb) { spy }
|
18
|
+
integrator.singleton_class.prepend(mod)
|
19
|
+
block = Proc.new { nil }
|
20
|
+
expect(spy).to receive(:register).with(:a, &block)
|
21
|
+
integrator.memorb!(:a, &block)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
describe '::inherited' do
|
25
|
+
it 'makes children of integrators get their own integration' do
|
26
|
+
child_integrator = ::Class.new(integrator)
|
27
|
+
integration = ::Memorb::Integration[child_integrator]
|
28
|
+
expect(integration).not_to be(nil)
|
29
|
+
expected_ancestry = [integration, child_integrator]
|
30
|
+
expect(child_integrator.ancestors).to start_with(*expected_ancestry)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
describe '::method_added' do
|
34
|
+
let(:method_name) { :method_1 }
|
35
|
+
|
36
|
+
it 'retains upstream behavior' do
|
37
|
+
spy = double('spy', spy!: nil)
|
38
|
+
::Memorb::RubyCompatibility
|
39
|
+
.define_method(integrator.singleton_class, :method_added) do |m|
|
40
|
+
spy.spy!(m)
|
41
|
+
end
|
42
|
+
expect(spy).to receive(:spy!).with(method_name)
|
43
|
+
::Memorb::RubyCompatibility
|
44
|
+
.define_method(integrator, method_name) { nil }
|
45
|
+
end
|
46
|
+
context 'when the method has been registered' do
|
47
|
+
it 'enables the method' do
|
48
|
+
integration.register(method_name)
|
49
|
+
expect(integration.enabled_methods).not_to include(method_name)
|
50
|
+
expect(integration.public_instance_methods).not_to include(method_name)
|
51
|
+
::Memorb::RubyCompatibility
|
52
|
+
.define_method(integrator, method_name) { nil }
|
53
|
+
expect(integration.enabled_methods).to include(method_name)
|
54
|
+
expect(integration.public_instance_methods).to include(method_name)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
context 'when automatic registration is enabled' do
|
58
|
+
it 'registers and enables new methods' do
|
59
|
+
integration.send(:_auto_registration).increment
|
60
|
+
::Memorb::RubyCompatibility
|
61
|
+
.define_method(integrator, method_name) { nil }
|
62
|
+
expect(integration.registered_methods).to include(method_name)
|
63
|
+
expect(integration.enabled_methods).to include(method_name)
|
64
|
+
expect(integration.public_instance_methods).to include(method_name)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
describe '::method_removed' do
|
69
|
+
let(:method_name) { :method_1 }
|
70
|
+
let(:method_id) { ::Memorb::MethodIdentifier.new(method_name) }
|
71
|
+
|
72
|
+
it 'retains upstream behavior' do
|
73
|
+
::Memorb::RubyCompatibility
|
74
|
+
.define_method(integrator, method_name) { nil }
|
75
|
+
spy = double('spy', spy!: nil)
|
76
|
+
::Memorb::RubyCompatibility
|
77
|
+
.define_method(integrator.singleton_class, :method_removed) do |m|
|
78
|
+
spy.spy!(m)
|
79
|
+
end
|
80
|
+
expect(spy).to receive(:spy!).with(method_name)
|
81
|
+
::Memorb::RubyCompatibility.remove_method(integrator, method_name)
|
82
|
+
end
|
83
|
+
it 'removes the override for the method' do
|
84
|
+
::Memorb::RubyCompatibility
|
85
|
+
.define_method(integrator, method_name) { nil }
|
86
|
+
integration.register(method_name)
|
87
|
+
expect(integration.enabled_methods).to include(method_name)
|
88
|
+
expect(integration.public_instance_methods).to include(method_name)
|
89
|
+
::Memorb::RubyCompatibility.remove_method(integrator, method_name)
|
90
|
+
expect(integration.enabled_methods).not_to include(method_name)
|
91
|
+
expect(integration.public_instance_methods).not_to include(method_name)
|
92
|
+
end
|
93
|
+
it 'clears cached data for the method in all instances' do
|
94
|
+
::Memorb::RubyCompatibility
|
95
|
+
.define_method(integrator, method_name) { nil }
|
96
|
+
integration.register(method_name)
|
97
|
+
instance.send(method_name)
|
98
|
+
store = instance.memorb.method_store.read(method_id)
|
99
|
+
expect(store.keys).not_to be_empty
|
100
|
+
::Memorb::RubyCompatibility.remove_method(integrator, method_name)
|
101
|
+
expect(store.keys).to be_empty
|
102
|
+
end
|
103
|
+
end
|
104
|
+
describe '::method_undefined' do
|
105
|
+
let(:method_name) { :method_1 }
|
106
|
+
let(:method_id) { ::Memorb::MethodIdentifier.new(method_name) }
|
107
|
+
|
108
|
+
it 'retains upstream behavior' do
|
109
|
+
::Memorb::RubyCompatibility
|
110
|
+
.define_method(integrator, method_name) { nil }
|
111
|
+
spy = double('spy', spy!: nil)
|
112
|
+
::Memorb::RubyCompatibility
|
113
|
+
.define_method(integrator.singleton_class, :method_undefined) do |m|
|
114
|
+
spy.spy!(m)
|
115
|
+
end
|
116
|
+
expect(spy).to receive(:spy!).with(method_name)
|
117
|
+
::Memorb::RubyCompatibility.undef_method(integrator, method_name)
|
118
|
+
end
|
119
|
+
it 'undefines the override for the method' do
|
120
|
+
::Memorb::RubyCompatibility
|
121
|
+
.define_method(integrator, method_name) { nil }
|
122
|
+
integration.register(method_name)
|
123
|
+
::Memorb::RubyCompatibility.undef_method(integrator, method_name)
|
124
|
+
instance = integrator.new
|
125
|
+
expect(integration.enabled_methods).not_to include(method_name)
|
126
|
+
expect(integration.public_instance_methods).not_to include(method_name)
|
127
|
+
expect {
|
128
|
+
instance.send(method_name)
|
129
|
+
}.to raise_error(::NoMethodError, /undefined method/)
|
130
|
+
end
|
131
|
+
it 'clears cached data for the method in all instances' do
|
132
|
+
::Memorb::RubyCompatibility
|
133
|
+
.define_method(integrator, method_name) { nil }
|
134
|
+
integration.register(method_name)
|
135
|
+
instance.send(method_name)
|
136
|
+
store = instance.memorb.method_store.read(method_id)
|
137
|
+
expect(store.keys).not_to be_empty
|
138
|
+
::Memorb::RubyCompatibility.undef_method(integrator, method_name)
|
139
|
+
expect(store.keys).to be_empty
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe ::Memorb::KeyValueStore do
|
4
|
+
let(:key) { :key }
|
5
|
+
let(:value) { 'value' }
|
6
|
+
|
7
|
+
describe '#write' do
|
8
|
+
it 'stores a value' do
|
9
|
+
subject.write(key, value)
|
10
|
+
data = subject.instance_variable_get(:@data)
|
11
|
+
expect(data).to include(key => value)
|
12
|
+
end
|
13
|
+
it 'returns the stored value' do
|
14
|
+
result = subject.write(key, value)
|
15
|
+
expect(result).to equal(value)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
describe '#read' do
|
19
|
+
it 'retrieves a value for a given key' do
|
20
|
+
data = subject.instance_variable_get(:@data)
|
21
|
+
data[key] = value
|
22
|
+
result = subject.read(key)
|
23
|
+
expect(result).to equal(value)
|
24
|
+
end
|
25
|
+
context 'when a value has not been set' do
|
26
|
+
it 'returns nil' do
|
27
|
+
result = subject.read(key)
|
28
|
+
expect(result).to eq(nil)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
describe '#has?' do
|
33
|
+
context 'when a value has not been set' do
|
34
|
+
it 'return false' do
|
35
|
+
result = subject.has?(:key)
|
36
|
+
expect(result).to eq(false)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
context 'when a value has been set' do
|
40
|
+
it 'returns true' do
|
41
|
+
data = subject.instance_variable_get(:@data)
|
42
|
+
data[key] = value
|
43
|
+
result = subject.has?(:key)
|
44
|
+
expect(result).to be(true)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
describe '#fetch' do
|
49
|
+
context 'when a value has not been set' do
|
50
|
+
it 'stores and returns the result of the block' do
|
51
|
+
result = subject.fetch(key) { value }
|
52
|
+
expect(result).to equal(value)
|
53
|
+
data = subject.instance_variable_get(:@data)
|
54
|
+
expect(data[key]).to equal(value)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
context 'when a value has been set' do
|
58
|
+
it 'retrieves the value that was already set' do
|
59
|
+
data = subject.instance_variable_get(:@data)
|
60
|
+
data[key] = value
|
61
|
+
result = subject.fetch(key) { 'other' }
|
62
|
+
expect(result).to equal(value)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
describe '#forget' do
|
67
|
+
it 'removes the entry for the given key' do
|
68
|
+
data = subject.instance_variable_get(:@data)
|
69
|
+
original = { :k1 => :v1, :k2 => :v2 }
|
70
|
+
addition = { :k3 => :v3 }
|
71
|
+
data.merge!(original).merge!(addition)
|
72
|
+
subject.forget(:k3)
|
73
|
+
expect(data).to eq(original)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
describe '#reset!' do
|
77
|
+
it 'clears all data' do
|
78
|
+
data = subject.instance_variable_get(:@data)
|
79
|
+
data[key] = value
|
80
|
+
subject.reset!
|
81
|
+
expect(data).to be_empty
|
82
|
+
end
|
83
|
+
end
|
84
|
+
describe '#inspect' do
|
85
|
+
it 'displays the keys that it stores' do
|
86
|
+
[:symbol, 'string', 123, [:a, :b]].each { |k| subject.write(k, value) }
|
87
|
+
expectation = '#<Memorb::KeyValueStore keys=[:symbol, "string", 123, [:a, :b]]>'
|
88
|
+
expect(subject.inspect).to eq(expectation)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe ::Memorb::MethodIdentifier do
|
4
|
+
let(:method_name) { :method_1 }
|
5
|
+
subject { described_class.new(method_name) }
|
6
|
+
|
7
|
+
describe '#hash' do
|
8
|
+
it 'does not equal the hash of the method name symbol' do
|
9
|
+
expect(subject.hash).not_to equal(method_name.hash)
|
10
|
+
end
|
11
|
+
context 'when given an other instance with the same method name' do
|
12
|
+
it 'shares its hash value with the other instance' do
|
13
|
+
subject_1 = described_class.new(method_name)
|
14
|
+
subject_2 = described_class.new(method_name)
|
15
|
+
expect(subject_1.hash).to equal(subject_2.hash)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
context 'when given an other instance with a different method name' do
|
19
|
+
it 'does not share its hash value with the other instance' do
|
20
|
+
subject_1 = described_class.new(:method_1)
|
21
|
+
subject_2 = described_class.new(:method_2)
|
22
|
+
expect(subject_1.hash).not_to equal(subject_2.hash)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
describe '#eql?' do
|
27
|
+
context 'when given an other instance with the same method name' do
|
28
|
+
it 'returns true' do
|
29
|
+
subject_1 = described_class.new(method_name)
|
30
|
+
subject_2 = described_class.new(method_name)
|
31
|
+
result = subject_1.eql?(subject_2)
|
32
|
+
expect(result).to be(true)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
context 'when given an other instance with a different method name' do
|
36
|
+
it 'returns false' do
|
37
|
+
subject_1 = described_class.new(:method_1)
|
38
|
+
subject_2 = described_class.new(:method_2)
|
39
|
+
result = subject_1.eql?(subject_2)
|
40
|
+
expect(result).to be(false)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
describe '#==' do
|
45
|
+
context 'when given an other instance with the same method name' do
|
46
|
+
it 'is considered equal to the other instance' do
|
47
|
+
subject_1 = described_class.new('method_1')
|
48
|
+
subject_2 = described_class.new('method_1')
|
49
|
+
expect(subject_1 == subject_2).to be(true)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
context 'when given an other instance with a different method name' do
|
53
|
+
it 'is not considered equal to the other instance' do
|
54
|
+
subject_1 = described_class.new('method_1')
|
55
|
+
subject_2 = described_class.new('method_2')
|
56
|
+
expect(subject_1 == subject_2).to be(false)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
describe '#to_s' do
|
61
|
+
it 'returns the originally provided method name as a string' do
|
62
|
+
expect(subject.to_s).to eq('method_1')
|
63
|
+
end
|
64
|
+
end
|
65
|
+
describe '#to_sym' do
|
66
|
+
it 'returns the originally provided method name as a symbol' do
|
67
|
+
expect(subject.to_sym).to be(:method_1)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
context 'when used as a key in a hash' do
|
71
|
+
context 'accessing with different instances for the same method name' do
|
72
|
+
it 'shares the same key in the hash with the other instance' do
|
73
|
+
instance_1 = described_class.new(method_name)
|
74
|
+
instance_2 = described_class.new(method_name)
|
75
|
+
hash = { instance_1 => nil }
|
76
|
+
expect(hash).to have_key(instance_2)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
it 'does not refer to the same key as the symbol of the method name' do
|
80
|
+
hash = { subject => nil }
|
81
|
+
expect(hash).not_to have_key(method_name)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/spec/memorb_spec.rb
ADDED
@@ -0,0 +1,198 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe ::Memorb do
|
4
|
+
context 'when integrating improperly' do
|
5
|
+
let(:error) { ::Memorb::InvalidIntegrationError }
|
6
|
+
let(:error_message) { 'Memorb must be integrated using `extend`' }
|
7
|
+
|
8
|
+
context 'when included on a target' do
|
9
|
+
it 'raises an error' do
|
10
|
+
expect {
|
11
|
+
::Class.new { include ::Memorb }
|
12
|
+
}.to raise_error(error, error_message)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
context 'when prepended on a target' do
|
16
|
+
it 'raises an error' do
|
17
|
+
expect {
|
18
|
+
::Class.new { prepend ::Memorb }
|
19
|
+
}.to raise_error(error, error_message)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'integrators' do
|
25
|
+
shared_examples 'for ancestry verification' do
|
26
|
+
it 'has the correct ancestry' do
|
27
|
+
expect(integration).not_to be(nil)
|
28
|
+
ancestors = integrator.ancestors
|
29
|
+
expected_ancestry = [integration, integrator]
|
30
|
+
relevant_ancestors = ancestors.select { |a| expected_ancestry.include?(a) }
|
31
|
+
expect(relevant_ancestors).to match_array(expected_ancestry)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
let(:integration) { ::Memorb::Integration[integrator] }
|
36
|
+
let(:instance) { integrator.new }
|
37
|
+
|
38
|
+
describe 'a basic integrator' do
|
39
|
+
let(:integrator) {
|
40
|
+
::Class.new do
|
41
|
+
extend ::Memorb
|
42
|
+
end
|
43
|
+
}
|
44
|
+
include_examples 'for ancestry verification'
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'an integrator that includes Memorb more than once' do
|
48
|
+
let(:integrator) {
|
49
|
+
::Class.new do
|
50
|
+
extend ::Memorb
|
51
|
+
extend ::Memorb
|
52
|
+
end
|
53
|
+
}
|
54
|
+
include_examples 'for ancestry verification'
|
55
|
+
end
|
56
|
+
|
57
|
+
describe 'a child of an integrator' do
|
58
|
+
let(:parent_integrator) {
|
59
|
+
::Class.new do
|
60
|
+
extend ::Memorb
|
61
|
+
end
|
62
|
+
}
|
63
|
+
let(:integrator) {
|
64
|
+
::Class.new(parent_integrator)
|
65
|
+
}
|
66
|
+
include_examples 'for ancestry verification'
|
67
|
+
end
|
68
|
+
|
69
|
+
describe 'a child of an integrator that includes Memorb again' do
|
70
|
+
let(:parent_integrator) {
|
71
|
+
::Class.new do
|
72
|
+
extend ::Memorb
|
73
|
+
end
|
74
|
+
}
|
75
|
+
let(:integrator) {
|
76
|
+
::Class.new(parent_integrator) do
|
77
|
+
extend ::Memorb
|
78
|
+
end
|
79
|
+
}
|
80
|
+
include_examples 'for ancestry verification'
|
81
|
+
end
|
82
|
+
|
83
|
+
describe 'an integrator that aliases a method after registration' do
|
84
|
+
let(:integrator) {
|
85
|
+
::Class.new(::SpecHelper.basic_target_class) do
|
86
|
+
extend ::Memorb
|
87
|
+
memorb.register(:increment)
|
88
|
+
alias_method :other_increment, :increment
|
89
|
+
end
|
90
|
+
}
|
91
|
+
|
92
|
+
it 'implements caching for the aliased method' do
|
93
|
+
result_1 = instance.other_increment
|
94
|
+
result_2 = instance.other_increment
|
95
|
+
expect(result_1).to eq(result_2)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe 'an integrator that aliases a method before registration' do
|
100
|
+
let(:integrator) {
|
101
|
+
::Class.new(::SpecHelper.basic_target_class) do
|
102
|
+
extend ::Memorb
|
103
|
+
alias_method :other_increment, :increment
|
104
|
+
memorb.register(:increment)
|
105
|
+
end
|
106
|
+
}
|
107
|
+
|
108
|
+
it 'does not implement caching for the aliased method' do
|
109
|
+
result_1 = instance.other_increment
|
110
|
+
result_2 = instance.other_increment
|
111
|
+
expect(result_1).not_to eq(result_2)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
describe 'an integrator that uses alias method chaining' do
|
116
|
+
let(:integrator) {
|
117
|
+
::Class.new(::SpecHelper.basic_target_class) do
|
118
|
+
extend ::Memorb
|
119
|
+
memorb.register(:increment)
|
120
|
+
def new_increment; old_increment; end
|
121
|
+
alias_method :old_increment, :increment
|
122
|
+
alias_method :increment, :new_increment
|
123
|
+
end
|
124
|
+
}
|
125
|
+
|
126
|
+
it 'results in infinite recursion when the method is called' do
|
127
|
+
error = case ::RUBY_ENGINE
|
128
|
+
when 'jruby'
|
129
|
+
[java.lang.StackOverflowError]
|
130
|
+
else
|
131
|
+
[::SystemStackError, 'stack level too deep']
|
132
|
+
end
|
133
|
+
expect { instance.increment }.to raise_error(*error)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe 'an integrator with a method that accepts a block' do
|
138
|
+
let(:integrator) {
|
139
|
+
::Class.new(::SpecHelper.basic_target_class) do
|
140
|
+
extend ::Memorb
|
141
|
+
def with_block(&block); block ? block.call(self) : increment; end
|
142
|
+
memorb.register(:with_block)
|
143
|
+
end
|
144
|
+
}
|
145
|
+
|
146
|
+
it 'still gets the block as a parameter' do
|
147
|
+
result = instance.with_block { |x| x }
|
148
|
+
expect(result).to be(instance)
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'considers calls with different blocks to have the same arguments' do
|
152
|
+
result_1 = instance.with_block { |x| x.increment }
|
153
|
+
result_2 = instance.with_block { |x| x.increment }
|
154
|
+
expect(result_1).to eq(result_2)
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'considers calls with different proc-blocks to have the same arguments' do
|
158
|
+
proc_1 = ::Proc.new { |x| x.increment }
|
159
|
+
proc_2 = ::Proc.new { |x| x.increment }
|
160
|
+
result_1 = instance.with_block(&proc_1)
|
161
|
+
result_2 = instance.with_block(&proc_2)
|
162
|
+
expect(result_1).to eq(result_2)
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'considers a call with a block to be the same as a call without one' do
|
166
|
+
result_1 = instance.with_block { |x| x.increment }
|
167
|
+
result_2 = instance.with_block
|
168
|
+
expect(result_1).to eq(result_2)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
::SpecHelper.for_testing_garbage_collection do
|
173
|
+
let(:integrator) {
|
174
|
+
::Class.new(::SpecHelper.basic_target_class) do
|
175
|
+
extend ::Memorb
|
176
|
+
memorb.register(:noop)
|
177
|
+
end
|
178
|
+
}
|
179
|
+
|
180
|
+
describe 'a method argument for a memoized method' do
|
181
|
+
it 'allows the argument to be garbage collected' do
|
182
|
+
ref = ::WeakRef.new(Object.new)
|
183
|
+
instance.send(:noop, ref.__getobj__)
|
184
|
+
::SpecHelper.force_garbage_collection
|
185
|
+
expect(ref.weakref_alive?).to be_falsey
|
186
|
+
end
|
187
|
+
end
|
188
|
+
describe 'a low-level cache fetch' do
|
189
|
+
it 'allows the cache key to be garbage collected' do
|
190
|
+
ref = ::WeakRef.new(Object.new)
|
191
|
+
instance.memorb.fetch(ref.__getobj__) { nil }
|
192
|
+
::SpecHelper.force_garbage_collection
|
193
|
+
expect(ref.weakref_alive?).to be_falsey
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|