Class: Cattri::Context

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

Overview

Cattri::Context encapsulates the class or module that attributes are being defined on.

It provides a safe interface for dynamically defining methods and tracking metadata, such as declared accessors, access visibility, and deferred attribute declarations.

It handles:

  • Attribute method definitions (reader/writer/predicate)
  • Visibility enforcement
  • Target resolution (instance vs. class-level)
  • Method deduplication and tracking

All method definitions occur directly on the resolved target (e.g., the class or its singleton).

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(target) ⇒ Context

Initializes the context wrapper.



28
29
30
# File 'lib/cattri/context.rb', line 28

def initialize(target)
  @target = target
end

Instance Attribute Details

#targetModule (readonly)

The class or module that owns the attributes.



23
24
25
# File 'lib/cattri/context.rb', line 23

def target
  @target
end

Instance Method Details

#attribute_lookup_sourcesArray<Module>

All ancestors and included modules used for attribute lookup and inheritance.



62
63
64
# File 'lib/cattri/context.rb', line 62

def attribute_lookup_sources
  ([@target] + @target.ancestors + @target.singleton_class.included_modules).uniq
end

#defer_definitions?Boolean

Whether this target should defer method definitions (e.g., if it's a module).



44
45
46
# File 'lib/cattri/context.rb', line 44

def defer_definitions?
  @target.is_a?(Module) && !@target.is_a?(Class)
end

#define_method(attribute, name: nil) { ... } ⇒ void

This method returns an undefined value.

Defines a method for the given attribute unless already defined locally.

Respects attribute-level force overwrite and enforces visibility rules.

Yields:

  • method implementation block

Raises:



75
76
77
78
79
80
81
82
83
84
# File 'lib/cattri/context.rb', line 75

def define_method(attribute, name: nil, &block)
  name = (name || attribute.name).to_sym
  target = target_for(attribute)

  if method_defined?(attribute, name: name)
    raise Cattri::AttributeError, "Method `:#{name}` already defined on #{target}"
  end

  define_method!(target, attribute, name, &block)
end

#defined_methodsHash{Symbol => Set<Symbol>}

Returns a frozen copy of all attribute methods explicitly defined by this context.

This does not include inherited or module-defined methods.



37
38
39
# File 'lib/cattri/context.rb', line 37

def defined_methods
  (@__cattri_defined_methods ||= {}).dup.freeze # steep:ignore
end

#ensure_deferred_support!void

This method returns an undefined value.

Ensures the target includes Cattri::DeferredAttributes if needed.

Used to prepare modules for later application of attributes when included elsewhere.



53
54
55
56
57
# File 'lib/cattri/context.rb', line 53

def ensure_deferred_support!
  return if @target < Cattri::DeferredAttributes

  @target.extend(Cattri::DeferredAttributes)
end

#method_defined?(attribute, name: nil) ⇒ Boolean

Checks if the given method is already defined on the resolved target.

Only checks methods directly defined on the class or singleton—not ancestors.



93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/cattri/context.rb', line 93

def method_defined?(attribute, name: nil)
  normalized_name = (name || attribute.name).to_sym
  target = target_for(attribute)

  defined_locally = (
    target.public_instance_methods(false) +
      target.protected_instance_methods(false) +
      target.private_instance_methods(false)
  )

  defined_locally.include?(normalized_name) ||
    __cattri_defined_methods[attribute.name].include?(normalized_name)
end

#storage_receiver_for(attribute, instance = nil) ⇒ Object

Determines the object (class/module or runtime instance) that should hold the backing storage for the given attribute.

  • For class-level attributes, uses the singleton class of defined_in or the module itself
  • For instance-level attributes, uses the provided instance

Raises:



131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/cattri/context.rb', line 131

def storage_receiver_for(attribute, instance = nil)
  receiver =
    if attribute.class_attribute?
      resolve_class_storage_for(attribute, instance)
    elsif instance
      instance
    else
      raise Cattri::Error, "Missing runtime instance for instance-level attribute :#{attribute.name}"
    end

  install_internal_store!(receiver)
  receiver
end

#target_for(attribute) ⇒ Module

Resolves and returns the module or class where methods and storage should be defined for the given attribute. Ensures the internal store is included on the resolved target.

  • For class-level attributes, this returns the singleton class unless it's already a singleton.
  • For instance-level attributes, this returns the class/module directly.


115
116
117
118
119
# File 'lib/cattri/context.rb', line 115

def target_for(attribute)
  return @target if attribute.class_attribute? && singleton_class?(@target)

  attribute.class_attribute? ? @target.singleton_class : @target
end