Last active
September 23, 2023 07:25
-
-
Save AMekss/ceb528f97638c45ad6c7 to your computer and use it in GitHub Desktop.
Convinient and simple way how to do instance level caching.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Source: https://gist.github.com/ceb528f97638c45ad6c7 | |
# Replacement for direct @var ||= solution but this one correctly memoize non-true values (nil, false, etc) and is simple.. | |
# Much simpler than ActiveSupport::Memoize which is deprecated (in Rails 3.2) by the way | |
module InstanceCaching | |
def remember(*args) | |
caller_name = caller(1..1).first[/`(.*)'/, 1] | |
caller_args_digest = Digest::MD5.hexdigest(args.to_s) | |
cache = @instance_caching_memoizations_aw5zdgf ||= {} | |
cache_key = "#{caller_name}_#{caller_args_digest}" | |
cache.key?(cache_key) ? cache[cache_key] : cache[cache_key] = yield | |
end | |
def reload(*) | |
@instance_caching_memoizations_aw5zdgf = {} | |
super if defined?(super) | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "rails_helper" | |
RSpec.describe InstanceCaching do | |
describe "#remember" do | |
let(:test_class) do | |
Struct.new(:test_phrase) do | |
include InstanceCaching | |
def cache_this | |
remember do | |
test_phrase | |
end | |
end | |
def cache_this_with_args(*args) | |
remember(*args) do | |
"#{test_phrase}_#{args}" | |
end | |
end | |
def cache_bool? | |
remember do | |
!test_phrase | |
end | |
end | |
def cache_nil! | |
remember do | |
test_phrase | |
nil | |
end | |
end | |
end | |
end | |
let(:instance1) { test_class.new(instance1_text) } | |
let(:instance1_text) { "this is test" } | |
let(:instance2) { test_class.new(instance2_text) } | |
let(:instance2_text) { "this is another test" } | |
before do | |
allow(instance1).to receive(:test_phrase).once.and_call_original | |
allow(instance2).to receive(:test_phrase).once.and_call_original | |
end | |
it "invokes code for instance method only once per instance" do | |
expect(instance1.cache_this).to eq instance1_text | |
expect(instance1.cache_this).to eq instance1_text | |
expect(instance2.cache_this).to eq instance2_text | |
expect(instance2.cache_this).to eq instance2_text | |
expect(instance2.cache_this).to eq instance2_text | |
end | |
it "is not confused by the bang! method returning nil" do | |
expect(instance1.cache_nil!).to eq instance1.cache_nil! | |
end | |
it "is not confused by the query? method returning boolean" do | |
expect(instance1.cache_bool?).to eq instance1.cache_bool? | |
end | |
it "invokes code for instance method with argumnents only once per instance" do | |
args = ["a", "b"] | |
expect(instance1.cache_this_with_args(*args)).to eq instance1.cache_this_with_args(*args) | |
expect(instance2.cache_this_with_args(*args)).to eq instance2.cache_this_with_args(*args) | |
end | |
context "#reload" do | |
before do | |
expect(instance1).to receive(:test_phrase).twice.and_call_original | |
end | |
it "resets state" do | |
expect(instance1.cache_this).to eq instance1_text | |
expect(instance1.cache_this).to eq instance1_text | |
instance1.reload | |
expect(instance1.cache_this).to eq instance1_text | |
expect(instance1.cache_this).to eq instance1_text | |
end | |
end | |
context "when args changing" do | |
before do | |
expect(instance1).to receive(:test_phrase).twice.and_call_original | |
end | |
it "invokes code for instance method only once per params set" do | |
expect(instance1.cache_this_with_args("foo")).to eq instance1.cache_this_with_args("foo") | |
expect(instance1.cache_this_with_args("bar")).to eq instance1.cache_this_with_args("bar") | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment