mirror of
https://github.com/mastodon/mastodon.git
synced 2026-03-27 12:55:32 -05:00
83 lines
2.6 KiB
Ruby
83 lines
2.6 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module Account::Counters
|
|
extend ActiveSupport::Concern
|
|
|
|
ALLOWED_COUNTER_KEYS = %i(statuses_count following_count followers_count).freeze
|
|
|
|
included do
|
|
has_one :account_stat, inverse_of: :account, dependent: nil
|
|
after_save :save_account_stat
|
|
end
|
|
|
|
delegate :statuses_count,
|
|
:statuses_count=,
|
|
:following_count,
|
|
:following_count=,
|
|
:followers_count,
|
|
:followers_count=,
|
|
:last_status_at,
|
|
to: :account_stat
|
|
|
|
# @param [Symbol] key
|
|
def increment_count!(key, status_created_at: nil)
|
|
update_count!(key, 1, status_created_at:)
|
|
end
|
|
|
|
# @param [Symbol] key
|
|
def decrement_count!(key)
|
|
update_count!(key, -1)
|
|
end
|
|
|
|
# @param [Symbol] key
|
|
# @param [Integer] value
|
|
def update_count!(key, value, status_created_at: nil)
|
|
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)
|
|
|
|
result = updated_account_stat(key, value.to_i, status_created_at:)
|
|
|
|
# Reload account_stat if it was loaded, taking into account newly-created unsaved records
|
|
if association(:account_stat).loaded?
|
|
account_stat.id = result.first['id'] if account_stat.new_record?
|
|
account_stat.reload
|
|
end
|
|
end
|
|
|
|
def account_stat
|
|
super || build_account_stat
|
|
end
|
|
|
|
private
|
|
|
|
def updated_account_stat(key, value, status_created_at: nil)
|
|
status_created_at = Time.now.utc if status_created_at.nil? || status_created_at > Time.now.utc
|
|
|
|
AccountStat.upsert(
|
|
initial_values(key, value, status_created_at:),
|
|
on_duplicate: Arel.sql(
|
|
duplicate_values(key, value, status_created_at:).join(', ')
|
|
),
|
|
unique_by: :account_id
|
|
)
|
|
end
|
|
|
|
def initial_values(key, value, status_created_at: nil)
|
|
{ :account_id => id, key => [value, 0].max }.tap do |values|
|
|
values.merge!(last_status_at: status_created_at) if key == :statuses_count
|
|
end
|
|
end
|
|
|
|
def duplicate_values(key, value, status_created_at: nil)
|
|
["#{key} = (account_stats.#{key} + #{value})", 'updated_at = CURRENT_TIMESTAMP'].tap do |values|
|
|
values << AccountStat.sanitize_sql_array(['last_status_at = GREATEST(account_stats.last_status_at, ?::timestamp)', status_created_at]) if key == :statuses_count && value.positive?
|
|
end
|
|
end
|
|
|
|
def save_account_stat
|
|
return unless association(:account_stat).loaded? && account_stat&.changed?
|
|
|
|
account_stat.save
|
|
end
|
|
end
|