From a4eae6a9c726837ba0aa7614b90cd7e1857f5699 Mon Sep 17 00:00:00 2001 From: Zach Gollwitzer Date: Thu, 15 May 2025 09:43:55 -0400 Subject: [PATCH] Sync broadcasters --- app/models/account/sync_complete_event.rb | 54 ++++++++++++++++++++ app/models/account/syncer.rb | 8 --- app/models/concerns/syncable.rb | 8 +++ app/models/family/sync_complete_event.rb | 21 ++++++++ app/models/family/syncer.rb | 1 - app/models/plaid_item/sync_complete_event.rb | 22 ++++++++ app/models/plaid_item/syncer.rb | 7 --- app/models/sync.rb | 5 ++ app/models/sync_complete_event.rb | 37 -------------- test/models/sync_test.rb | 11 ++++ 10 files changed, 121 insertions(+), 53 deletions(-) create mode 100644 app/models/account/sync_complete_event.rb create mode 100644 app/models/family/sync_complete_event.rb create mode 100644 app/models/plaid_item/sync_complete_event.rb delete mode 100644 app/models/sync_complete_event.rb diff --git a/app/models/account/sync_complete_event.rb b/app/models/account/sync_complete_event.rb new file mode 100644 index 00000000..3ef95507 --- /dev/null +++ b/app/models/account/sync_complete_event.rb @@ -0,0 +1,54 @@ +class Account::SyncCompleteEvent + attr_reader :account + + def initialize(account) + @account = account + end + + def broadcast + # Refresh entire account page (only applies if currently viewing this account) + account.broadcast_refresh + + # Replace account row in accounts list + account.broadcast_replace_to( + account.family, + target: "account_#{account.id}", + partial: "accounts/account", + locals: { account: account } + ) + + # Replace the groups this account belongs to in the sidebar + account_group_ids.each do |id| + account.broadcast_replace_to( + account.family, + target: id, + partial: "accounts/accountable_group", + locals: { account_group: account_group, open: true } + ) + end + + # If this is a manual, unlinked account (i.e. not part of a Plaid Item), + # trigger the family sync complete broadcast so net worth graph is updated + unless account.linked? + account.family.broadcast_sync_complete + end + end + + private + # The sidebar will show the account in both its classification tab and the "all" tab, + # so we need to broadcast to both. + def account_group_ids + id = account_group.id + [ id, "#{account_group.classification}_#{id}" ] + end + + def account_group + family_balance_sheet.account_groups.find do |group| + group.accounts.any? { |a| a.id == account.id } + end + end + + def family_balance_sheet + account.family.balance_sheet + end +end diff --git a/app/models/account/syncer.rb b/app/models/account/syncer.rb index 5554370b..7e5fabcd 100644 --- a/app/models/account/syncer.rb +++ b/app/models/account/syncer.rb @@ -12,14 +12,6 @@ class Account::Syncer def perform_post_sync account.family.auto_match_transfers! - account.broadcast_refresh - SyncCompleteEvent.new(account.family, accounts: [ account ]).broadcast - account.broadcast_replace_to( - account.family, - target: "account_#{account.id}", - partial: "accounts/account", - locals: { account: account } - ) end private diff --git a/app/models/concerns/syncable.rb b/app/models/concerns/syncable.rb index 5223d587..8dfa8e41 100644 --- a/app/models/concerns/syncable.rb +++ b/app/models/concerns/syncable.rb @@ -22,6 +22,10 @@ module Syncable syncer.perform_post_sync end + def broadcast_sync_complete + sync_broadcaster.broadcast + end + def sync_error latest_sync&.error end @@ -42,4 +46,8 @@ module Syncable def syncer self.class::Syncer.new(self) end + + def sync_broadcaster + self.class::SyncCompleteEvent.new(self) + end end diff --git a/app/models/family/sync_complete_event.rb b/app/models/family/sync_complete_event.rb new file mode 100644 index 00000000..628841d0 --- /dev/null +++ b/app/models/family/sync_complete_event.rb @@ -0,0 +1,21 @@ +class Family::SyncCompleteEvent + attr_reader :family + + def initialize(family) + @family = family + end + + def broadcast + family.broadcast_replace( + target: "balance-sheet", + partial: "pages/dashboard/balance_sheet", + locals: { balance_sheet: family.balance_sheet } + ) + + family.broadcast_replace( + target: "net-worth-chart", + partial: "pages/dashboard/net_worth_chart", + locals: { balance_sheet: family.balance_sheet, period: Period.last_30_days } + ) + end +end diff --git a/app/models/family/syncer.rb b/app/models/family/syncer.rb index ac06bce9..30ce2ad5 100644 --- a/app/models/family/syncer.rb +++ b/app/models/family/syncer.rb @@ -22,7 +22,6 @@ class Family::Syncer def perform_post_sync family.auto_match_transfers! - family.broadcast_refresh end private diff --git a/app/models/plaid_item/sync_complete_event.rb b/app/models/plaid_item/sync_complete_event.rb new file mode 100644 index 00000000..ca008a0a --- /dev/null +++ b/app/models/plaid_item/sync_complete_event.rb @@ -0,0 +1,22 @@ +class PlaidItem::SyncCompleteEvent + attr_reader :plaid_item + + def initialize(plaid_item) + @plaid_item = plaid_item + end + + def broadcast + plaid_item.accounts.each do |account| + account.broadcast_sync_complete + end + + plaid_item.broadcast_replace_to( + plaid_item.family, + target: "plaid_item_#{plaid_item.id}", + partial: "plaid_items/plaid_item", + locals: { plaid_item: plaid_item } + ) + + plaid_item.family.broadcast_sync_complete + end +end diff --git a/app/models/plaid_item/syncer.rb b/app/models/plaid_item/syncer.rb index 8baeed42..e32b9ed4 100644 --- a/app/models/plaid_item/syncer.rb +++ b/app/models/plaid_item/syncer.rb @@ -24,13 +24,6 @@ class PlaidItem::Syncer def perform_post_sync plaid_item.auto_match_categories! - SyncCompleteEvent.new(plaid_item.family, accounts: plaid_item.accounts).broadcast - plaid_item.broadcast_replace_to( - plaid_item.family, - target: "plaid_item_#{plaid_item.id}", - partial: "plaid_items/plaid_item", - locals: { plaid_item: plaid_item } - ) end private diff --git a/app/models/sync.rb b/app/models/sync.rb index cbe560d1..117e2d46 100644 --- a/app/models/sync.rb +++ b/app/models/sync.rb @@ -39,6 +39,8 @@ class Sync < ApplicationRecord Rails.logger.tagged("Sync", id, syncable_type, syncable_id) do start! + sleep 10 + begin syncable.perform_sync(self) rescue => e @@ -89,8 +91,11 @@ class Sync < ApplicationRecord end def perform_post_sync + Rails.logger.info("Performing post-sync for #{syncable_type} (#{syncable.id})") syncable.perform_post_sync + syncable.broadcast_sync_complete rescue => e + Rails.logger.error("Error performing post-sync for #{syncable_type} (#{syncable.id}): #{e.message}") report_error(e) end diff --git a/app/models/sync_complete_event.rb b/app/models/sync_complete_event.rb deleted file mode 100644 index 44a17d37..00000000 --- a/app/models/sync_complete_event.rb +++ /dev/null @@ -1,37 +0,0 @@ -class SyncCompleteEvent - attr_reader :family, :accounts - - def initialize(family, accounts: []) - @family = family - @accounts = accounts - end - - def broadcast - account_groups = family.balance_sheet.account_groups.select do |group| - group.accounts.any? { |a| accounts.include?(a) } - end - - account_groups.each do |account_group| - [ nil, "asset", "liability" ].each do |classification| - id = classification ? "#{classification}_#{account_group.id}" : account_group.id - family.broadcast_replace( - target: id, - partial: "accounts/accountable_group", - locals: { account_group: account_group, open: true } - ) - end - end - - family.broadcast_replace( - target: "balance-sheet", - partial: "pages/dashboard/balance_sheet", - locals: { balance_sheet: family.balance_sheet } - ) - - family.broadcast_replace( - target: "net-worth-chart", - partial: "pages/dashboard/net_worth_chart", - locals: { balance_sheet: family.balance_sheet, period: Period.last_30_days } - ) - end -end diff --git a/test/models/sync_test.rb b/test/models/sync_test.rb index f9ca4144..cbab9ed3 100644 --- a/test/models/sync_test.rb +++ b/test/models/sync_test.rb @@ -63,8 +63,11 @@ class SyncTest < ActiveSupport::TestCase # Since these are accessed through `parent`, they won't necessarily be the same # instance we configured above Account.any_instance.expects(:perform_post_sync).once + Account.any_instance.expects(:broadcast_sync_complete).once PlaidItem.any_instance.expects(:perform_post_sync).once + PlaidItem.any_instance.expects(:broadcast_sync_complete).once Family.any_instance.expects(:perform_post_sync).once + Family.any_instance.expects(:broadcast_sync_complete).once account_sync.perform @@ -108,6 +111,10 @@ class SyncTest < ActiveSupport::TestCase PlaidItem.any_instance.expects(:perform_post_sync).once Family.any_instance.expects(:perform_post_sync).once + Account.any_instance.expects(:broadcast_sync_complete).once + PlaidItem.any_instance.expects(:broadcast_sync_complete).once + Family.any_instance.expects(:broadcast_sync_complete).once + account_sync.perform assert_equal "failed", plaid_item_sync.reload.status @@ -150,6 +157,10 @@ class SyncTest < ActiveSupport::TestCase PlaidItem.any_instance.expects(:perform_post_sync).once Family.any_instance.expects(:perform_post_sync).once + Account.any_instance.expects(:broadcast_sync_complete).once + PlaidItem.any_instance.expects(:broadcast_sync_complete).once + Family.any_instance.expects(:broadcast_sync_complete).once + account_sync.perform assert_equal "failed", plaid_item_sync.reload.status