mirror of https://github.com/AMT-Cheif/drift.git
Option to generate mutable data classes (#551)
This commit is contained in:
parent
896d62d76c
commit
f6ab5f64a8
|
@ -62,6 +62,8 @@ At the moment, moor supports these options:
|
|||
If you're using this flag, please open an issue and explain how the new inference isn't working for you, thanks!
|
||||
* `data_class_to_companions` (defaults to `true`): Controls whether moor will write the `toCompanion` method in generated
|
||||
data classes.
|
||||
* `mutable_classes` (defaults to `false`): The fields generated in generated data, companion and result set classes are final
|
||||
by default. You can make them mutable by setting `mutable_classes: true`.
|
||||
|
||||
## Available extensions
|
||||
|
||||
|
|
|
@ -68,6 +68,9 @@ class MoorOptions {
|
|||
@JsonKey(name: 'data_class_to_companions', defaultValue: true)
|
||||
final bool dataClassToCompanions;
|
||||
|
||||
@JsonKey(name: 'mutable_classes', defaultValue: false)
|
||||
final bool generateMutableClasses;
|
||||
|
||||
/// Whether the [module] has been enabled in this configuration.
|
||||
bool hasModule(SqlModule module) => modules.contains(module);
|
||||
|
||||
|
@ -82,6 +85,7 @@ class MoorOptions {
|
|||
this.legacyTypeInference = false,
|
||||
this.eagerlyLoadDartAst = false,
|
||||
this.dataClassToCompanions = true,
|
||||
this.generateMutableClasses = false,
|
||||
this.modules = const [],
|
||||
});
|
||||
|
||||
|
|
|
@ -19,7 +19,8 @@ MoorOptions _$MoorOptionsFromJson(Map<String, dynamic> json) {
|
|||
'legacy_type_inference',
|
||||
'sqlite_modules',
|
||||
'eagerly_load_dart_ast',
|
||||
'data_class_to_companions'
|
||||
'data_class_to_companions',
|
||||
'mutable_classes'
|
||||
]);
|
||||
final val = MoorOptions(
|
||||
generateFromJsonStringConstructor: $checkedConvert(
|
||||
|
@ -54,6 +55,8 @@ MoorOptions _$MoorOptionsFromJson(Map<String, dynamic> json) {
|
|||
dataClassToCompanions:
|
||||
$checkedConvert(json, 'data_class_to_companions', (v) => v as bool) ??
|
||||
true,
|
||||
generateMutableClasses:
|
||||
$checkedConvert(json, 'mutable_classes', (v) => v as bool) ?? false,
|
||||
modules: $checkedConvert(
|
||||
json,
|
||||
'sqlite_modules',
|
||||
|
@ -76,6 +79,7 @@ MoorOptions _$MoorOptionsFromJson(Map<String, dynamic> json) {
|
|||
'legacyTypeInference': 'legacy_type_inference',
|
||||
'eagerlyLoadDartAst': 'eagerly_load_dart_ast',
|
||||
'dataClassToCompanions': 'data_class_to_companions',
|
||||
'generateMutableClasses': 'mutable_classes',
|
||||
'modules': 'sqlite_modules'
|
||||
});
|
||||
}
|
||||
|
|
|
@ -14,11 +14,14 @@ class ResultSetWriter {
|
|||
final into = scope.leaf();
|
||||
|
||||
into.write('class $className {\n');
|
||||
final modifier = scope.options.fieldModifier;
|
||||
|
||||
// write fields
|
||||
for (final column in query.resultSet.columns) {
|
||||
final name = query.resultSet.dartNameFor(column);
|
||||
final runtimeType = column.dartType;
|
||||
into.write('final $runtimeType $name\n;');
|
||||
|
||||
into.write('$modifier $runtimeType $name\n;');
|
||||
|
||||
fieldNames.add(name);
|
||||
}
|
||||
|
@ -27,7 +30,7 @@ class ResultSetWriter {
|
|||
final typeName = nested.table.dartTypeName;
|
||||
final fieldName = nested.dartFieldName;
|
||||
|
||||
into.write('final $typeName $fieldName;\n');
|
||||
into.write('$modifier $typeName $fieldName;\n');
|
||||
|
||||
fieldNames.add(fieldName);
|
||||
}
|
||||
|
|
|
@ -19,8 +19,9 @@ class DataClassWriter {
|
|||
|
||||
// write individual fields
|
||||
for (final column in table.columns) {
|
||||
_buffer
|
||||
.write('final ${column.dartTypeName} ${column.dartGetterName}; \n');
|
||||
final modifier = scope.options.fieldModifier;
|
||||
_buffer.write(
|
||||
'$modifier ${column.dartTypeName} ${column.dartGetterName}; \n');
|
||||
}
|
||||
|
||||
// write constructor with named optional fields
|
||||
|
|
|
@ -30,7 +30,8 @@ class UpdateCompanionWriter {
|
|||
|
||||
void _writeFields() {
|
||||
for (final column in table.columns) {
|
||||
_buffer.write('final Value<${column.dartTypeName}>'
|
||||
final modifier = scope.options.fieldModifier;
|
||||
_buffer.write('$modifier Value<${column.dartTypeName}>'
|
||||
' ${column.dartGetterName};\n');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -121,3 +121,7 @@ class DartScope {
|
|||
return other._id >= _id;
|
||||
}
|
||||
}
|
||||
|
||||
extension WriterUtilsForOptions on MoorOptions {
|
||||
String get fieldModifier => generateMutableClasses ? '' : 'final';
|
||||
}
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
import 'package:analyzer/dart/analysis/features.dart';
|
||||
import 'package:analyzer/dart/analysis/utilities.dart';
|
||||
import 'package:analyzer/dart/ast/ast.dart';
|
||||
import 'package:analyzer/file_system/memory_file_system.dart';
|
||||
import 'package:build/build.dart';
|
||||
import 'package:build_test/build_test.dart';
|
||||
import 'package:moor_generator/src/backends/build/moor_builder.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
const _testInput = r'''
|
||||
import 'package:moor/moor.dart';
|
||||
|
||||
part 'main.moor.dart';
|
||||
|
||||
class Users extends Table {
|
||||
IntColumn get id => integer().autoIncrement()();
|
||||
TextColumn get name => text()();
|
||||
}
|
||||
|
||||
@UseMoor(
|
||||
tables: [Users],
|
||||
queries: {
|
||||
'someQuery': 'SELECT 1 AS foo, 2 AS bar;',
|
||||
},
|
||||
)
|
||||
class Database extends _$Database {}
|
||||
''';
|
||||
|
||||
void main() {
|
||||
test('generates mutable classes if needed', () async {
|
||||
await testBuilder(
|
||||
MoorPartBuilder(const BuilderOptions({'mutable_classes': true})),
|
||||
const {'a|lib/main.dart': _testInput},
|
||||
reader: await PackageAssetReader.currentIsolate(),
|
||||
outputs: const {
|
||||
'a|lib/main.moor.dart': _GeneratesWithoutFinalFields(
|
||||
{'User', 'UsersCompanion', 'SomeQueryResult'},
|
||||
),
|
||||
},
|
||||
);
|
||||
}, tags: 'analyzer');
|
||||
}
|
||||
|
||||
class _GeneratesWithoutFinalFields extends Matcher {
|
||||
final Set<String> expectedWithoutFinals;
|
||||
|
||||
const _GeneratesWithoutFinalFields(this.expectedWithoutFinals);
|
||||
|
||||
@override
|
||||
Description describe(Description description) {
|
||||
return description.add('generates classes $expectedWithoutFinals without '
|
||||
'final fields.');
|
||||
}
|
||||
|
||||
@override
|
||||
bool matches(dynamic desc, Map matchState) {
|
||||
// Parse the file, assure we don't have final fields in data classes.
|
||||
final resourceProvider = MemoryResourceProvider();
|
||||
if (desc is List<int>) {
|
||||
resourceProvider.newFileWithBytes('/foo.dart', desc);
|
||||
} else if (desc is String) {
|
||||
resourceProvider.newFile('/foo.dart', desc);
|
||||
} else {
|
||||
desc['desc'] = 'Neither a List<int> or String - cannot be parsed';
|
||||
return false;
|
||||
}
|
||||
|
||||
final parsed = parseFile(
|
||||
path: '/foo.dart',
|
||||
featureSet: FeatureSet.forTesting(),
|
||||
resourceProvider: resourceProvider,
|
||||
).unit;
|
||||
|
||||
final remaining = expectedWithoutFinals.toSet();
|
||||
|
||||
final definedClasses = parsed.declarations.whereType<ClassDeclaration>();
|
||||
for (final definedClass in definedClasses) {
|
||||
if (expectedWithoutFinals.contains(definedClass.name.name)) {
|
||||
final fields = definedClass.members.whereType<FieldDeclaration>();
|
||||
|
||||
for (final field in fields) {
|
||||
if (field.fields.isFinal) {
|
||||
matchState['desc'] =
|
||||
'Field ${field.fields.variables.first.name.name} in '
|
||||
'${definedClass.name.name} is final.';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
remaining.remove(definedClass.name.name);
|
||||
}
|
||||
}
|
||||
|
||||
// Also ensure that all expected classes were generated.
|
||||
if (remaining.isNotEmpty) {
|
||||
matchState['desc'] = 'Did not generate $remaining classes';
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Description describeMismatch(dynamic item, Description mismatchDescription,
|
||||
Map matchState, bool verbose) {
|
||||
return mismatchDescription.add(matchState['desc'] as String);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue