Class: Cattri::Context
- Inherits:
-
Object
- Object
- Cattri::Context
- 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
-
#target ⇒ Module
readonly
The class or module that owns the attributes.
Instance Method Summary collapse
-
#attribute_lookup_sources ⇒ Array<Module>
All ancestors and included modules used for attribute lookup and inheritance.
-
#defer_definitions? ⇒ Boolean
Whether this target should defer method definitions (e.g., if it’s a module).
-
#define_method(attribute, name: nil) { ... } ⇒ void
Defines a method for the given attribute unless already defined locally.
-
#defined_methods ⇒ Hash{Symbol => Set<Symbol>}
Returns a frozen copy of all attribute methods explicitly defined by this context.
-
#ensure_deferred_support! ⇒ void
Ensures the target includes Cattri::DeferredAttributes if needed.
-
#initialize(target) ⇒ Context
constructor
Initializes the context wrapper.
-
#method_defined?(attribute, name: nil) ⇒ Boolean
Checks if the given method is already defined on the resolved target.
-
#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.
-
#target_for(attribute) ⇒ Module
Resolves and returns the module or class where methods and storage should be defined for the given attribute.
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
#target ⇒ Module (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_sources ⇒ Array<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.
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_methods ⇒ Hash{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
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 |