# frozen_string_literal: truerequire_relative"attribute_options"moduleCattri# @internal## Attribute acts as a thin wrapper around AttributeOptions,# exposing core attribute metadata and behavior in a safe, immutable way.## Each Attribute instance represents a single logical property,# and delegates its behavior (default, visibility, coercion, etc.) to its associated AttributeOptions.## @example# attribute = Attribute.new(:enabled, default: true, expose: :read_write)# attribute.name # => :enabled# attribute.default.call # => true# attribute.expose # => :read_writeclassAttribute# @return [Module] the class or module this attribute was defined inattr_reader:defined_in# Initializes a new attribute definition.## @param name [Symbol, String] the attribute name# @param defined_in [Module] the class or module where this attribute is defined# @param options [Hash] configuration options# @option options [Boolean] :scope whether the attribute is class-level (internally mapped to :class_attribute)# @param transformer [Proc] optional block used to coerce/validate assigned valuesdefinitialize(name,defined_in:,**options,&transformer)@options=Cattri::AttributeOptions.new(name,transformer: transformer,**options)@defined_in=defined_inend# Serializes this attribute and its configuration to a frozen hash.## @return [Hash<Symbol, Object>]defto_h{name: @options.name,ivar: @options.ivar,defined_in: @defined_in,final: @options.final,scope: @options.scope,predicate: @options.predicate,default: @options.default,transformer: @options.transformer,expose: @options.expose,visibility: @options.visibility}end# @!attribute [r] name# @return [Symbol] the canonical name of the attribute# @!attribute [r] ivar# @return [Symbol] the backing instance variable (e.g., :@enabled)# @!attribute [r] default# @return [Proc] a callable lambda for the attribute’s default value# @!attribute [r] transformer# @return [Proc] a callable transformer used to process assigned values# @!attribute [r] expose# @return [Symbol] method exposure type (:read, :write, :read_write, or :none)# @!attribute [r] visibility# @return [Symbol] method visibility (:public, :protected, :private)%i[
name
ivar
default
transformer
expose
visibility
].eachdo|option|define_method(option){@options.public_send(option)}end# @return [Boolean] whether the reader should remain internaldefinternal_reader?%i[write none].include?(@options.expose)end# @return [Boolean] whether the writer should remain internaldefinternal_writer?%i[read none].include?(@options.expose)end# @return [Boolean] whether the attribute allows readingdefreadable?%i[read read_write].include?(@options.expose)end# @return [Boolean] whether the attribute allows writingdefwritable?returnfalseif@options.expose==:none!readonly?end# @return [Boolean] whether the attribute is marked readonlydefreadonly?returnfalseif@options.expose==:none@options.expose==:read||final?end# @return [Boolean] whether the attribute is marked final (write-once)deffinal?@options.finalend# @return [Boolean] whether the attribute is class-leveldefclass_attribute?@options.scope==:classend# @return [Boolean] whether the attribute defines a predicate method (`:name?`)defwith_predicate?@options.predicateend# Returns the methods that will be defined for this attribute.## Includes the base accessor, optional writer, and optional predicate.## @return [Array<Symbol>] a list of method namesdefallowed_methods[name,(:"#{name}="ifwritable?),(:"#{name}?"ifwith_predicate?)].compact.freezeend# Validates whether this attribute is assignable in the current context.## @raise [Cattri::AttributeError] if assignment is disalloweddefvalidate_assignment!iffinal?raiseCattri::AttributeError,"Cannot assign to final attribute `:#{name}`"elsifreadonly?raiseCattri::AttributeError,"Cannot assign to readonly attribute `:#{name}`"endend# Resolves the default value for this attribute.## @return [Object] the evaluated default# @raise [Cattri::AttributeError] if default evaluation failsdefevaluate_default@options.default.callrescueStandardError=>eraiseCattri::AttributeError,"Failed to evaluate the default value for `:#{@options.name}`. Error: #{e.message}"end# Processes and transforms an incoming assignment for this attribute.## @param args [Array] positional arguments to pass to the transformer# @param kwargs [Hash] keyword arguments to pass to the transformer# @return [Object] the transformed value# @raise [Cattri::AttributeError] if transformation failsdefprocess_assignment(*args,**kwargs)@options.transformer.call(*args,**kwargs)rescueStandardError=>eraiseCattri::AttributeError,"Failed to evaluate the setter for `:#{@options.name}`. Error: #{e.message}"endendend