enum tests

This commit is contained in:
Tobe O 2019-01-23 17:08:26 -05:00
parent bc7021b598
commit d356375af5
9 changed files with 353 additions and 38 deletions

View file

@ -33,7 +33,7 @@ targets:
- :_standalone - :_standalone
sources: sources:
- test/models/book.dart - test/models/book.dart
# - test/models/has_car.dart - test/models/has_car.dart
- test/models/leg.dart - test/models/leg.dart
- test/models/order.dart - test/models/order.dart
- test/models/tree.dart - test/models/tree.dart

View file

@ -217,6 +217,7 @@ ColumnType inferColumnType(DartType type) {
return ColumnType.timeStamp; return ColumnType.timeStamp;
if (const TypeChecker.fromRuntime(Map).isAssignableFromType(type)) if (const TypeChecker.fromRuntime(Map).isAssignableFromType(type))
return ColumnType.jsonb; return ColumnType.jsonb;
if (type is InterfaceType && type.element.isEnum) return ColumnType.int;
return null; return null;
} }

View file

@ -145,6 +145,7 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
var args = <String, Expression>{}; var args = <String, Expression>{};
for (var field in ctx.effectiveFields) { for (var field in ctx.effectiveFields) {
var fType = field.type;
Reference type = convertTypeReference(field.type); Reference type = convertTypeReference(field.type);
if (isSpecialId(ctx, field)) type = refer('int'); if (isSpecialId(ctx, field)) type = refer('int');
@ -157,6 +158,8 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
expr = refer('json') expr = refer('json')
.property('decode') .property('decode')
.call([expr.asA(refer('String'))]).asA(type); .call([expr.asA(refer('String'))]).asA(type);
} else if (fType is InterfaceType && fType.element.isEnum) {
expr = type.property('values').index(expr.asA(refer('int')));
} else } else
expr = expr.asA(type); expr = expr.asA(type);
@ -481,6 +484,7 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
// Add builders for each field // Add builders for each field
for (var field in ctx.effectiveFields) { for (var field in ctx.effectiveFields) {
var name = field.name; var name = field.name;
var args = <Expression>[];
DartType type; DartType type;
Reference builderType; Reference builderType;
@ -496,6 +500,11 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
builderType = new TypeReference((b) => b builderType = new TypeReference((b) => b
..symbol = 'NumericSqlExpressionBuilder' ..symbol = 'NumericSqlExpressionBuilder'
..types.add(refer(isSpecialId(ctx, field) ? 'int' : type.name))); ..types.add(refer(isSpecialId(ctx, field) ? 'int' : type.name)));
} else if (type is InterfaceType && type.element.isEnum) {
builderType = new TypeReference((b) => b
..symbol = 'EnumSqlExpressionBuilder'
..types.add(convertTypeReference(type)));
args.add(CodeExpression(Code('(v) => v.index')));
} else if (const TypeChecker.fromRuntime(String).isExactlyType(type)) { } else if (const TypeChecker.fromRuntime(String).isExactlyType(type)) {
builderType = refer('StringSqlExpressionBuilder'); builderType = refer('StringSqlExpressionBuilder');
} else if (const TypeChecker.fromRuntime(bool).isExactlyType(type)) { } else if (const TypeChecker.fromRuntime(bool).isExactlyType(type)) {
@ -532,7 +541,7 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
.assign(builderType.newInstance([ .assign(builderType.newInstance([
refer('query'), refer('query'),
literalString(ctx.buildContext.resolveFieldName(field.name)) literalString(ctx.buildContext.resolveFieldName(field.name))
])) ].followedBy(args)))
.code, .code,
); );
})); }));
@ -558,31 +567,45 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
// Each field generates a getter for setter // Each field generates a getter for setter
for (var field in ctx.effectiveFields) { for (var field in ctx.effectiveFields) {
var fType = field.type;
var name = ctx.buildContext.resolveFieldName(field.name); var name = ctx.buildContext.resolveFieldName(field.name);
var type = isSpecialId(ctx, field) var type = isSpecialId(ctx, field)
? refer('int') ? refer('int')
: convertTypeReference(field.type); : convertTypeReference(field.type);
clazz.methods.add(new Method((b) { clazz.methods.add(new Method((b) {
var value = refer('values').index(literalString(name));
if (fType is InterfaceType && fType.element.isEnum) {
var asInt = value.asA(refer('int'));
var t = convertTypeReference(fType);
value = t.property('values').index(asInt);
} else {
value = value.asA(type);
}
b b
..name = field.name ..name = field.name
..type = MethodType.getter ..type = MethodType.getter
..returns = type ..returns = type
..body = new Block((b) => b.addExpression( ..body = new Block((b) => b.addExpression(value.returned));
refer('values').index(literalString(name)).asA(type).returned));
})); }));
clazz.methods.add(new Method((b) { clazz.methods.add(new Method((b) {
Expression value = refer('value');
if (fType is InterfaceType && fType.element.isEnum) {
value = value.property('index');
}
b b
..name = field.name ..name = field.name
..type = MethodType.setter ..type = MethodType.setter
..requiredParameters.add(new Parameter((b) => b ..requiredParameters.add(new Parameter((b) => b
..name = 'value' ..name = 'value'
..type = type)) ..type = type))
..body = refer('values') ..body =
.index(literalString(name)) refer('values').index(literalString(name)).assign(value).code;
.assign(refer('value'))
.code;
})); }));
} }

View file

@ -0,0 +1,36 @@
import 'package:test/test.dart';
import 'models/has_car.dart';
import 'common.dart';
main() {
PostgresExecutor executor;
setUp(() async {
executor = await connectToPostgres(['has_car']);
});
test('insert', () async {
var query = HasCarQuery()..values.type = CarType.sedan;
var result = await query.insert(executor);
expect(result.type, CarType.sedan);
});
group('query', () {
HasCar initialValue;
setUp(() async {
var query = HasCarQuery();
query.values.type = CarType.sedan;
initialValue = await query.insert(executor);
});
test('query by enum', () async {
// Check for mismatched type
var query = HasCarQuery()..where.type.equals(CarType.atv);
expect(await query.get(executor), isEmpty);
query = HasCarQuery()..where.type.equals(initialValue.type);
expect(await query.getOne(executor), initialValue);
});
});
}

View file

@ -0,0 +1,6 @@
CREATE TEMPORARY TABLE "has_cars" (
id serial PRIMARY KEY,
type int not null,
created_at timestamp,
updated_at timestamp
);

View file

@ -2,11 +2,24 @@ import 'package:angel_migration/angel_migration.dart';
import 'package:angel_model/angel_model.dart'; import 'package:angel_model/angel_model.dart';
import 'package:angel_orm/angel_orm.dart'; import 'package:angel_orm/angel_orm.dart';
import 'package:angel_serialize/angel_serialize.dart'; import 'package:angel_serialize/angel_serialize.dart';
import 'package:meta/meta.dart';
import 'car.dart'; import 'car.dart';
// part 'has_car.g.dart'; part 'has_car.g.dart';
Map _carToMap(Car car) => car.toJson();
Car _carFromMap(map) => CarSerializer.fromMap(map as Map);
enum CarType { sedan, suv, atv }
@orm @orm
@serializable @serializable
abstract class _PackageJson extends Model { abstract class _HasCar extends Model {
Car get car; // TODO: Do this without explicit serializers
// @SerializableField(
// serializesTo: Map, serializer: #_carToMap, deserializer: #_carFromMap)
// Car get car;
@SerializableField(isNullable: false)
CarType get type;
} }

View file

@ -0,0 +1,234 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'has_car.dart';
// **************************************************************************
// MigrationGenerator
// **************************************************************************
class HasCarMigration extends Migration {
@override
up(Schema schema) {
schema.create('has_cars', (table) {
table.serial('id')..primaryKey();
table.integer('type');
table.timeStamp('created_at');
table.timeStamp('updated_at');
});
}
@override
down(Schema schema) {
schema.drop('has_cars');
}
}
// **************************************************************************
// OrmGenerator
// **************************************************************************
class HasCarQuery extends Query<HasCar, HasCarQueryWhere> {
HasCarQuery() {
_where = new HasCarQueryWhere(this);
}
@override
final HasCarQueryValues values = new HasCarQueryValues();
HasCarQueryWhere _where;
@override
get tableName {
return 'has_cars';
}
@override
get fields {
return const ['id', 'type', 'created_at', 'updated_at'];
}
@override
HasCarQueryWhere get where {
return _where;
}
@override
HasCarQueryWhere newWhereClause() {
return new HasCarQueryWhere(this);
}
static HasCar parseRow(List row) {
if (row.every((x) => x == null)) return null;
var model = new HasCar(
id: row[0].toString(),
type: CarType.values[(row[1] as int)],
createdAt: (row[2] as DateTime),
updatedAt: (row[3] as DateTime));
return model;
}
@override
deserialize(List row) {
return parseRow(row);
}
}
class HasCarQueryWhere extends QueryWhere {
HasCarQueryWhere(HasCarQuery query)
: id = new NumericSqlExpressionBuilder<int>(query, 'id'),
type = new EnumSqlExpressionBuilder<CarType>(
query, 'type', (v) => v.index),
createdAt = new DateTimeSqlExpressionBuilder(query, 'created_at'),
updatedAt = new DateTimeSqlExpressionBuilder(query, 'updated_at');
final NumericSqlExpressionBuilder<int> id;
final EnumSqlExpressionBuilder<CarType> type;
final DateTimeSqlExpressionBuilder createdAt;
final DateTimeSqlExpressionBuilder updatedAt;
@override
get expressionBuilders {
return [id, type, createdAt, updatedAt];
}
}
class HasCarQueryValues extends MapQueryValues {
int get id {
return (values['id'] as int);
}
set id(int value) => values['id'] = value;
CarType get type {
return CarType.values[(values['type'] as int)];
}
set type(CarType value) => values['type'] = value.index;
DateTime get createdAt {
return (values['created_at'] as DateTime);
}
set createdAt(DateTime value) => values['created_at'] = value;
DateTime get updatedAt {
return (values['updated_at'] as DateTime);
}
set updatedAt(DateTime value) => values['updated_at'] = value;
void copyFrom(HasCar model) {
values.addAll({
'type': model.type,
'created_at': model.createdAt,
'updated_at': model.updatedAt
});
}
}
// **************************************************************************
// JsonModelGenerator
// **************************************************************************
@generatedSerializable
class HasCar extends _HasCar {
HasCar({this.id, @required this.type, this.createdAt, this.updatedAt});
@override
final String id;
@override
final CarType type;
@override
final DateTime createdAt;
@override
final DateTime updatedAt;
HasCar copyWith(
{String id, CarType type, DateTime createdAt, DateTime updatedAt}) {
return new HasCar(
id: id ?? this.id,
type: type ?? this.type,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt);
}
bool operator ==(other) {
return other is _HasCar &&
other.id == id &&
other.type == type &&
other.createdAt == createdAt &&
other.updatedAt == updatedAt;
}
@override
int get hashCode {
return hashObjects([id, type, createdAt, updatedAt]);
}
Map<String, dynamic> toJson() {
return HasCarSerializer.toMap(this);
}
}
// **************************************************************************
// SerializerGenerator
// **************************************************************************
abstract class HasCarSerializer {
static HasCar fromMap(Map map) {
if (map['type'] == null) {
throw new FormatException("Missing required field 'type' on HasCar.");
}
return new HasCar(
id: map['id'] as String,
type: map['type'] is CarType
? (map['type'] as CarType)
: (map['type'] is int ? CarType.values[map['type'] as int] : null),
createdAt: map['created_at'] != null
? (map['created_at'] is DateTime
? (map['created_at'] as DateTime)
: DateTime.parse(map['created_at'].toString()))
: null,
updatedAt: map['updated_at'] != null
? (map['updated_at'] is DateTime
? (map['updated_at'] as DateTime)
: DateTime.parse(map['updated_at'].toString()))
: null);
}
static Map<String, dynamic> toMap(_HasCar model) {
if (model == null) {
return null;
}
if (model.type == null) {
throw new FormatException("Missing required field 'type' on HasCar.");
}
return {
'id': model.id,
'type': model.type == null ? null : CarType.values.indexOf(model.type),
'created_at': model.createdAt?.toIso8601String(),
'updated_at': model.updatedAt?.toIso8601String()
};
}
}
abstract class HasCarFields {
static const List<String> allFields = const <String>[
id,
type,
createdAt,
updatedAt
];
static const String id = 'id';
static const String type = 'type';
static const String createdAt = 'created_at';
static const String updatedAt = 'updated_at';
}

View file

@ -9,13 +9,13 @@ part 'order.g.dart';
@orm @orm
@serializable @serializable
class _Order extends Model { abstract class _Order extends Model {
@Join(Customer, CustomerFields.id) @belongsTo
int customerId; Customer get customer;
int employeeId; int get employeeId;
DateTime orderDate; DateTime get orderDate;
int shipperId; int get shipperId;
} }

View file

@ -11,12 +11,12 @@ class OrderMigration extends Migration {
up(Schema schema) { up(Schema schema) {
schema.create('orders', (table) { schema.create('orders', (table) {
table.serial('id')..primaryKey(); table.serial('id')..primaryKey();
table.integer('customer_id');
table.integer('employee_id'); table.integer('employee_id');
table.timeStamp('order_date'); table.timeStamp('order_date');
table.integer('shipper_id'); table.integer('shipper_id');
table.timeStamp('created_at'); table.timeStamp('created_at');
table.timeStamp('updated_at'); table.timeStamp('updated_at');
table.integer('customer_id').references('customers', 'id');
}); });
} }
@ -33,6 +33,8 @@ class OrderMigration extends Migration {
class OrderQuery extends Query<Order, OrderQueryWhere> { class OrderQuery extends Query<Order, OrderQueryWhere> {
OrderQuery() { OrderQuery() {
_where = new OrderQueryWhere(this); _where = new OrderQueryWhere(this);
leftJoin('customers', 'customer_id', 'id',
additionalFields: const ['created_at', 'updated_at']);
} }
@override @override
@ -72,12 +74,15 @@ class OrderQuery extends Query<Order, OrderQueryWhere> {
if (row.every((x) => x == null)) return null; if (row.every((x) => x == null)) return null;
var model = new Order( var model = new Order(
id: row[0].toString(), id: row[0].toString(),
customerId: (row[1] as int),
employeeId: (row[2] as int), employeeId: (row[2] as int),
orderDate: (row[3] as DateTime), orderDate: (row[3] as DateTime),
shipperId: (row[4] as int), shipperId: (row[4] as int),
createdAt: (row[5] as DateTime), createdAt: (row[5] as DateTime),
updatedAt: (row[6] as DateTime)); updatedAt: (row[6] as DateTime));
if (row.length > 7) {
model = model.copyWith(
customer: CustomerQuery.parseRow(row.skip(7).toList()));
}
return model; return model;
} }
@ -163,13 +168,15 @@ class OrderQueryValues extends MapQueryValues {
set updatedAt(DateTime value) => values['updated_at'] = value; set updatedAt(DateTime value) => values['updated_at'] = value;
void copyFrom(Order model) { void copyFrom(Order model) {
values.addAll({ values.addAll({
'customer_id': model.customerId,
'employee_id': model.employeeId, 'employee_id': model.employeeId,
'order_date': model.orderDate, 'order_date': model.orderDate,
'shipper_id': model.shipperId, 'shipper_id': model.shipperId,
'created_at': model.createdAt, 'created_at': model.createdAt,
'updated_at': model.updatedAt 'updated_at': model.updatedAt
}); });
if (model.customer != null) {
values['customer_id'] = int.parse(model.customer.id);
}
} }
} }
@ -181,7 +188,7 @@ class OrderQueryValues extends MapQueryValues {
class Order extends _Order { class Order extends _Order {
Order( Order(
{this.id, {this.id,
this.customerId, this.customer,
this.employeeId, this.employeeId,
this.orderDate, this.orderDate,
this.shipperId, this.shipperId,
@ -192,7 +199,7 @@ class Order extends _Order {
final String id; final String id;
@override @override
final int customerId; final Customer customer;
@override @override
final int employeeId; final int employeeId;
@ -211,7 +218,7 @@ class Order extends _Order {
Order copyWith( Order copyWith(
{String id, {String id,
int customerId, Customer customer,
int employeeId, int employeeId,
DateTime orderDate, DateTime orderDate,
int shipperId, int shipperId,
@ -219,7 +226,7 @@ class Order extends _Order {
DateTime updatedAt}) { DateTime updatedAt}) {
return new Order( return new Order(
id: id ?? this.id, id: id ?? this.id,
customerId: customerId ?? this.customerId, customer: customer ?? this.customer,
employeeId: employeeId ?? this.employeeId, employeeId: employeeId ?? this.employeeId,
orderDate: orderDate ?? this.orderDate, orderDate: orderDate ?? this.orderDate,
shipperId: shipperId ?? this.shipperId, shipperId: shipperId ?? this.shipperId,
@ -230,7 +237,7 @@ class Order extends _Order {
bool operator ==(other) { bool operator ==(other) {
return other is _Order && return other is _Order &&
other.id == id && other.id == id &&
other.customerId == customerId && other.customer == customer &&
other.employeeId == employeeId && other.employeeId == employeeId &&
other.orderDate == orderDate && other.orderDate == orderDate &&
other.shipperId == shipperId && other.shipperId == shipperId &&
@ -240,15 +247,8 @@ class Order extends _Order {
@override @override
int get hashCode { int get hashCode {
return hashObjects([ return hashObjects(
id, [id, customer, employeeId, orderDate, shipperId, createdAt, updatedAt]);
customerId,
employeeId,
orderDate,
shipperId,
createdAt,
updatedAt
]);
} }
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
@ -264,7 +264,9 @@ abstract class OrderSerializer {
static Order fromMap(Map map) { static Order fromMap(Map map) {
return new Order( return new Order(
id: map['id'] as String, id: map['id'] as String,
customerId: map['customer_id'] as int, customer: map['customer'] != null
? CustomerSerializer.fromMap(map['customer'] as Map)
: null,
employeeId: map['employee_id'] as int, employeeId: map['employee_id'] as int,
orderDate: map['order_date'] != null orderDate: map['order_date'] != null
? (map['order_date'] is DateTime ? (map['order_date'] is DateTime
@ -290,7 +292,7 @@ abstract class OrderSerializer {
} }
return { return {
'id': model.id, 'id': model.id,
'customer_id': model.customerId, 'customer': CustomerSerializer.toMap(model.customer),
'employee_id': model.employeeId, 'employee_id': model.employeeId,
'order_date': model.orderDate?.toIso8601String(), 'order_date': model.orderDate?.toIso8601String(),
'shipper_id': model.shipperId, 'shipper_id': model.shipperId,
@ -303,7 +305,7 @@ abstract class OrderSerializer {
abstract class OrderFields { abstract class OrderFields {
static const List<String> allFields = const <String>[ static const List<String> allFields = const <String>[
id, id,
customerId, customer,
employeeId, employeeId,
orderDate, orderDate,
shipperId, shipperId,
@ -313,7 +315,7 @@ abstract class OrderFields {
static const String id = 'id'; static const String id = 'id';
static const String customerId = 'customer_id'; static const String customer = 'customer';
static const String employeeId = 'employee_id'; static const String employeeId = 'employee_id';