Skip to content

Commit

Permalink
Feature/sqflite (#18)
Browse files Browse the repository at this point in the history
* add SQLite integration

* refactor test suite

* more test fixes

* more test fixes
  • Loading branch information
danReynolds authored Nov 8, 2024
1 parent c8183bb commit 68ba4e8
Show file tree
Hide file tree
Showing 38 changed files with 1,433 additions and 2,049 deletions.
3 changes: 3 additions & 0 deletions .flutter-plugins
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ path_provider_android=/Users/dan/.pub-cache/hosted/pub.dev/path_provider_android
path_provider_foundation=/Users/dan/.pub-cache/hosted/pub.dev/path_provider_foundation-2.3.2/
path_provider_linux=/Users/dan/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/
path_provider_windows=/Users/dan/.pub-cache/hosted/pub.dev/path_provider_windows-2.2.1/
sqflite=/Users/dan/.pub-cache/hosted/pub.dev/sqflite-2.4.1/
sqflite_android=/Users/dan/.pub-cache/hosted/pub.dev/sqflite_android-2.4.0/
sqflite_darwin=/Users/dan/.pub-cache/hosted/pub.dev/sqflite_darwin-2.4.1/
8 changes: 0 additions & 8 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
Expand All @@ -12,8 +10,6 @@
"kind": "build",
"isDefault": true
},
"problemMatcher": [],
"detail": "Runs a specific Flutter test file on the Chrome platform."
},
{
"label": "Native tests",
Expand All @@ -24,8 +20,6 @@
"kind": "build",
"isDefault": true
},
"problemMatcher": [],
"detail": "Runs a specific Flutter test file on the Chrome platform."
},
{
"label": "Web tests",
Expand All @@ -41,8 +35,6 @@
"kind": "build",
"isDefault": true
},
"problemMatcher": [],
"detail": "Runs a specific Flutter test file on the Chrome platform."
}
]
}
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,8 @@ The currently available persistence options are broken down by platform:

### Native

* **FilePersistor**: The default file-based persistence implementation for native platforms. Documents are stored in one or more files based on the persistence configuration.
* **SqlitePersistor**: A SQLite persistence implementation using [sqflite](https://pub.dev/packages/sqflite). Documents are distributed in rows based on their persistence configuration.
* **FilePersistor**: A file-based persistence implementation for native platforms. Documents are stored in one or more files based on the persistence configuration.

### Web

Expand Down
89 changes: 49 additions & 40 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ import 'package:uuid/uuid.dart';

const uuid = Uuid();

final logger = Logger('Playground');

void main() async {
WidgetsFlutterBinding.ensureInitialized();

Loon.configure(
persistor: Persistor.current(),
enableLogging: true,
);

await Loon.hydrate();
await logger.measure('Hydrate', () => Loon.hydrate());

runApp(const MyApp());
}
Expand Down Expand Up @@ -130,40 +131,42 @@ class _MyHomePageState extends State<MyHomePage> {
userSnap.data.name.startsWith(_controller.text),
),
builder: (context, usersSnap) {
return ListView.builder(
shrinkWrap: true,
itemCount: usersSnap.length,
itemBuilder: (context, index) {
final userSnap = usersSnap[index];
final user = userSnap.data;

return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(user.name),
TextButton(
onPressed: () {
_showEditDialog(userSnap.doc);
},
child: const Text('Edit'),
),
TextButton(
onPressed: () {
UserModel.store.doc(userSnap.id).delete();
},
child: Text(
'Remove',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color: Colors.red,
),
return Flexible(
child: ListView.builder(
shrinkWrap: true,
itemCount: usersSnap.length,
itemBuilder: (context, index) {
final userSnap = usersSnap[index];
final user = userSnap.data;

return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(user.name),
TextButton(
onPressed: () {
_showEditDialog(userSnap.doc);
},
child: const Text('Edit'),
),
TextButton(
onPressed: () {
UserModel.store.doc(userSnap.id).delete();
},
child: Text(
'Remove',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color: Colors.red,
),
),
),
),
],
);
},
],
);
},
),
);
},
),
Expand All @@ -182,18 +185,24 @@ class _MyHomePageState extends State<MyHomePage> {
FloatingActionButton(
onPressed: () {
final id = uuid.v4();
final doc = UserModel.store.doc(id);

if (!doc.exists()) {
UserModel.store.doc(id).create(UserModel(name: 'User $id'));
},
child: const Icon(Icons.add),
),
const SizedBox(width: 24),
FloatingActionButton.extended(
label: const Text('Load test (10000)'),
onPressed: () {
for (int i = 0; i < 10000; i++) {
final id = uuid.v4();
UserModel.store.doc(id).create(UserModel(name: 'User $id'));
}
},
child: const Icon(Icons.add),
),
const SizedBox(width: 24),
FloatingActionButton(
onPressed: () {
UserModel.store.delete();
Loon.clearAll();
},
child: const Icon(Icons.delete),
),
Expand Down
2 changes: 2 additions & 0 deletions example/macos/Flutter/GeneratedPluginRegistrant.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import Foundation

import flutter_secure_storage_macos
import path_provider_foundation
import sqflite_darwin

func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
}
7 changes: 7 additions & 0 deletions example/macos/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ PODS:
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- sqflite_darwin (0.0.4):
- Flutter
- FlutterMacOS

DEPENDENCIES:
- flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`)
- FlutterMacOS (from `Flutter/ephemeral`)
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
- sqflite_darwin (from `Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin`)

EXTERNAL SOURCES:
flutter_secure_storage_macos:
Expand All @@ -18,11 +22,14 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral
path_provider_foundation:
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
sqflite_darwin:
:path: Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin

SPEC CHECKSUMS:
flutter_secure_storage_macos: 59459653abe1adb92abbc8ea747d79f8d19866c9
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d

PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367

Expand Down
52 changes: 50 additions & 2 deletions example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -331,10 +331,10 @@ packages:
dependency: transitive
description:
name: plugin_platform_interface
sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc"
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
version: "2.1.8"
pointycastle:
dependency: transitive
description:
Expand Down Expand Up @@ -372,6 +372,46 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.0.0"
sqflite:
dependency: transitive
description:
name: sqflite
sha256: "2d7299468485dca85efeeadf5d38986909c5eb0cd71fd3db2c2f000e6c9454bb"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
sqflite_android:
dependency: transitive
description:
name: sqflite_android
sha256: "78f489aab276260cdd26676d2169446c7ecd3484bbd5fead4ca14f3ed4dd9ee3"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
sha256: "4468b24876d673418a7b7147e5a08a715b4998a7ae69227acafaab762e0e5490"
url: "https://pub.dev"
source: hosted
version: "2.5.4+5"
sqflite_darwin:
dependency: transitive
description:
name: sqflite_darwin
sha256: "96a698e2bc82bd770a4d6aab00b42396a7c63d9e33513a56945cbccb594c2474"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
sqflite_platform_interface:
dependency: transitive
description:
name: sqflite_platform_interface
sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
stack_trace:
dependency: transitive
description:
Expand All @@ -396,6 +436,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.0"
synchronized:
dependency: transitive
description:
name: synchronized
sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225"
url: "https://pub.dev"
source: hosted
version: "3.3.0+3"
term_glyph:
dependency: transitive
description:
Expand Down
3 changes: 2 additions & 1 deletion lib/loon.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ library loon;
import 'dart:async';
import 'dart:convert';
import 'package:flutter/foundation.dart' hide Key;
import 'package:loon/persistor/file_persistor/file_persistor.dart';
import 'package:loon/persistor/indexed_db_persistor/indexed_db_persistor.dart';
import 'package:uuid/uuid.dart';
import 'dart:collection';
import 'persistor/platform_persistor/platform_persistor.dart';

export 'widgets/query_stream_builder.dart';
export 'widgets/document_stream_builder.dart';
Expand Down
34 changes: 20 additions & 14 deletions lib/persistor/data_store_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,42 +9,49 @@ class DataStoreManager {
/// The duration by which to throttle persistence changes to the file system.
final Duration persistenceThrottle;

/// The global persistor settings.
final PersistorSettings settings;

final void Function()? onSync;

final void Function(String text) onLog;

/// The resolver that contains a mapping of documents to the file data store in which
/// the document is currently stored.
final DataStoreResolver resolver;
final DataStoreFactory factory;

/// The index of [DualDataStore] objects by store name.
final Map<String, DualDataStore> index = {};
final Future<void> Function() _clearAll;

final DataStoreFactory factory;
final DataStoreResolverConfig resolverConfig;

/// The sync lock is used to block operations from accessing the file system while there is an ongoing sync
/// operation and conversely blocks a sync from starting until the ongoing operation holding the lock has finished.
final _syncLock = Lock();

late final _logger = Logger('DataStoreManager', output: onLog);

/// The sync timer is used to throttle syncing changes to the file system using
/// the given [persistenceThrottle]. After an that mutates the file system operation runs, it schedules
/// a sync to run on a timer. When the sync runs, it acquires the [_syncLock], blocking any operations
/// from being processed until the sync completes.
Timer? _syncTimer;

late final _logger = Logger('DataStoreManager', output: onLog);
/// The resolver that contains a mapping of documents to the file data store in which
/// the document is currently stored.
DataStoreResolver resolver;

/// The index of [DualDataStore] objects by store name.
Map<String, DualDataStore> index = {};

DataStoreManager({
required this.persistenceThrottle,
required this.onSync,
required this.onLog,
required this.settings,
required this.factory,
required DataStoreResolverConfig resolverConfig,
required this.resolverConfig,
required Set<String> initialStoreNames,
}) : resolver = DataStoreResolver(resolverConfig) {
required Future<void> Function() clearAll,
}) : _clearAll = clearAll,
resolver = DataStoreResolver(resolverConfig) {
for (final name in initialStoreNames) {
index[name] = DualDataStore(name, factory: factory);
}
Expand Down Expand Up @@ -311,12 +318,11 @@ class DataStoreManager {
// Cancel any pending sync, since all data stores are being cleared immediately.
_cancelSync();

await Future.wait([
...index.values.map((dataStore) => dataStore.delete()),
resolver.delete(),
]);
await _clearAll();

index.clear();
// Reset the data store index and resolver.
index = {};
resolver = DataStoreResolver(resolverConfig);
});
}
}
4 changes: 2 additions & 2 deletions lib/persistor/file_persistor/file_persistor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class FilePersistor extends Persistor {
super.settings = const PersistorSettings(),
super.persistenceThrottle = const Duration(milliseconds: 100),
DataStoreEncrypter? encrypter,
}) : encrypter = encrypter ?? DataStoreEncrypter(),
}) : encrypter = encrypter = encrypter ?? DataStoreEncrypter(),
logger = Logger('FilePersistor', output: Loon.logger.log);

void _onMessage(dynamic message) {
Expand Down Expand Up @@ -110,7 +110,7 @@ class FilePersistor extends Persistor {
return Isolate.spawn(
FilePersistorWorker.init,
initMessage,
debugName: 'Loon worker',
debugName: 'FilePersistorWorker',
);
});

Expand Down
Loading

0 comments on commit 68ba4e8

Please sign in to comment.