Skip to content

Commit

Permalink
Add finalizer to weak instance cache
Browse files Browse the repository at this point in the history
  • Loading branch information
chopraanmol1 committed Jan 11, 2019
1 parent aee36f2 commit 5ce39e0
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 0 deletions.
9 changes: 9 additions & 0 deletions lib/roo/helpers/weak_instance_cache.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,20 @@ def instance_cache(key)

unless object
object = yield
ObjectSpace.define_finalizer(object, instance_cache_finalizer(key))
instance_variable_set(key, WeakRef.new(object))
end

object
end

def instance_cache_finalizer(key)
proc do
if instance_variable_defined?(key) && (ref = instance_variable_get(key)) && !ref.weakref_alive?
remove_instance_variable(key)
end
end
end
end
end
end
69 changes: 69 additions & 0 deletions spec/lib/roo/weak_instance_cache_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
require 'spec_helper'

describe Roo::Helpers::WeakInstanceCache do
let(:klass) do
Class.new do
include Roo::Helpers::WeakInstanceCache

def memoized_data
instance_cache(:@memoized_data) do
"Some Costly Operation" * 1_000
end
end
end
end

subject do
klass.new
end

it 'should be lazy' do
expect(subject.instance_variables).to_not include(:@memoized_data)
data = subject.memoized_data
expect(subject.instance_variables).to include(:@memoized_data)
end


it 'should be memoized' do
data = subject.memoized_data
expect(subject.memoized_data).to equal(data)
end

it 'should recalculate after GC' do
GC.disable
original_id = subject.memoized_data.object_id
expect(subject.memoized_data.object_id).to eq(original_id)

GC.start
expect(subject.memoized_data.object_id).not_to eq(original_id)
end

it 'must remove instance variable' do
expect(subject.instance_variables).to_not include(:@memoized_data)
GC.disable
subject.memoized_data
expect(subject.instance_variables).to include(:@memoized_data)

GC.start
expect(subject.instance_variables).to_not include(:@memoized_data)
end

context '#inspect must not raise' do
it 'before calculation' do
expect{subject.inspect}.to_not raise_error
end
it 'after calculation' do
GC.disable
subject.memoized_data
expect{subject.inspect}.to_not raise_error
expect(subject.inspect).to include("Some Costly Operation")
GC.start
end
it 'after GC' do
subject.memoized_data
GC.start
expect{subject.inspect}.to_not raise_error
expect(subject.inspect).to_not include("Some Costly Operation")
end
end
end

0 comments on commit 5ce39e0

Please sign in to comment.