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

Returns:
  • (Array) - a list of method names
def allowed_methods
  [name, (:"#{name}=" if writable?), (:"#{name}?" if with_predicate?)].compact.freeze
end

def class_attribute?

Returns:
  • (Boolean) - whether the attribute is class-level
def class_attribute?
  @options.scope == :class
end

def evaluate_default

Raises:
  • (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?

Returns:
  • (Boolean) - whether the attribute is marked final (write-once)
def final?
  @options.final
end

def initialize(name, defined_in:, **options, &transformer)

Parameters:
  • 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?

Returns:
  • (Boolean) - whether the reader should remain internal
def internal_reader?
  %i[write none].include?(@options.expose)
end

def internal_writer?

Returns:
  • (Boolean) - whether the writer should remain internal
def internal_writer?
  %i[read none].include?(@options.expose)
end

def process_assignment(*args, **kwargs)

Raises:
  • (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?

Returns:
  • (Boolean) - whether the attribute allows reading
def readable?
  %i[read read_write].include?(@options.expose)
end

def readonly?

Returns:
  • (Boolean) - whether the attribute is marked readonly
def readonly?
  return false if @options.expose == :none
  @options.expose == :read || final?
end

def to_h

Returns:
  • (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!

Raises:
  • (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?

Returns:
  • (Boolean) - whether the attribute defines a predicate method (`:name?`)
def with_predicate?
  @options.predicate
end

def writable?

Returns:
  • (Boolean) - whether the attribute allows writing
def writable?
  return false if @options.expose == :none
  !readonly?
end