Class: Cattri::Attribute

Inherits:
Object
  • Object
show all
Defined in:
lib/cattri/attribute.rb

Overview

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.

Examples:

attribute = Attribute.new(:enabled, default: true, expose: :read_write)
attribute.name          # => :enabled
attribute.default.call  # => true
attribute.expose        # => :read_write

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, defined_in:, **options, &transformer) ⇒ Attribute

Initializes a new attribute definition.

Parameters:

  • name (Symbol, String)

    the attribute name

  • defined_in (Module)

    the class or module where this attribute is defined

  • options (Hash)

    configuration options

  • transformer (Proc)

    optional block used to coerce/validate assigned values

Options Hash (**options):

  • :scope (Boolean)

    whether the attribute is class-level (internally mapped to :class_attribute)



30
31
32
33
# File 'lib/cattri/attribute.rb', line 30

def initialize(name, defined_in:, **options, &transformer)
  @options = Cattri::AttributeOptions.new(name, transformer: transformer, **options)
  @defined_in = defined_in
end

Instance Attribute Details

#defaultProc (readonly)

Returns a callable lambda for the attribute’s default value.

Returns:

  • (Proc)

    a callable lambda for the attribute’s default value



# File 'lib/cattri/attribute.rb', line 59


#defined_inModule (readonly)

Returns the class or module this attribute was defined in.

Returns:

  • (Module)

    the class or module this attribute was defined in



21
22
23
# File 'lib/cattri/attribute.rb', line 21

def defined_in
  @defined_in
end

#exposeSymbol (readonly)

Returns method exposure type (:read, :write, :read_write, or :none).

Returns:

  • (Symbol)

    method exposure type (:read, :write, :read_write, or :none)



# File 'lib/cattri/attribute.rb', line 65


#ivarSymbol (readonly)

Returns the backing instance variable (e.g., :@enabled).

Returns:

  • (Symbol)

    the backing instance variable (e.g., :@enabled)



# File 'lib/cattri/attribute.rb', line 56


#nameSymbol (readonly)

Returns the canonical name of the attribute.

Returns:

  • (Symbol)

    the canonical name of the attribute



# File 'lib/cattri/attribute.rb', line 53


#transformerProc (readonly)

Returns a callable transformer used to process assigned values.

Returns:

  • (Proc)

    a callable transformer used to process assigned values



# File 'lib/cattri/attribute.rb', line 62


#visibilitySymbol (readonly)

Returns method visibility (:public, :protected, :private).

Returns:

  • (Symbol)

    method visibility (:public, :protected, :private)



71
72
73
74
75
76
77
78
79
80
# File 'lib/cattri/attribute.rb', line 71

i[
  name
  ivar
  default
  transformer
  expose
  visibility
].each do |option|
  define_method(option) { @options.public_send(option) }
end

Instance Method Details

#allowed_methodsArray<Symbol>

Returns the methods that will be defined for this attribute.

Includes the base accessor, optional writer, and optional predicate.

Returns:

  • (Array<Symbol>)

    a list of method names



131
132
133
# File 'lib/cattri/attribute.rb', line 131

def allowed_methods
  [name, (:"#{name}=" if writable?), (:"#{name}?" if with_predicate?)].compact.freeze
end

#class_attribute?Boolean

Returns whether the attribute is class-level.

Returns:

  • (Boolean)

    whether the attribute is class-level



117
118
119
# File 'lib/cattri/attribute.rb', line 117

def class_attribute?
  @options.scope == :class
end

#evaluate_defaultObject

Resolves the default value for this attribute.

Returns:

  • (Object)

    the evaluated default

Raises:



150
151
152
153
154
# File 'lib/cattri/attribute.rb', line 150

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

#final?Boolean

Returns whether the attribute is marked final (write-once).

Returns:

  • (Boolean)

    whether the attribute is marked final (write-once)



112
113
114
# File 'lib/cattri/attribute.rb', line 112

def final?
  @options.final
end

#internal_reader?Boolean

Returns whether the reader should remain internal.

Returns:

  • (Boolean)

    whether the reader should remain internal



83
84
85
# File 'lib/cattri/attribute.rb', line 83

def internal_reader?
  i[write none].include?(@options.expose)
end

#internal_writer?Boolean

Returns whether the writer should remain internal.

Returns:

  • (Boolean)

    whether the writer should remain internal



88
89
90
# File 'lib/cattri/attribute.rb', line 88

def internal_writer?
  i[read none].include?(@options.expose)
end

#process_assignment(*args, **kwargs) ⇒ Object

Processes and transforms an incoming assignment for this attribute.

Parameters:

  • args (Array)

    positional arguments to pass to the transformer

  • kwargs (Hash)

    keyword arguments to pass to the transformer

Returns:

  • (Object)

    the transformed value

Raises:



162
163
164
165
166
# File 'lib/cattri/attribute.rb', line 162

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

#readable?Boolean

Returns whether the attribute allows reading.

Returns:

  • (Boolean)

    whether the attribute allows reading



93
94
95
# File 'lib/cattri/attribute.rb', line 93

def readable?
  i[read read_write].include?(@options.expose)
end

#readonly?Boolean

Returns whether the attribute is marked readonly.

Returns:

  • (Boolean)

    whether the attribute is marked readonly



105
106
107
108
109
# File 'lib/cattri/attribute.rb', line 105

def readonly?
  return false if @options.expose == :none

  @options.expose == :read
end

#to_hHash<Symbol, Object>

Serializes this attribute and its configuration to a frozen hash.

Returns:

  • (Hash<Symbol, Object>)


38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/cattri/attribute.rb', line 38

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

#validate_assignment!Object

Validates whether this attribute is assignable in the current context.

Raises:



138
139
140
141
142
143
144
# File 'lib/cattri/attribute.rb', line 138

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

#with_predicate?Boolean

Returns whether the attribute defines a predicate method (:name?).

Returns:

  • (Boolean)

    whether the attribute defines a predicate method (:name?)



122
123
124
# File 'lib/cattri/attribute.rb', line 122

def with_predicate?
  @options.predicate
end

#writable?Boolean

Returns whether the attribute should define a writer (public or internal).

Returns:

  • (Boolean)

    whether the attribute should define a writer (public or internal)



98
99
100
101
102
# File 'lib/cattri/attribute.rb', line 98

def writable?
  return false if @options.expose == :none

  !readonly? || internal_writer?
end