2018-11-19 10:43:52 +11:00
# frozen_string_literal: true
2023-12-01 22:00:41 +11:00
module Account::Counters
2018-11-19 10:43:52 +11:00
extend ActiveSupport :: Concern
2021-03-19 23:14:57 +11:00
ALLOWED_COUNTER_KEYS = % i ( statuses_count following_count followers_count ) . freeze
2018-11-19 10:43:52 +11:00
included do
2023-12-02 02:52:47 +11:00
has_one :account_stat , inverse_of : :account , dependent : nil
2018-11-19 10:43:52 +11:00
after_save :save_account_stat
end
delegate :statuses_count ,
:statuses_count = ,
:following_count ,
:following_count = ,
:followers_count ,
:followers_count = ,
2018-12-07 03:36:11 +11:00
:last_status_at ,
2018-11-19 10:43:52 +11:00
to : :account_stat
2021-03-19 23:14:57 +11:00
# @param [Symbol] key
def increment_count! ( key )
update_count! ( key , 1 )
end
# @param [Symbol] key
def decrement_count! ( key )
update_count! ( key , - 1 )
end
# @param [Symbol] key
# @param [Integer] value
def update_count! ( key , value )
raise ArgumentError , " Invalid key #{ key } " unless ALLOWED_COUNTER_KEYS . include? ( key )
raise ArgumentError , 'Do not call update_count! on dirty objects' if association ( :account_stat ) . loaded? && account_stat & . changed? && account_stat . changed_attribute_names_to_save == %w( id )
2024-04-17 18:16:51 +10:00
result = updated_account_stat ( key , value . to_i )
2021-03-19 23:14:57 +11:00
# Reload account_stat if it was loaded, taking into account newly-created unsaved records
if association ( :account_stat ) . loaded?
2024-04-17 18:16:51 +10:00
account_stat . id = result . first [ 'id' ] if account_stat . new_record?
2021-03-19 23:14:57 +11:00
account_stat . reload
end
end
2018-11-19 10:43:52 +11:00
def account_stat
super || build_account_stat
end
private
2024-04-17 18:16:51 +10:00
def updated_account_stat ( key , value )
AccountStat . upsert (
initial_values ( key , value ) ,
on_duplicate : Arel . sql (
duplicate_values ( key , value ) . join ( ', ' )
) ,
unique_by : :account_id
)
end
def initial_values ( key , value )
{ :account_id = > id , key = > [ value , 0 ] . max } . tap do | values |
values . merge! ( last_status_at : Time . current ) if key == :statuses_count
end
end
def duplicate_values ( key , value )
[ " #{ key } = (account_stats. #{ key } + #{ value } ) " , 'updated_at = CURRENT_TIMESTAMP' ] . tap do | values |
values << 'last_status_at = CURRENT_TIMESTAMP' if key == :statuses_count && value . positive?
end
end
2018-11-19 10:43:52 +11:00
def save_account_stat
2019-08-18 22:55:03 +10:00
return unless association ( :account_stat ) . loaded? && account_stat & . changed?
2018-11-19 10:43:52 +11:00
account_stat . save
end
end