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';
part 'example.g.dart.custom';
part 'example.g.dart';
class Products extends Table {
@ -19,7 +18,6 @@ class Users extends Table {
@UseSally(tables: [Products, Users])
class ShopDb extends _$ShopDb {
ShopDb(SqlTypeSystem typeSystem, QueryExecutor executor) : super(typeSystem, executor);
Future<List<User>> allUsers() => select(users).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
// **************************************************************************
class ProductsData {
class Product {
final int id;
final String name;
ProductsData({this.id, this.name});
Product({this.id, this.name});
}
class _$ProductsTable extends Products
implements TableInfo<Products, ProductsData> {
class _$ProductsTable extends Products implements TableInfo<Products, Product> {
final GeneratedDatabase db;
_$ProductsTable(this.db);
@override
@ -29,23 +28,23 @@ class _$ProductsTable extends Products
@override
Set<Column> get $primaryKey => Set();
@override
ProductsData map(Map<String, dynamic> data) {
Product map(Map<String, dynamic> data) {
final intType = db.typeSystem.forDartType<int>();
final stringType = db.typeSystem.forDartType<String>();
return ProductsData(
return Product(
id: intType.mapFromDatabaseResponse(data['products_id']),
name: stringType.mapFromDatabaseResponse(data['name']),
);
}
}
class UsersData {
class User {
final int id;
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;
_$UsersTable(this.db);
@override
@ -61,12 +60,18 @@ class _$UsersTable extends Users implements TableInfo<Users, UsersData> {
@override
Set<Column> get $primaryKey => Set();
@override
UsersData map(Map<String, dynamic> data) {
User map(Map<String, dynamic> data) {
final intType = db.typeSystem.forDartType<int>();
final stringType = db.typeSystem.forDartType<String>();
return UsersData(
return User(
id: intType.mapFromDatabaseResponse(data['id']),
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_table.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/sally_generator.dart'; // ignore: implementation_imports
import 'package:recase/recase.dart';
@ -18,9 +19,8 @@ class TableParser extends ParserBase {
fromClass: element,
columns: _parseColumns(element),
sqlName: sqlName,
dartTypeName:
'${element.name}Data' // TODO better name for generated data classes
);
dartTypeName: dataClassNameForClassName(element.name)
);
}
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:build/build.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/parser/column_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';
class SallyGenerator extends GeneratorForAnnotation<UseSally> {
@ -40,6 +41,8 @@ class SallyGenerator extends GeneratorForAnnotation<UseSally> {
tableParser ??= TableParser(this);
columnParser ??= ColumnParser(this);
final tablesForThisDb = <SpecifiedTable>[];
for (var table in types) {
if (!tableTypeChecker.isAssignableFrom(table.element)) {
errors.add(SallyError(
@ -47,17 +50,19 @@ class SallyGenerator extends GeneratorForAnnotation<UseSally> {
message: 'The type $table is not a sally table',
affectedElement: element));
} 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)
return '';
final specifiedDb = SpecifiedDatabase(element as ClassElement, tablesForThisDb);
final buffer = StringBuffer();
for (var tbl in _foundTables.values) {
TableWriter(tbl).writeInto(buffer);
}
DatabaseWriter(specifiedDb).write(buffer);
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
for (var column in table.columns) {
final isPrimaryKey = table.primaryKey.contains(column); // todo
final isNullable = false;
// @override
// IntColumn get id => GeneratedIntColumn('sql_name', isPrimaryKey);
// IntColumn get id => GeneratedIntColumn('sql_name', isNullable);
buffer
..write('@override \n')
..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