Merge pull request #5319 from Matt-Yorkley/caching-by-class

[Caching] Caching data by class
This commit is contained in:
Luis Ramos
2020-05-07 11:43:44 +01:00
committed by GitHub
11 changed files with 144 additions and 10 deletions

View File

@@ -1,5 +1,5 @@
class DistributorShippingMethod < ActiveRecord::Base
self.table_name = "distributors_shipping_methods"
belongs_to :shipping_method, class_name: Spree::ShippingMethod
belongs_to :shipping_method, class_name: Spree::ShippingMethod, touch: true
belongs_to :distributor, class_name: Enterprise, touch: true
end

View File

@@ -1,5 +1,6 @@
Spree::Classification.class_eval do
belongs_to :product, class_name: "Spree::Product", touch: true
belongs_to :taxon, class_name: "Spree::Taxon", touch: true
before_destroy :dont_destroy_if_primary_taxon

View File

@@ -12,7 +12,7 @@ Spree::Product.class_eval do
has_many :option_types, through: :product_option_types, dependent: :destroy
belongs_to :supplier, class_name: 'Enterprise', touch: true
belongs_to :primary_taxon, class_name: 'Spree::Taxon'
belongs_to :primary_taxon, class_name: 'Spree::Taxon', touch: true
delegate_belongs_to :master, :unit_value, :unit_description
delegate :images_attributes=, :display_as=, to: :master

View File

@@ -22,4 +22,17 @@ class CacheService
def self.latest_timestamp_by_class(cached_class)
cached_class.maximum(:updated_at).to_i
end
module FragmentCaching
# Rails' caching in views is called "Fragment Caching" and uses some slightly different logic.
# Note: supplied keys are actually prepended with "view/" under the hood.
def self.ams_all_taxons_key
"inject-all-taxons-#{CacheService.latest_timestamp_by_class(Spree::Taxon)}"
end
def self.ams_all_properties_key
"inject-all-properties-#{CacheService.latest_timestamp_by_class(Spree::Property)}"
end
end
end

View File

@@ -48,8 +48,10 @@
= inject_current_hub
= inject_current_user
= inject_rails_flash
= inject_taxons
= inject_properties
- cache CacheService::FragmentCaching.ams_all_taxons_key do
= inject_taxons
- cache CacheService::FragmentCaching.ams_all_properties_key do
= inject_properties
= inject_current_order
= inject_currency_config
= yield :injection_data

View File

@@ -10,11 +10,25 @@ module OpenFoodNetwork
end
def shipping_method_services
@shipping_method_services ||= Spree::ShippingMethod.services
@shipping_method_services ||= begin
CacheService.cached_data_by_class("shipping_method_services", Spree::ShippingMethod) do
# This result relies on a simple join with DistributorShippingMethod.
# Updated DistributorShippingMethod records touch their associated Spree::ShippingMethod.
Spree::ShippingMethod.services
end
end
end
def supplied_taxons
@supplied_taxons ||= Spree::Taxon.supplied_taxons
@supplied_taxons ||= begin
CacheService.cached_data_by_class("supplied_taxons", Spree::Taxon) do
# This result relies on a join with associated supplied products, through the
# class Classification which maps the relationship. Classification records touch
# their associated Spree::Taxon when updated. A Spree::Product's primary_taxon
# is also touched when changed.
Spree::Taxon.supplied_taxons
end
end
end
def all_distributed_taxons

View File

@@ -0,0 +1,76 @@
# frozen_string_literal: true
require "spec_helper"
feature "Darkswarm data caching", js: true, caching: true do
let!(:taxon) { create(:taxon, name: "Cached Taxon") }
let!(:property) { create(:property, presentation: "Cached Property") }
let!(:producer) { create(:supplier_enterprise) }
let!(:distributor) { create(:distributor_enterprise, with_payment_and_shipping: true, is_primary_producer: true) }
let!(:product) { create(:simple_product, supplier: producer, primary_taxon: taxon, taxons: [taxon], properties: [property]) }
let!(:order_cycle) { create(:simple_order_cycle, distributors: [distributor], coordinator: distributor) }
let(:exchange) { order_cycle.exchanges.outgoing.where(receiver_id: distributor.id).first }
before do
exchange.variants << product.variants.first
end
describe "caching injected taxons and properties" do
it "caches taxons and properties" do
expect(Spree::Taxon).to receive(:all) { [taxon] }
expect(Spree::Property).to receive(:all) { [property] }
visit shops_path
expect(Spree::Taxon).to_not receive(:all)
expect(Spree::Property).to_not receive(:all)
visit shops_path
end
it "invalidates caches for taxons and properties" do
visit shops_path
taxon_timestamp1 = CacheService.latest_timestamp_by_class(Spree::Taxon)
expect_cached "views/#{CacheService::FragmentCaching.ams_all_taxons_key}"
property_timestamp1 = CacheService.latest_timestamp_by_class(Spree::Property)
expect_cached "views/#{CacheService::FragmentCaching.ams_all_properties_key}"
toggle_filters
within "#hubs .filter-row" do
expect(page).to have_content taxon.name
expect(page).to have_content property.presentation
end
taxon.name = "Changed Taxon"
taxon.save
property.presentation = "Changed Property"
property.save
visit shops_path
taxon_timestamp2 = CacheService.latest_timestamp_by_class(Spree::Taxon)
expect_cached "views/#{CacheService::FragmentCaching.ams_all_taxons_key}"
property_timestamp2 = CacheService.latest_timestamp_by_class(Spree::Property)
expect_cached "views/#{CacheService::FragmentCaching.ams_all_properties_key}"
expect(taxon_timestamp1).to_not eq taxon_timestamp2
expect(property_timestamp1).to_not eq property_timestamp2
toggle_filters
within "#hubs .filter-row" do
expect(page).to have_content "Changed Taxon"
expect(page).to have_content "Changed Property"
end
end
end
def expect_cached(key)
expect(Rails.cache.exist?(key)).to be true
end
end

View File

@@ -16,8 +16,8 @@ feature '
let(:taxon_fruit) { create(:taxon, name: 'Fruit') }
let(:taxon_veg) { create(:taxon, name: 'Vegetables') }
let!(:product1) { create(:simple_product, supplier: producer1, taxons: [taxon_fruit]) }
let!(:product2) { create(:simple_product, supplier: producer2, taxons: [taxon_veg]) }
let!(:product1) { create(:simple_product, supplier: producer1, primary_taxon: taxon_fruit, taxons: [taxon_fruit]) }
let!(:product2) { create(:simple_product, supplier: producer2, primary_taxon: taxon_veg, taxons: [taxon_veg]) }
let(:shop) { create(:distributor_enterprise) }
let!(:er) { create(:enterprise_relationship, parent: shop, child: producer1) }

View File

@@ -110,13 +110,13 @@ feature 'Shops', js: true do
describe "taxon badges" do
let!(:closed_oc) { create(:closed_order_cycle, distributors: [shop], variants: [p_closed.variants.first]) }
let!(:p_closed) { create(:simple_product, taxons: [taxon_closed]) }
let!(:p_closed) { create(:simple_product, primary_taxon: taxon_closed, taxons: [taxon_closed]) }
let(:shop) { create(:distributor_enterprise, with_payment_and_shipping: true) }
let(:taxon_closed) { create(:taxon, name: 'Closed') }
describe "open shops" do
let!(:open_oc) { create(:open_order_cycle, distributors: [shop], variants: [p_open.variants.first]) }
let!(:p_open) { create(:simple_product, taxons: [taxon_open]) }
let!(:p_open) { create(:simple_product, primary_taxon: taxon_open, taxons: [taxon_open]) }
let(:taxon_open) { create(:taxon, name: 'Open') }
it "shows taxons for open order cycles only" do

View File

@@ -109,5 +109,15 @@ module Spree
expect(shipping_method.include?(address)).to be true
end
end
describe "touches" do
let!(:distributor) { create(:distributor_enterprise) }
let!(:shipping_method) { create(:shipping_method) }
let(:add_distributor) { shipping_method.distributors << distributor }
it "is touched when applied to a distributor" do
expect{ add_distributor }.to change { shipping_method.reload.updated_at}
end
end
end
end

View File

@@ -28,5 +28,23 @@ module Spree
expect(Taxon.distributed_taxons(:current)).to eq(e.id => Set.new([t1.id]))
end
end
describe "touches" do
let!(:taxon1) { create(:taxon) }
let!(:taxon2) { create(:taxon) }
let!(:taxon3) { create(:taxon) }
let!(:product) { create(:simple_product, primary_taxon: taxon1, taxons: [taxon1, taxon2]) }
it "is touched when a taxon is applied to a product" do
expect{ product.taxons << taxon3 }.to change { taxon3.reload.updated_at }
end
it "is touched when assignment of primary_taxon on a product changes" do
expect do
product.primary_taxon = taxon2
product.save
end.to change { taxon2.reload.updated_at }
end
end
end
end