ORM gen updates

This commit is contained in:
Tobe O 2018-12-01 14:03:43 -05:00
parent e95c4d8c6b
commit 432bac7512
30 changed files with 639 additions and 595 deletions

View file

@ -3,14 +3,10 @@ builders:
import: "package:angel_orm_generator/angel_orm_generator.dart"
builder_factories:
- ormBuilder
#- mongoDBOrmBuilder
- postgreSqlOrmBuilder
auto_apply: root_package
build_to: source
build_extensions:
.dart:
- ".orm.g.dart"
- ".mongodb.orm.g.dart"
- ".mysql.orm.g.dart"
- ".rethinkdb.orm.g.dart"
- ".postgresql.orm.g.dart"
- ".angel_orm.g.part"
applies_builders:
["source_gen|combining_builder", "source_gen|part_cleanup"]

View file

@ -1,5 +1,4 @@
//export 'src/mongodb_orm_generator.dart';
export 'src/orm_build_context.dart';
export 'src/orm_generator.dart';
export 'src/postgresql_orm_generator.dart';
export 'src/readers.dart';

View file

@ -1,45 +0,0 @@
import 'dart:async';
import 'package:analyzer/dart/element/element.dart';
import 'package:angel_orm/angel_orm.dart';
import 'package:build/build.dart';
import 'package:code_builder/code_builder.dart' hide LibraryBuilder;
import 'package:path/path.dart' as p;
import 'package:source_gen/source_gen.dart';
import 'orm_build_context.dart';
Builder mongoDBOrmBuilder(_) {
return new LibraryBuilder(new MongoDBOrmGenerator(),
generatedExtension: '.mongodb.orm.g.dart');
}
/// Builder that generates `.orm.g.dart`, with an abstract `FooOrm` class.
class MongoDBOrmGenerator extends GeneratorForAnnotation<Orm> {
final bool autoSnakeCaseNames;
final bool autoIdAndDateFields;
MongoDBOrmGenerator({this.autoSnakeCaseNames, this.autoIdAndDateFields});
@override
Future<String> generateForAnnotatedElement(
Element element, ConstantReader annotation, BuildStep buildStep) async {
if (element is ClassElement) {
var ctx = await buildOrmContext(element, annotation, buildStep,
buildStep.resolver, autoSnakeCaseNames, autoIdAndDateFields);
var lib = buildOrmLibrary(buildStep.inputId, ctx);
return lib.accept(new DartEmitter()).toString();
} else {
throw 'The @Orm() annotation can only be applied to classes.';
}
}
Library buildOrmLibrary(AssetId inputId, OrmBuildContext ctx) {
return new Library((lib) {
// Add part of
var libFile =
p.setExtension(p.basename(inputId.uri.path), '.orm.g.dart');
lib.body.add(new Code("part of '$libFile';"));
});
}
}

View file

@ -1,18 +1,19 @@
import 'dart:async';
import 'dart:collection';
import 'package:analyzer/dart/element/element.dart';
import 'package:angel_orm/angel_orm.dart';
import 'package:angel_serialize_generator/angel_serialize_generator.dart';
import 'package:build/build.dart';
import 'package:code_builder/code_builder.dart' hide LibraryBuilder;
import 'package:path/path.dart' as p;
import 'package:recase/recase.dart';
import 'package:source_gen/source_gen.dart';
import 'orm_build_context.dart';
Builder ormBuilder(_) {
return new LibraryBuilder(new OrmGenerator(),
generatedExtension: '.orm.g.dart');
Builder ormBuilder(BuilderOptions options) {
return new SharedPartBuilder([
new OrmGenerator(
autoSnakeCaseNames: options.config['auto_snake_case_names'] != false,
autoIdAndDateFields: options.config['auto_id_and_date_fields'] != false)
], 'angel_orm');
}
TypeReference futureOf(String type) {
@ -34,156 +35,151 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
if (element is ClassElement) {
var ctx = await buildOrmContext(element, annotation, buildStep,
buildStep.resolver, autoSnakeCaseNames, autoIdAndDateFields);
var lib = buildOrmBaseLibrary(buildStep.inputId, ctx);
var lib = buildOrmLibrary(buildStep.inputId, ctx);
return lib.accept(new DartEmitter()).toString();
} else {
throw 'The @Orm() annotation can only be applied to classes.';
}
}
Library buildOrmBaseLibrary(AssetId inputId, OrmBuildContext ctx) {
Library buildOrmLibrary(AssetId inputId, OrmBuildContext ctx) {
return new Library((lib) {
// Necessary imports
var imports = new SplayTreeSet<String>.from(
['dart:async', p.basename(inputId.uri.path)]);
switch (ctx.ormAnnotation.type) {
// case OrmType.mongoDB:
// imports.add('package:mongo_dart/mongo_dart.dart');
// break;
case OrmType.postgreSql:
imports.add('package:postgres/postgres.dart');
break;
default:
break;
}
lib.directives.addAll(imports.map((url) => new Directive.import(url)));
// Add the corresponding `part`
String dbExtension;
switch (ctx.ormAnnotation.type) {
// case OrmType.mongoDB:
// dbExtension = 'mongodb';
// break;
case OrmType.rethinkDB:
dbExtension = 'rethinkdb';
break;
case OrmType.mySql:
dbExtension = 'mysql';
break;
case OrmType.postgreSql:
dbExtension = 'postgresql';
break;
default:
throw 'Unsupported ORM type: ${ctx.ormAnnotation.type}';
}
var dbFile = p.setExtension(
p.basename(inputId.uri.path), '.$dbExtension.orm.g.dart');
lib.body.add(new Code("part '$dbFile';"));
// Create `FooOrm` abstract class
var rc = ctx.buildContext.modelClassNameRecase;
lib.body.add(new Class((clazz) {
clazz
..name = '${rc.pascalCase}Orm'
..abstract = true;
// Add factory constructors.
switch (ctx.ormAnnotation.type) {
case OrmType.postgreSql:
clazz.constructors.add(new Constructor((b) {
b
..name = 'postgreSql'
..factory = true
..redirect = refer('PostgreSql${rc.pascalCase}Orm')
..requiredParameters.add(new Parameter((b) {
b
..name = 'connection'
..type = refer('PostgreSQLConnection');
}));
}));
dbExtension = 'postgresql';
break;
default:
break;
}
// Next, add method stubs.
// * getAll
// * getById
// * deleteById
// * updateX()
// * createX()
// * query()
// getAll
clazz.methods.add(new Method((m) {
m
..name = 'getAll'
..returns = new TypeReference((b) => b
..symbol = 'Future'
..types.add(new TypeReference((b) => b
..symbol = 'List'
..types.add(ctx.buildContext.modelClassType))));
}));
// getById
clazz.methods.add(new Method((m) {
m
..name = 'getById'
..returns = futureOf(ctx.buildContext.modelClassName)
..requiredParameters.add(new Parameter((b) => b
..name = 'id'
..type = refer('String')));
}));
// deleteById
clazz.methods.add(new Method((m) {
m
..name = 'deleteById'
..returns = futureOf(ctx.buildContext.modelClassName)
..requiredParameters.add(new Parameter((b) => b
..name = 'id'
..type = refer('String')));
}));
// createX()
clazz.methods.add(new Method((m) {
m
..name = 'create${ctx.buildContext.modelClassName}'
..returns = futureOf(ctx.buildContext.modelClassName)
..requiredParameters.add(new Parameter((b) => b
..name = 'model'
..type = ctx.buildContext.modelClassType));
}));
// updateX()
clazz.methods.add(new Method((m) {
m
..name = 'update${ctx.buildContext.modelClassName}'
..returns = futureOf(ctx.buildContext.modelClassName)
..requiredParameters.add(new Parameter((b) => b
..name = 'model'
..type = ctx.buildContext.modelClassType));
}));
// query()
clazz.methods.add(new Method((m) {
m
..name = 'query'
..returns = refer('${rc.pascalCase}Query');
}));
}));
// Create `FooQuery` class
lib.body.add(new Class((clazz) {
clazz..name = '${rc.pascalCase}Query';
// Create `FooQueryWhere` class
lib.body.add(buildQueryClass(ctx));
lib.body.add(buildWhereClass(ctx));
});
}
Class buildQueryClass(OrmBuildContext ctx) {
// TODO: Handle relations
return new Class((clazz) {
var rc = ctx.buildContext.modelClassNameRecase;
var queryWhereType = refer('${rc.pascalCase}QueryWhere');
clazz
..name = '${rc.pascalCase}Query'
..extend = new TypeReference((b) {
b
..symbol = 'Query'
..types.addAll([
ctx.buildContext.modelClassType,
queryWhereType,
]);
});
// Add tableName
clazz.methods.add(new Method((m) {
m
..name = 'tableName'
..annotations.add(refer('override'))
..type = MethodType.getter
..body = new Block((b) {
b.addExpression(literalString(ctx.tableName).returned);
});
}));
// Add fields getter
clazz.methods.add(new Method((m) {
m
..name = 'fields'
..annotations.add(refer('override'))
..type = MethodType.getter
..body = new Block((b) {
var names = ctx.buildContext.fields
.map((f) => literalString(f.name))
.toList();
b.addExpression(literalConstList(names).returned);
});
}));
// Add where member
clazz.fields.add(new Field((b) {
b
..annotations.add(refer('override'))
..name = 'where'
..modifier = FieldModifier.final$
..type = queryWhereType
..assignment = queryWhereType.newInstance([]).code;
}));
// Add deserialize()
clazz.methods.add(new Method((m) {
m
..name = 'deserialize'
..annotations.add(refer('override'))
..requiredParameters.add(new Parameter((b) => b
..name = 'row'
..type = refer('List')))
..body = new Block((b) {
int i = 0;
var args = <String, Expression>{};
for (var field in ctx.buildContext.fields) {
var type = convertTypeReference(field.type);
args[field.name] = (refer('row').index(literalNum(i))).asA(type);
}
b.addExpression(
ctx.buildContext.modelClassType.newInstance([], args).returned);
});
}));
});
}
Class buildWhereClass(OrmBuildContext ctx) {
return new Class((clazz) {
var rc = ctx.buildContext.modelClassNameRecase;
clazz
..name = '${rc.pascalCase}QueryWhere'
..extend = refer('QueryWhere');
// Build expressionBuilders getter
clazz.methods.add(new Method((m) {
m
..name = 'expressionBuilders'
..annotations.add(refer('override'))
..type = MethodType.getter
..body = new Block((b) {
var references = ctx.buildContext.fields.map((f) => refer(f.name));
b.addExpression(literalList(references).returned);
});
}));
// Add builders for each field
for (var field in ctx.buildContext.fields) {
// TODO: Handle fields with relations
Reference builderType;
if (const TypeChecker.fromRuntime(String).isExactlyType(field.type)) {
builderType = refer('StringSqlExpressionBuilder');
} else if (const TypeChecker.fromRuntime(bool)
.isExactlyType(field.type)) {
builderType = refer('BooleanSqlExpressionBuilder');
} else if (const TypeChecker.fromRuntime(DateTime)
.isExactlyType(field.type)) {
builderType = refer('DateTimeSqlExpressionBuilder');
} else if (const TypeChecker.fromRuntime(int)
.isExactlyType(field.type) ||
const TypeChecker.fromRuntime(double).isExactlyType(field.type)) {
builderType = new TypeReference((b) => b
..symbol = 'NumericSqlExpressionBuilder'
..types.add(refer(field.type.name)));
} else {
throw new UnsupportedError(
'Cannot generate ORM code for field of type ${field.type.name}.');
}
clazz.fields.add(new Field((b) {
b
..name = field.name
..modifier = FieldModifier.final$
..type = builderType
..assignment = builderType.newInstance([
literalString(ctx.buildContext.resolveFieldName(field.name))
]).code;
}));
}
});
}
}

View file

@ -1,348 +0,0 @@
import 'dart:async';
import 'package:analyzer/dart/element/element.dart';
import 'package:angel_orm/angel_orm.dart';
import 'package:angel_serialize_generator/angel_serialize_generator.dart';
import 'package:angel_serialize_generator/build_context.dart';
import 'package:build/build.dart';
import 'package:code_builder/code_builder.dart' hide LibraryBuilder;
import 'package:path/path.dart' as p;
import 'package:source_gen/source_gen.dart';
import 'orm_build_context.dart';
Builder postgreSqlOrmBuilder(_) {
return new LibraryBuilder(new PostgreSqlOrmGenerator(),
generatedExtension: '.postgresql.orm.g.dart');
}
/// Builder that generates `.orm.g.dart`, with an abstract `FooOrm` class.
class PostgreSqlOrmGenerator extends GeneratorForAnnotation<Orm> {
final bool autoSnakeCaseNames;
final bool autoIdAndDateFields;
PostgreSqlOrmGenerator({this.autoSnakeCaseNames, this.autoIdAndDateFields});
@override
Future<String> generateForAnnotatedElement(
Element element, ConstantReader annotation, BuildStep buildStep) async {
if (element is ClassElement) {
var ctx = await buildOrmContext(element, annotation, buildStep,
buildStep.resolver, autoSnakeCaseNames, autoIdAndDateFields);
var lib = buildOrmLibrary(buildStep.inputId, ctx);
return lib.accept(new DartEmitter()).toString();
} else {
throw 'The @Orm() annotation can only be applied to classes.';
}
}
Library buildOrmLibrary(AssetId inputId, OrmBuildContext ctx) {
return new Library((lib) {
// Add part of
var libFile = p.setExtension(p.basename(inputId.uri.path), '.orm.g.dart');
lib.body.add(new Code("part of '$libFile';"));
// Add _PostgresqlFooOrmImpl
lib.body.add(buildOrmClass(ctx));
});
}
Class buildOrmClass(OrmBuildContext ctx) {
return new Class((clazz) {
var rc = ctx.buildContext.modelClassNameRecase;
clazz
..name = 'PostgreSql${rc.pascalCase}Orm'
..implements.add(refer('${rc.pascalCase}Orm'))
// final PostgreSQLConnection connection;
..fields.add(new Field((b) {
b
..modifier = FieldModifier.final$
..name = 'connection'
..type = refer('PostgreSQLConnection');
}))
// _PostgresqlFooOrmImpl(this.connection);
..constructors.add(new Constructor((b) {
b
..requiredParameters.add(new Parameter((b) => b
..name = 'connection'
..toThis = true));
}))
..methods.add(buildParseRowMethod(ctx))
..methods.add(buildGetById(ctx))
..methods.add(buildDeleteById(ctx))
..methods.add(buildGetAll(ctx))
..methods.add(buildCreate(ctx))
..methods.add(buildUpdate(ctx))
..methods.add(buildQuery(ctx));
});
}
Method buildQuery(OrmBuildContext ctx) {
return new Method((m) {
m
..name = 'query'
..returns = refer('${ctx.buildContext.modelClassName}Query')
..body = new Block((b) => b.addExpression(literalNull.returned));
});
}
Method buildParseRowMethod(OrmBuildContext ctx) {
return new Method((m) {
m
..name = 'parseRow'
..static = true
..returns = ctx.buildContext.modelClassType
..requiredParameters.add(new Parameter((b) => b
..name = 'row'
..type = refer('List')))
..body = new Block((b) {
var args = <String, Expression>{};
for (int i = 0; i < ctx.buildContext.fields.length; i++) {
var field = ctx.buildContext.fields[i];
args[field.name] = refer('row')
.index(literalNum(i))
.asA(convertTypeReference(field.type));
}
var returnValue =
ctx.buildContext.modelClassType.newInstance([], args);
b.addExpression(returnValue.returned);
});
});
}
String buildFieldString(OrmBuildContext ctx) {
var queryString = new StringBuffer();
int i = 0;
for (var field in ctx.buildContext.fields) {
if (i++ > 0) queryString.write(',');
queryString.write(' ' + ctx.buildContext.resolveFieldName(field.name));
}
return queryString.toString();
}
String buildQuotedFieldString(OrmBuildContext ctx) {
var queryString = new StringBuffer();
int i = 0;
for (var field in ctx.buildContext.fields) {
if (i++ > 0) queryString.write(',');
queryString
.write(' "' + ctx.buildContext.resolveFieldName(field.name) + '"');
}
return queryString.toString();
}
String buildInsertionValueString(OrmBuildContext ctx) {
var buf = new StringBuffer('(');
int i = 0;
for (var field in ctx.buildContext.fields) {
if (i++ > 0) buf.write(',');
if (dateTimeTypeChecker.isAssignableFromType(field.type))
buf.write(
'CAST (@${field.name} AS ${ctx.columns[field.name].type.name})');
else
buf.write('@${field.name}');
}
return buf.toString() + ')';
}
Expression buildSubstitutionValues(OrmBuildContext ctx) {
var values = <Expression, Expression>{};
for (var field in ctx.buildContext.fields) {
values[literalString(field.name)] = refer('model').property(field.name);
}
return literalMap(values);
}
void applyQuery(
BlockBuilder b, String queryString, Expression substitutionValues) {
b.statements.add(refer('connection')
.property('query')
.call(
[literalString(queryString)],
substitutionValues == null
? {}
: {'substitutionValues': substitutionValues})
.awaited
.assignVar('r')
.statement);
}
void applyQueryAndReturnOne(
BlockBuilder b, String queryString, Expression substitutionValues) {
applyQuery(b, queryString, substitutionValues);
b.addExpression(
(refer('parseRow').call([refer('r').property('first')])).returned);
}
void applyQueryAndReturnList(
BlockBuilder b, String queryString, Expression substitutionValues) {
applyQuery(b, queryString, substitutionValues);
b.statements.add(new Code('return r.map(parseRow).toList();'));
}
Method buildGetById(OrmBuildContext ctx) {
/*
@override
Future<Author> getById(id) async {
var r = await connection.query('');
return parseRow(r.first);
}
*/
return new Method((m) {
m
..name = 'getById'
..annotations.add(refer('override'))
..modifier = MethodModifier.async
..requiredParameters.add(new Parameter((b) => b
..name = 'id'
..type = refer('String')))
..returns = new TypeReference((b) => b
..symbol = 'Future'
..types.add(ctx.buildContext.modelClassType))
..body = new Block((b) {
var fields = buildFieldString(ctx);
var queryString =
'SELECT $fields FROM "${ctx.tableName}" WHERE id = @id LIMIT 1;';
applyQueryAndReturnOne(
b,
queryString,
literalMap({
'id': refer('int').property('parse').call([refer('id')])
}));
});
});
}
Method buildDeleteById(OrmBuildContext ctx) {
/*
@override
Future<Author> getById(id) async {
var r = await connection.query('');
return parseRow(r.first);
}
*/
return new Method((m) {
m
..name = 'deleteById'
..annotations.add(refer('override'))
..modifier = MethodModifier.async
..requiredParameters.add(new Parameter((b) => b
..name = 'id'
..type = refer('String')))
..returns = new TypeReference((b) => b
..symbol = 'Future'
..types.add(ctx.buildContext.modelClassType))
..body = new Block((b) {
var fields = buildQuotedFieldString(ctx);
var queryString =
'DELETE FROM "${ctx.tableName}" WHERE id = @id RETURNING $fields;';
applyQueryAndReturnOne(
b,
queryString,
literalMap({
'id': refer('int').property('parse').call([refer('id')])
}));
});
});
}
Method buildGetAll(OrmBuildContext ctx) {
/*
@override
Future<List<Author>> getAll() async {
var r = await connection
.query('SELECT id, name, created_at, updated_at FROM "authors";');
return r.map(parseRow).toList();
}
*/
return new Method((method) {
method
..name = 'getAll'
..modifier = MethodModifier.async
..returns = refer('Future<List<${ctx.buildContext.modelClassName}>>')
..annotations.add(refer('override'))
..body = new Block((block) {
var fields = buildFieldString(ctx);
var queryString = 'SELECT $fields FROM "${ctx.tableName}";';
applyQueryAndReturnList(block, queryString, null);
});
});
}
Method buildCreate(OrmBuildContext ctx) {
/*
@override
Future<Author> createAuthor(Author model) async {
// ...
}
*/
return new Method((method) {
method
..name = 'create${ctx.buildContext.modelClassName}'
..modifier = MethodModifier.async
..annotations.add(refer('override'))
..returns = refer('Future<${ctx.buildContext.modelClassName}>')
..requiredParameters.add(new Parameter((b) => b
..name = 'model'
..type = ctx.buildContext.modelClassType))
..body = new Block((block) {
if (ctx.buildContext.autoIdAndDateFields != false) {
// If we are auto-managing created+updated at, do so now
block.statements.add(new Code(
'model = model.copyWith(createdAt: new DateTime.now(), updatedAt: new DateTime.now());'));
}
var fields = buildQuotedFieldString(ctx);
var fieldSet = buildInsertionValueString(ctx);
var queryString =
'INSERT INTO "${ctx.tableName}" ($fields) VALUES $fieldSet RETURNING $fields;';
applyQueryAndReturnOne(
block, queryString, buildSubstitutionValues(ctx));
});
});
}
Method buildUpdate(OrmBuildContext ctx) {
/*
@override
Future<Author> updateAuthor(Author model) async {
// ...
}
*/
return new Method((method) {
method
..name = 'update${ctx.buildContext.modelClassName}'
..modifier = MethodModifier.async
..annotations.add(refer('override'))
..returns = refer('Future<${ctx.buildContext.modelClassName}>')
..requiredParameters.add(new Parameter((b) => b
..name = 'model'
..type = ctx.buildContext.modelClassType))
..body = new Block((block) {
if (ctx.buildContext.autoIdAndDateFields != false) {
// If we are auto-managing created+updated at, do so now
block.statements.add(new Code(
'model = model.copyWith(updatedAt: new DateTime.now());'));
}
var fields = buildQuotedFieldString(ctx);
var fieldSet = buildInsertionValueString(ctx);
var queryString =
'UPDATE "${ctx.tableName}" SET ($fields) = $fieldSet RETURNING $fields;';
applyQueryAndReturnOne(
block, queryString, buildSubstitutionValues(ctx));
});
});
}
}

View file

@ -5,10 +5,7 @@ import 'package:source_gen/source_gen.dart';
const TypeChecker columnTypeChecker = const TypeChecker.fromRuntime(Column);
Orm reviveORMAnnotation(ConstantReader reader) {
return Orm(
OrmType.values[reader.read('type').objectValue.getField('index').toIntValue()],
tableName: reader.peek('tableName')?.stringValue,
);
return Orm(tableName: reader.peek('tableName')?.stringValue);
}
class ColumnReader {

View file

@ -8,7 +8,7 @@ environment:
dependencies:
angel_orm: ^2.0.0-dev
angel_serialize_generator: ^2.0.0
build: ^0.12.0
build: ">=0.12.0 <2.0.0"
build_config: ^0.3.0
code_builder: ^3.0.0
inflection:
@ -23,6 +23,6 @@ dev_dependencies:
angel_framework: ^2.0.0-alpha
#angel_migration: ^1.0.0-alpha
#angel_test: ^1.0.0
build_runner: ^0.10.0
build_runner: ^1.0.0
postgres: ^1.0.0
test: ^1.0.0

View file

@ -9,7 +9,7 @@ part 'author.g.dart';
part 'author.serializer.g.dart';
@serializable
@postgreSqlOrm
@orm
class _Author extends Model {
@Column(length: 255, indexType: IndexType.unique, defaultValue: 'Tobe Osakwe')
String name;

View file

@ -2,6 +2,52 @@
part of angel_orm.generator.models.author;
// **************************************************************************
// OrmGenerator
// **************************************************************************
class AuthorQuery extends Query<Author, AuthorQueryWhere> {
@override
final AuthorQueryWhere where = new AuthorQueryWhere();
@override
get tableName {
return 'authors';
}
@override
get fields {
return const ['id', 'name', 'createdAt', 'updatedAt'];
}
@override
deserialize(List row) {
return new Author(
id: (row[0] as String),
name: (row[0] as String),
createdAt: (row[0] as DateTime),
updatedAt: (row[0] as DateTime));
}
}
class AuthorQueryWhere extends QueryWhere {
final StringSqlExpressionBuilder id = new StringSqlExpressionBuilder('id');
final StringSqlExpressionBuilder name =
new StringSqlExpressionBuilder('name');
final DateTimeSqlExpressionBuilder createdAt =
new DateTimeSqlExpressionBuilder('created_at');
final DateTimeSqlExpressionBuilder updatedAt =
new DateTimeSqlExpressionBuilder('updated_at');
@override
get expressionBuilders {
return [id, name, createdAt, updatedAt];
}
}
// **************************************************************************
// JsonModelGenerator
// **************************************************************************
@ -39,6 +85,11 @@ class Author extends _Author {
other.updatedAt == updatedAt;
}
@override
int get hashCode {
return hashObjects([id, name, createdAt, updatedAt]);
}
Map<String, dynamic> toJson() {
return AuthorSerializer.toMap(this);
}

View file

@ -8,7 +8,7 @@ part 'book.g.dart';
part 'book.serializer.g.dart';
@serializable
@postgreSqlOrm
@orm
class _Book extends Model {
@belongsTo
Author author;

View file

@ -67,6 +67,12 @@ class Book extends _Book {
other.updatedAt == updatedAt;
}
@override
int get hashCode {
return hashObjects(
[id, author, partnerAuthor, authorId, name, createdAt, updatedAt]);
}
Map<String, dynamic> toJson() {
return BookSerializer.toMap(this);
}

View file

@ -7,7 +7,7 @@ part 'car.g.dart';
part 'car.serializer.g.dart';
@serializable
@postgreSqlOrm
@orm
class _Car extends Model {
String make;
String description;

View file

@ -2,6 +2,80 @@
part of angel_orm.generator.models.car;
// **************************************************************************
// OrmGenerator
// **************************************************************************
class CarQuery extends Query<Car, CarQueryWhere> {
@override
final CarQueryWhere where = new CarQueryWhere();
@override
get tableName {
return 'cars';
}
@override
get fields {
return const [
'id',
'make',
'description',
'familyFriendly',
'recalledAt',
'createdAt',
'updatedAt'
];
}
@override
deserialize(List row) {
return new Car(
id: (row[0] as String),
make: (row[0] as String),
description: (row[0] as String),
familyFriendly: (row[0] as bool),
recalledAt: (row[0] as DateTime),
createdAt: (row[0] as DateTime),
updatedAt: (row[0] as DateTime));
}
}
class CarQueryWhere extends QueryWhere {
final StringSqlExpressionBuilder id = new StringSqlExpressionBuilder('id');
final StringSqlExpressionBuilder make =
new StringSqlExpressionBuilder('make');
final StringSqlExpressionBuilder description =
new StringSqlExpressionBuilder('description');
final BooleanSqlExpressionBuilder familyFriendly =
new BooleanSqlExpressionBuilder('family_friendly');
final DateTimeSqlExpressionBuilder recalledAt =
new DateTimeSqlExpressionBuilder('recalled_at');
final DateTimeSqlExpressionBuilder createdAt =
new DateTimeSqlExpressionBuilder('created_at');
final DateTimeSqlExpressionBuilder updatedAt =
new DateTimeSqlExpressionBuilder('updated_at');
@override
get expressionBuilders {
return [
id,
make,
description,
familyFriendly,
recalledAt,
createdAt,
updatedAt
];
}
}
// **************************************************************************
// JsonModelGenerator
// **************************************************************************
@ -67,6 +141,19 @@ class Car extends _Car {
other.updatedAt == updatedAt;
}
@override
int get hashCode {
return hashObjects([
id,
make,
description,
familyFriendly,
recalledAt,
createdAt,
updatedAt
]);
}
Map<String, dynamic> toJson() {
return CarSerializer.toMap(this);
}

View file

@ -6,7 +6,7 @@ import 'package:angel_serialize/angel_serialize.dart';
part 'customer.g.dart';
part 'customer.serializer.g.dart';
@postgreSqlOrm
@orm
@serializable
class _Customer extends Model {
}

View file

@ -2,6 +2,48 @@
part of angel_orm_generator.test.models.customer;
// **************************************************************************
// OrmGenerator
// **************************************************************************
class CustomerQuery extends Query<Customer, CustomerQueryWhere> {
@override
final CustomerQueryWhere where = new CustomerQueryWhere();
@override
get tableName {
return 'customers';
}
@override
get fields {
return const ['id', 'createdAt', 'updatedAt'];
}
@override
deserialize(List row) {
return new Customer(
id: (row[0] as String),
createdAt: (row[0] as DateTime),
updatedAt: (row[0] as DateTime));
}
}
class CustomerQueryWhere extends QueryWhere {
final StringSqlExpressionBuilder id = new StringSqlExpressionBuilder('id');
final DateTimeSqlExpressionBuilder createdAt =
new DateTimeSqlExpressionBuilder('created_at');
final DateTimeSqlExpressionBuilder updatedAt =
new DateTimeSqlExpressionBuilder('updated_at');
@override
get expressionBuilders {
return [id, createdAt, updatedAt];
}
}
// **************************************************************************
// JsonModelGenerator
// **************************************************************************
@ -33,6 +75,11 @@ class Customer extends _Customer {
other.updatedAt == updatedAt;
}
@override
int get hashCode {
return hashObjects([id, createdAt, updatedAt]);
}
Map<String, dynamic> toJson() {
return CustomerSerializer.toMap(this);
}

View file

@ -7,7 +7,7 @@ part 'foot.g.dart';
part 'foot.serializer.g.dart';
@serializable
@postgreSqlOrm
@orm
class _Foot extends Model {
int legId, nToes;
}

View file

@ -2,6 +2,56 @@
part of angel_orm_generator.test.models.foot;
// **************************************************************************
// OrmGenerator
// **************************************************************************
class FootQuery extends Query<Foot, FootQueryWhere> {
@override
final FootQueryWhere where = new FootQueryWhere();
@override
get tableName {
return 'foots';
}
@override
get fields {
return const ['id', 'legId', 'nToes', 'createdAt', 'updatedAt'];
}
@override
deserialize(List row) {
return new Foot(
id: (row[0] as String),
legId: (row[0] as int),
nToes: (row[0] as int),
createdAt: (row[0] as DateTime),
updatedAt: (row[0] as DateTime));
}
}
class FootQueryWhere extends QueryWhere {
final StringSqlExpressionBuilder id = new StringSqlExpressionBuilder('id');
final NumericSqlExpressionBuilder<int> legId =
new NumericSqlExpressionBuilder<int>('leg_id');
final NumericSqlExpressionBuilder<int> nToes =
new NumericSqlExpressionBuilder<int>('n_toes');
final DateTimeSqlExpressionBuilder createdAt =
new DateTimeSqlExpressionBuilder('created_at');
final DateTimeSqlExpressionBuilder updatedAt =
new DateTimeSqlExpressionBuilder('updated_at');
@override
get expressionBuilders {
return [id, legId, nToes, createdAt, updatedAt];
}
}
// **************************************************************************
// JsonModelGenerator
// **************************************************************************
@ -48,6 +98,11 @@ class Foot extends _Foot {
other.updatedAt == updatedAt;
}
@override
int get hashCode {
return hashObjects([id, legId, nToes, createdAt, updatedAt]);
}
Map<String, dynamic> toJson() {
return FootSerializer.toMap(this);
}

View file

@ -7,7 +7,7 @@ part 'fruit.g.dart';
part 'fruit.serializer.g.dart';
@serializable
@postgreSqlOrm
@orm
class _Fruit extends Model {
int treeId;
String commonName;

View file

@ -2,6 +2,56 @@
part of angel_orm_generator.test.models.fruit;
// **************************************************************************
// OrmGenerator
// **************************************************************************
class FruitQuery extends Query<Fruit, FruitQueryWhere> {
@override
final FruitQueryWhere where = new FruitQueryWhere();
@override
get tableName {
return 'fruits';
}
@override
get fields {
return const ['id', 'treeId', 'commonName', 'createdAt', 'updatedAt'];
}
@override
deserialize(List row) {
return new Fruit(
id: (row[0] as String),
treeId: (row[0] as int),
commonName: (row[0] as String),
createdAt: (row[0] as DateTime),
updatedAt: (row[0] as DateTime));
}
}
class FruitQueryWhere extends QueryWhere {
final StringSqlExpressionBuilder id = new StringSqlExpressionBuilder('id');
final NumericSqlExpressionBuilder<int> treeId =
new NumericSqlExpressionBuilder<int>('tree_id');
final StringSqlExpressionBuilder commonName =
new StringSqlExpressionBuilder('common_name');
final DateTimeSqlExpressionBuilder createdAt =
new DateTimeSqlExpressionBuilder('created_at');
final DateTimeSqlExpressionBuilder updatedAt =
new DateTimeSqlExpressionBuilder('updated_at');
@override
get expressionBuilders {
return [id, treeId, commonName, createdAt, updatedAt];
}
}
// **************************************************************************
// JsonModelGenerator
// **************************************************************************
@ -49,6 +99,11 @@ class Fruit extends _Fruit {
other.updatedAt == updatedAt;
}
@override
int get hashCode {
return hashObjects([id, treeId, commonName, createdAt, updatedAt]);
}
Map<String, dynamic> toJson() {
return FruitSerializer.toMap(this);
}

View file

@ -8,7 +8,7 @@ part 'leg.g.dart';
part 'leg.serializer.g.dart';
@serializable
@postgreSqlOrm
@orm
class _Leg extends Model {
@hasOne
Foot foot;

View file

@ -48,6 +48,11 @@ class Leg extends _Leg {
other.updatedAt == updatedAt;
}
@override
int get hashCode {
return hashObjects([id, foot, name, createdAt, updatedAt]);
}
Map<String, dynamic> toJson() {
return LegSerializer.toMap(this);
}

View file

@ -7,7 +7,7 @@ import 'customer.dart';
part 'order.g.dart';
part 'order.serializer.g.dart';
@postgreSqlOrm
@orm
@serializable
class _Order extends Model {
@Join(Customer, CustomerFields.id)

View file

@ -2,6 +2,80 @@
part of angel_orm_generator.test.models.order;
// **************************************************************************
// OrmGenerator
// **************************************************************************
class OrderQuery extends Query<Order, OrderQueryWhere> {
@override
final OrderQueryWhere where = new OrderQueryWhere();
@override
get tableName {
return 'orders';
}
@override
get fields {
return const [
'id',
'customerId',
'employeeId',
'orderDate',
'shipperId',
'createdAt',
'updatedAt'
];
}
@override
deserialize(List row) {
return new Order(
id: (row[0] as String),
customerId: (row[0] as int),
employeeId: (row[0] as int),
orderDate: (row[0] as DateTime),
shipperId: (row[0] as int),
createdAt: (row[0] as DateTime),
updatedAt: (row[0] as DateTime));
}
}
class OrderQueryWhere extends QueryWhere {
final StringSqlExpressionBuilder id = new StringSqlExpressionBuilder('id');
final NumericSqlExpressionBuilder<int> customerId =
new NumericSqlExpressionBuilder<int>('customer_id');
final NumericSqlExpressionBuilder<int> employeeId =
new NumericSqlExpressionBuilder<int>('employee_id');
final DateTimeSqlExpressionBuilder orderDate =
new DateTimeSqlExpressionBuilder('order_date');
final NumericSqlExpressionBuilder<int> shipperId =
new NumericSqlExpressionBuilder<int>('shipper_id');
final DateTimeSqlExpressionBuilder createdAt =
new DateTimeSqlExpressionBuilder('created_at');
final DateTimeSqlExpressionBuilder updatedAt =
new DateTimeSqlExpressionBuilder('updated_at');
@override
get expressionBuilders {
return [
id,
customerId,
employeeId,
orderDate,
shipperId,
createdAt,
updatedAt
];
}
}
// **************************************************************************
// JsonModelGenerator
// **************************************************************************
@ -67,6 +141,19 @@ class Order extends _Order {
other.updatedAt == updatedAt;
}
@override
int get hashCode {
return hashObjects([
id,
customerId,
employeeId,
orderDate,
shipperId,
createdAt,
updatedAt
]);
}
Map<String, dynamic> toJson() {
return OrderSerializer.toMap(this);
}

View file

@ -7,7 +7,7 @@ part 'role.g.dart';
part 'role.serializer.g.dart';
@serializable
@postgreSqlOrm
@orm
class _Role extends Model {
String name;
}

View file

@ -2,6 +2,52 @@
part of angel_orm_generator.test.models.role;
// **************************************************************************
// OrmGenerator
// **************************************************************************
class RoleQuery extends Query<Role, RoleQueryWhere> {
@override
final RoleQueryWhere where = new RoleQueryWhere();
@override
get tableName {
return 'roles';
}
@override
get fields {
return const ['id', 'name', 'createdAt', 'updatedAt'];
}
@override
deserialize(List row) {
return new Role(
id: (row[0] as String),
name: (row[0] as String),
createdAt: (row[0] as DateTime),
updatedAt: (row[0] as DateTime));
}
}
class RoleQueryWhere extends QueryWhere {
final StringSqlExpressionBuilder id = new StringSqlExpressionBuilder('id');
final StringSqlExpressionBuilder name =
new StringSqlExpressionBuilder('name');
final DateTimeSqlExpressionBuilder createdAt =
new DateTimeSqlExpressionBuilder('created_at');
final DateTimeSqlExpressionBuilder updatedAt =
new DateTimeSqlExpressionBuilder('updated_at');
@override
get expressionBuilders {
return [id, name, createdAt, updatedAt];
}
}
// **************************************************************************
// JsonModelGenerator
// **************************************************************************
@ -39,6 +85,11 @@ class Role extends _Role {
other.updatedAt == updatedAt;
}
@override
int get hashCode {
return hashObjects([id, name, createdAt, updatedAt]);
}
Map<String, dynamic> toJson() {
return RoleSerializer.toMap(this);
}

View file

@ -8,7 +8,7 @@ part 'tree.g.dart';
part 'tree.serializer.g.dart';
@serializable
@postgreSqlOrm
@orm
class _Tree extends Model {
@Column(indexType: IndexType.unique, type: ColumnType.smallInt)
int rings;

View file

@ -55,6 +55,11 @@ class Tree extends _Tree {
other.updatedAt == updatedAt;
}
@override
int get hashCode {
return hashObjects([id, rings, fruits, createdAt, updatedAt]);
}
Map<String, dynamic> toJson() {
return TreeSerializer.toMap(this);
}

View file

@ -8,7 +8,7 @@ part 'user.g.dart';
part 'user.serializer.g.dart';
@serializable
@postgreSqlOrm
@orm
class _User extends Model {
String username, password, email;

View file

@ -69,6 +69,12 @@ class User extends _User {
other.updatedAt == updatedAt;
}
@override
int get hashCode {
return hashObjects(
[id, username, password, email, roles, createdAt, updatedAt]);
}
Map<String, dynamic> toJson() {
return UserSerializer.toMap(this);
}

View file

@ -5,18 +5,17 @@ import 'package:angel_orm/angel_orm.dart';
import 'package:postgres/postgres.dart';
import 'package:test/test.dart';
import 'models/car.dart';
import 'models/car.orm.g.dart';
import 'common.dart';
final DateTime MILENNIUM = new DateTime.utc(2000, 1, 1);
final DateTime y2k = new DateTime.utc(2000, 1, 1);
main() {
test('to where', () {
var query = new CarQuery();
query.where
..familyFriendly.equals(true)
..recalledAt.lessThanOrEqualTo(MILENNIUM, includeTime: false);
var whereClause = query.where.toWhereClause();
..recalledAt.lessThanOrEqualTo(y2k, includeTime: false);
var whereClause = query.where.compile(tableName: 'cars');
print('Where clause: $whereClause');
expect(whereClause,
'WHERE cars.family_friendly = TRUE AND cars.recalled_at <= \'2000-01-01\'');
@ -28,23 +27,20 @@ main() {
'Mazda',
'CX9',
true,
dateYmdHms.format(MILENNIUM),
dateYmdHms.format(MILENNIUM),
dateYmdHms.format(MILENNIUM)
dateYmdHms.format(y2k),
dateYmdHms.format(y2k),
dateYmdHms.format(y2k)
];
print(row);
var car = PostgreSqlCarOrm.parseRow(row);
var car = new CarQuery().deserialize(row);
print(car.toJson());
expect(car.id, '0');
expect(car.make, 'Mazda');
expect(car.description, 'CX9');
expect(car.familyFriendly, true);
expect(MILENNIUM.toIso8601String(),
startsWith(car.recalledAt.toIso8601String()));
expect(MILENNIUM.toIso8601String(),
startsWith(car.createdAt.toIso8601String()));
expect(MILENNIUM.toIso8601String(),
startsWith(car.updatedAt.toIso8601String()));
expect(y2k.toIso8601String(), startsWith(car.recalledAt.toIso8601String()));
expect(y2k.toIso8601String(), startsWith(car.createdAt.toIso8601String()));
expect(y2k.toIso8601String(), startsWith(car.updatedAt.toIso8601String()));
});
group('queries', () {
@ -56,7 +52,7 @@ main() {
group('selects', () {
test('select all', () async {
var cars = await CarQuery.getAll(connection).toList();
var cars = await CarQuery.getAll(connection);
expect(cars, []);
});
@ -72,12 +68,12 @@ main() {
test('where clause is applied', () async {
var query = new CarQuery()..where.familyFriendly.equals(true);
var cars = await query.get(connection).toList();
var cars = await query.get(connection);
expect(cars, isEmpty);
var sportsCars = new CarQuery()..where.familyFriendly.notEquals(true);
cars = await sportsCars.get(connection).toList();
print(cars.map((c) => c.toJson()).toList());
cars = await sportsCars.get(connection);
print(cars.map((c) => c.toJson()));
var car = cars.first;
expect(car.make, ferrari.make);
@ -93,27 +89,26 @@ main() {
query1
..union(query2)
..unionAll(query3);
print(query1.toSql());
print(query1.compile());
var cars = await query1.get(connection).toList();
var cars = await query1.get(connection);
expect(cars, hasLength(1));
});
test('or clause', () async {
var query = new CarQuery()
..where.make.like('Fer%')
..or(new CarQueryWhere()
..familyFriendly.equals(true)
..make.equals('Honda'));
print(query.toSql());
var cars = await query.get(connection).toList();
..orWhere((where) =>
where..familyFriendly.equals(true)..make.equals('Honda'));
print(query.compile());
var cars = await query.get(connection);
expect(cars, hasLength(1));
});
test('limit obeyed', () async {
var query = new CarQuery()..limit = 0;
print(query.toSql());
var cars = await query.get(connection).toList();
var query = new CarQuery()..limit(0);
print(query.compile());
var cars = await query.get(connection);
expect(cars, isEmpty);
});
@ -126,28 +121,28 @@ main() {
var car = await CarQuery.deleteOne(int.parse(ferrari.id), connection);
expect(car.toJson(), ferrari.toJson());
var cars = await CarQuery.getAll(connection).toList();
var cars = await CarQuery.getAll(connection);
expect(cars, isEmpty);
});
test('delete stream', () async {
var query = new CarQuery()..where.make.equals('Ferrari');
query.or(new CarQueryWhere()..familyFriendly.equals(true));
print(query.toSql('DELETE FROM "cars"'));
var cars = await query.delete(connection).toList();
print(query.compile('DELETE FROM "cars"'));
var cars = await query.delete(connection);
expect(cars, hasLength(1));
expect(cars.first.toJson(), ferrari.toJson());
});
test('update', () async {
var query = new CarQuery()..where.id.equals(int.parse(ferrari.id));
var cars = await query.update(connection, make: 'Hyundai').toList();
var cars = await query.update(connection, make: 'Hyundai');
expect(cars, hasLength(1));
expect(cars.first.make, 'Hyundai');
});
test('update car', () async {
var cloned = ferrari.clone()..make = 'Angel';
var cloned = ferrari.copyWith(make: 'Angel');
var car = await CarQuery.updateCar(connection, cloned);
print(car.toJson());
expect(car.toJson(), cloned.toJson());
@ -166,8 +161,7 @@ main() {
expect(car.make, 'Honda');
expect(car.description, 'Hello');
expect(car.familyFriendly, isTrue);
expect(
dateYmdHms.format(car.recalledAt), dateYmdHms.format(recalledAt));
expect(dateYmdHms.format(car.recalledAt), dateYmdHms.format(recalledAt));
expect(car.createdAt, allOf(isNotNull, equals(car.updatedAt)));
});