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.

Parameters:

  • target (Module, Class)

    the receiver where attributes are defined



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.

Returns:

  • (Module)


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.

Returns:

  • (Array<Module>)


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).

Returns:

  • (Boolean)


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.

Parameters:

  • attribute (Cattri::Attribute)
  • name (Symbol, nil) (defaults to: nil)

    optional method name override

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.

Returns:

  • (Hash{Symbol => Set<Symbol>})

    map of attribute name to defined method names



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.

Parameters:

Returns:

  • (Boolean)


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

Parameters:

  • attribute (Cattri::Attribute)
  • instance (Object, nil) (defaults to: nil)

    the runtime instance, if needed for instance-level access

Returns:

  • (Object)

    the receiver for attribute value storage

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.

Parameters:

Returns:

  • (Module)


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