Generate copyWith method for data classes

This commit is contained in:
Simon Binder 2019-03-05 20:36:06 +01:00
parent d818942c8d
commit 2b24684abb
No known key found for this signature in database
GPG Key ID: B807FDF954BA00CF
5 changed files with 100 additions and 10 deletions

View File

@ -253,6 +253,35 @@ We can now change the `database` class like this:
```
You can also add individual tables or drop them.
### Extracting functionality with DAOs
When you have a lot of queries, putting them all into one class quickly becomes
tedious. You can avoid this by extracting some queries into classes that are
available from your main database class. Consider the following code:
```dart
part 'todos_dao.g.dart';
// the _TodosDaoMixin will be created by sally. It contains all the necessary
// fields for the tables. The <MyDatabase> type annotation is the database class
// that should use this dao.
@UseDao(tables: [Todos])
class TodosDao extends DatabaseAccessor<MyDatabase> with _TodosDaoMixin {
// this constructor is required so that the main database can create an instance
// of this object.
TodosDao(Database db) : super(db);
Stream<List<TodoEntry>> todosInCategory(Category category) {
if (category == null) {
return (select(todos)..where((t) => isNull(t.category))).watch();
} else {
return (select(todos)..where((t) => t.category.equals(category.id)))
.watch();
}
}
}
```
If we now change the annotation on the `MyDatabase` class to `@UseSally(tables: [Todos, Categories], daos: [TodosDao])`
and re-run the code generation, a getter `todosDao` can be used to access the instance of that dao.
## TODO-List and current limitations
### Limitations (at the moment)
Please note that a workaround for most on this list exists with custom statements.
@ -266,11 +295,8 @@ These aren't sorted by priority. If you have more ideas or want some features ha
let us know by creating an issue!
- Specify primary keys
- Support an simplified update that doesn't need an explicit where based on the primary key
- Data classes: Generate a `copyWith` method.
- Simple `COUNT(*)` operations (group operations will be much more complicated)
- Support default values and expressions
- Allow using DAOs or some other mechanism instead of having to put everything in the main
database class.
- Support more Datatypes: We should at least support `Uint8List` out of the box,
supporting floating / fixed point numbers as well would be awesome
- Nullable / non-nullable datatypes

View File

@ -253,6 +253,35 @@ We can now change the `database` class like this:
```
You can also add individual tables or drop them.
### Extracting functionality with DAOs
When you have a lot of queries, putting them all into one class quickly becomes
tedious. You can avoid this by extracting some queries into classes that are
available from your main database class. Consider the following code:
```dart
part 'todos_dao.g.dart';
// the _TodosDaoMixin will be created by sally. It contains all the necessary
// fields for the tables. The <MyDatabase> type annotation is the database class
// that should use this dao.
@UseDao(tables: [Todos])
class TodosDao extends DatabaseAccessor<MyDatabase> with _TodosDaoMixin {
// this constructor is required so that the main database can create an instance
// of this object.
TodosDao(Database db) : super(db);
Stream<List<TodoEntry>> todosInCategory(Category category) {
if (category == null) {
return (select(todos)..where((t) => isNull(t.category))).watch();
} else {
return (select(todos)..where((t) => t.category.equals(category.id)))
.watch();
}
}
}
```
If we now change the annotation on the `MyDatabase` class to `@UseSally(tables: [Todos, Categories], daos: [TodosDao])`
and re-run the code generation, a getter `todosDao` can be used to access the instance of that dao.
## TODO-List and current limitations
### Limitations (at the moment)
Please note that a workaround for most on this list exists with custom statements.
@ -266,11 +295,8 @@ These aren't sorted by priority. If you have more ideas or want some features ha
let us know by creating an issue!
- Specify primary keys
- Support an simplified update that doesn't need an explicit where based on the primary key
- Data classes: Generate a `copyWith` method.
- Simple `COUNT(*)` operations (group operations will be much more complicated)
- Support default values and expressions
- Allow using DAOs or some other mechanism instead of having to put everything in the main
database class.
- Support more Datatypes: We should at least support `Uint8List` out of the box,
supporting floating / fixed point numbers as well would be awesome
- Nullable / non-nullable datatypes

View File

@ -1,3 +0,0 @@
# sally_example
This is a simple todo list app that uses sally as a persistence library.

View File

@ -23,6 +23,14 @@ class TodoEntry {
category: intType.mapFromDatabaseResponse(data['category']),
);
}
TodoEntry copyWith(
{int id, String content, DateTime targetDate, int category}) =>
TodoEntry(
id: id ?? this.id,
content: content ?? this.content,
targetDate: targetDate ?? this.targetDate,
category: category ?? this.category,
);
@override
int get hashCode =>
(((id.hashCode) * 31 + content.hashCode) * 31 + targetDate.hashCode) *
@ -109,6 +117,10 @@ class Category {
description: stringType.mapFromDatabaseResponse(data['`desc`']),
);
}
Category copyWith({int id, String description}) => Category(
id: id ?? this.id,
description: description ?? this.description,
);
@override
int get hashCode => (id.hashCode) * 31 + description.hashCode;
@override

View File

@ -26,6 +26,9 @@ class DataClassWriter {
// Also write parsing factory
_writeMappingConstructor(buffer);
// And a convenience method to copy data from this class.
_writeCopyWith(buffer);
buffer.write('@override\n int get hashCode => ');
if (table.columns.isEmpty) {
@ -36,7 +39,7 @@ class DataClassWriter {
}
// override ==
// return identical(this, other) || (other is DataClass && other.id == id && other.)
// return identical(this, other) || (other is DataClass && other.id == id && ...)
buffer
..write('@override\nbool operator ==(other) => ')
..write('identical(this, other) || (other is ${table.dartTypeName}');
@ -89,6 +92,32 @@ class DataClassWriter {
buffer.write(');}\n');
}
void _writeCopyWith(StringBuffer buffer) {
final dataClassName = table.dartTypeName;
buffer.write('$dataClassName copyWith({');
for (var i = 0; i < table.columns.length; i++) {
final column = table.columns[i];
final last = i == table.columns.length - 1;
buffer.write('${column.dartTypeName} ${column.dartGetterName}');
if (!last) {
buffer.write(',');
}
}
buffer.write('}) => $dataClassName(');
for (var column in table.columns) {
// we also have a method parameter called getter, so we can use
// field: field ?? this.field
final getter = column.dartGetterName;
buffer.write('$getter: $getter ?? this.$getter,');
}
buffer.write(');');
}
/// Recursively creates the implementation for hashCode of the data class,
/// assuming it has at least one field. When it has one field, we just return
/// the hash code of that field. Otherwise, we multiply it with 31 and add