# Resolvers Resolvers enable lazy evaluation of node values in TreeStore. Instead of storing a static value, a node can have a resolver that computes the value on demand. ## How Resolvers Work ```{mermaid} sequenceDiagram participant C as Client participant S as TreeStore participant N as Node participant R as Resolver C->>S: store['config.computed'] S->>N: get value N->>N: check if resolver exists N->>R: load() R-->>R: compute value R->>N: return + cache N-->>S: value S-->>C: value Note over R: Cached until TTL expires C->>S: store['config.computed'] (again) S->>N: get value N->>N: check cache N-->>S: cached value S-->>C: value (from cache) ``` ## Resolver Types ```{mermaid} graph TB subgraph "Resolver Hierarchy" BASE[TreeStoreResolver
Abstract base class] CB[CallbackResolver
Function-based] DIR[DirectoryResolver
Filesystem] TXT[TxtDocResolver
Text files] BASE --> CB BASE --> DIR BASE --> TXT end ``` | Resolver | Purpose | Caching | |----------|---------|---------| | `CallbackResolver` | Compute value via callback function | Optional TTL | | `DirectoryResolver` | Lazy-load directory contents | On demand | | `TxtDocResolver` | Load text file content | On demand | ## CallbackResolver The most flexible resolver - computes values using a callback function. ### Basic Usage ```python from genro_treestore import TreeStore, CallbackResolver store = TreeStore() store.set_item('config.base_url', 'https://api.example.com') store.set_item('config.version', 'v2') # Define callback that computes value def get_full_url(node): parent = node.parent # TreeStore containing this node base = parent.get_item('base_url') version = parent.get_item('version') return f"{base}/{version}" # Create node and attach resolver store.set_item('config.full_url') store.set_resolver('config.full_url', CallbackResolver(get_full_url)) # Value is computed on access print(store['config.full_url']) # 'https://api.example.com/v2' ``` ### With Caching ```python import time def expensive_computation(node): time.sleep(1) # Simulate expensive operation return "computed_value" # Cache for 60 seconds resolver = CallbackResolver(expensive_computation, cache_time=60) store.set_resolver('expensive.value', resolver) # First access: computes value (slow) value1 = store['expensive.value'] # Second access within 60s: returns cached value (fast) value2 = store['expensive.value'] # After 60s: recomputes ``` ### Callback Signature The callback receives the `TreeStoreNode` as its argument: ```python def my_callback(node: TreeStoreNode) -> Any: # node.label - the node's label # node.attr - the node's attributes dict # node.parent - the parent TreeStore # node.store - the root TreeStore return computed_value ``` ## DirectoryResolver Lazily loads directory contents into the tree structure. ```python from genro_treestore import TreeStore, DirectoryResolver store = TreeStore() # Attach resolver to a node store.set_item('filesystem') store.set_resolver('filesystem', DirectoryResolver('/path/to/directory')) # Directory contents are loaded on first access for name, node in store['filesystem'].items(): print(f"{name}: {node}") ``` ## TxtDocResolver Loads text file content on demand. ```python from genro_treestore import TreeStore, TxtDocResolver store = TreeStore() # Attach resolver store.set_item('readme') store.set_resolver('readme', TxtDocResolver('/path/to/README.md')) # Content is loaded on access print(store['readme']) # File contents ``` ## Creating Custom Resolvers Extend `TreeStoreResolver` to create custom resolvers: ```python from genro_treestore import TreeStoreResolver class DatabaseResolver(TreeStoreResolver): """Load value from database on demand.""" def __init__(self, query: str, connection): self.query = query self.connection = connection def load(self, node): """Called when node value is accessed.""" cursor = self.connection.execute(self.query) return cursor.fetchone() # Usage store.set_item('user.current') store.set_resolver('user.current', DatabaseResolver( "SELECT * FROM users WHERE id = 1", db_connection )) ``` ## Resolver Lifecycle ```{mermaid} stateDiagram-v2 [*] --> Attached: set_resolver() Attached --> Loading: value accessed Loading --> Cached: load() returns Cached --> Loading: cache expired Cached --> [*]: node deleted note right of Cached Value stored in node until TTL expires end note ``` ## Best Practices 1. **Use caching wisely**: Set appropriate `cache_time` for expensive computations 2. **Keep callbacks simple**: Complex logic should be in separate functions 3. **Handle errors**: Callbacks should handle exceptions gracefully 4. **Avoid circular dependencies**: Don't create resolvers that depend on each other cyclically ## See Also - {class}`~genro_treestore.TreeStoreResolver` - Base resolver class - {class}`~genro_treestore.CallbackResolver` - Function-based resolver - {class}`~genro_treestore.DirectoryResolver` - Filesystem resolver - {class}`~genro_treestore.TxtDocResolver` - Text file resolver