Migrate built value example to a modular builder

This commit is contained in:
Simon Binder 2023-10-03 21:14:20 +02:00
parent eb03ac5ff0
commit fcd984f134
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
6 changed files with 235 additions and 236 deletions

View File

@ -6,39 +6,37 @@ template: layouts/docs/single
--- ---
It is possible to use classes generated by drift in other builders. It is possible to use classes generated by drift in other builders.
Due to technicalities related to Dart's build system and `source_gen`, this approach requires a custom configuration Due to technicalities related to Dart's build system and `source_gen`, this approach requires a custom build configuration.
and minor code changes. Put this content in a file called `build.yaml` next to your `pubspec.yaml`: For complex builds like this, we recommend running drift in it's [modular mode]({{ 'modular.md' | pageUrl }}). This more is more efficient for larger builds and can be enabled by putting this
content in a file called `build.yaml` next to your `pubspec.yaml`:
```yaml ```yaml
targets: targets:
$default: drift:
# disable the default generators, we'll only use the non-shared drift generator here
auto_apply_builders: false auto_apply_builders: false
builders: builders:
drift_dev|not_shared: drift_dev:modular:
enabled: true
# If needed, you can configure the builder like this:
# options:
# skip_verification_code: true
# use_experimental_inference: true
# This builder is necessary for drift-file preprocessing. You can disable it if you're not
# using .drift files with type converters.
drift_dev|preparing_builder:
enabled: true enabled: true
run_built_value: $default:
dependencies: ['your_package_name'] dependencies:
# run drift's builder first
- ":drift"
builders: builders:
# Disable drift builders. By default, those would run on each target # This builder is enabled by default, but we're using the modular builder in
# its own target instead.
drift_dev: drift_dev:
enabled: false enabled: false
drift_dev|preparing_builder:
enabled: false
# we don't need to disable drift|not_shared, because it's disabled by default
``` ```
In all files that use generated drift code, you'll have to replace `part 'filename.g.dart'` with `part 'filename.drift.dart'`. With modular generation, you'll have to replace the `part` statement in the database file with an
If you use drift _and_ another builder in the same file, you'll need both `.g.dart` and `.drift.dart` as part-files. import to `filename.drift.dart`. Also, your database class now extends from `$DatabaseName`, without
a leading underscore.
By generating independent libraries, drift can manage imports on its own. By declaring a dependency in
`build.yaml`, the build system also ensures that drift-generated files are ready before `built_value`
or other builders that need to see them are running.
A full example is available as part of [the drift repo](https://github.com/simolus3/drift/tree/develop/examples/with_built_value). A full example is available as part of [the drift repo](https://github.com/simolus3/drift/tree/develop/examples/with_built_value).
@ -53,8 +51,8 @@ and `built_value` in the same project, those part files could be called `.drift.
Later, the common `source_gen` package would merge the part files into a single `.g.dart` file. Later, the common `source_gen` package would merge the part files into a single `.g.dart` file.
This works great for most use cases, but a downside is that each builder can't see the final `.g.dart` This works great for most use cases, but a downside is that each builder can't see the final `.g.dart`
file, or use any classes or methods defined in it. To fix that, drift offers an optional builder - file, or use any classes or methods defined in it. To fix that, drift offers other builders -
`drift_dev|not_shared` - that will generate a separate part file only containing `drift_dev|not_shared` and `drift_dev|modular` - those will generate a separate file only containing
code generated by drift. So most of the work resolves around disabling the default generator of drift code generated by drift. So most of the work resolves around disabling the default generator of drift
and use the non-shared generator instead. and use the non-shared generator instead.

View File

@ -1,25 +1,16 @@
targets: targets:
drift:
auto_apply_builders: false
builders:
drift_dev:modular:
enabled: true
$default: $default:
dependencies:
# run drift's builder first
- ":drift"
builders: builders:
# disables the SharedPartBuilder in favor of a PartBuilder from drift_dev # This builder is enabled by default, but we're using the modular builder in
# its own target instead.
drift_dev: drift_dev:
enabled: false enabled: false
drift_dev:preparing_builder:
enabled: true
drift_dev:not_shared:
enabled: true
# Run built_value_generator when drift is done (so after this target)
built_value_generator:built_value:
enabled: false
run_built_value:
dependencies: ['$default']
builders:
# Disable all auto-applied builders from drift
drift_dev:
enabled: false
drift_dev:preparing_builder:
enabled: false
build_resolvers:transitive_digests:
enabled: false

View File

@ -2,11 +2,13 @@ import 'package:built_value/built_value.dart';
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:drift/native.dart'; import 'package:drift/native.dart';
part 'database.drift.dart'; import 'database.drift.dart';
import 'tables.drift.dart';
part 'database.g.dart'; part 'database.g.dart';
abstract class Foo implements Built<Foo, FooBuilder> { abstract class Foo implements Built<Foo, FooBuilder> {
User get moorField; User get driftGeneratedField;
Foo._(); Foo._();
@ -14,7 +16,7 @@ abstract class Foo implements Built<Foo, FooBuilder> {
} }
@DriftDatabase(include: {'tables.drift'}) @DriftDatabase(include: {'tables.drift'})
class Database extends _$Database { class Database extends $Database {
Database() : super(NativeDatabase.memory()); Database() : super(NativeDatabase.memory());
@override @override

View File

@ -1,183 +1,13 @@
// ignore_for_file: type=lint // ignore_for_file: type=lint
part of 'database.dart'; import 'package:drift/drift.dart' as i0;
import 'package:with_built_value/tables.drift.dart' as i1;
class Users extends Table with TableInfo<Users, User> { abstract class $Database extends i0.GeneratedDatabase {
$Database(i0.QueryExecutor e) : super(e);
late final i1.Users users = i1.Users(this);
@override @override
final GeneratedDatabase attachedDatabase; Iterable<i0.TableInfo<i0.Table, Object?>> get allTables =>
final String? _alias; allSchemaEntities.whereType<i0.TableInfo<i0.Table, Object?>>();
Users(this.attachedDatabase, [this._alias]);
static const VerificationMeta _idMeta = const VerificationMeta('id');
late final GeneratedColumn<int> id = GeneratedColumn<int>(
'id', aliasedName, false,
hasAutoIncrement: true,
type: DriftSqlType.int,
requiredDuringInsert: false,
$customConstraints: 'NOT NULL PRIMARY KEY AUTOINCREMENT');
static const VerificationMeta _nameMeta = const VerificationMeta('name');
late final GeneratedColumn<String> name = GeneratedColumn<String>(
'name', aliasedName, false,
type: DriftSqlType.string,
requiredDuringInsert: true,
$customConstraints: 'NOT NULL');
@override @override
List<GeneratedColumn> get $columns => [id, name]; List<i0.DatabaseSchemaEntity> get allSchemaEntities => [users];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'users';
@override
VerificationContext validateIntegrity(Insertable<User> instance,
{bool isInserting = false}) {
final context = VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('id')) {
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
}
if (data.containsKey('name')) {
context.handle(
_nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta));
} else if (isInserting) {
context.missing(_nameMeta);
}
return context;
}
@override
Set<GeneratedColumn> get $primaryKey => {id};
@override
User map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return User(
id: attachedDatabase.typeMapping
.read(DriftSqlType.int, data['${effectivePrefix}id'])!,
name: attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}name'])!,
);
}
@override
Users createAlias(String alias) {
return Users(attachedDatabase, alias);
}
@override
bool get dontWriteConstraints => true;
}
class User extends DataClass implements Insertable<User> {
final int id;
final String name;
const User({required this.id, required this.name});
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
map['id'] = Variable<int>(id);
map['name'] = Variable<String>(name);
return map;
}
UsersCompanion toCompanion(bool nullToAbsent) {
return UsersCompanion(
id: Value(id),
name: Value(name),
);
}
factory User.fromJson(Map<String, dynamic> json,
{ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return User(
id: serializer.fromJson<int>(json['id']),
name: serializer.fromJson<String>(json['name']),
);
}
@override
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'id': serializer.toJson<int>(id),
'name': serializer.toJson<String>(name),
};
}
User copyWith({int? id, String? name}) => User(
id: id ?? this.id,
name: name ?? this.name,
);
@override
String toString() {
return (StringBuffer('User(')
..write('id: $id, ')
..write('name: $name')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(id, name);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is User && other.id == this.id && other.name == this.name);
}
class UsersCompanion extends UpdateCompanion<User> {
final Value<int> id;
final Value<String> name;
const UsersCompanion({
this.id = const Value.absent(),
this.name = const Value.absent(),
});
UsersCompanion.insert({
this.id = const Value.absent(),
required String name,
}) : name = Value(name);
static Insertable<User> custom({
Expression<int>? id,
Expression<String>? name,
}) {
return RawValuesInsertable({
if (id != null) 'id': id,
if (name != null) 'name': name,
});
}
UsersCompanion copyWith({Value<int>? id, Value<String>? name}) {
return UsersCompanion(
id: id ?? this.id,
name: name ?? this.name,
);
}
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
if (id.present) {
map['id'] = Variable<int>(id.value);
}
if (name.present) {
map['name'] = Variable<String>(name.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('UsersCompanion(')
..write('id: $id, ')
..write('name: $name')
..write(')'))
.toString();
}
}
abstract class _$Database extends GeneratedDatabase {
_$Database(QueryExecutor e) : super(e);
late final Users users = Users(this);
@override
Iterable<TableInfo<Table, Object?>> get allTables =>
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
@override
List<DatabaseSchemaEntity> get allSchemaEntities => [users];
} }

View File

@ -8,13 +8,14 @@ part of 'database.dart';
class _$Foo extends Foo { class _$Foo extends Foo {
@override @override
final User moorField; final User driftGeneratedField;
factory _$Foo([void Function(FooBuilder)? updates]) => factory _$Foo([void Function(FooBuilder)? updates]) =>
(new FooBuilder()..update(updates))._build(); (new FooBuilder()..update(updates))._build();
_$Foo._({required this.moorField}) : super._() { _$Foo._({required this.driftGeneratedField}) : super._() {
BuiltValueNullFieldError.checkNotNull(moorField, r'Foo', 'moorField'); BuiltValueNullFieldError.checkNotNull(
driftGeneratedField, r'Foo', 'driftGeneratedField');
} }
@override @override
@ -27,20 +28,21 @@ class _$Foo extends Foo {
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
if (identical(other, this)) return true; if (identical(other, this)) return true;
return other is Foo && moorField == other.moorField; return other is Foo && driftGeneratedField == other.driftGeneratedField;
} }
@override @override
int get hashCode { int get hashCode {
var _$hash = 0; var _$hash = 0;
_$hash = $jc(_$hash, moorField.hashCode); _$hash = $jc(_$hash, driftGeneratedField.hashCode);
_$hash = $jf(_$hash); _$hash = $jf(_$hash);
return _$hash; return _$hash;
} }
@override @override
String toString() { String toString() {
return (newBuiltValueToStringHelper(r'Foo')..add('moorField', moorField)) return (newBuiltValueToStringHelper(r'Foo')
..add('driftGeneratedField', driftGeneratedField))
.toString(); .toString();
} }
} }
@ -48,16 +50,17 @@ class _$Foo extends Foo {
class FooBuilder implements Builder<Foo, FooBuilder> { class FooBuilder implements Builder<Foo, FooBuilder> {
_$Foo? _$v; _$Foo? _$v;
User? _moorField; User? _driftGeneratedField;
User? get moorField => _$this._moorField; User? get driftGeneratedField => _$this._driftGeneratedField;
set moorField(User? moorField) => _$this._moorField = moorField; set driftGeneratedField(User? driftGeneratedField) =>
_$this._driftGeneratedField = driftGeneratedField;
FooBuilder(); FooBuilder();
FooBuilder get _$this { FooBuilder get _$this {
final $v = _$v; final $v = _$v;
if ($v != null) { if ($v != null) {
_moorField = $v.moorField; _driftGeneratedField = $v.driftGeneratedField;
_$v = null; _$v = null;
} }
return this; return this;
@ -80,8 +83,8 @@ class FooBuilder implements Builder<Foo, FooBuilder> {
_$Foo _build() { _$Foo _build() {
final _$result = _$v ?? final _$result = _$v ??
new _$Foo._( new _$Foo._(
moorField: BuiltValueNullFieldError.checkNotNull( driftGeneratedField: BuiltValueNullFieldError.checkNotNull(
moorField, r'Foo', 'moorField')); driftGeneratedField, r'Foo', 'driftGeneratedField'));
replace(_$result); replace(_$result);
return _$result; return _$result;
} }

View File

@ -0,0 +1,175 @@
// ignore_for_file: type=lint
import 'package:drift/drift.dart' as i0;
import 'package:with_built_value/tables.drift.dart' as i1;
class Users extends i0.Table with i0.TableInfo<Users, i1.User> {
@override
final i0.GeneratedDatabase attachedDatabase;
final String? _alias;
Users(this.attachedDatabase, [this._alias]);
static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id');
late final i0.GeneratedColumn<int> id = i0.GeneratedColumn<int>(
'id', aliasedName, false,
hasAutoIncrement: true,
type: i0.DriftSqlType.int,
requiredDuringInsert: false,
$customConstraints: 'NOT NULL PRIMARY KEY AUTOINCREMENT');
static const i0.VerificationMeta _nameMeta =
const i0.VerificationMeta('name');
late final i0.GeneratedColumn<String> name = i0.GeneratedColumn<String>(
'name', aliasedName, false,
type: i0.DriftSqlType.string,
requiredDuringInsert: true,
$customConstraints: 'NOT NULL');
@override
List<i0.GeneratedColumn> get $columns => [id, name];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'users';
@override
i0.VerificationContext validateIntegrity(i0.Insertable<i1.User> instance,
{bool isInserting = false}) {
final context = i0.VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('id')) {
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
}
if (data.containsKey('name')) {
context.handle(
_nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta));
} else if (isInserting) {
context.missing(_nameMeta);
}
return context;
}
@override
Set<i0.GeneratedColumn> get $primaryKey => {id};
@override
i1.User map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return i1.User(
id: attachedDatabase.typeMapping
.read(i0.DriftSqlType.int, data['${effectivePrefix}id'])!,
name: attachedDatabase.typeMapping
.read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!,
);
}
@override
Users createAlias(String alias) {
return Users(attachedDatabase, alias);
}
@override
bool get dontWriteConstraints => true;
}
class User extends i0.DataClass implements i0.Insertable<i1.User> {
final int id;
final String name;
const User({required this.id, required this.name});
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
map['id'] = i0.Variable<int>(id);
map['name'] = i0.Variable<String>(name);
return map;
}
i1.UsersCompanion toCompanion(bool nullToAbsent) {
return i1.UsersCompanion(
id: i0.Value(id),
name: i0.Value(name),
);
}
factory User.fromJson(Map<String, dynamic> json,
{i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return User(
id: serializer.fromJson<int>(json['id']),
name: serializer.fromJson<String>(json['name']),
);
}
@override
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'id': serializer.toJson<int>(id),
'name': serializer.toJson<String>(name),
};
}
i1.User copyWith({int? id, String? name}) => i1.User(
id: id ?? this.id,
name: name ?? this.name,
);
@override
String toString() {
return (StringBuffer('User(')
..write('id: $id, ')
..write('name: $name')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(id, name);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is i1.User && other.id == this.id && other.name == this.name);
}
class UsersCompanion extends i0.UpdateCompanion<i1.User> {
final i0.Value<int> id;
final i0.Value<String> name;
const UsersCompanion({
this.id = const i0.Value.absent(),
this.name = const i0.Value.absent(),
});
UsersCompanion.insert({
this.id = const i0.Value.absent(),
required String name,
}) : name = i0.Value(name);
static i0.Insertable<i1.User> custom({
i0.Expression<int>? id,
i0.Expression<String>? name,
}) {
return i0.RawValuesInsertable({
if (id != null) 'id': id,
if (name != null) 'name': name,
});
}
i1.UsersCompanion copyWith({i0.Value<int>? id, i0.Value<String>? name}) {
return i1.UsersCompanion(
id: id ?? this.id,
name: name ?? this.name,
);
}
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
if (id.present) {
map['id'] = i0.Variable<int>(id.value);
}
if (name.present) {
map['name'] = i0.Variable<String>(name.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('UsersCompanion(')
..write('id: $id, ')
..write('name: $name')
..write(')'))
.toString();
}
}