Generate database classes

This commit is contained in:
Simon Binder 2019-02-09 13:13:08 +01:00
parent 665d80eaba
commit e4913219e4
9 changed files with 89 additions and 74 deletions

View File

@ -1,6 +1,5 @@
import 'package:sally/sally.dart'; import 'package:sally/sally.dart';
part 'example.g.dart.custom';
part 'example.g.dart'; part 'example.g.dart';
class Products extends Table { class Products extends Table {
@ -19,7 +18,6 @@ class Users extends Table {
@UseSally(tables: [Products, Users]) @UseSally(tables: [Products, Users])
class ShopDb extends _$ShopDb { class ShopDb extends _$ShopDb {
ShopDb(SqlTypeSystem typeSystem, QueryExecutor executor) : super(typeSystem, executor);
Future<List<User>> allUsers() => select(users).get(); Future<List<User>> allUsers() => select(users).get();
Future<List<User>> userByName(String name) => (select(users)..where((u) => u.name.equalsVal(name))).get(); Future<List<User>> userByName(String name) => (select(users)..where((u) => u.name.equalsVal(name))).get();

View File

@ -6,14 +6,13 @@ part of 'example.dart';
// SallyGenerator // SallyGenerator
// ************************************************************************** // **************************************************************************
class ProductsData { class Product {
final int id; final int id;
final String name; final String name;
ProductsData({this.id, this.name}); Product({this.id, this.name});
} }
class _$ProductsTable extends Products class _$ProductsTable extends Products implements TableInfo<Products, Product> {
implements TableInfo<Products, ProductsData> {
final GeneratedDatabase db; final GeneratedDatabase db;
_$ProductsTable(this.db); _$ProductsTable(this.db);
@override @override
@ -29,23 +28,23 @@ class _$ProductsTable extends Products
@override @override
Set<Column> get $primaryKey => Set(); Set<Column> get $primaryKey => Set();
@override @override
ProductsData map(Map<String, dynamic> data) { Product map(Map<String, dynamic> data) {
final intType = db.typeSystem.forDartType<int>(); final intType = db.typeSystem.forDartType<int>();
final stringType = db.typeSystem.forDartType<String>(); final stringType = db.typeSystem.forDartType<String>();
return ProductsData( return Product(
id: intType.mapFromDatabaseResponse(data['products_id']), id: intType.mapFromDatabaseResponse(data['products_id']),
name: stringType.mapFromDatabaseResponse(data['name']), name: stringType.mapFromDatabaseResponse(data['name']),
); );
} }
} }
class UsersData { class User {
final int id; final int id;
final String name; final String name;
UsersData({this.id, this.name}); User({this.id, this.name});
} }
class _$UsersTable extends Users implements TableInfo<Users, UsersData> { class _$UsersTable extends Users implements TableInfo<Users, User> {
final GeneratedDatabase db; final GeneratedDatabase db;
_$UsersTable(this.db); _$UsersTable(this.db);
@override @override
@ -61,12 +60,18 @@ class _$UsersTable extends Users implements TableInfo<Users, UsersData> {
@override @override
Set<Column> get $primaryKey => Set(); Set<Column> get $primaryKey => Set();
@override @override
UsersData map(Map<String, dynamic> data) { User map(Map<String, dynamic> data) {
final intType = db.typeSystem.forDartType<int>(); final intType = db.typeSystem.forDartType<int>();
final stringType = db.typeSystem.forDartType<String>(); final stringType = db.typeSystem.forDartType<String>();
return UsersData( return User(
id: intType.mapFromDatabaseResponse(data['id']), id: intType.mapFromDatabaseResponse(data['id']),
name: stringType.mapFromDatabaseResponse(data['name']), name: stringType.mapFromDatabaseResponse(data['name']),
); );
} }
} }
abstract class _$ShopDb extends GeneratedDatabase {
_$ShopDb() : super(const SqlTypeSystem.withDefaults(), null);
_$ProductsTable get products => _$ProductsTable(this);
_$UsersTable get users => _$UsersTable(this);
}

View File

@ -1,50 +0,0 @@
part of 'example.dart';
abstract class _$ShopDb extends GeneratedDatabase {
_$ShopDb(SqlTypeSystem typeSystem, QueryExecutor executor) : super(typeSystem, executor);
UsersTable get users => null;
}
class User {
final int id;
final String name;
User(this.id, this.name);
}
class UsersTable extends Users implements TableInfo<Users, User> {
final GeneratedDatabase db;
UsersTable(this.db);
@override
List<Column> get $columns => [id, name];
@override
String get $tableName => "users";
@override
IntColumn get id => GeneratedIntColumn("id", true);
@override
TextColumn get name => GeneratedTextColumn("name", false);
@override
Users get asDslTable => this;
@override
User map(Map<String, dynamic> data) {
final intType = db.typeSystem.forDartType<int>();
final stringType = db.typeSystem.forDartType<String>();
return User(intType.mapFromDatabaseResponse(data["id"]), stringType.mapFromDatabaseResponse(data["name"]));
}
@override
Set<Column> get $primaryKey => Set()..add(id);
}

View File

@ -0,0 +1,11 @@
import 'package:analyzer/dart/element/element.dart';
import 'package:sally_generator/src/model/specified_table.dart';
class SpecifiedDatabase {
final ClassElement fromClass;
final List<SpecifiedTable> tables;
SpecifiedDatabase(this.fromClass, this.tables);
}

View File

@ -4,6 +4,7 @@ import 'package:sally_generator/src/errors.dart';
import 'package:sally_generator/src/model/specified_column.dart'; import 'package:sally_generator/src/model/specified_column.dart';
import 'package:sally_generator/src/model/specified_table.dart'; import 'package:sally_generator/src/model/specified_table.dart';
import 'package:sally_generator/src/parser/parser.dart'; import 'package:sally_generator/src/parser/parser.dart';
import 'package:sally_generator/src/utils/names.dart';
import 'package:sally_generator/src/utils/type_utils.dart'; import 'package:sally_generator/src/utils/type_utils.dart';
import 'package:sally_generator/src/sally_generator.dart'; // ignore: implementation_imports import 'package:sally_generator/src/sally_generator.dart'; // ignore: implementation_imports
import 'package:recase/recase.dart'; import 'package:recase/recase.dart';
@ -18,9 +19,8 @@ class TableParser extends ParserBase {
fromClass: element, fromClass: element,
columns: _parseColumns(element), columns: _parseColumns(element),
sqlName: sqlName, sqlName: sqlName,
dartTypeName: dartTypeName: dataClassNameForClassName(element.name)
'${element.name}Data' // TODO better name for generated data classes );
);
} }
String _parseTableName(ClassElement element) { String _parseTableName(ClassElement element) {

View File

@ -5,10 +5,11 @@ import 'package:analyzer/src/dart/analysis/results.dart'; // ignore: implementat
import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/element.dart';
import 'package:build/build.dart'; import 'package:build/build.dart';
import 'package:sally_generator/src/errors.dart'; import 'package:sally_generator/src/errors.dart';
import 'package:sally_generator/src/model/specified_database.dart';
import 'package:sally_generator/src/model/specified_table.dart'; import 'package:sally_generator/src/model/specified_table.dart';
import 'package:sally_generator/src/parser/column_parser.dart'; import 'package:sally_generator/src/parser/column_parser.dart';
import 'package:sally_generator/src/parser/table_parser.dart'; import 'package:sally_generator/src/parser/table_parser.dart';
import 'package:sally_generator/src/writer/table_writer.dart'; import 'package:sally_generator/src/writer/database_writer.dart';
import 'package:source_gen/source_gen.dart'; import 'package:source_gen/source_gen.dart';
class SallyGenerator extends GeneratorForAnnotation<UseSally> { class SallyGenerator extends GeneratorForAnnotation<UseSally> {
@ -40,6 +41,8 @@ class SallyGenerator extends GeneratorForAnnotation<UseSally> {
tableParser ??= TableParser(this); tableParser ??= TableParser(this);
columnParser ??= ColumnParser(this); columnParser ??= ColumnParser(this);
final tablesForThisDb = <SpecifiedTable>[];
for (var table in types) { for (var table in types) {
if (!tableTypeChecker.isAssignableFrom(table.element)) { if (!tableTypeChecker.isAssignableFrom(table.element)) {
errors.add(SallyError( errors.add(SallyError(
@ -47,17 +50,19 @@ class SallyGenerator extends GeneratorForAnnotation<UseSally> {
message: 'The type $table is not a sally table', message: 'The type $table is not a sally table',
affectedElement: element)); affectedElement: element));
} else { } else {
_foundTables[table] = tableParser.parse(table.element as ClassElement); final specifiedTable = tableParser.parse(table.element as ClassElement);
_foundTables[table] = specifiedTable;
tablesForThisDb.add(specifiedTable);
} }
} }
if (_foundTables.isEmpty) if (_foundTables.isEmpty)
return ''; return '';
final specifiedDb = SpecifiedDatabase(element as ClassElement, tablesForThisDb);
final buffer = StringBuffer(); final buffer = StringBuffer();
for (var tbl in _foundTables.values) { DatabaseWriter(specifiedDb).write(buffer);
TableWriter(tbl).writeInto(buffer);
}
return buffer.toString(); return buffer.toString();
} }

View File

@ -0,0 +1,16 @@
String dataClassNameForClassName(String tableName) {
// This implementation is very primitive at the moment. The basic idea is
// that, very often, table names are formed from the plural of the entity
// they're storing (users, products, ...). We try to find the singular word
// from the table name.
// todo we might want to implement some edge cases according to
// https://en.wikipedia.org/wiki/English_plurals
if (tableName.endsWith('s')) {
return tableName.substring(0, tableName.length - 1);
}
// Default behavior if the table name is not a valid plural.
return '${tableName}Data';
}

View File

@ -0,0 +1,30 @@
import 'package:recase/recase.dart';
import 'package:sally_generator/src/model/specified_database.dart';
import 'package:sally_generator/src/writer/table_writer.dart';
class DatabaseWriter {
final SpecifiedDatabase db;
DatabaseWriter(this.db);
void write(StringBuffer buffer) {
for (final table in db.tables) {
TableWriter(table).writeInto(buffer);
}
// Write the database class
final className = '_\$${db.fromClass.name}';
buffer.write('abstract class $className extends GeneratedDatabase {\n'
'$className() : super(const SqlTypeSystem.withDefaults(), null); \n');
for (var table in db.tables) {
final tableFieldName = ReCase(table.fromClass.name).camelCase;
final tableClassName = table.tableInfoName;
buffer.write('$tableClassName get $tableFieldName => $tableClassName(this);');
}
buffer.write('}');
}
}

View File

@ -44,14 +44,14 @@ class TableWriter {
// Generate the columns // Generate the columns
for (var column in table.columns) { for (var column in table.columns) {
final isPrimaryKey = table.primaryKey.contains(column); // todo final isNullable = false;
// @override // @override
// IntColumn get id => GeneratedIntColumn('sql_name', isPrimaryKey); // IntColumn get id => GeneratedIntColumn('sql_name', isNullable);
buffer buffer
..write('@override \n') ..write('@override \n')
..write('${column.dslColumnTypeName} get ${column.dartGetterName} => ' ..write('${column.dslColumnTypeName} get ${column.dartGetterName} => '
'${column.implColumnTypeName}(\'${column.name.name}\', $isPrimaryKey);\n'); '${column.implColumnTypeName}(\'${column.name.name}\', $isNullable);\n');
} }
// Generate $columns, $tableName, asDslTable getters // Generate $columns, $tableName, asDslTable getters