From 958302c253a358a44122dcee6998515e8d7c0f75 Mon Sep 17 00:00:00 2001 From: westito Date: Sun, 17 Oct 2021 20:09:53 +0200 Subject: [PATCH 1/7] Add custom row class toCompanion generator --- .../docs/Advanced Features/builder_options.md | 1 + drift_dev/lib/src/analyzer/options.dart | 5 +++ drift_dev/lib/src/analyzer/options.g.dart | 4 ++ .../tables/update_companion_writer.dart | 42 +++++++++++++++++++ 4 files changed, 52 insertions(+) diff --git a/docs/pages/docs/Advanced Features/builder_options.md b/docs/pages/docs/Advanced Features/builder_options.md index 19d99012..73f9f26a 100644 --- a/docs/pages/docs/Advanced Features/builder_options.md +++ b/docs/pages/docs/Advanced Features/builder_options.md @@ -68,6 +68,7 @@ At the moment, drift supports these options: instead of lazily when it reads a table. This is used to investigate rare builder crashes. * `data_class_to_companions` (defaults to `true`): Controls whether drift will write the `toCompanion` method in generated data classes. +* `generate_to_companion_extension`: Controls whether drift will generate `toCompanion` extension function for custom row 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`. * `raw_result_set_data`: The generator will expose the underlying `QueryRow` for generated result set classes diff --git a/drift_dev/lib/src/analyzer/options.dart b/drift_dev/lib/src/analyzer/options.dart index b7a6dc02..fee654e3 100644 --- a/drift_dev/lib/src/analyzer/options.dart +++ b/drift_dev/lib/src/analyzer/options.dart @@ -71,6 +71,9 @@ class MoorOptions { @JsonKey(name: 'data_class_to_companions', defaultValue: true) final bool dataClassToCompanions; + @JsonKey(name: 'generate_to_companion_extension', defaultValue: false) + final bool generateToCompanionExtension; + @JsonKey(name: 'mutable_classes', defaultValue: false) final bool generateMutableClasses; @@ -108,6 +111,7 @@ class MoorOptions { this.generateConnectConstructor = false, this.eagerlyLoadDartAst = false, this.dataClassToCompanions = true, + this.generateToCompanionExtension = false, this.generateMutableClasses = false, this.rawResultSetData = false, this.applyConvertersOnVariables = false, @@ -130,6 +134,7 @@ class MoorOptions { required this.generateConnectConstructor, required this.eagerlyLoadDartAst, required this.dataClassToCompanions, + required this.generateToCompanionExtension, required this.generateMutableClasses, required this.rawResultSetData, required this.applyConvertersOnVariables, diff --git a/drift_dev/lib/src/analyzer/options.g.dart b/drift_dev/lib/src/analyzer/options.g.dart index 1f6ba25a..d54befde 100644 --- a/drift_dev/lib/src/analyzer/options.g.dart +++ b/drift_dev/lib/src/analyzer/options.g.dart @@ -24,6 +24,7 @@ MoorOptions _$MoorOptionsFromJson(Map json) => $checkedCreate( 'sqlite', 'eagerly_load_dart_ast', 'data_class_to_companions', + 'generate_to_companion_extension', 'mutable_classes', 'raw_result_set_data', 'apply_converters_on_variables', @@ -46,6 +47,8 @@ MoorOptions _$MoorOptionsFromJson(Map json) => $checkedCreate( 'skip_verification_code', (v) => v as bool? ?? false), useDataClassNameForCompanions: $checkedConvert( 'use_data_class_name_for_companions', (v) => v as bool? ?? false), + generateToCompanionExtension: $checkedConvert( + 'generate_to_companion_extension', (v) => v as bool? ?? false), useColumnNameAsJsonKeyWhenDefinedInMoorFile: $checkedConvert( 'use_column_name_as_json_key_when_defined_in_moor_file', (v) => v as bool? ?? true), @@ -98,6 +101,7 @@ MoorOptions _$MoorOptionsFromJson(Map json) => $checkedCreate( 'generateConnectConstructor': 'generate_connect_constructor', 'eagerlyLoadDartAst': 'eagerly_load_dart_ast', 'dataClassToCompanions': 'data_class_to_companions', + 'generateToCompanionExtension': 'generate_to_companion_extension', 'generateMutableClasses': 'mutable_classes', 'rawResultSetData': 'raw_result_set_data', 'applyConvertersOnVariables': 'apply_converters_on_variables', diff --git a/drift_dev/lib/src/writer/tables/update_companion_writer.dart b/drift_dev/lib/src/writer/tables/update_companion_writer.dart index 41622966..cb31476b 100644 --- a/drift_dev/lib/src/writer/tables/update_companion_writer.dart +++ b/drift_dev/lib/src/writer/tables/update_companion_writer.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart'; import 'package:drift_dev/moor_generator.dart'; import 'package:drift_dev/src/utils/string_escaper.dart'; import 'package:drift_dev/src/writer/utils/override_toString.dart'; @@ -28,6 +29,10 @@ class UpdateCompanionWriter { _writeToString(); _buffer.write('}\n'); + + if (scope.options.generateToCompanionExtension) { + _writeToCompanionExtension(); + } } void _writeFields() { @@ -211,4 +216,41 @@ class UpdateCompanionWriter { _buffer, ); } + + void _writeToCompanionExtension() { + final info = table.existingRowClass; + if (info == null) return; + + final companionName = table.getNameForCompanionClass(scope.options); + + _buffer.write('\n'); + _buffer.write('extension ${table.dartTypeName}ToCompanion'); + _buffer.write(' on ${table.dartTypeName} {'); + _buffer.write('$companionName toCompanion() {'); + + _buffer + ..write('return ') + ..write(table.getNameForCompanionClass(scope.options)) + ..write('('); + + final named = {}; + + info.mapping.forEach((column, parameter) { + if (parameter.isNamed) { + named[column] = parameter.name; + } + }); + + for (final field in named.values) { + final column = table.columns + .firstWhereOrNull((element) => element.name.name == field); + + if (column == null) break; + + final dartName = column.dartGetterName; + _buffer.write('$dartName: Value ($dartName),'); + } + + _buffer.writeln(');\n}}\n'); + } } From b8fb002c41361a907babb8fc57301cf4dc1e4689 Mon Sep 17 00:00:00 2001 From: westito Date: Mon, 18 Oct 2021 15:17:12 +0200 Subject: [PATCH 2/7] Change toCompanion generator builder option to @UseRowClass parameter --- .../docs/Advanced Features/builder_options.md | 1 - drift/lib/src/dsl/table.dart | 14 +++++++++++++- drift_dev/lib/src/analyzer/dart/table_parser.dart | 10 ++++++++-- drift_dev/lib/src/analyzer/options.dart | 5 ----- drift_dev/lib/src/analyzer/options.g.dart | 4 ---- drift_dev/lib/src/model/table.dart | 4 ++++ .../src/writer/tables/update_companion_writer.dart | 2 +- 7 files changed, 26 insertions(+), 14 deletions(-) diff --git a/docs/pages/docs/Advanced Features/builder_options.md b/docs/pages/docs/Advanced Features/builder_options.md index 73f9f26a..19d99012 100644 --- a/docs/pages/docs/Advanced Features/builder_options.md +++ b/docs/pages/docs/Advanced Features/builder_options.md @@ -68,7 +68,6 @@ At the moment, drift supports these options: instead of lazily when it reads a table. This is used to investigate rare builder crashes. * `data_class_to_companions` (defaults to `true`): Controls whether drift will write the `toCompanion` method in generated data classes. -* `generate_to_companion_extension`: Controls whether drift will generate `toCompanion` extension function for custom row 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`. * `raw_result_set_data`: The generator will expose the underlying `QueryRow` for generated result set classes diff --git a/drift/lib/src/dsl/table.dart b/drift/lib/src/dsl/table.dart index 6046abf8..6487a331 100644 --- a/drift/lib/src/dsl/table.dart +++ b/drift/lib/src/dsl/table.dart @@ -155,9 +155,21 @@ class UseRowClass { /// used to map database rows to the desired row class. final String constructor; + /// Generate a companion constructor mapping the referenced [type] to a companion. + /// + /// When this option is set (it defaults to `false`), moor generates a constructor in the + /// matching companion class of this table mapping an instance of [type] to a suitable + /// companion. + /// This can be useful when a custom data class should be used for inserts or updates. + /// For this purpose, make it implement the `Insertable` interface, enable + /// [generateToCompanion] and use the generated companion in your `toColumns` + /// implementation. + final bool generateToCompanion; + /// Customize the class used by drift to hold an instance of an annotated /// table. /// /// For details, see the overall documentation on [UseRowClass]. - const UseRowClass(this.type, {this.constructor = ''}); + const UseRowClass(this.type, + {this.constructor = '', this.generateToCompanion = false}); } diff --git a/drift_dev/lib/src/analyzer/dart/table_parser.dart b/drift_dev/lib/src/analyzer/dart/table_parser.dart index 59444408..a5247e80 100644 --- a/drift_dev/lib/src/analyzer/dart/table_parser.dart +++ b/drift_dev/lib/src/analyzer/dart/table_parser.dart @@ -21,6 +21,7 @@ class TableParser { sqlName: escapeIfNeeded(sqlName), dartTypeName: dataClassInfo.enforcedName, existingRowClass: dataClassInfo.existingClass, + generateToCompanion: dataClassInfo.generateToCompanion, primaryKey: primaryKey, overrideWithoutRowId: await _overrideWithoutRowId(element), declaration: DartTableDeclaration(element, base.step.file), @@ -70,6 +71,7 @@ class TableParser { String name; FoundDartClass? existingClass; String? constructorInExistingClass; + var generateToCompanion = false; if (dataClassName != null) { name = dataClassName.getField('name')!.toStringValue()!; @@ -81,6 +83,8 @@ class TableParser { final type = useRowClass.getField('type')!.toTypeValue(); constructorInExistingClass = useRowClass.getField('constructor')!.toStringValue()!; + generateToCompanion = + useRowClass.getField('generateToCompanion')!.toBoolValue()!; if (type is InterfaceType) { existingClass = FoundDartClass(type.element, type.typeArguments); @@ -97,7 +101,7 @@ class TableParser { ? null : validateExistingClass(columns, existingClass, constructorInExistingClass!, base.step.errors); - return _DataClassInformation(name, verified); + return _DataClassInformation(name, verified, generateToCompanion); } Future _parseTableName(ClassElement element) async { @@ -226,8 +230,10 @@ class TableParser { class _DataClassInformation { final String enforcedName; final ExistingRowClass? existingClass; + bool generateToCompanion; - _DataClassInformation(this.enforcedName, this.existingClass); + _DataClassInformation( + this.enforcedName, this.existingClass, this.generateToCompanion); } extension on Element { diff --git a/drift_dev/lib/src/analyzer/options.dart b/drift_dev/lib/src/analyzer/options.dart index fee654e3..b7a6dc02 100644 --- a/drift_dev/lib/src/analyzer/options.dart +++ b/drift_dev/lib/src/analyzer/options.dart @@ -71,9 +71,6 @@ class MoorOptions { @JsonKey(name: 'data_class_to_companions', defaultValue: true) final bool dataClassToCompanions; - @JsonKey(name: 'generate_to_companion_extension', defaultValue: false) - final bool generateToCompanionExtension; - @JsonKey(name: 'mutable_classes', defaultValue: false) final bool generateMutableClasses; @@ -111,7 +108,6 @@ class MoorOptions { this.generateConnectConstructor = false, this.eagerlyLoadDartAst = false, this.dataClassToCompanions = true, - this.generateToCompanionExtension = false, this.generateMutableClasses = false, this.rawResultSetData = false, this.applyConvertersOnVariables = false, @@ -134,7 +130,6 @@ class MoorOptions { required this.generateConnectConstructor, required this.eagerlyLoadDartAst, required this.dataClassToCompanions, - required this.generateToCompanionExtension, required this.generateMutableClasses, required this.rawResultSetData, required this.applyConvertersOnVariables, diff --git a/drift_dev/lib/src/analyzer/options.g.dart b/drift_dev/lib/src/analyzer/options.g.dart index d54befde..1f6ba25a 100644 --- a/drift_dev/lib/src/analyzer/options.g.dart +++ b/drift_dev/lib/src/analyzer/options.g.dart @@ -24,7 +24,6 @@ MoorOptions _$MoorOptionsFromJson(Map json) => $checkedCreate( 'sqlite', 'eagerly_load_dart_ast', 'data_class_to_companions', - 'generate_to_companion_extension', 'mutable_classes', 'raw_result_set_data', 'apply_converters_on_variables', @@ -47,8 +46,6 @@ MoorOptions _$MoorOptionsFromJson(Map json) => $checkedCreate( 'skip_verification_code', (v) => v as bool? ?? false), useDataClassNameForCompanions: $checkedConvert( 'use_data_class_name_for_companions', (v) => v as bool? ?? false), - generateToCompanionExtension: $checkedConvert( - 'generate_to_companion_extension', (v) => v as bool? ?? false), useColumnNameAsJsonKeyWhenDefinedInMoorFile: $checkedConvert( 'use_column_name_as_json_key_when_defined_in_moor_file', (v) => v as bool? ?? true), @@ -101,7 +98,6 @@ MoorOptions _$MoorOptionsFromJson(Map json) => $checkedCreate( 'generateConnectConstructor': 'generate_connect_constructor', 'eagerlyLoadDartAst': 'eagerly_load_dart_ast', 'dataClassToCompanions': 'data_class_to_companions', - 'generateToCompanionExtension': 'generate_to_companion_extension', 'generateMutableClasses': 'mutable_classes', 'rawResultSetData': 'raw_result_set_data', 'applyConvertersOnVariables': 'apply_converters_on_variables', diff --git a/drift_dev/lib/src/model/table.dart b/drift_dev/lib/src/model/table.dart index f56df93b..d35c7bf2 100644 --- a/drift_dev/lib/src/model/table.dart +++ b/drift_dev/lib/src/model/table.dart @@ -51,6 +51,9 @@ class MoorTable extends MoorEntityWithResultSet { @override final String dartTypeName; + /// Generate toCompanion for data class + final bool generateToCompanion; + /// The getter name used for this table in a generated database or dao class. @override String get dbGetterName => dbFieldName(_baseName); @@ -138,6 +141,7 @@ class MoorTable extends MoorEntityWithResultSet { this.columns = const [], required this.sqlName, required this.dartTypeName, + this.generateToCompanion = false, this.primaryKey, String? overriddenName, this.overrideWithoutRowId, diff --git a/drift_dev/lib/src/writer/tables/update_companion_writer.dart b/drift_dev/lib/src/writer/tables/update_companion_writer.dart index cb31476b..ae9016d8 100644 --- a/drift_dev/lib/src/writer/tables/update_companion_writer.dart +++ b/drift_dev/lib/src/writer/tables/update_companion_writer.dart @@ -30,7 +30,7 @@ class UpdateCompanionWriter { _buffer.write('}\n'); - if (scope.options.generateToCompanionExtension) { + if (table.generateToCompanion) { _writeToCompanionExtension(); } } From 59ec14d0601e7261e915fdfa3fe11522c5f4f122 Mon Sep 17 00:00:00 2001 From: westito Date: Mon, 18 Oct 2021 15:43:15 +0200 Subject: [PATCH 3/7] Fix line length in comments --- drift/lib/src/dsl/table.dart | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drift/lib/src/dsl/table.dart b/drift/lib/src/dsl/table.dart index 6487a331..a4b4d000 100644 --- a/drift/lib/src/dsl/table.dart +++ b/drift/lib/src/dsl/table.dart @@ -155,12 +155,14 @@ class UseRowClass { /// used to map database rows to the desired row class. final String constructor; - /// Generate a companion constructor mapping the referenced [type] to a companion. - /// - /// When this option is set (it defaults to `false`), moor generates a constructor in the - /// matching companion class of this table mapping an instance of [type] to a suitable + /// Generate a companion constructor mapping the referenced [type] to a /// companion. - /// This can be useful when a custom data class should be used for inserts or updates. + /// + /// When this option is set (it defaults to `false`), moor generates a + /// constructor in the matching companion class of this table mapping an + /// instance of [type] to a suitable companion. + /// This can be useful when a custom data class should be used for inserts or + /// updates. /// For this purpose, make it implement the `Insertable` interface, enable /// [generateToCompanion] and use the generated companion in your `toColumns` /// implementation. From 610acfad61f72fb2e27d20ad954dc2bbb5928a6f Mon Sep 17 00:00:00 2001 From: westito Date: Tue, 19 Oct 2021 09:14:31 +0200 Subject: [PATCH 4/7] Apply suggestions from code review Co-authored-by: Simon Binder --- drift/lib/src/dsl/table.dart | 2 +- drift_dev/lib/src/analyzer/dart/table_parser.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drift/lib/src/dsl/table.dart b/drift/lib/src/dsl/table.dart index a4b4d000..333bb9c4 100644 --- a/drift/lib/src/dsl/table.dart +++ b/drift/lib/src/dsl/table.dart @@ -158,7 +158,7 @@ class UseRowClass { /// Generate a companion constructor mapping the referenced [type] to a /// companion. /// - /// When this option is set (it defaults to `false`), moor generates a + /// When this option is set (it defaults to `false`), drift generates a /// constructor in the matching companion class of this table mapping an /// instance of [type] to a suitable companion. /// This can be useful when a custom data class should be used for inserts or diff --git a/drift_dev/lib/src/analyzer/dart/table_parser.dart b/drift_dev/lib/src/analyzer/dart/table_parser.dart index a5247e80..67f04d1f 100644 --- a/drift_dev/lib/src/analyzer/dart/table_parser.dart +++ b/drift_dev/lib/src/analyzer/dart/table_parser.dart @@ -230,7 +230,7 @@ class TableParser { class _DataClassInformation { final String enforcedName; final ExistingRowClass? existingClass; - bool generateToCompanion; + final bool generateToCompanion; _DataClassInformation( this.enforcedName, this.existingClass, this.generateToCompanion); From 9b4f25718dd115e0595041e97adb0ba5d7b2ea5d Mon Sep 17 00:00:00 2001 From: westito Date: Tue, 19 Oct 2021 09:30:57 +0200 Subject: [PATCH 5/7] Moved generateToCompanion option to ExistingRowClass --- .../lib/src/analyzer/custom_row_class.dart | 11 ++++++++--- .../lib/src/analyzer/dart/table_parser.dart | 17 +++++++++-------- .../src/analyzer/moor/create_table_reader.dart | 2 +- .../lib/src/analyzer/view/view_analyzer.dart | 2 +- drift_dev/lib/src/model/base_entity.dart | 4 ++++ drift_dev/lib/src/model/table.dart | 4 ---- .../writer/tables/update_companion_writer.dart | 2 +- 7 files changed, 24 insertions(+), 18 deletions(-) diff --git a/drift_dev/lib/src/analyzer/custom_row_class.dart b/drift_dev/lib/src/analyzer/custom_row_class.dart index 9d30634e..48faf6b7 100644 --- a/drift_dev/lib/src/analyzer/custom_row_class.dart +++ b/drift_dev/lib/src/analyzer/custom_row_class.dart @@ -15,8 +15,12 @@ class FoundDartClass { FoundDartClass(this.classElement, this.instantiation); } -ExistingRowClass? validateExistingClass(Iterable columns, - FoundDartClass dartClass, String constructor, ErrorSink errors) { +ExistingRowClass? validateExistingClass( + Iterable columns, + FoundDartClass dartClass, + String constructor, + bool generateToCompanion, + ErrorSink errors) { final desiredClass = dartClass.classElement; ConstructorElement? ctor; @@ -63,7 +67,8 @@ ExistingRowClass? validateExistingClass(Iterable columns, } } - return ExistingRowClass(desiredClass, ctor, columnsToParameter, + return ExistingRowClass( + desiredClass, ctor, columnsToParameter, generateToCompanion, typeInstantiation: dartClass.instantiation ?? const []); } diff --git a/drift_dev/lib/src/analyzer/dart/table_parser.dart b/drift_dev/lib/src/analyzer/dart/table_parser.dart index 67f04d1f..cc3cc701 100644 --- a/drift_dev/lib/src/analyzer/dart/table_parser.dart +++ b/drift_dev/lib/src/analyzer/dart/table_parser.dart @@ -21,7 +21,6 @@ class TableParser { sqlName: escapeIfNeeded(sqlName), dartTypeName: dataClassInfo.enforcedName, existingRowClass: dataClassInfo.existingClass, - generateToCompanion: dataClassInfo.generateToCompanion, primaryKey: primaryKey, overrideWithoutRowId: await _overrideWithoutRowId(element), declaration: DartTableDeclaration(element, base.step.file), @@ -71,7 +70,7 @@ class TableParser { String name; FoundDartClass? existingClass; String? constructorInExistingClass; - var generateToCompanion = false; + bool? generateToCompanion; if (dataClassName != null) { name = dataClassName.getField('name')!.toStringValue()!; @@ -99,9 +98,13 @@ class TableParser { final verified = existingClass == null ? null - : validateExistingClass(columns, existingClass, - constructorInExistingClass!, base.step.errors); - return _DataClassInformation(name, verified, generateToCompanion); + : validateExistingClass( + columns, + existingClass, + constructorInExistingClass!, + generateToCompanion!, + base.step.errors); + return _DataClassInformation(name, verified); } Future _parseTableName(ClassElement element) async { @@ -230,10 +233,8 @@ class TableParser { class _DataClassInformation { final String enforcedName; final ExistingRowClass? existingClass; - final bool generateToCompanion; - _DataClassInformation( - this.enforcedName, this.existingClass, this.generateToCompanion); + _DataClassInformation(this.enforcedName, this.existingClass); } extension on Element { diff --git a/drift_dev/lib/src/analyzer/moor/create_table_reader.dart b/drift_dev/lib/src/analyzer/moor/create_table_reader.dart index 9bf0d98b..b01b3edb 100644 --- a/drift_dev/lib/src/analyzer/moor/create_table_reader.dart +++ b/drift_dev/lib/src/analyzer/moor/create_table_reader.dart @@ -181,7 +181,7 @@ class CreateTableReader { )); } else { existingRowClass = validateExistingClass( - foundColumns.values, clazz, '', step.errors); + foundColumns.values, clazz, '', false, step.errors); dataClassName = existingRowClass?.targetClass.name; } } else if (overriddenNames.contains('/')) { diff --git a/drift_dev/lib/src/analyzer/view/view_analyzer.dart b/drift_dev/lib/src/analyzer/view/view_analyzer.dart index 7a7b7b9d..0535f4a9 100644 --- a/drift_dev/lib/src/analyzer/view/view_analyzer.dart +++ b/drift_dev/lib/src/analyzer/view/view_analyzer.dart @@ -64,7 +64,7 @@ class ViewAnalyzer extends BaseAnalyzer { )); } else { final rowClass = view.existingRowClass = - validateExistingClass(columns, clazz, '', step.errors); + validateExistingClass(columns, clazz, '', false, step.errors); final newName = rowClass?.targetClass.name; if (newName != null) { view.dartTypeName = rowClass!.targetClass.name; diff --git a/drift_dev/lib/src/model/base_entity.dart b/drift_dev/lib/src/model/base_entity.dart index 71ef2ee0..65de0c04 100644 --- a/drift_dev/lib/src/model/base_entity.dart +++ b/drift_dev/lib/src/model/base_entity.dart @@ -69,7 +69,11 @@ class ExistingRowClass { final ConstructorElement constructor; final Map mapping; + /// Generate toCompanion for data class + final bool generateToCompanion; + ExistingRowClass(this.targetClass, this.constructor, this.mapping, + this.generateToCompanion, {this.typeInstantiation = const []}); String dartType([GenerationOptions options = const GenerationOptions()]) { diff --git a/drift_dev/lib/src/model/table.dart b/drift_dev/lib/src/model/table.dart index d35c7bf2..f56df93b 100644 --- a/drift_dev/lib/src/model/table.dart +++ b/drift_dev/lib/src/model/table.dart @@ -51,9 +51,6 @@ class MoorTable extends MoorEntityWithResultSet { @override final String dartTypeName; - /// Generate toCompanion for data class - final bool generateToCompanion; - /// The getter name used for this table in a generated database or dao class. @override String get dbGetterName => dbFieldName(_baseName); @@ -141,7 +138,6 @@ class MoorTable extends MoorEntityWithResultSet { this.columns = const [], required this.sqlName, required this.dartTypeName, - this.generateToCompanion = false, this.primaryKey, String? overriddenName, this.overrideWithoutRowId, diff --git a/drift_dev/lib/src/writer/tables/update_companion_writer.dart b/drift_dev/lib/src/writer/tables/update_companion_writer.dart index ae9016d8..8f7febea 100644 --- a/drift_dev/lib/src/writer/tables/update_companion_writer.dart +++ b/drift_dev/lib/src/writer/tables/update_companion_writer.dart @@ -30,7 +30,7 @@ class UpdateCompanionWriter { _buffer.write('}\n'); - if (table.generateToCompanion) { + if (table.existingRowClass?.generateToCompanion ?? false) { _writeToCompanionExtension(); } } From a5e4a7851e5e55f9fd5fe02a0adaea65edc8401f Mon Sep 17 00:00:00 2001 From: westito Date: Tue, 19 Oct 2021 15:58:39 +0200 Subject: [PATCH 6/7] Remove named function requirements, fix name matching, update test --- drift/test/data/tables/todos.dart | 10 ++++------ drift/test/data/tables/todos.g.dart | 10 ++++++++++ .../lib/src/analyzer/custom_row_class.dart | 17 +++++++++++++++-- .../writer/tables/update_companion_writer.dart | 6 ++---- 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/drift/test/data/tables/todos.dart b/drift/test/data/tables/todos.dart index 2ddbe441..d0174334 100644 --- a/drift/test/data/tables/todos.dart +++ b/drift/test/data/tables/todos.dart @@ -58,7 +58,7 @@ class SharedTodos extends Table { const _uuid = Uuid(); -@UseRowClass(CustomRowClass, constructor: 'map') +@UseRowClass(CustomRowClass, constructor: 'map', generateToCompanion: true) class TableWithoutPK extends Table { IntColumn get notReallyAnId => integer()(); RealColumn get someFloat => real()(); @@ -74,6 +74,8 @@ class CustomRowClass implements Insertable { final String? notFromDb; + double get someFloat => anotherName; + CustomRowClass._( this.notReallyAnId, this.anotherName, this.custom, this.notFromDb); @@ -84,11 +86,7 @@ class CustomRowClass implements Insertable { @override Map toColumns(bool nullToAbsent) { - return TableWithoutPKCompanion( - notReallyAnId: Value(notReallyAnId), - someFloat: Value(anotherName), - custom: Value(custom), - ).toColumns(nullToAbsent); + return toCompanion().toColumns(nullToAbsent); } } diff --git a/drift/test/data/tables/todos.g.dart b/drift/test/data/tables/todos.g.dart index ba717b1b..ead06165 100644 --- a/drift/test/data/tables/todos.g.dart +++ b/drift/test/data/tables/todos.g.dart @@ -1054,6 +1054,16 @@ class TableWithoutPKCompanion extends UpdateCompanion { } } +extension CustomRowClassToCompanion on CustomRowClass { + TableWithoutPKCompanion toCompanion() { + return TableWithoutPKCompanion( + notReallyAnId: Value(notReallyAnId), + someFloat: Value(someFloat), + custom: Value(custom), + ); + } +} + class $TableWithoutPKTable extends TableWithoutPK with TableInfo<$TableWithoutPKTable, CustomRowClass> { final GeneratedDatabase _db; diff --git a/drift_dev/lib/src/analyzer/custom_row_class.dart b/drift_dev/lib/src/analyzer/custom_row_class.dart index 48faf6b7..28d6459e 100644 --- a/drift_dev/lib/src/analyzer/custom_row_class.dart +++ b/drift_dev/lib/src/analyzer/custom_row_class.dart @@ -56,8 +56,21 @@ ExistingRowClass? validateExistingClass( for (final parameter in ctor.parameters) { final column = unmatchedColumnsByName.remove(parameter.name); if (column != null) { - columnsToParameter[column] = parameter; - _checkType(parameter, column, errors); + final matchField = !generateToCompanion || + dartClass.classElement.fields + .any((field) => field.name == parameter.name); + if (matchField) { + columnsToParameter[column] = parameter; + _checkType(parameter, column, errors); + } else { + errors.report(ErrorInDartCode( + affectedElement: parameter, + severity: Severity.error, + message: 'Constructor parameter ${parameter.name} has no matching ' + 'field. When "generateToCompanion" enabled, all constructor ' + 'parameter must have a matching field. Alternatively, you can ' + 'declare a getter field.')); + } } else if (!parameter.isOptional) { errors.report(ErrorInDartCode( affectedElement: parameter, diff --git a/drift_dev/lib/src/writer/tables/update_companion_writer.dart b/drift_dev/lib/src/writer/tables/update_companion_writer.dart index 8f7febea..a25cace5 100644 --- a/drift_dev/lib/src/writer/tables/update_companion_writer.dart +++ b/drift_dev/lib/src/writer/tables/update_companion_writer.dart @@ -236,14 +236,12 @@ class UpdateCompanionWriter { final named = {}; info.mapping.forEach((column, parameter) { - if (parameter.isNamed) { - named[column] = parameter.name; - } + named[column] = parameter.name; }); for (final field in named.values) { final column = table.columns - .firstWhereOrNull((element) => element.name.name == field); + .firstWhereOrNull((element) => element.dartGetterName == field); if (column == null) break; From 40f4c1bd1f5c5088a61b6db8eaaf102d365738f0 Mon Sep 17 00:00:00 2001 From: westito Date: Wed, 20 Oct 2021 16:58:27 +0200 Subject: [PATCH 7/7] Rewrite toCompanion to insertable --- drift/lib/src/dsl/table.dart | 14 ++---- drift/test/data/tables/todos.dart | 9 +--- drift/test/data/tables/todos.g.dart | 23 ++++++--- drift/test/insert_test.dart | 3 +- .../lib/src/analyzer/custom_row_class.dart | 15 +++--- .../lib/src/analyzer/dart/table_parser.dart | 14 ++---- drift_dev/lib/src/model/base_entity.dart | 6 +-- .../tables/update_companion_writer.dart | 48 +++++++++---------- 8 files changed, 65 insertions(+), 67 deletions(-) diff --git a/drift/lib/src/dsl/table.dart b/drift/lib/src/dsl/table.dart index 333bb9c4..95b3caad 100644 --- a/drift/lib/src/dsl/table.dart +++ b/drift/lib/src/dsl/table.dart @@ -155,23 +155,17 @@ class UseRowClass { /// used to map database rows to the desired row class. final String constructor; - /// Generate a companion constructor mapping the referenced [type] to a - /// companion. + /// Generate a `toInsertable()` extension function for [type] mapping all + /// fields to an insertable object. /// - /// When this option is set (it defaults to `false`), drift generates a - /// constructor in the matching companion class of this table mapping an - /// instance of [type] to a suitable companion. /// This can be useful when a custom data class should be used for inserts or /// updates. - /// For this purpose, make it implement the `Insertable` interface, enable - /// [generateToCompanion] and use the generated companion in your `toColumns` - /// implementation. - final bool generateToCompanion; + final bool generateInsertable; /// Customize the class used by drift to hold an instance of an annotated /// table. /// /// For details, see the overall documentation on [UseRowClass]. const UseRowClass(this.type, - {this.constructor = '', this.generateToCompanion = false}); + {this.constructor = '', this.generateInsertable = false}); } diff --git a/drift/test/data/tables/todos.dart b/drift/test/data/tables/todos.dart index d0174334..b8ef38e8 100644 --- a/drift/test/data/tables/todos.dart +++ b/drift/test/data/tables/todos.dart @@ -58,7 +58,7 @@ class SharedTodos extends Table { const _uuid = Uuid(); -@UseRowClass(CustomRowClass, constructor: 'map', generateToCompanion: true) +@UseRowClass(CustomRowClass, constructor: 'map', generateInsertable: true) class TableWithoutPK extends Table { IntColumn get notReallyAnId => integer()(); RealColumn get someFloat => real()(); @@ -67,7 +67,7 @@ class TableWithoutPK extends Table { text().map(const CustomConverter()).clientDefault(_uuid.v4)(); } -class CustomRowClass implements Insertable { +class CustomRowClass { final int notReallyAnId; final double anotherName; final MyCustomObject custom; @@ -83,11 +83,6 @@ class CustomRowClass implements Insertable { {required MyCustomObject custom, String? notFromDb}) { return CustomRowClass._(notReallyAnId, someFloat, custom, notFromDb); } - - @override - Map toColumns(bool nullToAbsent) { - return toCompanion().toColumns(nullToAbsent); - } } class PureDefaults extends Table { diff --git a/drift/test/data/tables/todos.g.dart b/drift/test/data/tables/todos.g.dart index ead06165..d1469edb 100644 --- a/drift/test/data/tables/todos.g.dart +++ b/drift/test/data/tables/todos.g.dart @@ -1054,13 +1054,24 @@ class TableWithoutPKCompanion extends UpdateCompanion { } } -extension CustomRowClassToCompanion on CustomRowClass { - TableWithoutPKCompanion toCompanion() { +class _$CustomRowClassInsertable implements Insertable { + CustomRowClass _object; + + _$CustomRowClassInsertable(this._object); + + @override + Map toColumns(bool nullToAbsent) { return TableWithoutPKCompanion( - notReallyAnId: Value(notReallyAnId), - someFloat: Value(someFloat), - custom: Value(custom), - ); + notReallyAnId: Value(_object.notReallyAnId), + someFloat: Value(_object.someFloat), + custom: Value(_object.custom), + ).toColumns(false); + } +} + +extension CustomRowClassToInsertable on CustomRowClass { + _$CustomRowClassInsertable toInsertable() { + return _$CustomRowClassInsertable(this); } } diff --git a/drift/test/insert_test.dart b/drift/test/insert_test.dart index 31b76c3b..f3615e87 100644 --- a/drift/test/insert_test.dart +++ b/drift/test/insert_test.dart @@ -31,7 +31,8 @@ void main() { test('can insert floating point values', () async { // regression test for https://github.com/simolus3/moor/issues/30 await db.into(db.tableWithoutPK).insert( - CustomRowClass.map(42, 3.1415, custom: MyCustomObject('custom'))); + CustomRowClass.map(42, 3.1415, custom: MyCustomObject('custom')) + .toInsertable()); verify(executor.runInsert( 'INSERT INTO table_without_p_k ' diff --git a/drift_dev/lib/src/analyzer/custom_row_class.dart b/drift_dev/lib/src/analyzer/custom_row_class.dart index 28d6459e..14ae8850 100644 --- a/drift_dev/lib/src/analyzer/custom_row_class.dart +++ b/drift_dev/lib/src/analyzer/custom_row_class.dart @@ -19,7 +19,7 @@ ExistingRowClass? validateExistingClass( Iterable columns, FoundDartClass dartClass, String constructor, - bool generateToCompanion, + bool generateInsertable, ErrorSink errors) { final desiredClass = dartClass.classElement; ConstructorElement? ctor; @@ -56,20 +56,21 @@ ExistingRowClass? validateExistingClass( for (final parameter in ctor.parameters) { final column = unmatchedColumnsByName.remove(parameter.name); if (column != null) { - final matchField = !generateToCompanion || + final matchField = !generateInsertable || dartClass.classElement.fields .any((field) => field.name == parameter.name); if (matchField) { columnsToParameter[column] = parameter; _checkType(parameter, column, errors); } else { - errors.report(ErrorInDartCode( + final error = ErrorInDartCode( affectedElement: parameter, - severity: Severity.error, + severity: Severity.criticalError, message: 'Constructor parameter ${parameter.name} has no matching ' - 'field. When "generateToCompanion" enabled, all constructor ' + 'field. When "generateInsertable" enabled, all constructor ' 'parameter must have a matching field. Alternatively, you can ' - 'declare a getter field.')); + 'declare a getter field.'); + throw Exception(error); } } else if (!parameter.isOptional) { errors.report(ErrorInDartCode( @@ -81,7 +82,7 @@ ExistingRowClass? validateExistingClass( } return ExistingRowClass( - desiredClass, ctor, columnsToParameter, generateToCompanion, + desiredClass, ctor, columnsToParameter, generateInsertable, typeInstantiation: dartClass.instantiation ?? const []); } diff --git a/drift_dev/lib/src/analyzer/dart/table_parser.dart b/drift_dev/lib/src/analyzer/dart/table_parser.dart index cc3cc701..bc60b7de 100644 --- a/drift_dev/lib/src/analyzer/dart/table_parser.dart +++ b/drift_dev/lib/src/analyzer/dart/table_parser.dart @@ -70,7 +70,7 @@ class TableParser { String name; FoundDartClass? existingClass; String? constructorInExistingClass; - bool? generateToCompanion; + bool? generateInsertable; if (dataClassName != null) { name = dataClassName.getField('name')!.toStringValue()!; @@ -82,8 +82,8 @@ class TableParser { final type = useRowClass.getField('type')!.toTypeValue(); constructorInExistingClass = useRowClass.getField('constructor')!.toStringValue()!; - generateToCompanion = - useRowClass.getField('generateToCompanion')!.toBoolValue()!; + generateInsertable = + useRowClass.getField('generateInsertable')!.toBoolValue()!; if (type is InterfaceType) { existingClass = FoundDartClass(type.element, type.typeArguments); @@ -98,12 +98,8 @@ class TableParser { final verified = existingClass == null ? null - : validateExistingClass( - columns, - existingClass, - constructorInExistingClass!, - generateToCompanion!, - base.step.errors); + : validateExistingClass(columns, existingClass, + constructorInExistingClass!, generateInsertable!, base.step.errors); return _DataClassInformation(name, verified); } diff --git a/drift_dev/lib/src/model/base_entity.dart b/drift_dev/lib/src/model/base_entity.dart index 65de0c04..0529cc52 100644 --- a/drift_dev/lib/src/model/base_entity.dart +++ b/drift_dev/lib/src/model/base_entity.dart @@ -70,10 +70,10 @@ class ExistingRowClass { final Map mapping; /// Generate toCompanion for data class - final bool generateToCompanion; + final bool generateInsertable; - ExistingRowClass(this.targetClass, this.constructor, this.mapping, - this.generateToCompanion, + ExistingRowClass( + this.targetClass, this.constructor, this.mapping, this.generateInsertable, {this.typeInstantiation = const []}); String dartType([GenerationOptions options = const GenerationOptions()]) { diff --git a/drift_dev/lib/src/writer/tables/update_companion_writer.dart b/drift_dev/lib/src/writer/tables/update_companion_writer.dart index a25cace5..63940f3d 100644 --- a/drift_dev/lib/src/writer/tables/update_companion_writer.dart +++ b/drift_dev/lib/src/writer/tables/update_companion_writer.dart @@ -30,7 +30,7 @@ class UpdateCompanionWriter { _buffer.write('}\n'); - if (table.existingRowClass?.generateToCompanion ?? false) { + if (table.existingRowClass?.generateInsertable ?? false) { _writeToCompanionExtension(); } } @@ -222,33 +222,33 @@ class UpdateCompanionWriter { if (info == null) return; final companionName = table.getNameForCompanionClass(scope.options); + final className = table.dartTypeName; + final insertableClass = '_\$${className}Insertable'; - _buffer.write('\n'); - _buffer.write('extension ${table.dartTypeName}ToCompanion'); - _buffer.write(' on ${table.dartTypeName} {'); - _buffer.write('$companionName toCompanion() {'); + _buffer.write('class $insertableClass implements ' + 'Insertable<$className> {\n' + '$className _object;\n\n' + '$insertableClass(this._object);\n\n' + '@override\n' + 'Map toColumns(bool nullToAbsent) {\n' + 'return $companionName(\n'); - _buffer - ..write('return ') - ..write(table.getNameForCompanionClass(scope.options)) - ..write('('); + for (final field in info.mapping.values) { + final column = + table.columns.firstWhereOrNull((e) => e.dartGetterName == field.name); - final named = {}; - - info.mapping.forEach((column, parameter) { - named[column] = parameter.name; - }); - - for (final field in named.values) { - final column = table.columns - .firstWhereOrNull((element) => element.dartGetterName == field); - - if (column == null) break; - - final dartName = column.dartGetterName; - _buffer.write('$dartName: Value ($dartName),'); + if (column != null) { + final dartName = column.dartGetterName; + _buffer.write('$dartName: Value (_object.$dartName),\n'); + } } - _buffer.writeln(');\n}}\n'); + _buffer + ..write(').toColumns(false);\n}\n}\n\n') + ..write('extension ${table.dartTypeName}ToInsertable ' + 'on ${table.dartTypeName} {') + ..write('$insertableClass toInsertable() {\n') + ..write('return _\$${className}Insertable(this);\n') + ..write('}\n}\n'); } }