diff --git a/app/services/cache_service.rb b/app/services/cache_service.rb new file mode 100644 index 0000000000..4a87dcea9a --- /dev/null +++ b/app/services/cache_service.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class CacheService + def self.cache(cache_key, options = {}) + Rails.cache.fetch cache_key.to_s, options do + yield + end + end + + # Yields a cached query, expired by the most recently updated record for a given class. + # E.g: if *any* Spree::Taxon record is updated, all keys based on Spree::Taxon will auto-expire. + def self.cached_data_by_class(cache_key, cached_class) + Rails.cache.fetch "#{cache_key}-#{cached_class}-#{latest_timestamp_by_class(cached_class)}" do + yield + end + end + + # Gets the :updated_at value of the most recently updated record for a given class, and returns + # it as a timestamp, eg: `1583836069`. + def self.latest_timestamp_by_class(cached_class) + cached_class.maximum(:updated_at).to_i + end +end diff --git a/spec/services/cache_service_spec.rb b/spec/services/cache_service_spec.rb new file mode 100644 index 0000000000..22a6e2b772 --- /dev/null +++ b/spec/services/cache_service_spec.rb @@ -0,0 +1,50 @@ +require 'spec_helper' + +describe CacheService do + let(:rails_cache) { Rails.cache } + + describe "#cache" do + before do + rails_cache.stub(:fetch) + end + + it "provides a wrapper for basic #fetch calls to Rails.cache" do + CacheService.cache("test-cache-key", expires_in: 10.seconds) do + "TEST" + end + + expect(rails_cache).to have_received(:fetch).with("test-cache-key", expires_in: 10.seconds) + end + end + + describe "#cached_data_by_class" do + let(:timestamp) { Time.now.to_i } + + before do + rails_cache.stub(:fetch) + CacheService.stub(:latest_timestamp_by_class) { timestamp } + end + + it "caches data by timestamp for last record of that class" do + CacheService.cached_data_by_class("test-cache-key", Enterprise) do + "TEST" + end + + expect(CacheService).to have_received(:latest_timestamp_by_class).with(Enterprise) + expect(rails_cache).to have_received(:fetch).with("test-cache-key-Enterprise-#{timestamp}") + end + end + + describe "#latest_timestamp_by_class" do + let!(:taxon1) { create(:taxon) } + let!(:taxon2) { create(:taxon) } + + it "gets the :updated_at value of the last record for a given class and returns a timestamp" do + taxon1.touch + expect(CacheService.latest_timestamp_by_class(Spree::Taxon)).to eq taxon1.updated_at.to_i + + taxon2.touch + expect(CacheService.latest_timestamp_by_class(Spree::Taxon)).to eq taxon2.updated_at.to_i + end + end +end