@hasOne Done

This commit is contained in:
thosakwe 2017-08-01 01:45:54 -04:00
parent fa3ce3abc8
commit 62a4c15a2b
25 changed files with 850 additions and 93 deletions

View file

@ -0,0 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="tests in has_one_test" type="DartTestRunConfigurationType" factoryName="Dart Test" folderName="leg" singleton="true">
<option name="filePath" value="$PROJECT_DIR$/angel_orm_generator/test/has_one_test.dart" />
<method />
</configuration>
</component>

View file

@ -35,11 +35,17 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
@override
Future<String> generateForAnnotatedElement(
Element element, ORM annotation, BuildStep buildStep) async {
if (buildStep.inputId.path.contains('.orm.g.dart')) {
return null;
}
if (element is! ClassElement)
throw 'Only classes can be annotated with @serializable.';
throw 'Only classes can be annotated with @ORM().';
var resolver = await buildStep.resolver;
return prettyToSource(
generateOrmLibrary(element.library, resolver, buildStep).buildAst());
var lib =
generateOrmLibrary(element.library, resolver, buildStep).buildAst();
if (lib == null) return null;
return prettyToSource(lib);
}
LibraryBuilder generateOrmLibrary(
@ -91,6 +97,8 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
}
}
if (contexts.isEmpty) return null;
done.clear();
for (var element in contexts.keys) {
if (!done.contains(element.name)) {
@ -252,12 +260,10 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
ctx.relationships.forEach((name, r) {
var relationship = ctx.populateRelationship(name);
// TODO: Has one, has many
if (relationship.isBelongsTo) {
var b = new StringBuffer(
' INNER JOIN ${relationship.foreignTable} ON ${ctx.tableName}.${relationship.localKey} = ${relationship.foreignTable}.${relationship.foreignKey}');
relationsIfThen
.addStatement(buf.invoke('write', [literal(b.toString())]));
// TODO: Belongs to many, has many
if (relationship.isSingular) {
String b = ' LEFT OUTER JOIN ${relationship.foreignTable} ON ${ctx.tableName}.${relationship.localKey} = ${relationship.foreignTable}.${relationship.foreignKey}';
relationsIfThen.addStatement(buf.invoke('write', [literal(b)]));
}
});
@ -559,11 +565,8 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
meth.addStatement(varField('whereClause',
value: reference('where').invoke('toWhereClause', [])));
meth.addStatement(ifThen(whereClause.equals(literal(null)), [
$buf.invoke('write', [literal('WHERE "id" = @id')]),
elseThen([
$buf.invoke('write', [whereClause])
])
meth.addStatement(ifThen(whereClause.notEquals(literal(null)), [
$buf.invoke('write', [whereClause])
]));
var buf2 = new StringBuffer();
@ -690,38 +693,37 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
void _applyRelationshipsToOutput(PostgresBuildContext ctx,
ExpressionBuilder output, ExpressionBuilder row, MethodBuilder meth) {
// Every relationship should fill itself in with a query
// TODO: Has one, has many, belongs to many
ctx.relationships.forEach((name, r) {
var relationship = ctx.populateRelationship(name);
if (relationship.isBelongsTo) {
var rc = new ReCase(relationship.isList
? relationship.modelType.name
: relationship.dartType.name);
var type = new TypeBuilder('${rc.pascalCase}Query');
var rc = new ReCase(relationship.isList
? relationship.modelType.name
: relationship.dartType.name);
var type = new TypeBuilder('${rc.pascalCase}Query');
// Resolve index within row...
bool matched = false;
int col = 0;
for (var field in ctx.fields) {
if (field is RelationshipConstraintField &&
field.originalName == name) {
matched = true;
break;
} else
col++;
}
// Resolve index within row...
bool matched = false;
int col = 0;
for (var field in ctx.fields) {
if (field is RelationshipConstraintField &&
field.originalName == name) {
matched = true;
break;
} else
col++;
}
if (!matched) {
matched = ctx.resolveRelationshipField(name) != null;
}
if (!matched) {
matched = ctx.resolveRelationshipField(name) != null;
}
if (!matched)
throw 'Couldn\'t resolve row index for relationship "${name}".';
if (!matched)
throw 'Couldn\'t resolve row index for relationship "${name}".';
var idAsInt = row[literal(col)];
var idAsInt = row[literal(col)];
if (relationship.isSingular) {
if (relationship.isSingular) {
if (relationship.isBelongsTo) {
meth.addStatement(type
.invoke('getOne', [idAsInt, reference('connection')])
.asAwait()
@ -730,20 +732,38 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
var query = reference('${rc.camelCase}Query');
meth.addStatement(
varField('${rc.camelCase}Query', value: type.newInstance([])));
ExpressionBuilder fetched;
// TODO: HasMany
if (relationship.isBelongsTo) {
meth.addStatement(query
.property('where')
.property('id')
.invoke('equals', [idAsInt]));
fetched = query.invoke('get', [reference('connection')]).invoke(
'toList', []).asAwait();
}
meth.addStatement(output.property(name).invoke('addAll', [fetched]));
// Set id to row[0]
meth.addStatement(query
.property('where')
.property('id')
.invoke('equals', [row[literal(0)]]));
var fetched = query
.invoke('get', [reference('connection')])
.property('first')
.invoke('catchError', [
new MethodBuilder.closure(returns: literal(null))
..addPositional(parameter('_'))
])
.asAwait();
meth.addStatement(fetched.asAssign(output.property(name)));
}
} else {
var query = reference('${rc.camelCase}Query');
meth.addStatement(
varField('${rc.camelCase}Query', value: type.newInstance([])));
ExpressionBuilder fetched;
// TODO: HasMany
if (relationship.isBelongsTo) {
meth.addStatement(query
.property('where')
.property('id')
.invoke('equals', [idAsInt]));
fetched = query.invoke('get', [reference('connection')]).invoke(
'toList', []).asAwait();
}
meth.addStatement(output.property(name).invoke('addAll', [fetched]));
}
});
}

View file

@ -109,11 +109,12 @@ class PostgresBuildContext extends BuildContext {
if (relationship.type == RelationshipType.HAS_ONE ||
relationship.type == RelationshipType.HAS_MANY) {
var foreignKey = relationship.localKey ??
var single = singularize(tableName);
var foreignKey = relationship.foreignTable ??
(autoSnakeCaseNames != false
? '${rc.snakeCase}_id'
: '${typeName}Id');
var localKey = relationship.foreignKey ?? 'id';
? '${single}_id'
: '${single}Id');
var localKey = relationship.localKey ?? 'id';
var foreignTable = relationship.foreignTable ??
(autoSnakeCaseNames != false
? pluralize(rc.snakeCase)

View file

@ -1,5 +1,5 @@
name: angel_orm_generator
version: 1.0.0-alpha+2
version: 1.0.0-alpha+3
description: Code generators for Angel's ORM.
author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/orm

View file

@ -12,9 +12,9 @@ main() {
User john;
setUp(() async {
connection = await connectToPostgres();
connection = await connectToPostgres(['user', 'role']);
});
tearDown(() => connection.close());

View file

@ -15,7 +15,7 @@ main() {
Book deathlyHallows;
setUp(() async {
connection = await connectToPostgres();
connection = await connectToPostgres(['author', 'book']);
// Insert an author
rowling = await AuthorQuery.insert(connection, name: 'J.K. Rowling');

View file

@ -2,22 +2,15 @@ import 'dart:async';
import 'dart:io';
import 'package:postgres/postgres.dart';
Future<PostgreSQLConnection> connectToPostgres() async {
Future<PostgreSQLConnection> connectToPostgres(Iterable<String> schemas) async {
var conn = new PostgreSQLConnection('127.0.0.1', 5432, 'angel_orm_test',
username: Platform.environment['POSTGRES_USERNAME'] ?? 'postgres',
password: Platform.environment['POSTGRES_PASSWORD'] ?? 'password');
await conn.open();
var query = await new File('test/models/car.up.g.sql').readAsString();
await conn.execute(query);
query = await new File('test/models/author.up.g.sql').readAsString();
await conn.execute(query);
query = await new File('test/models/book.up.g.sql').readAsString();
await conn.execute(query);
query = await new File('test/models/role.up.g.sql').readAsString();
await conn.execute(query);
query = await new File('test/models/user.up.g.sql').readAsString();
await conn.execute(query);
for (var s in schemas)
await conn
.execute(await new File('test/models/$s.up.g.sql').readAsString());
return conn;
}

View file

@ -0,0 +1,73 @@
/// Tests for @hasOne...
library angel_orm_generator.test.has_one_test;
import 'package:postgres/postgres.dart';
import 'package:test/test.dart';
import 'models/foot.orm.g.dart';
import 'models/leg.dart';
import 'models/leg.orm.g.dart';
import 'common.dart';
main() {
PostgreSQLConnection connection;
Leg originalLeg;
setUp(() async {
connection = await connectToPostgres(['leg', 'foot']);
originalLeg = await LegQuery.insert(connection, name: 'Left');
});
test('sets to null if no child', () async {
var leg = await LegQuery.getOne(int.parse(originalLeg.id), connection);
expect(leg.name, originalLeg.name);
expect(leg.id, originalLeg.id);
expect(leg.foot, isNull);
});
test('can fetch one foot', () async {
var foot = await FootQuery.insert(connection,
legId: int.parse(originalLeg.id), nToes: 5);
var leg = await LegQuery.getOne(int.parse(originalLeg.id), connection);
expect(leg.name, originalLeg.name);
expect(leg.id, originalLeg.id);
expect(leg.foot, isNotNull);
expect(leg.foot.id, foot.id);
expect(leg.foot.nToes, foot.nToes);
});
test('only fetches one foot even if there are multiple', () async {
var foot = await FootQuery.insert(connection,
legId: int.parse(originalLeg.id), nToes: 5);
await FootQuery.insert(connection,
legId: int.parse(originalLeg.id), nToes: 24);
var leg = await LegQuery.getOne(int.parse(originalLeg.id), connection);
expect(leg.name, originalLeg.name);
expect(leg.id, originalLeg.id);
expect(leg.foot, isNotNull);
expect(leg.foot.id, foot.id);
expect(leg.foot.nToes, foot.nToes);
});
test('sets foot on update', () async {
var foot = await FootQuery.insert(connection,
legId: int.parse(originalLeg.id), nToes: 5);
var leg = await LegQuery.updateLeg(
connection, originalLeg.clone()..name = 'Right');
print(leg.toJson());
expect(leg.name, 'Right');
expect(leg.foot, isNotNull);
expect(leg.foot.id, foot.id);
expect(leg.foot.nToes, foot.nToes);
});
test('sets foot on delete', () async {
var foot = await FootQuery.insert(connection,
legId: int.parse(originalLeg.id), nToes: 5);
var leg = await LegQuery.deleteOne(int.parse(originalLeg.id), connection);
print(leg.toJson());
expect(leg.name, originalLeg.name);
expect(leg.foot, isNotNull);
expect(leg.foot.id, foot.id);
expect(leg.foot.nToes, foot.nToes);
});
}

View file

@ -125,9 +125,7 @@ class AuthorQuery {
var buf = new StringBuffer(
'UPDATE "authors" SET ("name", "created_at", "updated_at") = (@name, @createdAt, @updatedAt) ');
var whereClause = where.toWhereClause();
if (whereClause == null) {
buf.write('WHERE "id" = @id');
} else {
if (whereClause != null) {
buf.write(whereClause);
}
var __ormNow__ = new DateTime.now();

View file

@ -54,7 +54,7 @@ class BookQuery {
? prefix
: 'SELECT books.id, books.name, books.created_at, books.updated_at, books.author_id, authors.id, authors.name, authors.created_at, authors.updated_at FROM "books"');
if (prefix == null) {
buf.write(' INNER JOIN authors ON books.author_id = authors.id');
buf.write(' LEFT OUTER JOIN authors ON books.author_id = authors.id');
}
var whereClause = where.toWhereClause();
if (whereClause != null) {
@ -133,9 +133,7 @@ class BookQuery {
var buf = new StringBuffer(
'UPDATE "books" SET ("name", "created_at", "updated_at", "author_id") = (@name, @createdAt, @updatedAt, @authorId) ');
var whereClause = where.toWhereClause();
if (whereClause == null) {
buf.write('WHERE "id" = @id');
} else {
if (whereClause != null) {
buf.write(whereClause);
}
var __ormNow__ = new DateTime.now();

View file

@ -133,9 +133,7 @@ class CarQuery {
var buf = new StringBuffer(
'UPDATE "cars" SET ("make", "description", "family_friendly", "recalled_at", "created_at", "updated_at") = (@make, @description, @familyFriendly, @recalledAt, @createdAt, @updatedAt) ');
var whereClause = where.toWhereClause();
if (whereClause == null) {
buf.write('WHERE "id" = @id');
} else {
if (whereClause != null) {
buf.write(whereClause);
}
var __ormNow__ = new DateTime.now();

View file

@ -0,0 +1,12 @@
library angel_orm_generator.test.models.foot;
import 'package:angel_model/angel_model.dart';
import 'package:angel_orm/angel_orm.dart';
import 'package:angel_serialize/angel_serialize.dart';
part 'foot.g.dart';
@serializable
@orm
class _Foot extends Model {
int legId, nToes;
}

View file

@ -0,0 +1 @@
DROP TABLE "foots";

View file

@ -0,0 +1,58 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of angel_orm_generator.test.models.foot;
// **************************************************************************
// Generator: JsonModelGenerator
// Target: class _Foot
// **************************************************************************
class Foot extends _Foot {
@override
String id;
@override
int legId;
@override
int nToes;
@override
DateTime createdAt;
@override
DateTime updatedAt;
Foot({this.id, this.legId, this.nToes, this.createdAt, this.updatedAt});
factory Foot.fromJson(Map data) {
return new Foot(
id: data['id'],
legId: data['leg_id'],
nToes: data['n_toes'],
createdAt: data['created_at'] is DateTime
? data['created_at']
: (data['created_at'] is String
? DateTime.parse(data['created_at'])
: null),
updatedAt: data['updated_at'] is DateTime
? data['updated_at']
: (data['updated_at'] is String
? DateTime.parse(data['updated_at'])
: null));
}
Map<String, dynamic> toJson() => {
'id': id,
'leg_id': legId,
'n_toes': nToes,
'created_at': createdAt == null ? null : createdAt.toIso8601String(),
'updated_at': updatedAt == null ? null : updatedAt.toIso8601String()
};
static Foot parse(Map map) => new Foot.fromJson(map);
Foot clone() {
return new Foot.fromJson(toJson());
}
}

View file

@ -0,0 +1,253 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// **************************************************************************
// Generator: PostgresORMGenerator
// Target: class _Foot
// **************************************************************************
import 'dart:async';
import 'package:angel_orm/angel_orm.dart';
import 'package:postgres/postgres.dart';
import 'foot.dart';
class FootQuery {
final Map<FootQuery, bool> _unions = {};
String _sortKey;
String _sortMode;
int limit;
int offset;
final List<FootQueryWhere> _or = [];
final FootQueryWhere where = new FootQueryWhere();
void union(FootQuery query) {
_unions[query] = false;
}
void unionAll(FootQuery query) {
_unions[query] = true;
}
void sortDescending(String key) {
_sortMode = 'Descending';
_sortKey = ('' + key);
}
void sortAscending(String key) {
_sortMode = 'Ascending';
_sortKey = ('' + key);
}
void or(FootQueryWhere selector) {
_or.add(selector);
}
String toSql([String prefix]) {
var buf = new StringBuffer();
buf.write(prefix != null
? prefix
: 'SELECT id, leg_id, n_toes, created_at, updated_at FROM "foots"');
if (prefix == null) {}
var whereClause = where.toWhereClause();
if (whereClause != null) {
buf.write(' ' + whereClause);
}
_or.forEach((x) {
var whereClause = x.toWhereClause(keyword: false);
if (whereClause != null) {
buf.write(' OR (' + whereClause + ')');
}
});
if (prefix == null) {
if (limit != null) {
buf.write(' LIMIT ' + limit.toString());
}
if (offset != null) {
buf.write(' OFFSET ' + offset.toString());
}
if (_sortMode == 'Descending') {
buf.write(' ORDER BY "' + _sortKey + '" DESC');
}
if (_sortMode == 'Ascending') {
buf.write(' ORDER BY "' + _sortKey + '" ASC');
}
_unions.forEach((query, all) {
buf.write(' UNION');
if (all) {
buf.write(' ALL');
}
buf.write(' (');
var sql = query.toSql().replaceAll(';', '');
buf.write(sql + ')');
});
buf.write(';');
}
return buf.toString();
}
static Foot parseRow(List row) {
var result = new Foot.fromJson({
'id': row[0].toString(),
'leg_id': row[1],
'n_toes': row[2],
'created_at': row[3],
'updated_at': row[4]
});
return result;
}
Stream<Foot> get(PostgreSQLConnection connection) {
StreamController<Foot> ctrl = new StreamController<Foot>();
connection.query(toSql()).then((rows) async {
var futures = rows.map((row) async {
var parsed = parseRow(row);
return parsed;
});
var output = await Future.wait(futures);
output.forEach(ctrl.add);
ctrl.close();
}).catchError(ctrl.addError);
return ctrl.stream;
}
static Future<Foot> getOne(int id, PostgreSQLConnection connection) {
var query = new FootQuery();
query.where.id.equals(id);
return query.get(connection).first.catchError((_) => null);
}
Stream<Foot> update(PostgreSQLConnection connection,
{int legId, int nToes, DateTime createdAt, DateTime updatedAt}) {
var buf = new StringBuffer(
'UPDATE "foots" SET ("leg_id", "n_toes", "created_at", "updated_at") = (@legId, @nToes, @createdAt, @updatedAt) ');
var whereClause = where.toWhereClause();
if (whereClause != null) {
buf.write(whereClause);
}
var __ormNow__ = new DateTime.now();
var ctrl = new StreamController<Foot>();
connection.query(
buf.toString() +
' RETURNING "id", "leg_id", "n_toes", "created_at", "updated_at";',
substitutionValues: {
'legId': legId,
'nToes': nToes,
'createdAt': createdAt != null ? createdAt : __ormNow__,
'updatedAt': updatedAt != null ? updatedAt : __ormNow__
}).then((rows) async {
var futures = rows.map((row) async {
var parsed = parseRow(row);
return parsed;
});
var output = await Future.wait(futures);
output.forEach(ctrl.add);
ctrl.close();
}).catchError(ctrl.addError);
return ctrl.stream;
}
Stream<Foot> delete(PostgreSQLConnection connection) {
StreamController<Foot> ctrl = new StreamController<Foot>();
connection
.query(toSql('DELETE FROM "foots"') +
' RETURNING "id", "leg_id", "n_toes", "created_at", "updated_at";')
.then((rows) async {
var futures = rows.map((row) async {
var parsed = parseRow(row);
return parsed;
});
var output = await Future.wait(futures);
output.forEach(ctrl.add);
ctrl.close();
}).catchError(ctrl.addError);
return ctrl.stream;
}
static Future<Foot> deleteOne(int id, PostgreSQLConnection connection) {
var query = new FootQuery();
query.where.id.equals(id);
return query.delete(connection).first;
}
static Future<Foot> insert(PostgreSQLConnection connection,
{int legId, int nToes, DateTime createdAt, DateTime updatedAt}) async {
var __ormNow__ = new DateTime.now();
var result = await connection.query(
'INSERT INTO "foots" ("leg_id", "n_toes", "created_at", "updated_at") VALUES (@legId, @nToes, @createdAt, @updatedAt) RETURNING "id", "leg_id", "n_toes", "created_at", "updated_at";',
substitutionValues: {
'legId': legId,
'nToes': nToes,
'createdAt': createdAt != null ? createdAt : __ormNow__,
'updatedAt': updatedAt != null ? updatedAt : __ormNow__
});
var output = parseRow(result[0]);
return output;
}
static Future<Foot> insertFoot(PostgreSQLConnection connection, Foot foot) {
return FootQuery.insert(connection,
legId: foot.legId,
nToes: foot.nToes,
createdAt: foot.createdAt,
updatedAt: foot.updatedAt);
}
static Future<Foot> updateFoot(PostgreSQLConnection connection, Foot foot) {
var query = new FootQuery();
query.where.id.equals(int.parse(foot.id));
return query
.update(connection,
legId: foot.legId,
nToes: foot.nToes,
createdAt: foot.createdAt,
updatedAt: foot.updatedAt)
.first;
}
static Stream<Foot> getAll(PostgreSQLConnection connection) =>
new FootQuery().get(connection);
}
class FootQueryWhere {
final NumericSqlExpressionBuilder<int> id =
new NumericSqlExpressionBuilder<int>();
final NumericSqlExpressionBuilder<int> legId =
new NumericSqlExpressionBuilder<int>();
final NumericSqlExpressionBuilder<int> nToes =
new NumericSqlExpressionBuilder<int>();
final DateTimeSqlExpressionBuilder createdAt =
new DateTimeSqlExpressionBuilder('foots.created_at');
final DateTimeSqlExpressionBuilder updatedAt =
new DateTimeSqlExpressionBuilder('foots.updated_at');
String toWhereClause({bool keyword}) {
final List<String> expressions = [];
if (id.hasValue) {
expressions.add('foots.id ' + id.compile());
}
if (legId.hasValue) {
expressions.add('foots.leg_id ' + legId.compile());
}
if (nToes.hasValue) {
expressions.add('foots.n_toes ' + nToes.compile());
}
if (createdAt.hasValue) {
expressions.add(createdAt.compile());
}
if (updatedAt.hasValue) {
expressions.add(updatedAt.compile());
}
return expressions.isEmpty
? null
: ((keyword != false ? 'WHERE ' : '') + expressions.join(' AND '));
}
}

View file

@ -0,0 +1,8 @@
CREATE TEMPORARY TABLE "foots" (
"id" serial,
"leg_id" int,
"n_toes" int,
"created_at" timestamp,
"updated_at" timestamp,
PRIMARY KEY(id)
);

View file

@ -0,0 +1,16 @@
library angel_orm_generator.test.models.leg;
import 'package:angel_model/angel_model.dart';
import 'package:angel_orm/angel_orm.dart';
import 'package:angel_serialize/angel_serialize.dart';
import 'foot.dart';
part 'leg.g.dart';
@serializable
@orm
class _Leg extends Model {
@hasOne
Foot foot;
String name;
}

View file

@ -0,0 +1 @@
DROP TABLE "legs";

View file

@ -0,0 +1,58 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of angel_orm_generator.test.models.leg;
// **************************************************************************
// Generator: JsonModelGenerator
// Target: class _Leg
// **************************************************************************
class Leg extends _Leg {
@override
String id;
@override
dynamic foot;
@override
String name;
@override
DateTime createdAt;
@override
DateTime updatedAt;
Leg({this.id, this.foot, this.name, this.createdAt, this.updatedAt});
factory Leg.fromJson(Map data) {
return new Leg(
id: data['id'],
foot: data['foot'],
name: data['name'],
createdAt: data['created_at'] is DateTime
? data['created_at']
: (data['created_at'] is String
? DateTime.parse(data['created_at'])
: null),
updatedAt: data['updated_at'] is DateTime
? data['updated_at']
: (data['updated_at'] is String
? DateTime.parse(data['updated_at'])
: null));
}
Map<String, dynamic> toJson() => {
'id': id,
'foot': foot,
'name': name,
'created_at': createdAt == null ? null : createdAt.toIso8601String(),
'updated_at': updatedAt == null ? null : updatedAt.toIso8601String()
};
static Leg parse(Map map) => new Leg.fromJson(map);
Leg clone() {
return new Leg.fromJson(toJson());
}
}

View file

@ -0,0 +1,258 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// **************************************************************************
// Generator: PostgresORMGenerator
// Target: class _Leg
// **************************************************************************
import 'dart:async';
import 'package:angel_orm/angel_orm.dart';
import 'package:postgres/postgres.dart';
import 'leg.dart';
import 'foot.orm.g.dart';
class LegQuery {
final Map<LegQuery, bool> _unions = {};
String _sortKey;
String _sortMode;
int limit;
int offset;
final List<LegQueryWhere> _or = [];
final LegQueryWhere where = new LegQueryWhere();
void union(LegQuery query) {
_unions[query] = false;
}
void unionAll(LegQuery query) {
_unions[query] = true;
}
void sortDescending(String key) {
_sortMode = 'Descending';
_sortKey = ('legs.' + key);
}
void sortAscending(String key) {
_sortMode = 'Ascending';
_sortKey = ('legs.' + key);
}
void or(LegQueryWhere selector) {
_or.add(selector);
}
String toSql([String prefix]) {
var buf = new StringBuffer();
buf.write(prefix != null
? prefix
: 'SELECT legs.id, legs.name, legs.created_at, legs.updated_at, foots.id, foots.leg_id, foots.n_toes, foots.created_at, foots.updated_at FROM "legs"');
if (prefix == null) {
buf.write(' LEFT OUTER JOIN foots ON legs.id = foots.leg_id');
}
var whereClause = where.toWhereClause();
if (whereClause != null) {
buf.write(' ' + whereClause);
}
_or.forEach((x) {
var whereClause = x.toWhereClause(keyword: false);
if (whereClause != null) {
buf.write(' OR (' + whereClause + ')');
}
});
if (prefix == null) {
if (limit != null) {
buf.write(' LIMIT ' + limit.toString());
}
if (offset != null) {
buf.write(' OFFSET ' + offset.toString());
}
if (_sortMode == 'Descending') {
buf.write(' ORDER BY "' + _sortKey + '" DESC');
}
if (_sortMode == 'Ascending') {
buf.write(' ORDER BY "' + _sortKey + '" ASC');
}
_unions.forEach((query, all) {
buf.write(' UNION');
if (all) {
buf.write(' ALL');
}
buf.write(' (');
var sql = query.toSql().replaceAll(';', '');
buf.write(sql + ')');
});
buf.write(';');
}
return buf.toString();
}
static Leg parseRow(List row) {
var result = new Leg.fromJson({
'id': row[0].toString(),
'name': row[1],
'created_at': row[2],
'updated_at': row[3]
});
if (row.length > 4) {
result.foot =
FootQuery.parseRow([row[4], row[5], row[6], row[7], row[8]]);
}
return result;
}
Stream<Leg> get(PostgreSQLConnection connection) {
StreamController<Leg> ctrl = new StreamController<Leg>();
connection.query(toSql()).then((rows) async {
var futures = rows.map((row) async {
var parsed = parseRow(row);
var footQuery = new FootQuery();
footQuery.where.id.equals(row[0]);
parsed.foot =
await footQuery.get(connection).first.catchError((_) => null);
return parsed;
});
var output = await Future.wait(futures);
output.forEach(ctrl.add);
ctrl.close();
}).catchError(ctrl.addError);
return ctrl.stream;
}
static Future<Leg> getOne(int id, PostgreSQLConnection connection) {
var query = new LegQuery();
query.where.id.equals(id);
return query.get(connection).first.catchError((_) => null);
}
Stream<Leg> update(PostgreSQLConnection connection,
{String name, DateTime createdAt, DateTime updatedAt}) {
var buf = new StringBuffer(
'UPDATE "legs" SET ("name", "created_at", "updated_at") = (@name, @createdAt, @updatedAt) ');
var whereClause = where.toWhereClause();
if (whereClause != null) {
buf.write(whereClause);
}
var __ormNow__ = new DateTime.now();
var ctrl = new StreamController<Leg>();
connection.query(
buf.toString() + ' RETURNING "id", "name", "created_at", "updated_at";',
substitutionValues: {
'name': name,
'createdAt': createdAt != null ? createdAt : __ormNow__,
'updatedAt': updatedAt != null ? updatedAt : __ormNow__
}).then((rows) async {
var futures = rows.map((row) async {
var parsed = parseRow(row);
var footQuery = new FootQuery();
footQuery.where.id.equals(row[0]);
parsed.foot =
await footQuery.get(connection).first.catchError((_) => null);
return parsed;
});
var output = await Future.wait(futures);
output.forEach(ctrl.add);
ctrl.close();
}).catchError(ctrl.addError);
return ctrl.stream;
}
Stream<Leg> delete(PostgreSQLConnection connection) {
StreamController<Leg> ctrl = new StreamController<Leg>();
connection
.query(toSql('DELETE FROM "legs"') +
' RETURNING "id", "name", "created_at", "updated_at";')
.then((rows) async {
var futures = rows.map((row) async {
var parsed = parseRow(row);
var footQuery = new FootQuery();
footQuery.where.id.equals(row[0]);
parsed.foot =
await footQuery.get(connection).first.catchError((_) => null);
return parsed;
});
var output = await Future.wait(futures);
output.forEach(ctrl.add);
ctrl.close();
}).catchError(ctrl.addError);
return ctrl.stream;
}
static Future<Leg> deleteOne(int id, PostgreSQLConnection connection) {
var query = new LegQuery();
query.where.id.equals(id);
return query.delete(connection).first;
}
static Future<Leg> insert(PostgreSQLConnection connection,
{String name, DateTime createdAt, DateTime updatedAt}) async {
var __ormNow__ = new DateTime.now();
var result = await connection.query(
'INSERT INTO "legs" ("name", "created_at", "updated_at") VALUES (@name, @createdAt, @updatedAt) RETURNING "id", "name", "created_at", "updated_at";',
substitutionValues: {
'name': name,
'createdAt': createdAt != null ? createdAt : __ormNow__,
'updatedAt': updatedAt != null ? updatedAt : __ormNow__
});
var output = parseRow(result[0]);
var footQuery = new FootQuery();
footQuery.where.id.equals(result[0][0]);
output.foot = await footQuery.get(connection).first.catchError((_) => null);
return output;
}
static Future<Leg> insertLeg(PostgreSQLConnection connection, Leg leg) {
return LegQuery.insert(connection,
name: leg.name, createdAt: leg.createdAt, updatedAt: leg.updatedAt);
}
static Future<Leg> updateLeg(PostgreSQLConnection connection, Leg leg) {
var query = new LegQuery();
query.where.id.equals(int.parse(leg.id));
return query
.update(connection,
name: leg.name, createdAt: leg.createdAt, updatedAt: leg.updatedAt)
.first;
}
static Stream<Leg> getAll(PostgreSQLConnection connection) =>
new LegQuery().get(connection);
}
class LegQueryWhere {
final NumericSqlExpressionBuilder<int> id =
new NumericSqlExpressionBuilder<int>();
final StringSqlExpressionBuilder name = new StringSqlExpressionBuilder();
final DateTimeSqlExpressionBuilder createdAt =
new DateTimeSqlExpressionBuilder('legs.created_at');
final DateTimeSqlExpressionBuilder updatedAt =
new DateTimeSqlExpressionBuilder('legs.updated_at');
String toWhereClause({bool keyword}) {
final List<String> expressions = [];
if (id.hasValue) {
expressions.add('legs.id ' + id.compile());
}
if (name.hasValue) {
expressions.add('legs.name ' + name.compile());
}
if (createdAt.hasValue) {
expressions.add(createdAt.compile());
}
if (updatedAt.hasValue) {
expressions.add(updatedAt.compile());
}
return expressions.isEmpty
? null
: ((keyword != false ? 'WHERE ' : '') + expressions.join(' AND '));
}
}

View file

@ -0,0 +1,7 @@
CREATE TEMPORARY TABLE "legs" (
"id" serial,
"name" varchar,
"created_at" timestamp,
"updated_at" timestamp,
PRIMARY KEY(id)
);

View file

@ -125,9 +125,7 @@ class RoleQuery {
var buf = new StringBuffer(
'UPDATE "roles" SET ("name", "created_at", "updated_at") = (@name, @createdAt, @updatedAt) ');
var whereClause = where.toWhereClause();
if (whereClause == null) {
buf.write('WHERE "id" = @id');
} else {
if (whereClause != null) {
buf.write(whereClause);
}
var __ormNow__ = new DateTime.now();

View file

@ -53,9 +53,7 @@ class UserQuery {
buf.write(prefix != null
? prefix
: 'SELECT users.id, users.username, users.password, users.email, users.created_at, users.updated_at, roles.id, roles.name, roles.created_at, roles.updated_at FROM "users"');
if (prefix == null) {
buf.write(' INNER JOIN roles ON users.role_id = roles.id');
}
if (prefix == null) {}
var whereClause = where.toWhereClause();
if (whereClause != null) {
buf.write(' ' + whereClause);
@ -140,9 +138,7 @@ class UserQuery {
var buf = new StringBuffer(
'UPDATE "users" SET ("username", "password", "email", "created_at", "updated_at") = (@username, @password, @email, @createdAt, @updatedAt) ');
var whereClause = where.toWhereClause();
if (whereClause == null) {
buf.write('WHERE "id" = @id');
} else {
if (whereClause != null) {
buf.write(whereClause);
}
var __ormNow__ = new DateTime.now();

View file

@ -51,7 +51,7 @@ main() {
PostgreSQLConnection connection;
setUp(() async {
connection = await connectToPostgres();
connection = await connectToPostgres(['car']);
});
group('selects', () {

View file

@ -6,12 +6,16 @@ import 'package:angel_serialize_generator/angel_serialize_generator.dart';
final InputSet ALL_MODELS =
new InputSet('angel_orm_generator', const ['test/models/*.dart']);
final InputSet STANDALONE_MODELS = new InputSet('angel_orm_generator', const [
'test/models/car.dart',
'test/models/author.dart',
'test/models/car.dart',
'test/models/foot.dart',
'test/models/role.dart'
]);
final InputSet DEPENDENT_MODELS = new InputSet('angel_orm_generator',
const ['test/models/book.dart', 'test/models/user.dart']);
final InputSet DEPENDENT_MODELS = new InputSet('angel_orm_generator', const [
'test/models/book.dart',
'test/models/leg.dart',
'test/models/user.dart'
]);
final PhaseGroup PHASES = new PhaseGroup()
..addPhase(new Phase()