Add LazyDatabase wrapper to create a database async

This commit is contained in:
Simon Binder 2019-09-20 20:23:35 +02:00
parent 7d962a1f01
commit 87c50de1e1
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
5 changed files with 122 additions and 0 deletions

View File

@ -54,6 +54,8 @@ TODO: Describe ffi port
### Minor changes
- a `Constant<String>` can now be written to SQL, it used to throw before. This is useful
if you need default values for strings columns.
- new `LazyDatabase` when you want to construct a database asynchronously (for instance, if
you first need to find a file before you can open a database).
### Breaking changes
- __THIS LIKELY AFFECTS YOUR APP:__ Removed the `transaction` parameter for callbacks

View File

@ -35,3 +35,4 @@ export 'package:moor/src/runtime/migration.dart';
export 'package:moor/src/runtime/exceptions.dart';
export 'package:moor/src/utils/expand_variables.dart';
export 'package:moor/src/utils/hash.dart';
export 'package:moor/src/utils/lazy_database.dart';

View File

@ -0,0 +1,66 @@
import 'dart:async';
import 'package:moor/backends.dart';
/// Signature of a function that opens a database connection when instructed to.
typedef DatabaseOpener = FutureOr<QueryExecutor> Function();
/// A special database executor that delegates work to another [QueryExecutor].
/// The other executor is lazily opened by a [DatabaseOpener].
class LazyDatabase extends QueryExecutor {
QueryExecutor _delegate;
Completer<void> _openDelegate;
/// The function that will open the database when this [LazyDatabase] gets
/// opened for the first time.
final DatabaseOpener opener;
LazyDatabase(this.opener);
Future<void> _awaitOpened() {
if (_delegate != null) {
return Future.value();
} else if (_openDelegate != null) {
return _openDelegate.future;
} else {
_openDelegate = Completer();
Future.value(opener()).then((database) {
_delegate = database;
_openDelegate.complete();
});
return _openDelegate.future;
}
}
@override
TransactionExecutor beginTransaction() => _delegate.beginTransaction();
@override
Future<bool> ensureOpen() {
return _awaitOpened().then((_) => _delegate.ensureOpen());
}
@override
Future<void> runBatched(List<BatchedStatement> statements) =>
_delegate.runBatched(statements);
@override
Future<void> runCustom(String statement, [List args]) =>
_delegate.runCustom(statement, args);
@override
Future<int> runDelete(String statement, List args) =>
_delegate.runDelete(statement, args);
@override
Future<int> runInsert(String statement, List args) =>
_delegate.runInsert(statement, args);
@override
Future<List<Map<String, dynamic>>> runSelect(String statement, List args) =>
_delegate.runSelect(statement, args);
@override
Future<int> runUpdate(String statement, List args) =>
_delegate.runUpdate(statement, args);
}

View File

@ -34,6 +34,11 @@ class MockExecutor extends Mock implements QueryExecutor {
return transactions;
});
when(ensureOpen()).thenAnswer((i) {
_opened = true;
return Future.value(true);
});
when(doWhenOpened(any)).thenAnswer((i) {
_opened = true;
final action = i.positionalArguments.single as _EnsureOpenAction;

View File

@ -0,0 +1,48 @@
import 'package:moor/moor.dart';
import 'package:pedantic/pedantic.dart';
import 'package:test_api/test_api.dart';
import '../data/utils/mocks.dart';
void main() {
test('lazy database delegates work', () async {
final inner = MockExecutor();
final lazy = LazyDatabase(() => inner);
await lazy.ensureOpen();
clearInteractions(inner);
lazy.beginTransaction();
await lazy.runBatched(null);
await lazy.runCustom('custom_stmt');
await lazy.runDelete('delete_stmt', [1]);
await lazy.runInsert('insert_stmt', [2]);
await lazy.runSelect('select_stmt', [3]);
await lazy.runUpdate('update_stmt', [4]);
verifyInOrder([
inner.runBatched(null),
inner.runCustom('custom_stmt'),
inner.runDelete('delete_stmt', [1]),
inner.runInsert('insert_stmt', [2]),
inner.runSelect('select_stmt', [3]),
inner.runUpdate('update_stmt', [4]),
]);
});
test('database is only opened once', () async {
final inner = MockExecutor();
var openCount = 0;
final lazy = LazyDatabase(() {
openCount++;
return inner;
});
for (var i = 0; i < 10; i++) {
unawaited(lazy.ensureOpen());
}
await pumpEventQueue();
expect(openCount, 1);
});
}