lib/cattri/initializer_patch.rb



# frozen_string_literal: true

module Cattri
  # Provides a patch to `#initialize` that ensures all final attributes
  # are initialized with their default values if not already set.
  #
  # This module is prepended into Cattri-including classes to enforce
  # write-once semantics for instance-level `final: true` attributes.
  #
  # @example
  #   class MyClass
  #     include Cattri
  #
  #     cattri :id, -> { SecureRandom.uuid }, final: true
  #   end
  #
  #   MyClass.new # => will have a UUID assigned to @id unless explicitly set
  module InitializerPatch
    # Hooked constructor that initializes final attributes using their defaults
    # if no value has been set by the user.
    #
    # @param args [Array] any positional arguments passed to initialize
    # @param kwargs [Hash] any keyword arguments passed to initialize
    # @yield an optional block to pass to `super`
    # @return [void]
    def initialize(*args, **kwargs, &block)
      super

      registry = self.class.send(:attribute_registry)
      registry.defined_attributes(with_ancestors: true).each_value do |attribute| # steep:ignore
        next if cattri_variable_defined?(attribute.ivar) # steep:ignore
        next unless attribute.final?

        cattri_variable_set(attribute.ivar, attribute.evaluate_default) # steep:ignore
      end
    end
  end
end