forked from pschiffmann/zone_di.dart
/
main.dart
125 lines (104 loc) · 3.17 KB
/
main.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/* Copyright (C) S. Brett Sutton - All Rights Reserved
* Unauthorized copying of this file, via any medium is strictly prohibited
* Proprietary and confidential
* Written by Brett Sutton <bsutton@onepub.dev>, Jan 2022
*/
/// Example of how you might use Scope to implement a Db Transaction
/// and inject depedencies such as the tenant into each query.
///
/// Class names borrowed from [simple_mysql_orm](https://pub.dev/packages/simple_mysql_orm).
library scope.example;
import 'package:money2/money2.dart';
import 'package:scope/scope.dart';
/// declare the keys we use to inject values.
final tenantKey = ScopeKey<int>();
final licenseType = ScopeKey<LicenseType>();
final licenseFee = ScopeKey<Money>();
void main() async {
/// Inject details required to bill a tenant.
final scope = Scope()
..value(tenantKey, 1)
..value(licenseType, LicenseType.pro)
..single(licenseFee, getLicenseFee);
await scope.run(() async {
await Tenant().bill();
});
}
class Tenant {
Future<void> bill() async {
await withTransaction<void>(() async {
/// get the injected Db.
Transaction.current.db
..query(
'select * from tenant where tenant = ?',
[use(tenantKey)],
)
..query(
'insert into billing (tenantId, date, amountInCents) values(?,?,?)',
[use(tenantKey), DateTime.now(), use(licenseFee).minorUnits],
);
});
}
}
/// Use Scope to create transactions scopes that
/// inject a Db connection.
Future<R?> withTransaction<R>(Future<R> Function() action) async {
final db = DbPool().obtain();
try {
final transaction = Transaction<R>(db);
return await (Scope()..value(Transaction.transactionKey, transaction))
.runSync(() async => transaction.run(action));
} finally {
DbPool().release(db);
}
}
class Transaction<R> {
/// Create a database transaction for [db].
///
Transaction(
this.db,
);
// ignore: strict_raw_type
static Transaction get current {
// ignore: strict_raw_type
Transaction transaction;
try {
transaction = use(transactionKey);
// ignore: strict_raw_type
} on MissingDependencyException catch (_) {
throw TransactionNotInScopeException();
}
return transaction;
}
final Db db;
// ignore: strict_raw_type
static final ScopeKey<Transaction> transactionKey =
// ignore: strict_raw_type
ScopeKey<Transaction>('transaction');
Future<R?> run(Future<R> Function() action) async {
/// run using a transaction
final result = db.transaction(() async => action());
return result;
}
}
class TransactionNotInScopeException implements Exception {}
class Db {
R transaction<R>(Future<R> Function() param0) => 1 as R;
void query(String s, List<Object?> list) {}
}
class DbPool {
Db obtain() => Db();
void release(Db db) {}
}
enum LicenseType { pro, team, enterprise }
/// calculate the license fee
Money getLicenseFee() {
switch (use(licenseType)) {
case LicenseType.pro:
return Money.parse(r'$10.00', code: 'USD');
case LicenseType.team:
return Money.parse(r'$15.00', code: 'USD');
case LicenseType.enterprise:
return Money.parse(r'$20.00', code: 'USD');
}
}