Skip to content

Commit 3a52b51

Browse files
committed
✨ add itemHolder feature to serializable container.
1 parent 1f9b57e commit 3a52b51

File tree

1 file changed

+43
-19
lines changed

1 file changed

+43
-19
lines changed

packages/hyper_storage/lib/src/api/serializable_container.dart

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@ abstract class SerializableStorageContainer<E extends Object> extends StorageCon
121121
/// behavior in testing scenarios.
122122
final Random _random;
123123

124+
/// Internal cache of item holders by their keys.
125+
final Map<String, SerializableItemHolder<E>> _holders = {};
126+
124127
/// Creates a new [SerializableStorageContainer].
125128
///
126129
/// The constructor initializes a container for storing serializable objects
@@ -703,6 +706,9 @@ abstract class SerializableStorageContainer<E extends Object> extends StorageCon
703706
@override
704707
Future<void> close() async {
705708
removeAllListeners();
709+
for (final holder in _holders.values) {
710+
holder.dispose();
711+
}
706712
await backend.close();
707713
}
708714

@@ -714,27 +720,45 @@ abstract class SerializableStorageContainer<E extends Object> extends StorageCon
714720
/// to avoid memory leaks.
715721
///
716722
/// This can be done by cancelling the subscription to the stream.
717-
Stream<E?> stream(String key) async* {
718-
final E? itemValue = await get(key);
719-
yield itemValue;
720-
721-
late final void Function() retrieveAndAdd;
722-
final controller = StreamController<E?>(
723-
onCancel: () => removeKeyListener(key, retrieveAndAdd),
724-
);
725-
726-
retrieveAndAdd = () async {
727-
if (controller.isClosed) return;
728-
final E? value = await get(key);
729-
if (!controller.isClosed) {
730-
controller.add(value);
731-
}
732-
};
723+
Stream<E?> stream(String key) => itemHolder(key);
733724

734-
addKeyListener(key, retrieveAndAdd);
725+
/// Creates an item holder for storing a single primitive value at the specified key.
726+
///
727+
/// [ItemHolder] provides a convenient API for managing a single item/key in the storage/container.
728+
/// This makes it easier to pass around a fragment of the storage (without needing to reference
729+
/// the entire storage/container) that knows how perform operations on that specific key.
730+
///
731+
/// Note: Calling this method with the same key multiple times will return
732+
/// the same instance of [JsonItemHolder] if it already exists, ensuring that
733+
/// there is only one holder per key. If the existing holder has been closed,
734+
/// a new instance will be created.
735+
///
736+
/// Parameters:
737+
/// - [key]: The key under which to store the item. Must be non-empty and not only whitespace.
738+
/// This key is relative to the container (not encoded).
739+
///
740+
/// Returns:
741+
/// A [ItemHolder] configured to manage the item at the specified key.
742+
///
743+
/// Throws:
744+
/// - [ArgumentError] if the key is invalid.
745+
SerializableItemHolder<E> itemHolder(String key) {
746+
validateKey(key);
747+
var existing = _holders[key];
748+
if (existing != null && existing.isClosed) {
749+
_holders.remove(key);
750+
existing = null;
751+
}
752+
if (existing != null) return existing;
735753

736-
yield* controller.stream;
737-
await controller.close(); // coverage:ignore-line
754+
final holder = SerializableItemHolder<E>(
755+
this,
756+
encodeKey(key),
757+
serialize: serialize,
758+
deserialize: deserialize,
759+
);
760+
_holders[key] = holder;
761+
return holder;
738762
}
739763

740764
/// Provides a [Stream] of values for all items in the container.

0 commit comments

Comments
 (0)