lib/fbe/repeatedly.rb



# frozen_string_literal: true

# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Zerocracy
# SPDX-License-Identifier: MIT

require_relative '../fbe'
require_relative 'fb'
require_relative 'overwrite'

# Run the block provided every X hours based on PMP configuration.
#
# Similar to Fbe.regularly but works with hour intervals instead of days.
# Executes a block periodically, maintaining a single fact that tracks the
# last execution time. The fact is overwritten on each run rather than
# creating new facts.
#
# @param [String] area The name of the PMP area
# @param [String] p_every_hours PMP property name for interval (defaults to 24 hours if not in PMP)
# @param [Factbase] fb The factbase (defaults to Fbe.fb)
# @param [String] judge The name of the judge (uses $judge global)
# @param [Loog] loog The logging facility (uses $loog global)
# @yield [Factbase::Fact] The judge fact to populate with execution details
# @return [nil] Nothing
# @raise [RuntimeError] If required parameters or globals are nil
# @note Skips execution if judge was run within the interval period
# @note Overwrites the 'when' property of existing judge fact
# @example Run a monitoring task every 6 hours
#   Fbe.repeatedly('monitoring', 'hours_between_checks') do |f|
#     f.servers_checked = check_all_servers
#     f.issues_found = count_issues
#     # PMP might have: hours_between_checks=6
#   end
def Fbe.repeatedly(area, p_every_hours, fb: Fbe.fb, judge: $judge, loog: $loog, &)
  raise 'The area is nil' if area.nil?
  raise 'The p_every_hours is nil' if p_every_hours.nil?
  raise 'The fb is nil' if fb.nil?
  raise 'The $judge is not set' if judge.nil?
  raise 'The $loog is not set' if loog.nil?
  pmp = fb.query("(and (eq what 'pmp') (eq area '#{area}') (exists #{p_every_hours}))").each.to_a.first
  hours = pmp.nil? ? 24 : pmp[p_every_hours].first
  unless fb.query(
    "(and
      (eq what '#{judge}')
      (gt when (minus (to_time (env 'TODAY' '#{Time.now.utc.iso8601}')) '#{hours} hours')))"
  ).each.to_a.empty?
    loog.debug("#{$judge} has recently been executed, skipping now")
    return
  end
  f = fb.query("(and (eq what '#{judge}'))").each.to_a.first
  if f.nil?
    f = fb.insert
    f.what = judge
  end
  Fbe.overwrite(f, 'when', Time.now)
  yield fb.query("(and (eq what '#{judge}'))").each.to_a.first
  nil
end