From 8af6866ae4775fc614fe7ab72d7500fd575d6adf Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 5 Feb 2016 10:37:05 +1100 Subject: [PATCH] Refresh products cache when exchange is changed or destroyed --- app/models/exchange.rb | 10 +++ lib/open_food_network/products_cache.rb | 38 ++++++++--- .../open_food_network/products_cache_spec.rb | 65 +++++++++++++++++++ spec/models/exchange_spec.rb | 15 +++++ 4 files changed, 120 insertions(+), 8 deletions(-) diff --git a/app/models/exchange.rb b/app/models/exchange.rb index 137924f7ac..94aada7e0e 100644 --- a/app/models/exchange.rb +++ b/app/models/exchange.rb @@ -13,6 +13,9 @@ class Exchange < ActiveRecord::Base validates_presence_of :order_cycle, :sender, :receiver validates_uniqueness_of :sender_id, :scope => [:order_cycle_id, :receiver_id, :incoming] + after_save :refresh_products_cache + after_destroy :refresh_products_cache_from_destroy + accepts_nested_attributes_for :variants scope :in_order_cycle, lambda { |order_cycle| where(order_cycle_id: order_cycle) } @@ -75,4 +78,11 @@ class Exchange < ActiveRecord::Base end end + def refresh_products_cache + OpenFoodNetwork::ProductsCache.exchange_changed self + end + + def refresh_products_cache_from_destroy + OpenFoodNetwork::ProductsCache.exchange_destroyed self + end end diff --git a/lib/open_food_network/products_cache.rb b/lib/open_food_network/products_cache.rb index b1d63c0b9a..d1ce117734 100644 --- a/lib/open_food_network/products_cache.rb +++ b/lib/open_food_network/products_cache.rb @@ -50,6 +50,20 @@ module OpenFoodNetwork end + def self.exchange_changed(exchange) + if exchange.incoming + refresh_incoming_exchanges(Exchange.where(id: exchange)) + else + refresh_outgoing_exchange(exchange) + end + end + + + def self.exchange_destroyed(exchange) + exchange_changed exchange + end + + def self.enterprise_fee_changed(enterprise_fee) refresh_supplier_fee enterprise_fee refresh_coordinator_fee enterprise_fee @@ -73,19 +87,27 @@ module OpenFoodNetwork end - def self.refresh_supplier_fee(enterprise_fee) - outgoing_exchanges = Set.new - - incoming_exchanges(enterprise_fee.exchanges).each do |exchange| - outgoing_exchanges.merge outgoing_exchanges_with_variants(exchange.order_cycle, exchange.variant_ids) - end - - outgoing_exchanges.each do |exchange| + def self.refresh_incoming_exchanges(exchanges) + incoming_exchanges(exchanges).map do |exchange| + outgoing_exchanges_with_variants(exchange.order_cycle, exchange.variant_ids) + end.flatten.uniq.each do |exchange| refresh_cache exchange.receiver, exchange.order_cycle end end + def self.refresh_outgoing_exchange(exchange) + if exchange.order_cycle.dated? && !exchange.order_cycle.closed? + refresh_cache exchange.receiver, exchange.order_cycle + end + end + + + def self.refresh_supplier_fee(enterprise_fee) + refresh_incoming_exchanges(enterprise_fee.exchanges) + end + + def self.refresh_coordinator_fee(enterprise_fee) enterprise_fee.order_cycles.each do |order_cycle| order_cycle_changed order_cycle diff --git a/spec/lib/open_food_network/products_cache_spec.rb b/spec/lib/open_food_network/products_cache_spec.rb index 56b557a7ee..6c5d3019d3 100644 --- a/spec/lib/open_food_network/products_cache_spec.rb +++ b/spec/lib/open_food_network/products_cache_spec.rb @@ -164,6 +164,71 @@ module OpenFoodNetwork end + describe "when an exchange is changed" do + let(:s) { create(:supplier_enterprise) } + let(:c) { create(:distributor_enterprise) } + let(:d1) { create(:distributor_enterprise) } + let(:d2) { create(:distributor_enterprise) } + let(:v) { create(:variant) } + let(:oc) { create(:open_order_cycle, coordinator: c) } + + describe "incoming exchanges" do + let!(:ex1) { create(:exchange, order_cycle: oc, sender: s, receiver: c, incoming: true, variants: [v]) } + let!(:ex2) { create(:exchange, order_cycle: oc, sender: c, receiver: d1, incoming: false, variants: [v]) } + let!(:ex3) { create(:exchange, order_cycle: oc, sender: c, receiver: d2, incoming: false, variants: []) } + + before { oc.reload } + + it "updates distributions that include one of the supplier's variants" do + expect(ProductsCache).to receive(:refresh_cache).with(d1, oc).once + ProductsCache.exchange_changed ex1 + end + + it "doesn't update distributions that don't include any of the supplier's variants" do + expect(ProductsCache).to receive(:refresh_cache).with(d2, oc).never + ProductsCache.exchange_changed ex1 + end + end + + describe "outgoing exchanges" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: c, receiver: d1, incoming: false) } + + it "does not update for undated order cycles" do + oc.update_attributes! orders_open_at: nil, orders_close_at: nil + expect(ProductsCache).to receive(:refresh_cache).with(d1, oc).never + ProductsCache.exchange_changed ex + end + + it "updates for upcoming order cycles" do + oc.update_attributes! orders_open_at: 1.week.from_now, orders_close_at: 2.weeks.from_now + expect(ProductsCache).to receive(:refresh_cache).with(d1, oc).once + ProductsCache.exchange_changed ex + end + + it "updates for open order cycles" do + expect(ProductsCache).to receive(:refresh_cache).with(d1, oc).once + ProductsCache.exchange_changed ex + end + + it "does not update for closed order cycles" do + oc.update_attributes! orders_open_at: 2.weeks.ago, orders_close_at: 1.week.ago + expect(ProductsCache).to receive(:refresh_cache).with(d1, oc).never + ProductsCache.exchange_changed ex + end + end + end + + + describe "when an exchange is destroyed" do + let(:exchange) { double(:exchange) } + + it "triggers the same update as a change to the exchange" do + expect(ProductsCache).to receive(:exchange_changed).with(exchange) + ProductsCache.exchange_destroyed exchange + end + end + + describe "when an enterprise fee is changed" do let(:s) { create(:supplier_enterprise) } let(:c) { create(:distributor_enterprise) } diff --git a/spec/models/exchange_spec.rb b/spec/models/exchange_spec.rb index ac6b7681a8..4b8f0ed1a3 100644 --- a/spec/models/exchange_spec.rb +++ b/spec/models/exchange_spec.rb @@ -91,6 +91,21 @@ describe Exchange do end end + describe "products caching" do + let!(:exchange) { create(:exchange) } + + it "refreshes the products cache on change" do + expect(OpenFoodNetwork::ProductsCache).to receive(:exchange_changed).with(exchange) + exchange.pickup_time = 'asdf' + exchange.save + end + + it "refreshes the products cache on destruction" do + expect(OpenFoodNetwork::ProductsCache).to receive(:exchange_destroyed).with(exchange) + exchange.destroy + end + end + describe "scopes" do let(:supplier) { create(:supplier_enterprise) } let(:coordinator) { create(:distributor_enterprise, is_primary_producer: true) }