# encoding: UTF-8 # frozen_string_literal: true # Refinements # ======================================================================= require 'nrser/refinements/types' using NRSER::Types # Definitions # ======================================================================= # Mixin for common stuff that system agents do different. # # System agents are specific types of {Locd::Agent} built in to Loc'd to # handle things like proxying HTTP request site ({Locd::Agent::Proxy}), # rotating logs so they don't get unwieldily ({Locd::Agent::RotaeLogs}), # and probably more in the future, like serving a site index, API, etc. # # System agents are singular - there's only one of each for a Loc'd # label namespace, which is set via the configuration, and Loc'd uses # that label namespace, together with the each system agent's `.label_name`, # to form the agent's unique label, which is how it finds the system agents. # # This was really just to make it simpler by side-stepping how to handle # many proxies and log rotators and such because besides the dev/release # situation described above I can't really think of any reason you would want # to create and manage multiple of each system agent. # # As a consequence, {Locd::Agent} subclasses that mix {Locd::Agent::System} # in must define a `.label_name` class method. # module Locd::Agent::System @@classes = Hamster::Set.new def self.classes @@classes end # Is an agent label for a system agent of *this* Loc'd instance? # # @note # We can't tell if a label is a system agent label for *any* Loc'd instance # since the prefix can be configured to anything (via the # `locd:namespace:label` key). # # @return [Boolean] # def self.instance_label? label label.is_a?( String ) && label.start_with?( "#{ Locd.config[:namespace, :label] }." ) end # Is the plist for a system agent of *any* Loc'd instance? # # In case it's not obvious, this tests true given plists of system agents # of *other* Loc'd instance configurations besides just this one, which can # be useful to weed things out. Maybe. # # @param plist (see Locd::Agent.plist?) # # @return [Boolean] # `true` if the plist is for a system agent. # def self.plist? plist plist.dig( Locd.config[:agent, :config_key], 'is_system' ) == true end # .plist? # Is a plist for a system agent of *this* Loc'd instance? # # @see plist? # @see instance_label? # # @param plist (see Locd::Agent.plist?) # # @return [Boolean] # `true` if the plist is for a system agent for this Loc'd instance config. # def self.instance_plist? plist plist?( plist ) && instance_label?( plist[ 'Label' ] ) end # Find the concrete {Locd::Agent} subclass (that has mixed in # {Locd::Agent::System}) for a property list. # # @param plist (see Locd::Agent.plist?) # # @return [Class] # If the plist is for one of {.classes}, returns that class. # # @return [nil] # If the plist is: # # 1. Not a system plist (see {.plist?}) # # 2. If none of {.classes} claim it (via their `.plist?` method). # # Though, really, this one shouldn't happen except in weird version # switching situations maybe or something. # def self.class_for plist return nil unless plist?( plist ) classes.to_a.find_bounded( max: 1 ) { |cls| cls.plist? plist }.first end # .class_for def self.included base base.extend ClassMethods @@classes = @@classes.add base end # Mixed in to classes themselves that include {Locd::Agent::System}. # module ClassMethods def label "#{ Locd.config[:namespace, :label] }.#{ label_name }" end # The property lists for system agents are identified by their unique # label (unique to the label namespace). # # @param (see Locd::Agent.plist?) # @return (see Locd::Agent.plist?) # def plist? plist plist['Label'] == self.label end # Overridden as convenience, defaults the label to `.label`. # # @param (see Locd::Agent.plist_abs_path) # @return (see Locd::Agent.plist_abs_path) # def plist_abs_path label = self.label super label end # .plist_abs_path # By default system agent logs are placed in the Loc'd log directory, # which is `/log` and named `