diff --git a/angel_orm/CHANGELOG.md b/angel_orm/CHANGELOG.md index 428d192f..b30d3e50 100644 --- a/angel_orm/CHANGELOG.md +++ b/angel_orm/CHANGELOG.md @@ -1,3 +1,13 @@ +# 2.0.0-dev.9 +* Permanent preamble fix + +# 2.0.0-dev.8 +* Escapes + +# 2.0.0-dev.7 +* Update `toSql` +* Add `isTrue` and `isFalse` + # 2.0.0-dev.6 * Add `delete`, `insert` and `update` methods to `Query`. diff --git a/angel_orm/example/main.dart b/angel_orm/example/main.dart index be3c28ad..19250a97 100644 --- a/angel_orm/example/main.dart +++ b/angel_orm/example/main.dart @@ -19,7 +19,7 @@ class _FakeExecutor extends QueryExecutor { const _FakeExecutor(); @override - Future> query(String query, returningFields) async { + Future> query(String query, [returningFields]) async { var now = new DateTime.now(); print('_FakeExecutor received query: $query'); return [ diff --git a/angel_orm/lib/src/builder.dart b/angel_orm/lib/src/builder.dart index 6d809f19..82795519 100644 --- a/angel_orm/lib/src/builder.dart +++ b/angel_orm/lib/src/builder.dart @@ -223,6 +223,10 @@ class BooleanSqlExpressionBuilder implements SqlExpressionBuilder { return '$_op $v'; } + Null get isTrue => equals(true); + + Null get isFalse => equals(false); + void equals(bool value) { _change('=', value); } diff --git a/angel_orm/lib/src/query.dart b/angel_orm/lib/src/query.dart index 38b9e589..53ca8075 100644 --- a/angel_orm/lib/src/query.dart +++ b/angel_orm/lib/src/query.dart @@ -1,7 +1,10 @@ import 'dart:async'; +import 'package:charcode/ascii.dart'; import 'annotations.dart'; import 'builder.dart'; +bool isAscii(int ch) => ch >= $nul && ch <= $del; + /// A base class for objects that compile to SQL queries, typically within an ORM. abstract class QueryBase { /// The list of fields returned by this query. @@ -18,9 +21,7 @@ abstract class QueryBase { Future> get(QueryExecutor executor) async { var sql = compile(); - return executor - .query(sql, fields) - .then((it) => it.map(deserialize).toList()); + return executor.query(sql).then((it) => it.map(deserialize).toList()); } Future getOne(QueryExecutor executor) { @@ -47,14 +48,33 @@ class OrderBy { String toSql(Object obj) { if (obj is DateTime) { - return dateYmdHms.format(obj); + return "'${dateYmdHms.format(obj)}'"; } else if (obj is bool) { return obj ? 'TRUE' : 'FALSE'; } else if (obj == null) { return 'NULL'; } else if (obj is String) { - // TODO: Proper escapes - return obj; + var b = new StringBuffer(); + var it = obj.runes.iterator; + + while (it.moveNext()) { + if (it.current == $nul) + continue; // Skip null byte + else if (isAscii(it.current)) { + b.writeCharCode(it.current); + } else if (it.currentSize == 1) { + b.write('\\u'); + b.write(it.current.toRadixString(16).padLeft(4, '0')); + } else if (it.currentSize == 2) { + b.write('\\U'); + b.write(it.current.toRadixString(16).padLeft(8, '0')); + } else { + throw new UnsupportedError( + 'toSql() cannot encode a rune of size (${it.currentSize})'); + } + } + + return "'$b'"; } else { return obj.toString(); } @@ -174,7 +194,8 @@ abstract class Query extends QueryBase { @override String compile({bool includeTableName: false, String preamble}) { - var b = new StringBuffer(preamble ?? 'SELECT '); + var b = new StringBuffer(preamble ?? 'SELECT'); + b.write(' '); var f = fields ?? ['*']; if (includeTableName) f = f.map((s) => '$tableName.$s').toList(); b.write(f.join(', ')); @@ -210,21 +231,19 @@ abstract class Query extends QueryBase { } Future insert(QueryExecutor executor) { - var sql = new StringBuffer('INSERT INTO $tableName ($fieldSet)'); - var valuesClause = values.compileForInsert(); + var sql = values.compileInsert(tableName); - if (valuesClause == null) { + if (sql == null) { throw new StateError('No values have been specified for update.'); } else { - sql.write(' $valuesClause'); return executor - .query(sql.toString(), fields) + .query(sql, fields) .then((it) => it.isEmpty ? null : deserialize(it.first)); } } Future> update(QueryExecutor executor) async { - var sql = new StringBuffer('UPDATE $tableName'); + var sql = new StringBuffer('UPDATE $tableName '); var valuesClause = values.compileForUpdate(); if (valuesClause == null) { @@ -249,10 +268,12 @@ abstract class Query extends QueryBase { abstract class QueryValues { Map toMap(); - String compileForInsert() { + String compileInsert(String tableName) { var data = toMap(); if (data.isEmpty) return null; - var b = new StringBuffer('VALUES ('); + + var fieldSet = data.keys.join(', '); + var b = new StringBuffer('INSERT INTO $tableName ($fieldSet) VALUES ('); int i = 0; for (var entry in data.entries) { @@ -318,7 +339,12 @@ abstract class QueryWhere { if (tableName != null) key = '$tableName.$key'; if (builder.hasValue) { if (i++ > 0) b.write(' AND '); - b.write('$key ${builder.compile()}'); + if (builder is DateTimeSqlExpressionBuilder) { + if (tableName != null) b.write('$tableName.'); + b.write(builder.compile()); + } else { + b.write('$key ${builder.compile()}'); + } } } @@ -411,5 +437,5 @@ class JoinOn { abstract class QueryExecutor { const QueryExecutor(); - Future> query(String query, List returningFields); + Future> query(String query, [List returningFields]); } diff --git a/angel_orm/pubspec.yaml b/angel_orm/pubspec.yaml index e64e1254..37cb57db 100644 --- a/angel_orm/pubspec.yaml +++ b/angel_orm/pubspec.yaml @@ -1,11 +1,12 @@ name: angel_orm -version: 2.0.0-dev.6 +version: 2.0.0-dev.9 description: Runtime support for Angel's ORM. Includes base classes for queries. author: Tobe O homepage: https://github.com/angel-dart/orm environment: sdk: '>=2.0.0-dev.1.2 <3.0.0' dependencies: + charcode: ^1.0.0 intl: ^0.15.7 meta: ^1.0.0 string_scanner: ^1.0.0 @@ -13,4 +14,5 @@ dev_dependencies: angel_model: ^1.0.0 angel_serialize: ^2.0.0 angel_serialize_generator: ^2.0.0 - build_runner: ^1.0.0 \ No newline at end of file + build_runner: ^1.0.0 + test: ^1.0.0 \ No newline at end of file diff --git a/angel_orm/test/to_sql_test.dart b/angel_orm/test/to_sql_test.dart new file mode 100644 index 00000000..8e2a6522 --- /dev/null +++ b/angel_orm/test/to_sql_test.dart @@ -0,0 +1,17 @@ +import 'package:angel_orm/angel_orm.dart'; +import 'package:test/test.dart'; + +void main() { + test('simple', () { + expect(toSql('ABC _!'), "'ABC _!'"); + }); + + test('ignores null byte', () { + expect(toSql('a\x00bc'), "'abc'"); + }); + + test('unicode', () { + expect(toSql('東'), r"'\u6771'"); + expect(toSql('𐐀'), r"'\U00010400'"); + }); +} diff --git a/angel_orm_generator/lib/src/orm_generator.dart b/angel_orm_generator/lib/src/orm_generator.dart index a2713f35..38bf3696 100644 --- a/angel_orm_generator/lib/src/orm_generator.dart +++ b/angel_orm_generator/lib/src/orm_generator.dart @@ -48,6 +48,7 @@ class OrmGenerator extends GeneratorForAnnotation { // Create `FooQueryWhere` class lib.body.add(buildQueryClass(ctx)); lib.body.add(buildWhereClass(ctx)); + lib.body.add(buildValuesClass(ctx)); }); } @@ -68,6 +69,17 @@ class OrmGenerator extends GeneratorForAnnotation { ]); }); + // Add values + clazz.fields.add(new Field((b) { + var type = refer('${rc.pascalCase}QueryValues'); + b + ..name = 'values' + ..modifier = FieldModifier.final$ + ..annotations.add(refer('override')) + ..type = type + ..assignment = type.newInstance([]).code; + })); + // Add tableName clazz.methods.add(new Method((m) { m @@ -101,6 +113,15 @@ class OrmGenerator extends GeneratorForAnnotation { ..assignment = queryWhereType.newInstance([]).code; })); + clazz.methods.add(new Method((b) { + b + ..name = 'newWhereClause' + ..annotations.add(refer('override')) + ..returns = queryWhereType + ..body = new Block( + (b) => b.addExpression(queryWhereType.newInstance([]).returned)); + })); + // Add deserialize() clazz.methods.add(new Method((m) { m @@ -193,4 +214,66 @@ class OrmGenerator extends GeneratorForAnnotation { } }); } + + Class buildValuesClass(OrmBuildContext ctx) { + return new Class((clazz) { + var rc = ctx.buildContext.modelClassNameRecase; + clazz + ..name = '${rc.pascalCase}QueryValues' + ..extend = refer('MapQueryValues'); + + // Each field generates a getter for setter + for (var field in ctx.buildContext.fields) { + var name = ctx.buildContext.resolveFieldName(field.name); + var type = isSpecialId(field) + ? refer('int') + : convertTypeReference(field.type); + + clazz.methods.add(new Method((b) { + b + ..name = field.name + ..type = MethodType.getter + ..returns = type + ..body = new Block((b) => b.addExpression( + refer('values').index(literalString(name)).asA(type).returned)); + })); + + clazz.methods.add(new Method((b) { + b + ..name = field.name + ..type = MethodType.setter + ..returns = refer('void') + ..requiredParameters.add(new Parameter((b) => b + ..name = 'value' + ..type = type)) + ..body = refer('values') + .index(literalString(name)) + .assign(refer('value')) + .code; + })); + } + + // Add an copyFrom(model) + clazz.methods.add(new Method((b) { + b + ..name = 'copyFrom' + ..returns = refer('void') + ..requiredParameters.add(new Parameter((b) => b + ..name = 'model' + ..type = ctx.buildContext.modelClassType)) + ..body = new Block((b) { + var args = {}; + + for (var field in ctx.buildContext.fields) { + if (isSpecialId(field)) continue; + args[ctx.buildContext.resolveFieldName(field.name)] = + refer('model').property(field.name); + } + + b.addExpression( + refer('values').property('addAll').call([literalMap(args)])); + }); + })); + }); + } } diff --git a/angel_orm_generator/pubspec.yaml b/angel_orm_generator/pubspec.yaml index 1b3ae2d6..e53e8702 100644 --- a/angel_orm_generator/pubspec.yaml +++ b/angel_orm_generator/pubspec.yaml @@ -1,6 +1,6 @@ name: angel_orm_generator -version: 2.0.0-dev -description: Code generators for Angel's ORM. +version: 2.0.0-alpha +description: Code generators for Angel's ORM. Generates query builder classes. author: Tobe O homepage: https://github.com/angel-dart/orm environment: diff --git a/angel_orm_generator/test/common.dart b/angel_orm_generator/test/common.dart index ebcd5353..98eef24c 100644 --- a/angel_orm_generator/test/common.dart +++ b/angel_orm_generator/test/common.dart @@ -10,8 +10,7 @@ Future connectToPostgres(Iterable schemas) async { await conn.open(); for (var s in schemas) - await conn - .execute(await new File('test/models/$s.up.g.sql').readAsString()); + await conn.execute(await new File('test/migrations/$s.sql').readAsString()); return new PostgresExecutor(conn); } @@ -24,9 +23,14 @@ class PostgresExecutor extends QueryExecutor { Future close() => connection.close(); @override - Future> query(String query, List returningFields) { - var fields = returningFields.join(', '); - var returning = 'RETURNING ($fields)'; - return connection.query('$query $returning'); + Future> query(String query, [List returningFields]) { + if (returningFields != null) { + var fields = returningFields.join(', '); + var returning = 'RETURNING $fields'; + query = '$query $returning'; + } + + print('Running: $query'); + return connection.query(query); } } diff --git a/angel_orm_generator/test/migrations/car.sql b/angel_orm_generator/test/migrations/car.sql new file mode 100644 index 00000000..239d285d --- /dev/null +++ b/angel_orm_generator/test/migrations/car.sql @@ -0,0 +1,9 @@ +CREATE TEMPORARY TABLE "cars" ( + id serial PRIMARY KEY, + make varchar(255) NOT NULL, + description TEXT NOT NULL, + family_friendly BOOLEAN NOT NULL, + recalled_at timestamp, + created_at timestamp, + updated_at timestamp +); \ No newline at end of file diff --git a/angel_orm_generator/test/models/author.g.dart b/angel_orm_generator/test/models/author.g.dart index 3de90385..7286a5eb 100644 --- a/angel_orm_generator/test/models/author.g.dart +++ b/angel_orm_generator/test/models/author.g.dart @@ -7,6 +7,9 @@ part of angel_orm.generator.models.author; // ************************************************************************** class AuthorQuery extends Query { + @override + final AuthorQueryValues values = new AuthorQueryValues(); + @override final AuthorQueryWhere where = new AuthorQueryWhere(); @@ -20,6 +23,11 @@ class AuthorQuery extends Query { return AuthorFields.allFields; } + @override + AuthorQueryWhere newWhereClause() { + return new AuthorQueryWhere(); + } + @override deserialize(List row) { return new Author( @@ -49,6 +57,36 @@ class AuthorQueryWhere extends QueryWhere { } } +class AuthorQueryValues extends MapQueryValues { + int get id { + return (values['id'] as int); + } + + void set id(int value) => values['id'] = value; + String get name { + return (values['name'] as String); + } + + void set name(String value) => values['name'] = value; + DateTime get createdAt { + return (values['created_at'] as DateTime); + } + + void set createdAt(DateTime value) => values['created_at'] = value; + DateTime get updatedAt { + return (values['updated_at'] as DateTime); + } + + void set updatedAt(DateTime value) => values['updated_at'] = value; + void copyFrom(Author model) { + values.addAll({ + 'name': model.name, + 'created_at': model.createdAt, + 'updated_at': model.updatedAt + }); + } +} + // ************************************************************************** // JsonModelGenerator // ************************************************************************** diff --git a/angel_orm_generator/test/models/car.g.dart b/angel_orm_generator/test/models/car.g.dart index 61d33adb..979546b3 100644 --- a/angel_orm_generator/test/models/car.g.dart +++ b/angel_orm_generator/test/models/car.g.dart @@ -7,6 +7,9 @@ part of angel_orm.generator.models.car; // ************************************************************************** class CarQuery extends Query { + @override + final CarQueryValues values = new CarQueryValues(); + @override final CarQueryWhere where = new CarQueryWhere(); @@ -20,6 +23,11 @@ class CarQuery extends Query { return CarFields.allFields; } + @override + CarQueryWhere newWhereClause() { + return new CarQueryWhere(); + } + @override deserialize(List row) { return new Car( @@ -69,6 +77,54 @@ class CarQueryWhere extends QueryWhere { } } +class CarQueryValues extends MapQueryValues { + int get id { + return (values['id'] as int); + } + + void set id(int value) => values['id'] = value; + String get make { + return (values['make'] as String); + } + + void set make(String value) => values['make'] = value; + String get description { + return (values['description'] as String); + } + + void set description(String value) => values['description'] = value; + bool get familyFriendly { + return (values['family_friendly'] as bool); + } + + void set familyFriendly(bool value) => values['family_friendly'] = value; + DateTime get recalledAt { + return (values['recalled_at'] as DateTime); + } + + void set recalledAt(DateTime value) => values['recalled_at'] = value; + DateTime get createdAt { + return (values['created_at'] as DateTime); + } + + void set createdAt(DateTime value) => values['created_at'] = value; + DateTime get updatedAt { + return (values['updated_at'] as DateTime); + } + + void set updatedAt(DateTime value) => values['updated_at'] = value; + void copyFrom(Car model) { + values.addAll({ + 'make': model.make, + 'description': model.description, + 'family_friendly': model.familyFriendly, + 'recalled_at': model.recalledAt, + 'created_at': model.createdAt, + 'updated_at': model.updatedAt + }); + } +} + // ************************************************************************** // JsonModelGenerator // ************************************************************************** diff --git a/angel_orm_generator/test/models/customer.g.dart b/angel_orm_generator/test/models/customer.g.dart index 07958111..8ca43b40 100644 --- a/angel_orm_generator/test/models/customer.g.dart +++ b/angel_orm_generator/test/models/customer.g.dart @@ -7,6 +7,9 @@ part of angel_orm_generator.test.models.customer; // ************************************************************************** class CustomerQuery extends Query { + @override + final CustomerQueryValues values = new CustomerQueryValues(); + @override final CustomerQueryWhere where = new CustomerQueryWhere(); @@ -20,6 +23,11 @@ class CustomerQuery extends Query { return CustomerFields.allFields; } + @override + CustomerQueryWhere newWhereClause() { + return new CustomerQueryWhere(); + } + @override deserialize(List row) { return new Customer( @@ -45,6 +53,28 @@ class CustomerQueryWhere extends QueryWhere { } } +class CustomerQueryValues extends MapQueryValues { + int get id { + return (values['id'] as int); + } + + void set id(int value) => values['id'] = value; + DateTime get createdAt { + return (values['created_at'] as DateTime); + } + + void set createdAt(DateTime value) => values['created_at'] = value; + DateTime get updatedAt { + return (values['updated_at'] as DateTime); + } + + void set updatedAt(DateTime value) => values['updated_at'] = value; + void copyFrom(Customer model) { + values + .addAll({'created_at': model.createdAt, 'updated_at': model.updatedAt}); + } +} + // ************************************************************************** // JsonModelGenerator // ************************************************************************** diff --git a/angel_orm_generator/test/models/foot.g.dart b/angel_orm_generator/test/models/foot.g.dart index 0ec36497..b0959d1e 100644 --- a/angel_orm_generator/test/models/foot.g.dart +++ b/angel_orm_generator/test/models/foot.g.dart @@ -7,6 +7,9 @@ part of angel_orm_generator.test.models.foot; // ************************************************************************** class FootQuery extends Query { + @override + final FootQueryValues values = new FootQueryValues(); + @override final FootQueryWhere where = new FootQueryWhere(); @@ -20,6 +23,11 @@ class FootQuery extends Query { return FootFields.allFields; } + @override + FootQueryWhere newWhereClause() { + return new FootQueryWhere(); + } + @override deserialize(List row) { return new Foot( @@ -53,6 +61,42 @@ class FootQueryWhere extends QueryWhere { } } +class FootQueryValues extends MapQueryValues { + int get id { + return (values['id'] as int); + } + + void set id(int value) => values['id'] = value; + int get legId { + return (values['leg_id'] as int); + } + + void set legId(int value) => values['leg_id'] = value; + int get nToes { + return (values['n_toes'] as int); + } + + void set nToes(int value) => values['n_toes'] = value; + DateTime get createdAt { + return (values['created_at'] as DateTime); + } + + void set createdAt(DateTime value) => values['created_at'] = value; + DateTime get updatedAt { + return (values['updated_at'] as DateTime); + } + + void set updatedAt(DateTime value) => values['updated_at'] = value; + void copyFrom(Foot model) { + values.addAll({ + 'leg_id': model.legId, + 'n_toes': model.nToes, + 'created_at': model.createdAt, + 'updated_at': model.updatedAt + }); + } +} + // ************************************************************************** // JsonModelGenerator // ************************************************************************** diff --git a/angel_orm_generator/test/models/fruit.g.dart b/angel_orm_generator/test/models/fruit.g.dart index 45159c4f..b510e0f4 100644 --- a/angel_orm_generator/test/models/fruit.g.dart +++ b/angel_orm_generator/test/models/fruit.g.dart @@ -7,6 +7,9 @@ part of angel_orm_generator.test.models.fruit; // ************************************************************************** class FruitQuery extends Query { + @override + final FruitQueryValues values = new FruitQueryValues(); + @override final FruitQueryWhere where = new FruitQueryWhere(); @@ -20,6 +23,11 @@ class FruitQuery extends Query { return FruitFields.allFields; } + @override + FruitQueryWhere newWhereClause() { + return new FruitQueryWhere(); + } + @override deserialize(List row) { return new Fruit( @@ -53,6 +61,42 @@ class FruitQueryWhere extends QueryWhere { } } +class FruitQueryValues extends MapQueryValues { + int get id { + return (values['id'] as int); + } + + void set id(int value) => values['id'] = value; + int get treeId { + return (values['tree_id'] as int); + } + + void set treeId(int value) => values['tree_id'] = value; + String get commonName { + return (values['common_name'] as String); + } + + void set commonName(String value) => values['common_name'] = value; + DateTime get createdAt { + return (values['created_at'] as DateTime); + } + + void set createdAt(DateTime value) => values['created_at'] = value; + DateTime get updatedAt { + return (values['updated_at'] as DateTime); + } + + void set updatedAt(DateTime value) => values['updated_at'] = value; + void copyFrom(Fruit model) { + values.addAll({ + 'tree_id': model.treeId, + 'common_name': model.commonName, + 'created_at': model.createdAt, + 'updated_at': model.updatedAt + }); + } +} + // ************************************************************************** // JsonModelGenerator // ************************************************************************** diff --git a/angel_orm_generator/test/models/order.g.dart b/angel_orm_generator/test/models/order.g.dart index 7a8d030d..0285824d 100644 --- a/angel_orm_generator/test/models/order.g.dart +++ b/angel_orm_generator/test/models/order.g.dart @@ -7,6 +7,9 @@ part of angel_orm_generator.test.models.order; // ************************************************************************** class OrderQuery extends Query { + @override + final OrderQueryValues values = new OrderQueryValues(); + @override final OrderQueryWhere where = new OrderQueryWhere(); @@ -20,6 +23,11 @@ class OrderQuery extends Query { return OrderFields.allFields; } + @override + OrderQueryWhere newWhereClause() { + return new OrderQueryWhere(); + } + @override deserialize(List row) { return new Order( @@ -69,6 +77,54 @@ class OrderQueryWhere extends QueryWhere { } } +class OrderQueryValues extends MapQueryValues { + int get id { + return (values['id'] as int); + } + + void set id(int value) => values['id'] = value; + int get customerId { + return (values['customer_id'] as int); + } + + void set customerId(int value) => values['customer_id'] = value; + int get employeeId { + return (values['employee_id'] as int); + } + + void set employeeId(int value) => values['employee_id'] = value; + DateTime get orderDate { + return (values['order_date'] as DateTime); + } + + void set orderDate(DateTime value) => values['order_date'] = value; + int get shipperId { + return (values['shipper_id'] as int); + } + + void set shipperId(int value) => values['shipper_id'] = value; + DateTime get createdAt { + return (values['created_at'] as DateTime); + } + + void set createdAt(DateTime value) => values['created_at'] = value; + DateTime get updatedAt { + return (values['updated_at'] as DateTime); + } + + void set updatedAt(DateTime value) => values['updated_at'] = value; + void copyFrom(Order model) { + values.addAll({ + 'customer_id': model.customerId, + 'employee_id': model.employeeId, + 'order_date': model.orderDate, + 'shipper_id': model.shipperId, + 'created_at': model.createdAt, + 'updated_at': model.updatedAt + }); + } +} + // ************************************************************************** // JsonModelGenerator // ************************************************************************** diff --git a/angel_orm_generator/test/models/role.g.dart b/angel_orm_generator/test/models/role.g.dart index 5b9de74a..a41a849c 100644 --- a/angel_orm_generator/test/models/role.g.dart +++ b/angel_orm_generator/test/models/role.g.dart @@ -7,6 +7,9 @@ part of angel_orm_generator.test.models.role; // ************************************************************************** class RoleQuery extends Query { + @override + final RoleQueryValues values = new RoleQueryValues(); + @override final RoleQueryWhere where = new RoleQueryWhere(); @@ -20,6 +23,11 @@ class RoleQuery extends Query { return RoleFields.allFields; } + @override + RoleQueryWhere newWhereClause() { + return new RoleQueryWhere(); + } + @override deserialize(List row) { return new Role( @@ -49,6 +57,36 @@ class RoleQueryWhere extends QueryWhere { } } +class RoleQueryValues extends MapQueryValues { + int get id { + return (values['id'] as int); + } + + void set id(int value) => values['id'] = value; + String get name { + return (values['name'] as String); + } + + void set name(String value) => values['name'] = value; + DateTime get createdAt { + return (values['created_at'] as DateTime); + } + + void set createdAt(DateTime value) => values['created_at'] = value; + DateTime get updatedAt { + return (values['updated_at'] as DateTime); + } + + void set updatedAt(DateTime value) => values['updated_at'] = value; + void copyFrom(Role model) { + values.addAll({ + 'name': model.name, + 'created_at': model.createdAt, + 'updated_at': model.updatedAt + }); + } +} + // ************************************************************************** // JsonModelGenerator // ************************************************************************** diff --git a/angel_orm_generator/test/standalone_test.dart b/angel_orm_generator/test/standalone_test.dart index f0cf74bc..213ff129 100644 --- a/angel_orm_generator/test/standalone_test.dart +++ b/angel_orm_generator/test/standalone_test.dart @@ -12,24 +12,16 @@ main() { test('to where', () { var query = new CarQuery(); query.where - ..familyFriendly.equals(true) + ..familyFriendly.isTrue ..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\''); + 'cars.family_friendly = TRUE AND cars.recalled_at <= \'2000-01-01\''); }); test('parseRow', () { - var row = [ - 0, - 'Mazda', - 'CX9', - true, - dateYmdHms.format(y2k), - dateYmdHms.format(y2k), - dateYmdHms.format(y2k) - ]; + var row = [0, 'Mazda', 'CX9', true, y2k, y2k, y2k]; print(row); var car = new CarQuery().deserialize(row); print(car.toJson()); @@ -59,20 +51,22 @@ main() { Car ferrari; setUp(() async { - ferrari = await CarQuery.insert(connection, - make: 'Ferrari', - description: 'Vroom vroom!', - familyFriendly: false); + var query = new CarQuery(); + query.values + ..make = 'Ferrari東' + ..description = 'Vroom vroom!' + ..familyFriendly = false; + ferrari = await query.insert(connection); }); tearDown(() => connection.close()); test('where clause is applied', () async { - var query = new CarQuery()..where.familyFriendly.equals(true); + var query = new CarQuery()..where.familyFriendly.isTrue; var cars = await query.get(connection); expect(cars, isEmpty); - var sportsCars = new CarQuery()..where.familyFriendly.notEquals(true); + var sportsCars = new CarQuery()..where.familyFriendly.isFalse; cars = await sportsCars.get(connection); print(cars.map((c) => c.toJson())); @@ -85,22 +79,20 @@ main() { test('union', () async { var query1 = new CarQuery()..where.make.like('%Fer%'); - var query2 = new CarQuery()..where.familyFriendly.equals(true); + var query2 = new CarQuery()..where.familyFriendly.isTrue; var query3 = new CarQuery()..where.description.equals('Submarine'); - query1 - ..union(query2) - ..unionAll(query3); - print(query1.compile()); - - var cars = await query1.get(connection); + var union = query1.union(query2).unionAll(query3); + print(union.compile()); + var cars = await union.get(connection); expect(cars, hasLength(1)); }); test('or clause', () async { var query = new CarQuery() ..where.make.like('Fer%') - ..orWhere((where) => - where..familyFriendly.equals(true)..make.equals('Honda')); + ..orWhere((where) => where + ..familyFriendly.isTrue + ..make.equals('Honda')); print(query.compile()); var cars = await query.get(connection); expect(cars, hasLength(1)); @@ -132,25 +124,28 @@ main() { test('delete stream', () async { var query = new CarQuery() - ..where.make.equals('Ferrari') - ..orWhere((w) => w.familyFriendly.equals(true)); - + ..where.make.equals('Ferrari東') + ..orWhere((w) => w.familyFriendly.isTrue); print(query.compile(preamble: 'DELETE FROM "cars"')); - var cars = await query.get(connection); + + 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'); + var query = new CarQuery() + ..where.id.equals(int.parse(ferrari.id)) + ..values.make = 'Hyundai'; + var cars = await query.update(connection); expect(cars, hasLength(1)); expect(cars.first.make, 'Hyundai'); }); test('update car', () async { var cloned = ferrari.copyWith(make: 'Angel'); - var car = await CarQuery.updateCar(connection, cloned); + var query = new CarQuery()..values.copyFrom(cloned); + var car = await query.updateOne(connection); print(car.toJson()); expect(car.toJson(), cloned.toJson()); }); @@ -159,11 +154,13 @@ main() { test('insert', () async { var recalledAt = new DateTime.now(); - var car = await CarQuery.insert(connection, - make: 'Honda', - description: 'Hello', - familyFriendly: true, - recalledAt: recalledAt); + var query = new CarQuery(); + query.values + ..make = 'Honda' + ..description = 'Hello' + ..familyFriendly = true + ..recalledAt = recalledAt; + var car = await query.insert(connection); expect(car.id, isNotNull); expect(car.make, 'Honda'); expect(car.description, 'Hello'); @@ -179,7 +176,8 @@ main() { description: 'Herbie', familyFriendly: true, recalledAt: recalledAt); - var car = await CarQuery.insertCar(connection, beetle); + var query = new CarQuery()..values.copyFrom(beetle); + var car = await query.insert(connection); print(car.toJson()); expect(car.make, beetle.make); expect(car.description, beetle.description);