mirror of https://github.com/AMT-Cheif/drift.git
Generate update companions for data classes
This commit is contained in:
parent
214b5fd978
commit
ec2592203c
|
@ -59,6 +59,28 @@ class Category extends DataClass {
|
|||
(other is Category && other.id == id && other.description == description);
|
||||
}
|
||||
|
||||
class CategoriesCompanion implements UpdateCompanion<Category> {
|
||||
final Value<int> id;
|
||||
final Value<String> description;
|
||||
const CategoriesCompanion({
|
||||
this.id = Value.absent(),
|
||||
this.description = Value.absent(),
|
||||
});
|
||||
@override
|
||||
bool isValuePresent(int index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return id.present;
|
||||
case 1:
|
||||
return description.present;
|
||||
default:
|
||||
throw ArgumentError(
|
||||
'Hit an invalid state while serializing data. Did you run the build step?');
|
||||
}
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
class $CategoriesTable extends Categories
|
||||
with TableInfo<$CategoriesTable, Category> {
|
||||
final GeneratedDatabase _db;
|
||||
|
@ -203,6 +225,36 @@ class Recipe extends DataClass {
|
|||
other.category == category);
|
||||
}
|
||||
|
||||
class RecipesCompanion implements UpdateCompanion<Recipe> {
|
||||
final Value<int> id;
|
||||
final Value<String> title;
|
||||
final Value<String> instructions;
|
||||
final Value<int> category;
|
||||
const RecipesCompanion({
|
||||
this.id = Value.absent(),
|
||||
this.title = Value.absent(),
|
||||
this.instructions = Value.absent(),
|
||||
this.category = Value.absent(),
|
||||
});
|
||||
@override
|
||||
bool isValuePresent(int index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return id.present;
|
||||
case 1:
|
||||
return title.present;
|
||||
case 2:
|
||||
return instructions.present;
|
||||
case 3:
|
||||
return category.present;
|
||||
default:
|
||||
throw ArgumentError(
|
||||
'Hit an invalid state while serializing data. Did you run the build step?');
|
||||
}
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
class $RecipesTable extends Recipes with TableInfo<$RecipesTable, Recipe> {
|
||||
final GeneratedDatabase _db;
|
||||
final String _alias;
|
||||
|
@ -366,6 +418,32 @@ class Ingredient extends DataClass {
|
|||
other.caloriesPer100g == caloriesPer100g);
|
||||
}
|
||||
|
||||
class IngredientsCompanion implements UpdateCompanion<Ingredient> {
|
||||
final Value<int> id;
|
||||
final Value<String> name;
|
||||
final Value<int> caloriesPer100g;
|
||||
const IngredientsCompanion({
|
||||
this.id = Value.absent(),
|
||||
this.name = Value.absent(),
|
||||
this.caloriesPer100g = Value.absent(),
|
||||
});
|
||||
@override
|
||||
bool isValuePresent(int index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return id.present;
|
||||
case 1:
|
||||
return name.present;
|
||||
case 2:
|
||||
return caloriesPer100g.present;
|
||||
default:
|
||||
throw ArgumentError(
|
||||
'Hit an invalid state while serializing data. Did you run the build step?');
|
||||
}
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
class $IngredientsTable extends Ingredients
|
||||
with TableInfo<$IngredientsTable, Ingredient> {
|
||||
final GeneratedDatabase _db;
|
||||
|
@ -520,6 +598,33 @@ class IngredientInRecipe extends DataClass {
|
|||
other.amountInGrams == amountInGrams);
|
||||
}
|
||||
|
||||
class IngredientInRecipesCompanion
|
||||
implements UpdateCompanion<IngredientInRecipe> {
|
||||
final Value<int> recipe;
|
||||
final Value<int> ingredient;
|
||||
final Value<int> amountInGrams;
|
||||
const IngredientInRecipesCompanion({
|
||||
this.recipe = Value.absent(),
|
||||
this.ingredient = Value.absent(),
|
||||
this.amountInGrams = Value.absent(),
|
||||
});
|
||||
@override
|
||||
bool isValuePresent(int index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return recipe.present;
|
||||
case 1:
|
||||
return ingredient.present;
|
||||
case 2:
|
||||
return amountInGrams.present;
|
||||
default:
|
||||
throw ArgumentError(
|
||||
'Hit an invalid state while serializing data. Did you run the build step?');
|
||||
}
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
class $IngredientInRecipesTable extends IngredientInRecipes
|
||||
with TableInfo<$IngredientInRecipesTable, IngredientInRecipe> {
|
||||
final GeneratedDatabase _db;
|
||||
|
|
|
@ -18,6 +18,7 @@ export 'package:moor/src/runtime/expressions/user_api.dart';
|
|||
export 'package:moor/src/runtime/executor/transactions.dart';
|
||||
export 'package:moor/src/runtime/statements/query.dart';
|
||||
export 'package:moor/src/runtime/statements/select.dart';
|
||||
export 'package:moor/src/runtime/statements/update.dart';
|
||||
export 'package:moor/src/runtime/statements/insert.dart';
|
||||
export 'package:moor/src/runtime/statements/delete.dart';
|
||||
export 'package:moor/src/runtime/structure/columns.dart';
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:moor/moor.dart';
|
||||
|
||||
/// A common supertype for all data classes generated by moor. Data classes are
|
||||
/// immutable structures that represent a single row.
|
||||
abstract class DataClass {
|
||||
/// immutable structures that represent a single row in a database table.
|
||||
abstract class DataClass implements UpdateCompanion {
|
||||
const DataClass();
|
||||
|
||||
/// Converts this object into a representation that can be encoded with
|
||||
|
@ -20,6 +21,9 @@ abstract class DataClass {
|
|||
return json.encode(toJson(serializer: serializer));
|
||||
}
|
||||
|
||||
@override
|
||||
bool isValuePresent(int index) => true;
|
||||
|
||||
/// Used internally be generated code
|
||||
@protected
|
||||
static dynamic parseJson(String jsonString) {
|
||||
|
@ -27,6 +31,31 @@ abstract class DataClass {
|
|||
}
|
||||
}
|
||||
|
||||
/// An update companion for a [DataClass] which is used to write data into a
|
||||
/// database using [InsertStatement.insert] or [UpdateStatement.write].
|
||||
///
|
||||
/// See also:
|
||||
/// - https://github.com/simolus3/moor/issues/25
|
||||
abstract class UpdateCompanion<D extends DataClass> {
|
||||
/// Used internally by moor.
|
||||
///
|
||||
/// Returns true if the column at the position [index] has been explicitly
|
||||
/// set to a value.
|
||||
bool isValuePresent(int index);
|
||||
}
|
||||
|
||||
/// A wrapper around arbitrary data [T] to indicate presence or absence
|
||||
/// explicitly.
|
||||
class Value<T> {
|
||||
final bool present;
|
||||
final T value;
|
||||
|
||||
const Value.use(this.value) : present = true;
|
||||
const Value.absent()
|
||||
: value = null,
|
||||
present = false;
|
||||
}
|
||||
|
||||
/// Serializer responsible for mapping atomic types from and to json.
|
||||
abstract class ValueSerializer {
|
||||
const ValueSerializer();
|
||||
|
|
|
@ -88,7 +88,7 @@ mixin QueryEngine on DatabaseConnectionUser {
|
|||
/// to write data into the [table] by using [InsertStatement.insert].
|
||||
@protected
|
||||
@visibleForTesting
|
||||
InsertStatement<T> into<T>(TableInfo<Table, T> table) =>
|
||||
InsertStatement<T> into<T extends DataClass>(TableInfo<Table, T> table) =>
|
||||
InsertStatement<T>(this, table);
|
||||
|
||||
/// Starts an [UpdateStatement] for the given table. You can use that
|
||||
|
|
|
@ -6,11 +6,11 @@ import 'package:moor/src/runtime/components/component.dart';
|
|||
|
||||
import 'update.dart';
|
||||
|
||||
class InsertStatement<DataClass> {
|
||||
class InsertStatement<D extends DataClass> {
|
||||
@protected
|
||||
final QueryEngine database;
|
||||
@protected
|
||||
final TableInfo<Table, DataClass> table;
|
||||
final TableInfo<Table, D> table;
|
||||
|
||||
InsertStatement(this.database, this.table);
|
||||
|
||||
|
@ -26,7 +26,7 @@ class InsertStatement<DataClass> {
|
|||
///
|
||||
/// If the table contains an auto-increment column, the generated value will
|
||||
/// be returned.
|
||||
Future<int> insert(DataClass entity, {bool orReplace = false}) async {
|
||||
Future<int> insert(D entity, {bool orReplace = false}) async {
|
||||
_validateIntegrity(entity);
|
||||
final ctx = _createContext(entity, orReplace);
|
||||
|
||||
|
@ -45,7 +45,7 @@ class InsertStatement<DataClass> {
|
|||
/// When a row with the same primary or unique key already exists in the
|
||||
/// database, the insert will fail. Use [orReplace] to replace rows that
|
||||
/// already exist.
|
||||
Future<void> insertAll(List<DataClass> rows, {bool orReplace = false}) async {
|
||||
Future<void> insertAll(List<D> rows, {bool orReplace = false}) async {
|
||||
final statements = <String, List<GenerationContext>>{};
|
||||
|
||||
// Not every insert has the same sql, as fields which are set to null are
|
||||
|
@ -78,11 +78,11 @@ class InsertStatement<DataClass> {
|
|||
///
|
||||
/// However, if no such row exists, a new row will be written instead.
|
||||
@Deprecated('Use insert with orReplace: true instead')
|
||||
Future<void> insertOrReplace(DataClass entity) async {
|
||||
Future<void> insertOrReplace(D entity) async {
|
||||
return await insert(entity, orReplace: true);
|
||||
}
|
||||
|
||||
GenerationContext _createContext(DataClass entry, bool replace) {
|
||||
GenerationContext _createContext(D entry, bool replace) {
|
||||
final map = table.entityToSql(entry)
|
||||
..removeWhere((_, value) => value == null);
|
||||
|
||||
|
@ -111,7 +111,7 @@ class InsertStatement<DataClass> {
|
|||
return ctx;
|
||||
}
|
||||
|
||||
void _validateIntegrity(DataClass d) {
|
||||
void _validateIntegrity(D d) {
|
||||
if (d == null) {
|
||||
throw InvalidDataException(
|
||||
'Cannot writee null row into ${table.$tableName}');
|
||||
|
|
|
@ -97,6 +97,40 @@ class TodoEntry extends DataClass {
|
|||
other.category == category);
|
||||
}
|
||||
|
||||
class TodosTableCompanion implements UpdateCompanion<TodoEntry> {
|
||||
final Value<int> id;
|
||||
final Value<String> title;
|
||||
final Value<String> content;
|
||||
final Value<DateTime> targetDate;
|
||||
final Value<int> category;
|
||||
const TodosTableCompanion({
|
||||
this.id = Value.absent(),
|
||||
this.title = Value.absent(),
|
||||
this.content = Value.absent(),
|
||||
this.targetDate = Value.absent(),
|
||||
this.category = Value.absent(),
|
||||
});
|
||||
@override
|
||||
bool isValuePresent(int index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return id.present;
|
||||
case 1:
|
||||
return title.present;
|
||||
case 2:
|
||||
return content.present;
|
||||
case 3:
|
||||
return targetDate.present;
|
||||
case 4:
|
||||
return category.present;
|
||||
default:
|
||||
throw ArgumentError(
|
||||
'Hit an invalid state while serializing data. Did you run the build step?');
|
||||
}
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
class $TodosTableTable extends TodosTable
|
||||
with TableInfo<$TodosTableTable, TodoEntry> {
|
||||
final GeneratedDatabase _db;
|
||||
|
@ -271,6 +305,28 @@ class Category extends DataClass {
|
|||
(other is Category && other.id == id && other.description == description);
|
||||
}
|
||||
|
||||
class CategoriesCompanion implements UpdateCompanion<Category> {
|
||||
final Value<int> id;
|
||||
final Value<String> description;
|
||||
const CategoriesCompanion({
|
||||
this.id = Value.absent(),
|
||||
this.description = Value.absent(),
|
||||
});
|
||||
@override
|
||||
bool isValuePresent(int index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return id.present;
|
||||
case 1:
|
||||
return description.present;
|
||||
default:
|
||||
throw ArgumentError(
|
||||
'Hit an invalid state while serializing data. Did you run the build step?');
|
||||
}
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
class $CategoriesTable extends Categories
|
||||
with TableInfo<$CategoriesTable, Category> {
|
||||
final GeneratedDatabase _db;
|
||||
|
@ -434,6 +490,40 @@ class User extends DataClass {
|
|||
other.creationTime == creationTime);
|
||||
}
|
||||
|
||||
class UsersCompanion implements UpdateCompanion<User> {
|
||||
final Value<int> id;
|
||||
final Value<String> name;
|
||||
final Value<bool> isAwesome;
|
||||
final Value<Uint8List> profilePicture;
|
||||
final Value<DateTime> creationTime;
|
||||
const UsersCompanion({
|
||||
this.id = Value.absent(),
|
||||
this.name = Value.absent(),
|
||||
this.isAwesome = Value.absent(),
|
||||
this.profilePicture = Value.absent(),
|
||||
this.creationTime = Value.absent(),
|
||||
});
|
||||
@override
|
||||
bool isValuePresent(int index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return id.present;
|
||||
case 1:
|
||||
return name.present;
|
||||
case 2:
|
||||
return isAwesome.present;
|
||||
case 3:
|
||||
return profilePicture.present;
|
||||
case 4:
|
||||
return creationTime.present;
|
||||
default:
|
||||
throw ArgumentError(
|
||||
'Hit an invalid state while serializing data. Did you run the build step?');
|
||||
}
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
class $UsersTable extends Users with TableInfo<$UsersTable, User> {
|
||||
final GeneratedDatabase _db;
|
||||
final String _alias;
|
||||
|
@ -602,6 +692,28 @@ class SharedTodo extends DataClass {
|
|||
(other is SharedTodo && other.todo == todo && other.user == user);
|
||||
}
|
||||
|
||||
class SharedTodosCompanion implements UpdateCompanion<SharedTodo> {
|
||||
final Value<int> todo;
|
||||
final Value<int> user;
|
||||
const SharedTodosCompanion({
|
||||
this.todo = Value.absent(),
|
||||
this.user = Value.absent(),
|
||||
});
|
||||
@override
|
||||
bool isValuePresent(int index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return todo.present;
|
||||
case 1:
|
||||
return user.present;
|
||||
default:
|
||||
throw ArgumentError(
|
||||
'Hit an invalid state while serializing data. Did you run the build step?');
|
||||
}
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
class $SharedTodosTable extends SharedTodos
|
||||
with TableInfo<$SharedTodosTable, SharedTodo> {
|
||||
final GeneratedDatabase _db;
|
||||
|
@ -731,6 +843,28 @@ class TableWithoutPKData extends DataClass {
|
|||
other.someFloat == someFloat);
|
||||
}
|
||||
|
||||
class TableWithoutPKCompanion implements UpdateCompanion<TableWithoutPKData> {
|
||||
final Value<int> notReallyAnId;
|
||||
final Value<double> someFloat;
|
||||
const TableWithoutPKCompanion({
|
||||
this.notReallyAnId = Value.absent(),
|
||||
this.someFloat = Value.absent(),
|
||||
});
|
||||
@override
|
||||
bool isValuePresent(int index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return notReallyAnId.present;
|
||||
case 1:
|
||||
return someFloat.present;
|
||||
default:
|
||||
throw ArgumentError(
|
||||
'Hit an invalid state while serializing data. Did you run the build step?');
|
||||
}
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
class $TableWithoutPKTable extends TableWithoutPK
|
||||
with TableInfo<$TableWithoutPKTable, TableWithoutPKData> {
|
||||
final GeneratedDatabase _db;
|
||||
|
|
|
@ -10,6 +10,7 @@ class SpecifiedTable {
|
|||
final String dartTypeName;
|
||||
|
||||
String get tableInfoName => tableInfoNameForTableClass(fromClass);
|
||||
String get updateCompanionName => _updateCompanionName(fromClass);
|
||||
|
||||
/// The set of primary keys, if they have been explicitly defined by
|
||||
/// overriding `primaryKey` in the table class. `null` if the primary key has
|
||||
|
@ -26,3 +27,6 @@ class SpecifiedTable {
|
|||
|
||||
String tableInfoNameForTableClass(ClassElement fromClass) =>
|
||||
'\$${fromClass.name}Table';
|
||||
|
||||
String _updateCompanionName(ClassElement fromClass) =>
|
||||
'${fromClass.name}Companion';
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:moor_generator/src/model/specified_column.dart';
|
|||
import 'package:moor_generator/src/model/specified_table.dart';
|
||||
import 'package:moor_generator/src/options.dart';
|
||||
import 'package:moor_generator/src/writer/data_class_writer.dart';
|
||||
import 'package:moor_generator/src/writer/update_companion_writer.dart';
|
||||
import 'package:moor_generator/src/writer/utils.dart';
|
||||
|
||||
class TableWriter {
|
||||
|
@ -17,6 +18,7 @@ class TableWriter {
|
|||
|
||||
void writeDataClass(StringBuffer buffer) {
|
||||
DataClassWriter(table, options).writeInto(buffer);
|
||||
UpdateCompanionWriter(table, options).writeInto(buffer);
|
||||
}
|
||||
|
||||
void writeTableInfoClass(StringBuffer buffer) {
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
import 'package:moor_generator/src/model/specified_table.dart';
|
||||
import 'package:moor_generator/src/options.dart';
|
||||
|
||||
class UpdateCompanionWriter {
|
||||
final SpecifiedTable table;
|
||||
final MoorOptions options;
|
||||
|
||||
UpdateCompanionWriter(this.table, this.options);
|
||||
|
||||
void writeInto(StringBuffer buffer) {
|
||||
buffer.write('class ${table.updateCompanionName} '
|
||||
'implements UpdateCompanion<${table.dartTypeName}> {\n');
|
||||
_writeFields(buffer);
|
||||
_writeConstructor(buffer);
|
||||
_writeIsPresentOverride(buffer);
|
||||
|
||||
buffer.write('}\n');
|
||||
}
|
||||
|
||||
void _writeFields(StringBuffer buffer) {
|
||||
for (var column in table.columns) {
|
||||
buffer.write('final Value<${column.dartTypeName}>'
|
||||
' ${column.dartGetterName};\n');
|
||||
}
|
||||
}
|
||||
|
||||
void _writeConstructor(StringBuffer buffer) {
|
||||
buffer.write('const ${table.updateCompanionName}({');
|
||||
|
||||
for (var column in table.columns) {
|
||||
buffer.write('this.${column.dartGetterName} = Value.absent(),');
|
||||
}
|
||||
|
||||
buffer.write('});\n');
|
||||
}
|
||||
|
||||
void _writeIsPresentOverride(StringBuffer buffer) {
|
||||
buffer
|
||||
..write('@override\nbool isValuePresent(int index) {\n')
|
||||
..write('switch (index) {');
|
||||
|
||||
for (var i = 0; i < table.columns.length; i++) {
|
||||
final getterName = table.columns[i].dartGetterName;
|
||||
buffer.write('case $i: return $getterName.present;\n');
|
||||
}
|
||||
|
||||
buffer
|
||||
..write('default: throw ArgumentError('
|
||||
"'Hit an invalid state while serializing data. Did you run the build "
|
||||
"step?');")
|
||||
..write('};}\n');
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue