Migrated angel_orm_generator

This commit is contained in:
thomashii@dukefirehawk.com 2021-05-02 19:33:32 +08:00
parent d2af68779e
commit af36c3b339
8 changed files with 214 additions and 209 deletions

View file

@ -31,8 +31,8 @@
* Created basic-sdk-2.12.x_nnbd template (1/1 test passed) <= Milestone 1
* Migrated angel_serialize to 4.0.0 (0/0 test passed)
* Migrated angel_serialize_generator to 4.0.0 (33/33 tests passed)
* Updated angel_orm to 3.0.0 (0/0 tests passed)
* Updated angel_migration to 3.0.0 (0/0 tests passed)
* Migrated angel_orm to 3.0.0 (0/0 tests passed)
* Migrated angel_migration to 3.0.0 (0/0 tests passed)
* Added inflection2 and migrated to 1.0.0 (28/32 tests passed)
* Updated angel_orm_generator to 3.0.0 (in progress)
* Updated angel_migration_runner to 3.0.0 (in progress)

View file

@ -8,12 +8,12 @@ part 'main.g.dart';
main() async {
var query = EmployeeQuery()
..where.firstName.equals('Rich')
..where.lastName.equals('Person')
..orWhere((w) => w.salary.greaterThanOrEqualTo(75000))
..where!.firstName.equals('Rich')
..where!.lastName.equals('Person')
..orWhere((w) => w!.salary.greaterThanOrEqualTo(75000))
..join('companies', 'company_id', 'id');
var richPerson = await query.getOne(_FakeExecutor());
var richPerson = await (query.getOne(_FakeExecutor()) as FutureOr<Employee>);
print(richPerson.toJson());
}
@ -22,7 +22,7 @@ class _FakeExecutor extends QueryExecutor {
@override
Future<List<List>> query(
String tableName, String query, Map<String, dynamic> substitutionValues,
String tableName, String? query, Map<String, dynamic> substitutionValues,
[returningFields]) async {
var now = DateTime.now();
print(
@ -41,12 +41,12 @@ class _FakeExecutor extends QueryExecutor {
@orm
@serializable
abstract class _Employee extends Model {
String get firstName;
String? get firstName;
String get lastName;
String? get lastName;
@Column(indexType: IndexType.unique)
String uniqueId;
String? uniqueId;
double get salary;
double? get salary;
}

View file

@ -8,9 +8,9 @@ part of 'main.dart';
class EmployeeMigration extends Migration {
@override
up(Schema schema) {
void up(Schema schema) {
schema.create('employees', (table) {
table.serial('id')..primaryKey();
table.serial('id').primaryKey();
table.timeStamp('created_at');
table.timeStamp('updated_at');
table.varChar('unique_id');
@ -21,7 +21,7 @@ class EmployeeMigration extends Migration {
}
@override
down(Schema schema) {
void down(Schema schema) {
schema.drop('employees');
}
}
@ -30,8 +30,8 @@ class EmployeeMigration extends Migration {
// OrmGenerator
// **************************************************************************
class EmployeeQuery extends Query<Employee, EmployeeQueryWhere> {
EmployeeQuery({Set<String> trampoline}) {
class EmployeeQuery extends Query<Employee?, EmployeeQueryWhere?> {
EmployeeQuery({Set<String>? trampoline}) {
trampoline ??= Set();
trampoline.add(tableName);
_where = EmployeeQueryWhere(this);
@ -40,7 +40,7 @@ class EmployeeQuery extends Query<Employee, EmployeeQueryWhere> {
@override
final EmployeeQueryValues values = EmployeeQueryValues();
EmployeeQueryWhere _where;
EmployeeQueryWhere? _where;
@override
get casts {
@ -66,7 +66,7 @@ class EmployeeQuery extends Query<Employee, EmployeeQueryWhere> {
}
@override
EmployeeQueryWhere get where {
EmployeeQueryWhere? get where {
return _where;
}
@ -75,15 +75,15 @@ class EmployeeQuery extends Query<Employee, EmployeeQueryWhere> {
return EmployeeQueryWhere(this);
}
static Employee parseRow(List row) {
static Employee? parseRow(List row) {
if (row.every((x) => x == null)) return null;
var model = Employee(
id: (row[0] as String),
createdAt: (row[1] as DateTime),
updatedAt: (row[2] as DateTime),
uniqueId: (row[3] as String),
firstName: (row[4] as String),
lastName: (row[5] as String),
id: (row[0] as String?),
createdAt: (row[1] as DateTime?),
updatedAt: (row[2] as DateTime?),
uniqueId: (row[3] as String?),
firstName: (row[4] as String?),
lastName: (row[5] as String?),
salary: double.tryParse(row[6].toString()));
return model;
}
@ -130,41 +130,41 @@ class EmployeeQueryValues extends MapQueryValues {
return {'salary': 'decimal'};
}
String get id {
return (values['id'] as String);
String? get id {
return (values['id'] as String?);
}
set id(String value) => values['id'] = value;
DateTime get createdAt {
return (values['created_at'] as DateTime);
set id(String? value) => values['id'] = value;
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 createdAt(DateTime? value) => values['created_at'] = value;
DateTime? get updatedAt {
return (values['updated_at'] as DateTime?);
}
set updatedAt(DateTime value) => values['updated_at'] = value;
String get uniqueId {
return (values['unique_id'] as String);
set updatedAt(DateTime? value) => values['updated_at'] = value;
String? get uniqueId {
return (values['unique_id'] as String?);
}
set uniqueId(String value) => values['unique_id'] = value;
String get firstName {
return (values['first_name'] as String);
set uniqueId(String? value) => values['unique_id'] = value;
String? get firstName {
return (values['first_name'] as String?);
}
set firstName(String value) => values['first_name'] = value;
String get lastName {
return (values['last_name'] as String);
set firstName(String? value) => values['first_name'] = value;
String? get lastName {
return (values['last_name'] as String?);
}
set lastName(String value) => values['last_name'] = value;
double get salary {
set lastName(String? value) => values['last_name'] = value;
double? get salary {
return double.tryParse((values['salary'] as String));
}
set salary(double value) => values['salary'] = value.toString();
set salary(double? value) => values['salary'] = value.toString();
void copyFrom(Employee model) {
id = model.id;
createdAt = model.createdAt;
@ -193,36 +193,36 @@ class Employee extends _Employee {
/// A unique identifier corresponding to this item.
@override
String id;
String? id;
/// The time at which this item was created.
@override
DateTime createdAt;
DateTime? createdAt;
/// The last time at which this item was updated.
@override
DateTime updatedAt;
DateTime? updatedAt;
@override
String uniqueId;
String? uniqueId;
@override
final String firstName;
final String? firstName;
@override
final String lastName;
final String? lastName;
@override
final double salary;
final double? salary;
Employee copyWith(
{String id,
DateTime createdAt,
DateTime updatedAt,
String uniqueId,
String firstName,
String lastName,
double salary}) {
{String? id,
DateTime? createdAt,
DateTime? updatedAt,
String? uniqueId,
String? firstName,
String? lastName,
double? salary}) {
return Employee(
id: id ?? this.id,
createdAt: createdAt ?? this.createdAt,
@ -284,32 +284,29 @@ class EmployeeSerializer extends Codec<Employee, Map> {
const EmployeeSerializer();
@override
get encoder => const EmployeeEncoder();
EmployeeEncoder get encoder => const EmployeeEncoder();
@override
get decoder => const EmployeeDecoder();
EmployeeDecoder get decoder => const EmployeeDecoder();
static Employee fromMap(Map map) {
return Employee(
id: map['id'] as String,
id: map['id'] as String?,
createdAt: map['created_at'] != null
? (map['created_at'] is DateTime
? (map['created_at'] as 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)
? (map['updated_at'] as DateTime?)
: DateTime.parse(map['updated_at'].toString()))
: null,
uniqueId: map['unique_id'] as String,
firstName: map['first_name'] as String,
lastName: map['last_name'] as String,
salary: map['salary'] as double);
uniqueId: map['unique_id'] as String?,
firstName: map['first_name'] as String?,
lastName: map['last_name'] as String?,
salary: map['salary'] as double?);
}
static Map<String, dynamic> toMap(_Employee model) {
if (model == null) {
return null;
}
return {
'id': model.id,
'created_at': model.createdAt?.toIso8601String(),

View file

@ -29,7 +29,7 @@ class MigrationGenerator extends GeneratorForAnnotation<Orm> {
const MigrationGenerator({this.autoSnakeCaseNames = true});
@override
Future<String> generateForAnnotatedElement(
Future<String?> generateForAnnotatedElement(
Element element, ConstantReader annotation, BuildStep buildStep) async {
if (element is! ClassElement) {
throw 'Only classes can be annotated with @ORM().';
@ -43,20 +43,19 @@ class MigrationGenerator extends GeneratorForAnnotation<Orm> {
}
var resolver = await buildStep.resolver;
var ctx = await buildOrmContext({}, element as ClassElement, annotation,
buildStep, resolver, autoSnakeCaseNames != false);
var lib = generateMigrationLibrary(
ctx, element as ClassElement, resolver, buildStep);
var ctx = await buildOrmContext({}, element, annotation, buildStep,
resolver, autoSnakeCaseNames != false);
var lib = generateMigrationLibrary(ctx, element, resolver, buildStep);
if (lib == null) return null;
return DartFormatter().format(lib.accept(DartEmitter()).toString());
}
Library generateMigrationLibrary(OrmBuildContext ctx, ClassElement element,
Library generateMigrationLibrary(OrmBuildContext? ctx, ClassElement element,
Resolver resolver, BuildStep buildStep) {
return Library((lib) {
lib.body.add(Class((clazz) {
clazz
..name = '${ctx.buildContext.modelClassName}Migration'
..name = '${ctx!.buildContext.modelClassName}Migration'
..extend = refer('Migration')
..methods
.addAll([buildUpMigration(ctx, lib), buildDownMigration(ctx)]);
@ -64,10 +63,10 @@ class MigrationGenerator extends GeneratorForAnnotation<Orm> {
});
}
Method buildUpMigration(OrmBuildContext ctx, LibraryBuilder lib) {
Method buildUpMigration(OrmBuildContext? ctx, LibraryBuilder lib) {
return Method((meth) {
var autoIdAndDateFields = const TypeChecker.fromRuntime(Model)
.isAssignableFromType(ctx.buildContext.clazz.thisType);
.isAssignableFromType(ctx!.buildContext.clazz.thisType);
meth
..name = 'up'
..annotations.add(refer('override'))
@ -80,7 +79,7 @@ class MigrationGenerator extends GeneratorForAnnotation<Orm> {
..body = Block((closureBody) {
var table = refer('table');
List<String> dup = [];
List<String?> dup = [];
ctx.columns.forEach((name, col) {
// Skip custom-expression columns.
if (col.hasExpression) return;
@ -103,7 +102,7 @@ class MigrationGenerator extends GeneratorForAnnotation<Orm> {
(autoIdAndDateFields != false && name == 'id'))) {
// Check for relationships that might duplicate
for (var rName in ctx.relations.keys) {
var relationship = ctx.relations[rName];
var relationship = ctx.relations[rName]!;
if (relationship.localKey == key) return;
}
}
@ -111,7 +110,7 @@ class MigrationGenerator extends GeneratorForAnnotation<Orm> {
dup.add(key);
}
String methodName;
String? methodName;
List<Expression> positional = [literal(key)];
Map<String, Expression> named = {};
@ -159,13 +158,13 @@ class MigrationGenerator extends GeneratorForAnnotation<Orm> {
if (col.length == null) {
methodName = 'declare';
provColumn = columnTypeType.newInstance([
literal(col.type.name),
literal(col.type!.name),
]);
} else {
methodName = 'declareColumn';
provColumn = colType.newInstance([], {
'type': columnTypeType.newInstance([
literal(col.type.name),
literal(col.type!.name),
]),
'length': literal(col.length),
});
@ -183,10 +182,10 @@ class MigrationGenerator extends GeneratorForAnnotation<Orm> {
if (defaultValue != null && !defaultValue.isNull) {
var type = defaultValue.type;
Expression defaultExpr;
Expression? defaultExpr;
if (const TypeChecker.fromRuntime(RawSql)
.isAssignableFromType(defaultValue.type)) {
.isAssignableFromType(defaultValue.type!)) {
var value =
ConstantReader(defaultValue).read('value').stringValue;
defaultExpr =
@ -195,7 +194,7 @@ class MigrationGenerator extends GeneratorForAnnotation<Orm> {
// Default to enum index.
try {
var index =
ConstantReader(defaultValue).read('index')?.intValue;
ConstantReader(defaultValue).read('index').intValue;
if (index != null) defaultExpr = literalNum(index);
} catch (_) {
// Extremely weird error occurs here: `Not an instance of int`.
@ -203,7 +202,7 @@ class MigrationGenerator extends GeneratorForAnnotation<Orm> {
}
} else {
defaultExpr = CodeExpression(
Code(dartObjectToString(defaultValue)),
Code(dartObjectToString(defaultValue)!),
);
}
@ -250,7 +249,7 @@ class MigrationGenerator extends GeneratorForAnnotation<Orm> {
var columnTypeType = refer('ColumnType');
var key = relationship.localKey;
var keyType = relationship
.foreign.columns[relationship.foreignKey].type.name;
.foreign!.columns[relationship.foreignKey!]!.type!.name;
var field = table.property('declare').call([
literal(key),
@ -284,7 +283,7 @@ class MigrationGenerator extends GeneratorForAnnotation<Orm> {
});
}
Method buildDownMigration(OrmBuildContext ctx) {
Method buildDownMigration(OrmBuildContext? ctx) {
return Method((b) {
b
..name = 'down'
@ -293,7 +292,7 @@ class MigrationGenerator extends GeneratorForAnnotation<Orm> {
..body = Block((b) {
var named = <String, Expression>{};
if (ctx.relations.values.any((r) =>
if (ctx!.relations.values.any((r) =>
r.type == RelationshipType.hasOne ||
r.type == RelationshipType.hasMany ||
r.isManyToMany)) {
@ -302,7 +301,7 @@ class MigrationGenerator extends GeneratorForAnnotation<Orm> {
b.addExpression(_schema
.property('drop')
.call([literalString(ctx.tableName)], named));
.call([literalString(ctx.tableName!)], named));
});
});
}

View file

@ -10,6 +10,7 @@ import 'package:angel_serialize_generator/angel_serialize_generator.dart';
import 'package:angel_serialize_generator/build_context.dart';
import 'package:angel_serialize_generator/context.dart';
import 'package:build/build.dart';
import 'package:collection/collection.dart' show IterableExtension;
import 'package:inflection2/inflection2.dart';
import 'package:recase/recase.dart';
import 'package:source_gen/source_gen.dart';
@ -19,21 +20,21 @@ import 'readers.dart';
bool isHasRelation(Relationship r) =>
r.type == RelationshipType.hasOne || r.type == RelationshipType.hasMany;
bool isSpecialId(OrmBuildContext ctx, FieldElement field) {
bool isSpecialId(OrmBuildContext? ctx, FieldElement field) {
return
// field is ShimFieldImpl &&
field is! RelationFieldImpl &&
(field.name == 'id' &&
const TypeChecker.fromRuntime(Model)
.isAssignableFromType(ctx.buildContext.clazz.thisType));
.isAssignableFromType(ctx!.buildContext.clazz.thisType));
}
Element _findElement(FieldElement field) {
return (field.setter == null ? field.getter : field) ?? field;
}
FieldElement findPrimaryFieldInList(
OrmBuildContext ctx, Iterable<FieldElement> fields) {
FieldElement? findPrimaryFieldInList(
OrmBuildContext? ctx, Iterable<FieldElement> fields) {
for (var field_ in fields) {
var field = field_ is RelationFieldImpl ? field_.originalField : field_;
var element = _findElement(field);
@ -51,39 +52,38 @@ FieldElement findPrimaryFieldInList(
}
}
var specialId =
fields.firstWhere((f) => isSpecialId(ctx, f), orElse: () => null);
var specialId = fields.firstWhereOrNull((f) => isSpecialId(ctx, f));
// print(
// 'Special ID on ${ctx.buildContext.originalClassName} => ${specialId?.name}');
return specialId;
}
Future<OrmBuildContext> buildOrmContext(
Future<OrmBuildContext?> buildOrmContext(
Map<String, OrmBuildContext> cache,
ClassElement clazz,
ConstantReader annotation,
BuildStep buildStep,
Resolver resolver,
bool autoSnakeCaseNames,
bool? autoSnakeCaseNames,
{bool heedExclude = true}) async {
// Check for @generatedSerializable
// ignore: unused_local_variable
DartObject generatedSerializable;
DartObject? generatedSerializable;
while ((generatedSerializable =
const TypeChecker.fromRuntime(GeneratedSerializable)
.firstAnnotationOf(clazz)) !=
null) {
clazz = clazz.supertype.element;
clazz = clazz.supertype!.element;
}
var id = clazz.location.components.join('-');
var id = clazz.location!.components.join('-');
if (cache.containsKey(id)) {
return cache[id];
}
var buildCtx = await buildContext(
clazz, annotation, buildStep, resolver, autoSnakeCaseNames,
heedExclude: heedExclude);
var buildCtx = await (buildContext(
clazz, annotation, buildStep, resolver, autoSnakeCaseNames!,
heedExclude: heedExclude) as FutureOr<BuildContext>);
var ormAnnotation = reviveORMAnnotation(annotation);
// print(
// 'tableName (${annotation.objectValue.type.name}) => ${ormAnnotation.tableName} from ${clazz.name} (${annotation.revive().namedArguments})');
@ -98,7 +98,7 @@ Future<OrmBuildContext> buildOrmContext(
// Read all fields
for (var field in buildCtx.fields) {
// Check for column annotation...
Column column;
Column? column;
var element = _findElement(field);
var columnAnnotation = columnTypeChecker.firstAnnotationOf(element);
// print('${element.name} => $columnAnnotation');
@ -134,7 +134,7 @@ Future<OrmBuildContext> buildOrmContext(
// Try to find a relationship
var el = _findElement(field);
el ??= field;
//el ??= field;
var ann = relationshipTypeChecker.firstAnnotationOf(el);
if (ann != null) {
@ -146,7 +146,7 @@ Future<OrmBuildContext> buildOrmContext(
var foreignTable = cr.peek('foreignTable')?.stringValue;
var cascadeOnDelete = cr.peek('cascadeOnDelete')?.boolValue == true;
var through = cr.peek('through')?.typeValue;
OrmBuildContext foreign, throughContext;
OrmBuildContext? foreign, throughContext;
if (foreignTable == null) {
// if (!isModelClass(field.type) &&
@ -166,7 +166,7 @@ Future<OrmBuildContext> buildOrmContext(
const TypeChecker.fromRuntime(List)
.isAssignableFromType(refType) &&
refType.typeArguments.length == 1) {
refType = (refType as InterfaceType).typeArguments[0];
refType = refType.typeArguments[0];
}
var modelType = firstModelAncestor(refType) ?? refType;
@ -175,7 +175,7 @@ Future<OrmBuildContext> buildOrmContext(
cache,
modelType.element as ClassElement,
ConstantReader(const TypeChecker.fromRuntime(Orm)
.firstAnnotationOf(modelType.element)),
.firstAnnotationOf(modelType.element!)),
buildStep,
resolver,
autoSnakeCaseNames);
@ -186,14 +186,14 @@ Future<OrmBuildContext> buildOrmContext(
cache,
through.element,
ConstantReader(const TypeChecker.fromRuntime(Serializable)
.firstAnnotationOf(modelType.element)),
.firstAnnotationOf(modelType.element!)),
buildStep,
resolver,
autoSnakeCaseNames);
}
var ormAnn = const TypeChecker.fromRuntime(Orm)
.firstAnnotationOf(modelType.element);
.firstAnnotationOf(modelType.element!);
if (ormAnn != null) {
foreignTable =
@ -201,7 +201,7 @@ Future<OrmBuildContext> buildOrmContext(
}
foreignTable ??=
pluralize(foreign.buildContext.modelClassNameRecase.snakeCase);
pluralize(foreign!.buildContext.modelClassNameRecase.snakeCase);
} on StackOverflowError {
throw UnsupportedError(
'There is an infinite cycle between ${clazz.name} and ${field.type.name}. This triggered a stack overflow.');
@ -233,7 +233,7 @@ Future<OrmBuildContext> buildOrmContext(
foreignKey ??= '${rc.snakeCase}_$localKey';
} else if (type == RelationshipType.belongsTo) {
foreignKey ??=
ctx.buildContext.resolveFieldName(keyName(foreign, 'foreign key'));
ctx.buildContext.resolveFieldName(keyName(foreign!, 'foreign key'));
localKey ??= '${rcc.snakeCase}_$foreignKey';
}
@ -271,7 +271,7 @@ Future<OrmBuildContext> buildOrmContext(
// 'foreignKey=$foreignKey, localKey=$localKey');
if (relation.type == RelationshipType.belongsTo) {
var name = ReCase(relation.localKey).camelCase;
var name = ReCase(relation.localKey!).camelCase;
ctx.buildContext.aliases[name] = relation.localKey;
if (!ctx.effectiveFields.any((f) => f.name == field.name)) {
@ -290,7 +290,7 @@ Future<OrmBuildContext> buildOrmContext(
ctx.relations[field.name] = relation;
} else {
if (column?.type == null) {
if (column.type == null) {
throw 'Cannot infer SQL column type for field "${ctx.buildContext.originalClassName}.${field.name}" with type "${field.type.displayName}".';
}
@ -315,7 +315,7 @@ Future<OrmBuildContext> buildOrmContext(
return ctx;
}
ColumnType inferColumnType(DartType type) {
ColumnType? inferColumnType(DartType type) {
if (const TypeChecker.fromRuntime(String).isAssignableFromType(type)) {
return ColumnType.varChar;
}
@ -345,18 +345,18 @@ ColumnType inferColumnType(DartType type) {
}
Column reviveColumn(ConstantReader cr) {
ColumnType columnType;
ColumnType? columnType;
var indexTypeObj = cr.peek('indexType')?.objectValue;
indexTypeObj ??= cr.revive().namedArguments['indexType'];
var columnObj =
cr.peek('type')?.objectValue?.getField('name')?.toStringValue();
cr.peek('type')?.objectValue.getField('name')?.toStringValue();
var indexType = IndexType.values[
indexTypeObj?.getField('index')?.toIntValue() ?? IndexType.none.index];
if (const TypeChecker.fromRuntime(PrimaryKey)
.isAssignableFromType(cr.objectValue.type)) {
.isAssignableFromType(cr.objectValue.type!)) {
indexType = IndexType.primaryKey;
}
@ -365,7 +365,7 @@ Column reviveColumn(ConstantReader cr) {
}
return Column(
isNullable: cr.peek('isNullable')?.boolValue,
isNullable: cr.peek('isNullable')?.boolValue ?? false,
length: cr.peek('length')?.intValue,
type: columnType,
indexType: indexType,
@ -378,7 +378,7 @@ const TypeChecker relationshipTypeChecker =
class OrmBuildContext {
final BuildContext buildContext;
final Orm ormAnnotation;
final String tableName;
final String? tableName;
final Map<String, Column> columns = {};
final List<FieldElement> effectiveFields = [];
@ -411,13 +411,13 @@ class RelationFieldImpl extends ShimFieldImpl {
String get originalFieldName => originalField.name;
PropertyAccessorElement get getter => originalField.getter;
PropertyAccessorElement? get getter => originalField.getter;
}
InterfaceType firstModelAncestor(DartType type) {
InterfaceType? firstModelAncestor(DartType? type) {
if (type is InterfaceType) {
if (type.superclass != null &&
const TypeChecker.fromRuntime(Model).isExactlyType(type.superclass)) {
const TypeChecker.fromRuntime(Model).isExactlyType(type.superclass!)) {
return type;
} else {
return type.superclass == null

View file

@ -31,7 +31,7 @@ TypeReference futureOf(String type) {
/// Builder that generates `.orm.g.dart`, with an abstract `FooOrm` class.
class OrmGenerator extends GeneratorForAnnotation<Orm> {
final bool autoSnakeCaseNames;
final bool? autoSnakeCaseNames;
OrmGenerator({this.autoSnakeCaseNames});
@ -48,7 +48,7 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
}
}
Library buildOrmLibrary(AssetId inputId, OrmBuildContext ctx) {
Library buildOrmLibrary(AssetId inputId, OrmBuildContext? ctx) {
return Library((lib) {
// Create `FooQuery` class
// Create `FooQueryWhere` class
@ -58,9 +58,9 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
});
}
Class buildQueryClass(OrmBuildContext ctx) {
Class buildQueryClass(OrmBuildContext? ctx) {
return Class((clazz) {
var rc = ctx.buildContext.modelClassNameRecase;
var rc = ctx!.buildContext.modelClassNameRecase;
var queryWhereType = refer('${rc.pascalCase}QueryWhere');
clazz
..name = '${rc.pascalCase}Query'
@ -80,7 +80,7 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
..annotations.add(refer('override'))
..type = MethodType.getter
..body = Block((b) {
var args = <String, Expression>{};
var args = <String?, Expression>{};
for (var field in ctx.effectiveFields) {
var name = ctx.buildContext.resolveFieldName(field.name);
@ -113,7 +113,7 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
..annotations.add(refer('override'))
..type = MethodType.getter
..body = Block((b) {
b.addExpression(literalString(ctx.tableName).returned);
b.addExpression(literalString(ctx.tableName!).returned);
});
}));
@ -126,7 +126,7 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
..body = Block((b) {
var names = ctx.effectiveFields
.map((f) =>
literalString(ctx.buildContext.resolveFieldName(f.name)))
literalString(ctx.buildContext.resolveFieldName(f.name)!))
.toList();
b.addExpression(literalConstList(names).returned);
});
@ -168,7 +168,7 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
..name = 'row'
..type = refer('List')))
..body = Block((b) {
int i = 0;
var i = 0;
var args = <String, Expression>{};
for (var field in ctx.effectiveFields) {
@ -211,12 +211,12 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
RelationshipType.belongsTo,
RelationshipType.hasMany
].contains(relation.type)) return;
var foreign = relation.foreign;
var foreign = relation.foreign!;
var skipToList = refer('row')
.property('skip')
.call([literalNum(i)])
.property('take')
.call([literalNum(relation.foreign.effectiveFields.length)])
.call([literalNum(relation.foreign!.effectiveFields.length)])
.property('toList')
.call([]);
var parsed = refer(
@ -236,7 +236,7 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
var blockStr = block.accept(DartEmitter());
var ifStr = 'if (row.length > $i) { $blockStr }';
b.statements.add(Code(ifStr));
i += relation.foreign.effectiveFields.length;
i += relation.foreign!.effectiveFields.length;
});
b.addExpression(refer('model').returned);
@ -277,10 +277,10 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
// Add any manual SQL expressions.
ctx.columns.forEach((name, col) {
if (col != null && col.hasExpression) {
if (col.hasExpression) {
var lhs = refer('expressions').index(
literalString(ctx.buildContext.resolveFieldName(name)));
var rhs = literalString(col.expression);
literalString(ctx.buildContext.resolveFieldName(name)!));
var rhs = literalString(col.expression!);
b.addExpression(lhs.assign(rhs));
}
});
@ -301,13 +301,15 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
// If this is a many-to-many, add the fields from the other object.
var additionalStrs = relation.foreign.effectiveFields.map((f) =>
relation.foreign.buildContext.resolveFieldName(f.name));
var additionalFields = additionalStrs.map(literalString);
var additionalStrs = relation.foreign!.effectiveFields.map(
(f) => relation.foreign!.buildContext
.resolveFieldName(f.name));
var additionalFields = additionalStrs
.map(literalString as Expression Function(String?));
var joinArgs = [relation.localKey, relation.foreignKey]
.map(literalString)
.toList();
.map(literalString as Function(String?))
.toList() as List<Expression>;
// In the case of a many-to-many, we don't generate a subquery field,
// as it easily leads to stack overflows.
@ -318,10 +320,10 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
// FROM users
// LEFT JOIN role_users ON role_users.user_id=users.id)
var foreignFields = additionalStrs
.map((f) => '${relation.foreign.tableName}.$f');
.map((f) => '${relation.foreign!.tableName}.$f');
var b = StringBuffer('(SELECT ');
// role_users.role_id
b.write('${relation.throughContext.tableName}');
b.write('${relation.throughContext!.tableName}');
b.write('.${relation.foreignKey}');
// , <user_fields>
b.write(foreignFields.isEmpty
@ -329,39 +331,39 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
: ', ' + foreignFields.join(', '));
// FROM users
b.write(' FROM ');
b.write(relation.foreign.tableName);
b.write(relation.foreign!.tableName);
// LEFT JOIN role_users
b.write(' LEFT JOIN ${relation.throughContext.tableName}');
b.write(' LEFT JOIN ${relation.throughContext!.tableName}');
// Figure out which field on the "through" table points to users (foreign).
var throughRelation =
relation.throughContext.relations.values.firstWhere((e) {
return e.foreignTable == relation.foreign.tableName;
relation.throughContext!.relations.values.firstWhere((e) {
return e.foreignTable == relation.foreign!.tableName;
}, orElse: () {
// _Role has a many-to-many to _User through _RoleUser, but
// _RoleUser has no relation pointing to _User.
var b = StringBuffer();
b.write(ctx.buildContext.modelClassName);
b.write('has a many-to-many relationship to ');
b.write(relation.foreign.buildContext.modelClassName);
b.write(relation.foreign!.buildContext.modelClassName);
b.write(' through ');
b.write(
relation.throughContext.buildContext.modelClassName);
relation.throughContext!.buildContext.modelClassName);
b.write(', but ');
b.write(
relation.throughContext.buildContext.modelClassName);
relation.throughContext!.buildContext.modelClassName);
b.write('has no relation pointing to ');
b.write(relation.foreign.buildContext.modelClassName);
b.write(relation.foreign!.buildContext.modelClassName);
b.write('.');
throw b.toString();
});
// ON role_users.user_id=users.id)
b.write(' ON ');
b.write('${relation.throughContext.tableName}');
b.write('${relation.throughContext!.tableName}');
b.write('.');
b.write(throughRelation.localKey);
b.write('=');
b.write(relation.foreign.tableName);
b.write(relation.foreign!.tableName);
b.write('.');
b.write(throughRelation.foreignKey);
b.write(')');
@ -377,7 +379,7 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
// There'll be a private `_field`, and then a getter, named `field`,
// that returns the subquery object.
var foreignQueryType = refer(
foreign.buildContext.modelClassNameRecase.pascalCase +
foreign!.buildContext.modelClassNameRecase.pascalCase +
'Query');
clazz
..fields.add(Field((b) => b
@ -415,7 +417,7 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
if (manyToMany.isNotEmpty) {
var outExprs = manyToMany.map<Expression>((e) {
var foreignTableName = e.value.throughContext.tableName;
var foreignTableName = e.value.throughContext!.tableName;
return CodeExpression(Code('''
(!(
trampoline.contains('${ctx.tableName}')
@ -495,9 +497,9 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
});
}
Class buildWhereClass(OrmBuildContext ctx) {
Class buildWhereClass(OrmBuildContext? ctx) {
return Class((clazz) {
var rc = ctx.buildContext.modelClassNameRecase;
var rc = ctx!.buildContext.modelClassNameRecase;
clazz
..name = '${rc.pascalCase}QueryWhere'
..extend = refer('QueryWhere');
@ -519,7 +521,7 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
// Add builders for each field
for (var field in ctx.effectiveNormalFields) {
var name = field.name;
String? name = field.name;
var args = <Expression>[];
DartType type;
Reference builderType;
@ -535,7 +537,9 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
isSpecialId(ctx, field)) {
builderType = TypeReference((b) => b
..symbol = 'NumericSqlExpressionBuilder'
..types.add(refer(isSpecialId(ctx, field) ? 'int' : type.name)));
..types.add(refer(isSpecialId(ctx, field)
? 'int'
: type.getDisplayString(withNullability: true))));
} else if (type is InterfaceType && type.element.isEnum) {
builderType = TypeReference((b) => b
..symbol = 'EnumSqlExpressionBuilder'
@ -555,7 +559,7 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
.isAssignableFromType(type)) {
builderType = refer('ListSqlExpressionBuilder');
} else if (ctx.relations.containsKey(field.name)) {
var relation = ctx.relations[field.name];
var relation = ctx.relations[field.name]!;
if (relation.type != RelationshipType.belongsTo) {
continue;
} else {
@ -566,7 +570,7 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
}
} else {
throw UnsupportedError(
'Cannot generate ORM code for field of type ${field.type.name}.');
'Cannot generate ORM code for field of type ${field.type.getDisplayString(withNullability: false)}.');
}
clazz.fields.add(Field((b) {
@ -579,7 +583,7 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
refer(field.name)
.assign(builderType.newInstance([
refer('query'),
literalString(ctx.buildContext.resolveFieldName(field.name))
literalString(ctx.buildContext.resolveFieldName(field.name)!)
].followedBy(args)))
.code,
);
@ -597,9 +601,9 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
});
}
Class buildValuesClass(OrmBuildContext ctx) {
Class buildValuesClass(OrmBuildContext? ctx) {
return Class((clazz) {
var rc = ctx.buildContext.modelClassNameRecase;
var rc = ctx!.buildContext.modelClassNameRecase;
clazz
..name = '${rc.pascalCase}QueryValues'
..extend = refer('MapQueryValues');
@ -611,7 +615,7 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
..annotations.add(refer('override'))
..type = MethodType.getter
..body = Block((b) {
var args = <String, Expression>{};
var args = <String?, Expression>{};
for (var field in ctx.effectiveFields) {
var fType = field.type;
@ -637,7 +641,7 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
var type = convertTypeReference(field.type);
clazz.methods.add(Method((b) {
var value = refer('values').index(literalString(name));
var value = refer('values').index(literalString(name!));
if (fType is InterfaceType && fType.element.isEnum) {
var asInt = value.asA(refer('int'));
@ -682,7 +686,7 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
..name = 'value'
..type = type))
..body =
refer('values').index(literalString(name)).assign(value).code;
refer('values').index(literalString(name!)).assign(value).code;
}));
}
@ -709,7 +713,7 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
var prop = refer('model').property(original);
// Add only if present
var target = refer('values').index(literalString(
ctx.buildContext.resolveFieldName(field.name)));
ctx.buildContext.resolveFieldName(field.name)!));
var foreign = field.relationship.throughContext ??
field.relationship.foreign;
var foreignField = field.relationship.findForeignField(ctx);

View file

@ -20,21 +20,21 @@ class ColumnReader {
bool get isNullable => reader.peek('isNullable')?.boolValue ?? true;
int get length => reader.peek('length')?.intValue;
int? get length => reader.peek('length')?.intValue;
DartObject get defaultValue => reader.peek('defaultValue')?.objectValue;
DartObject? get defaultValue => reader.peek('defaultValue')?.objectValue;
}
class RelationshipReader {
final int type;
final String localKey;
final String foreignKey;
final String foreignTable;
final bool cascadeOnDelete;
final DartType through;
final OrmBuildContext foreign;
final OrmBuildContext throughContext;
final JoinType joinType;
final String? localKey;
final String? foreignKey;
final String? foreignTable;
final bool? cascadeOnDelete;
final DartType? through;
final OrmBuildContext? foreign;
final OrmBuildContext? throughContext;
final JoinType? joinType;
const RelationshipReader(this.type,
{this.localKey,
@ -72,16 +72,16 @@ class RelationshipReader {
orElse: () {
throw '${ctx.buildContext.clazz.name} has no field that maps to the name "$localKey", '
'but it has a @HasMany() relation that expects such a field.';
});
} as FieldElement Function()?);
}
FieldElement findForeignField(OrmBuildContext ctx) {
var foreign = throughContext ?? this.foreign;
FieldElement findForeignField(OrmBuildContext? ctx) {
var foreign = throughContext ?? this.foreign!;
return foreign.effectiveFields.firstWhere(
(f) => foreign.buildContext.resolveFieldName(f.name) == foreignKey,
orElse: () {
throw '${foreign.buildContext.clazz.name} has no field that maps to the name "$foreignKey", '
'but ${ctx.buildContext.clazz.name} has a @HasMany() relation that expects such a field.';
});
'but ${ctx!.buildContext.clazz.name} has a @HasMany() relation that expects such a field.';
} as FieldElement Function()?);
}
}

View file

@ -4,54 +4,59 @@ description: Code generators for Angel's ORM. Generates query builder classes.
homepage: https://github.com/dukefirehawk/angel
publish_to: none
environment:
sdk: '>=2.10.0 <3.0.0'
sdk: '>=2.12.0 <3.0.0'
dependencies:
analyzer: ">=0.35.0 <2.0.0"
analyzer: ^1.5.0
angel_model:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
ref: sdk-2.12.x_nnbd
path: packages/model
angel_serialize:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
ref: sdk-2.12.x_nnbd
path: packages/serialize/angel_serialize
angel_orm:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
ref: sdk-2.12.x_nnbd
path: packages/orm/angel_orm
angel_serialize_generator:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
ref: sdk-2.12.x_nnbd
path: packages/serialize/angel_serialize_generator
build: ^1.0.0
build_config: ^0.4.0
code_builder: ^3.0.0
dart_style: ^1.0.0
inflection2: ^0.4.2
meta: ^1.0.0
path: ^1.0.0
recase: ^3.0.1
source_gen: ^0.9.0
inflection2:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x_nnbd
path: packages/inflection2
build: ^2.0.1
build_config: ^1.0.0
code_builder: ^4.0.0
dart_style: ^2.0.1
meta: ^1.3.0
path: ^1.8.0
recase: ^4.0.0
source_gen: ^1.0.0
collection: ^1.15.0-nullsafety.4
dev_dependencies:
angel_framework:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
ref: sdk-2.12.x_nnbd
path: packages/framework
angel_migration:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
ref: sdk-2.12.x_nnbd
path: packages/orm/angel_migration
#angel_test: ^1.0.0
build_runner: ^1.11.5
collection: ^1.0.0
build_runner: ^2.0.1
collection: ^1.15.0
pedantic: ^1.11.0
postgres: ^2.2.0
postgres: ^2.3.2
# git:
# url: https://github.com/dukefirehawk/postgresql-dart
test: ^1.16.5
test: ^1.17.3