class Cattri::Attribute
attribute.expose # => :read_write
attribute.default.call # => true
attribute.name # => :enabled
attribute = Attribute.new(:enabled, default: true, expose: :read_write)
@example
and delegates its behavior (default, visibility, coercion, etc.) to its associated AttributeOptions.
Each Attribute instance represents a single logical property,
exposing core attribute metadata and behavior in a safe, immutable way.
Attribute acts as a thin wrapper around AttributeOptions,
@internal
def allowed_methods
-
(Array
- a list of method names)
def allowed_methods [name, (:"#{name}=" if writable?), (:"#{name}?" if with_predicate?)].compact.freeze end
def class_attribute?
-
(Boolean)
- whether the attribute is class-level
def class_attribute? @options.scope == :class end
def evaluate_default
-
(Cattri::AttributeError)
- if default evaluation fails
Returns:
-
(Object)
- the evaluated default
def evaluate_default @options.default.call rescue StandardError => e raise Cattri::AttributeError, "Failed to evaluate the default value for `:#{@options.name}`. Error: #{e.message}" end
def final?
-
(Boolean)
- whether the attribute is marked final (write-once)
def final? @options.final end
def initialize(name, defined_in:, **options, &transformer)
-
transformer
(Proc
) -- optional block used to coerce/validate assigned values -
options
(Hash
) -- configuration options -
defined_in
(Module
) -- the class or module where this attribute is defined -
name
(Symbol, String
) -- the attribute name
Options Hash:
(**options)
-
:scope
(Boolean
) -- whether the attribute is class-level (internally mapped to :class_attribute)
def initialize(name, defined_in:, **options, &transformer) @options = Cattri::AttributeOptions.new(name, transformer: transformer, **options) @defined_in = defined_in end
def internal_reader?
-
(Boolean)
- whether the reader should remain internal
def internal_reader? %i[write none].include?(@options.expose) end
def internal_writer?
-
(Boolean)
- whether the writer should remain internal
def internal_writer? %i[read none].include?(@options.expose) end
def process_assignment(*args, **kwargs)
-
(Cattri::AttributeError)
- if transformation fails
Returns:
-
(Object)
- the transformed value
Parameters:
-
kwargs
(Hash
) -- keyword arguments to pass to the transformer -
args
(Array
) -- positional arguments to pass to the transformer
def process_assignment(*args, **kwargs) @options.transformer.call(*args, **kwargs) rescue StandardError => e raise Cattri::AttributeError, "Failed to evaluate the setter for `:#{@options.name}`. Error: #{e.message}" end
def readable?
-
(Boolean)
- whether the attribute allows reading
def readable? %i[read read_write].include?(@options.expose) end
def readonly?
-
(Boolean)
- whether the attribute is marked readonly
def readonly? return false if @options.expose == :none @options.expose == :read || final? end
def to_h
-
(Hash
-)
def to_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
def validate_assignment!
-
(Cattri::AttributeError)
- if assignment is disallowed
def validate_assignment! if final? raise Cattri::AttributeError, "Cannot assign to final attribute `:#{name}`" elsif readonly? raise Cattri::AttributeError, "Cannot assign to readonly attribute `:#{name}`" end end
def with_predicate?
-
(Boolean)
- whether the attribute defines a predicate method (`:name?`)
def with_predicate? @options.predicate end
def writable?
-
(Boolean)
- whether the attribute allows writing
def writable? return false if @options.expose == :none !readonly? end