Files
redux-scraper/app/models/redux_application_record.rb
2025-08-14 17:03:50 +00:00

120 lines
3.2 KiB
Ruby

# typed: strict
class ReduxApplicationRecord < ActiveRecord::Base
extend T::Sig
include HasPrometheusClient
include HasColorLogger
# hack to make sorbet recognize the `@after_save_deferred_jobs` instance variable
sig { params(attributes: T.untyped).void }
def initialize(attributes = nil)
@after_save_deferred_jobs =
T.let(
nil,
T.nilable(T::Array[[DeferredJob, T.nilable(Scraper::JobBase)]]),
)
super(attributes)
end
self.abstract_class = true
after_initialize { observe(:initialize) }
after_create { observe(:create) }
after_update { observe(:update) }
after_destroy { observe(:destroy) }
TRUNCATE_FIELDS = %w[
json_attributes
tags_array
flags_array
pools_array
sources_array
artists_array
].freeze
# clean up the json_attributes field in console output
sig { params(attr_name: T.any(Symbol, String)).returns(T.untyped) }
def attribute_for_inspect(attr_name)
if attr_name.to_s.ends_with?("_backup")
return "(backup value)"
elsif TRUNCATE_FIELDS.include?(attr_name.to_s)
str_value = read_attribute(attr_name).inspect
str_value = "#{str_value[0, 50]}..." if str_value.length > 50
return str_value
end
super
end
sig { returns(T::Array[String]) }
def attributes_for_inspect
super.reject { |attr_name| attr_name.start_with?("json_attributes-") }
end
sig do
params(
job_class: T.class_of(Scraper::JobBase),
params: T::Hash[Symbol, T.untyped],
set_args: T::Hash[Symbol, T.untyped],
).void
end
def enqueue_job_after_save(job_class, params, set_args = {})
@after_save_deferred_jobs ||= []
@after_save_deferred_jobs << [
DeferredJob.new(job_class:, params:, set_args:),
Scraper::JobBase.current_scraper_job,
]
end
after_save do
# T.bind(self, ReduxApplicationRecord)
@after_save_deferred_jobs ||=
T.let([], T.nilable(T::Array[[DeferredJob, T.nilable(Scraper::JobBase)]]))
GoodJob::Bulk.enqueue do
@after_save_deferred_jobs.each do |deferred_job, current_job|
job_params = deferred_job.params
if current_job
job_params.merge!(
caused_by_entry: current_job.causing_log_entry,
caused_by_job_id: current_job.job_id,
)
end
job =
deferred_job
.job_class
.set(deferred_job.set_args)
.perform_later(job_params)
if job
logger.info(
"[class: #{self.class.name}][id: #{id}][enqueued job: #{deferred_job.job_class.name}][job_id: #{job.job_id}]",
)
end
Scraper::Metrics::JobBaseMetrics.observe_job_enqueued(
source_class: self.class,
enqueued_class: deferred_job.job_class,
)
end
end
end
private
ACTIVE_RECORD_COUNTER =
T.let(
PrometheusExporter::Client.default.register(
:counter,
"active_record_lifecycle",
"active record lifecycle statistics",
),
PrometheusExporter::Client::RemoteMetric,
)
sig { params(action: Symbol).void }
def observe(action)
ACTIVE_RECORD_COUNTER.increment(
{ method: action, class_name: self.class.name },
)
end
end