utilities_ab 0.0.7
utilities_ab: ^0.0.7 copied to clipboard
Dart utility helper classes and functions for general purpose use.
A comprehensive Dart utilities package providing essential tools for Flutter and Dart applications including caching, error handling, and logging utilities.
Features #
- 🔧 Cache Module: Generic cache with LFU/LRU eviction algorithms
- ⚠️ Error Handling: Structured exception handling with stack trace support
- 📝 Logging: Configurable logging system with console and developer tools support
Installation #
Add to your pubspec.yaml
:
dependencies:
utilities_ab: ^0.0.5
Usage #
Import the entire utilities package: #
import 'package:utilities_ab/utilities.dart';
Or import specific modules: #
import 'package:utilities_ab/cache/cache.dart';
import 'package:utilities_ab/error/app_exception.dart';
import 'package:utilities_ab/logging/init_logging.dart';
🔧 Cache Module #
A generic cache implementation supporting LFU and LRU eviction algorithms with automatic loading from backing stores.
Features #
- Generic Type Support: Works with any key and value types
- Multiple Eviction Algorithms: Supports both LFU (Least Frequently Used) and LRU (Least Recently Used)
- Automatic Loading: Automatically loads missing values from backing stores using callback functions
- Overloaded Indexing Operator: Access cache values using
cache[key]
syntax - Pluggable Storage: Extensible storage interface with in-memory implementation included
- Async Support: Full async/await support for all operations
Basic Usage #
import 'package:utilities_ab/cache/cache.dart';
// Create a loader function that fetches data from your backing store
Future<String> loadUserData(String userId) async {
// Simulate API call or database query
await Future.delayed(Duration(milliseconds: 100));
return 'User data for $userId';
}
// Create an LRU cache with max 100 items
final cache = Cache<String, String>(
storage: InMemoryStorage<String, CacheEntry<String, String>>(),
algorithm: EvictionAlgorithm.lru,
maxSize: 100,
loader: loadUserData,
);
// Access data - automatically loads if not in cache
final userData = await cache['user123'];
print(userData); // "User data for user123"
// Subsequent accesses use cached data
final cachedData = await cache['user123']; // No loading, uses cache
LFU Cache Example #
// Create an LFU cache
final lfuCache = Cache<String, Map<String, dynamic>>(
storage: InMemoryStorage<String, CacheEntry<String, Map<String, dynamic>>>(),
algorithm: EvictionAlgorithm.lfu,
maxSize: 50,
loader: (key) async {
// Load complex data structure
return {'id': key, 'data': 'complex object'};
},
);
// Access data multiple times to increase frequency
await lfuCache['key1'];
await lfuCache['key1']; // Increases frequency
await lfuCache['key1']; // Increases frequency again
Direct Assignment #
// Store values directly in cache (immediate storage, no eviction check)
cache['manual'] = 'Directly stored value';
// Or use the async put method (with eviction check)
await cache.put('manual', 'Directly stored value');
// Retrieve the value
final value = await cache['manual'];
print(value); // "Directly stored value"
Cache Management #
// Check if key exists
if (await cache.containsKey('user123')) {
print('User data is cached');
}
// Remove specific item
await cache.remove('user123');
// Get cache statistics
final size = await cache.size;
final keys = await cache.keys;
final values = await cache.values;
// Clear entire cache
await cache.clear();
Cache Architecture #
Components
- Cache: Main cache class with eviction logic
- CacheEntry: Wrapper for cached values with metadata
- CacheStorage: Interface for storage implementations
- InMemoryStorage: In-memory storage implementation
- EvictionAlgorithm: Enum for supported algorithms
Eviction Algorithms
LRU (Least Recently Used)
- Removes items that haven't been accessed for the longest time
- Good for temporal locality patterns
- Updates access time on every read/write
LFU (Least Frequently Used)
- Removes items that are accessed least often
- Good for frequency-based access patterns
- Increments frequency counter on every access
Storage Interface
The CacheStorage
interface allows you to implement custom storage backends:
class CustomStorage<K, V> implements CacheStorage<K, V> {
@override
Future<bool> put(K key, V value) async {
// Your implementation
}
@override
Future<V?> get(K key) async {
// Your implementation
}
// ... implement other methods
}
⚠️ Error Handling Module #
The AppException
class provides structured exception handling with support for nested exceptions and detailed stack traces.
Features #
- Structured Exceptions: Consistent exception format across your application
- Nested Exceptions: Support for chaining exceptions with inner exceptions
- Stack Trace Management: Automatic stack trace capture and formatting
- Logging Integration: Built-in logging support for exception tracking
- Customizable Output: Formatted exception messages for debugging
Basic Usage #
import 'package:utilities_ab/error/app_exception.dart';
try {
// Your code that might throw an exception
throw Exception('Something went wrong');
} catch (e, stackTrace) {
// Create a structured exception
final appException = AppException(
message: 'Failed to process user data',
stackTrace: stackTrace,
innerException: e is Exception ? e : null,
);
// Log or handle the exception
print(appException.toString());
}
Nested Exceptions #
try {
// Outer operation
try {
// Inner operation that fails
throw Exception('Database connection failed');
} catch (innerError, innerStackTrace) {
throw AppException(
message: 'Failed to save user data',
stackTrace: StackTrace.current,
innerException: innerError is Exception ? innerError : null,
innerStackTrace: innerStackTrace,
);
}
} catch (outerError, outerStackTrace) {
if (outerError is AppException) {
print(outerError.toString());
// Output will show the chain of exceptions
}
}
Exception Output Format #
The AppException
provides formatted output including:
- Exception type and message
- Formatted stack trace
- Nested exception information
- Clean stack trace formatting (removes internal framework calls)
Example output:
AppException: Failed to process user data
at MyClass.processData (my_file.dart:25)
at MyClass.main (my_file.dart:10)
Caused by: Exception: Database connection failed
at Database.connect (database.dart:15)
📝 Logging Module #
The logging module provides a configurable logging system that integrates with Flutter's developer tools and console output.
Features #
- Configurable Log Levels: Set appropriate log levels for different environments
- Console Integration: Optional console output for debugging
- Developer Tools Integration: Logs appear in Flutter DevTools
- Structured Logging: Consistent log format with timestamps and context
- Error Tracking: Automatic error and stack trace logging
Basic Usage #
import 'package:utilities_ab/logging/init_logging.dart';
import 'package:logging/logging.dart';
void main() {
// Initialize logging with INFO level
initLogging(Level.INFO, logToConsole: true);
// Create a logger for your class
final logger = Logger('MyClass');
// Use the logger
logger.info('Application started');
logger.warning('This is a warning');
logger.severe('This is an error');
}
Configuration Options #
// Development environment - verbose logging to console
initLogging(
Level.ALL,
logToConsole: true,
);
// Production environment - only errors and warnings
initLogging(
Level.WARNING,
logToConsole: false,
);
// Custom logging functions
initLogging(
Level.INFO,
logToConsole: false,
logFn: (message, {time, level, name, error, stackTrace}) {
// Custom logging implementation
print('[$name] $level: $message');
},
consoleLogFn: (object) {
// Custom console output
print('CONSOLE: $object');
},
);
Log Levels #
- ALL: Log everything
- FINEST: Very detailed tracing
- FINER: More detailed tracing
- FINE: Detailed tracing
- CONFIG: Static configuration messages
- INFO: General information
- WARNING: Potential issues
- SEVERE: Serious problems
- SHOUT: Critical errors
- OFF: Disable all logging
Error Logging #
final logger = Logger('ErrorHandler');
try {
// Risky operation
riskyOperation();
} catch (error, stackTrace) {
logger.severe(
'Operation failed',
error,
stackTrace,
);
}
Log Output Format #
Console Output:
2024-01-15 10:30:45.123 [MyClass] INFO: Application started
2024-01-15 10:30:46.456 [MyClass] WARNING: This is a warning
2024-01-15 10:30:47.789 [MyClass] SEVERE: This is an error
Error with Stack Trace:
2024-01-15 10:30:47.789 [MyClass] SEVERE: Operation failed =>
====================
Exception: Something went wrong
at MyClass.riskyOperation (my_file.dart:25)
at MyClass.main (my_file.dart:10)
Performance Considerations #
Cache Module #
- Memory Usage: In-memory storage keeps all data in RAM
- Eviction Overhead: O(n) complexity for eviction decisions
- Concurrency: Not thread-safe by default
- Async Operations: All storage operations are async for flexibility
Error Handling #
- Stack Trace Overhead: Capturing stack traces has performance impact
- Memory Usage: Exception objects hold references to stack traces
- Logging Impact: Exception logging can be expensive in high-frequency scenarios
Logging #
- Console Output: Console logging can impact performance in production
- Log Level Filtering: Use appropriate log levels to minimize overhead
- Async Logging: Consider async logging for high-frequency applications
Testing #
Run the tests to verify functionality:
# Run all tests
dart test
# Run specific test files
dart test test/cache_test.dart
dart test test/app_exception_test.dart
Test Coverage #
- Cache Tests: LRU/LFU eviction logic, storage interface, direct assignment
- Error Tests: Exception formatting, nested exceptions, stack trace handling
- Logging Tests: Log level configuration, output formatting
Contributing #
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
License #
This project is licensed under the MIT License - see the LICENSE file for details.