memorb 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|