Apply pedantic lints

This commit is contained in:
Tobe O 2019-08-17 18:35:55 -04:00
parent 9f22b0e3b4
commit 30914cc767
5 changed files with 159 additions and 142 deletions

View file

@ -7,13 +7,13 @@ import 'package:angel_serialize/angel_serialize.dart';
part 'main.g.dart'; part 'main.g.dart';
main() async { main() async {
var query = new EmployeeQuery() var query = EmployeeQuery()
..where.firstName.equals('Rich') ..where.firstName.equals('Rich')
..where.lastName.equals('Person') ..where.lastName.equals('Person')
..orWhere((w) => w.salary.greaterThanOrEqualTo(75000)) ..orWhere((w) => w.salary.greaterThanOrEqualTo(75000))
..join('companies', 'company_id', 'id'); ..join('companies', 'company_id', 'id');
var richPerson = await query.getOne(new _FakeExecutor()); var richPerson = await query.getOne(_FakeExecutor());
print(richPerson.toJson()); print(richPerson.toJson());
} }
@ -24,7 +24,7 @@ class _FakeExecutor extends QueryExecutor {
Future<List<List>> query( Future<List<List>> query(
String tableName, String query, Map<String, dynamic> substitutionValues, String tableName, String query, Map<String, dynamic> substitutionValues,
[returningFields]) async { [returningFields]) async {
var now = new DateTime.now(); var now = DateTime.now();
print( print(
'_FakeExecutor received query: $query and values: $substitutionValues'); '_FakeExecutor received query: $query and values: $substitutionValues');
return [ return [
@ -34,7 +34,7 @@ class _FakeExecutor extends QueryExecutor {
@override @override
Future<T> transaction<T>(FutureOr<T> Function(QueryExecutor) f) { Future<T> transaction<T>(FutureOr<T> Function(QueryExecutor) f) {
throw new UnsupportedError('Transactions are not supported.'); throw UnsupportedError('Transactions are not supported.');
} }
} }

View file

@ -11,14 +11,14 @@ import 'package:source_gen/source_gen.dart' hide LibraryBuilder;
import 'orm_build_context.dart'; import 'orm_build_context.dart';
Builder migrationBuilder(BuilderOptions options) { Builder migrationBuilder(BuilderOptions options) {
return new SharedPartBuilder([ return SharedPartBuilder([
new MigrationGenerator( MigrationGenerator(
autoSnakeCaseNames: options.config['auto_snake_case_names'] != false) autoSnakeCaseNames: options.config['auto_snake_case_names'] != false)
], 'angel_migration'); ], 'angel_migration');
} }
class MigrationGenerator extends GeneratorForAnnotation<Orm> { class MigrationGenerator extends GeneratorForAnnotation<Orm> {
static final Parameter _schemaParam = new Parameter((b) => b static final Parameter _schemaParam = Parameter((b) => b
..name = 'schema' ..name = 'schema'
..type = refer('Schema')); ..type = refer('Schema'));
static final Reference _schema = refer('schema'); static final Reference _schema = refer('schema');
@ -26,13 +26,14 @@ class MigrationGenerator extends GeneratorForAnnotation<Orm> {
/// If `true` (default), then field names will automatically be (de)serialized as snake_case. /// If `true` (default), then field names will automatically be (de)serialized as snake_case.
final bool autoSnakeCaseNames; final bool autoSnakeCaseNames;
const MigrationGenerator({this.autoSnakeCaseNames: true}); const MigrationGenerator({this.autoSnakeCaseNames = true});
@override @override
Future<String> generateForAnnotatedElement( Future<String> generateForAnnotatedElement(
Element element, ConstantReader annotation, BuildStep buildStep) async { Element element, ConstantReader annotation, BuildStep buildStep) async {
if (element is! ClassElement) if (element is! ClassElement) {
throw 'Only classes can be annotated with @ORM().'; throw 'Only classes can be annotated with @ORM().';
}
var generateMigrations = var generateMigrations =
annotation.peek('generateMigrations')?.boolValue ?? true; annotation.peek('generateMigrations')?.boolValue ?? true;
@ -47,13 +48,13 @@ class MigrationGenerator extends GeneratorForAnnotation<Orm> {
var lib = generateMigrationLibrary( var lib = generateMigrationLibrary(
ctx, element as ClassElement, resolver, buildStep); ctx, element as ClassElement, resolver, buildStep);
if (lib == null) return null; if (lib == null) return null;
return new DartFormatter().format(lib.accept(new DartEmitter()).toString()); return DartFormatter().format(lib.accept(DartEmitter()).toString());
} }
Library generateMigrationLibrary(OrmBuildContext ctx, ClassElement element, Library generateMigrationLibrary(OrmBuildContext ctx, ClassElement element,
Resolver resolver, BuildStep buildStep) { Resolver resolver, BuildStep buildStep) {
return new Library((lib) { return Library((lib) {
lib.body.add(new Class((clazz) { lib.body.add(Class((clazz) {
clazz clazz
..name = '${ctx.buildContext.modelClassName}Migration' ..name = '${ctx.buildContext.modelClassName}Migration'
..extend = refer('Migration') ..extend = refer('Migration')
@ -64,7 +65,7 @@ class MigrationGenerator extends GeneratorForAnnotation<Orm> {
} }
Method buildUpMigration(OrmBuildContext ctx, LibraryBuilder lib) { Method buildUpMigration(OrmBuildContext ctx, LibraryBuilder lib) {
return new Method((meth) { return Method((meth) {
var autoIdAndDateFields = const TypeChecker.fromRuntime(Model) var autoIdAndDateFields = const TypeChecker.fromRuntime(Model)
.isAssignableFromType(ctx.buildContext.clazz.type); .isAssignableFromType(ctx.buildContext.clazz.type);
meth meth
@ -72,20 +73,20 @@ class MigrationGenerator extends GeneratorForAnnotation<Orm> {
..annotations.add(refer('override')) ..annotations.add(refer('override'))
..requiredParameters.add(_schemaParam); ..requiredParameters.add(_schemaParam);
//var closure = new Method.closure()..addPositional(parameter('table')); //var closure = Method.closure()..addPositional(parameter('table'));
var closure = new Method((closure) { var closure = Method((closure) {
closure closure
..requiredParameters.add(new Parameter((b) => b..name = 'table')) ..requiredParameters.add(Parameter((b) => b..name = 'table'))
..body = new Block((closureBody) { ..body = Block((closureBody) {
var table = refer('table'); var table = refer('table');
List<String> dup = []; List<String> dup = [];
ctx.columns.forEach((name, col) { ctx.columns.forEach((name, col) {
var key = ctx.buildContext.resolveFieldName(name); var key = ctx.buildContext.resolveFieldName(name);
if (dup.contains(key)) if (dup.contains(key)) {
return; return;
else { } else {
// if (key != 'id' || autoIdAndDateFields == false) { // if (key != 'id' || autoIdAndDateFields == false) {
// // Check for relationships that might duplicate // // Check for relationships that might duplicate
// for (var rName in ctx.relations.keys) { // for (var rName in ctx.relations.keys) {
@ -111,15 +112,17 @@ class MigrationGenerator extends GeneratorForAnnotation<Orm> {
List<Expression> positional = [literal(key)]; List<Expression> positional = [literal(key)];
Map<String, Expression> named = {}; Map<String, Expression> named = {};
if (autoIdAndDateFields != false && name == 'id') if (autoIdAndDateFields != false && name == 'id') {
methodName = 'serial'; methodName = 'serial';
}
if (methodName == null) { if (methodName == null) {
switch (col.type) { switch (col.type) {
case ColumnType.varChar: case ColumnType.varChar:
methodName = 'varChar'; methodName = 'varChar';
if (col.length != null) if (col.length != null) {
named['length'] = literal(col.length); named['length'] = literal(col.length);
}
break; break;
case ColumnType.serial: case ColumnType.serial:
methodName = 'serial'; methodName = 'serial';
@ -196,13 +199,14 @@ class MigrationGenerator extends GeneratorForAnnotation<Orm> {
// Definitely an analyzer issue. // Definitely an analyzer issue.
} }
} else { } else {
defaultExpr = new CodeExpression( defaultExpr = CodeExpression(
new Code(dartObjectToString(defaultValue)), Code(dartObjectToString(defaultValue)),
); );
} }
if (defaultExpr != null) if (defaultExpr != null) {
cascade.add(refer('defaultsTo').call([defaultExpr])); cascade.add(refer('defaultsTo').call([defaultExpr]));
}
} }
if (col.indexType == IndexType.primaryKey || if (col.indexType == IndexType.primaryKey ||
@ -212,20 +216,20 @@ class MigrationGenerator extends GeneratorForAnnotation<Orm> {
cascade.add(refer('unique').call([])); cascade.add(refer('unique').call([]));
} }
if (col.isNullable != true) if (col.isNullable != true) {
cascade.add(refer('notNull').call([])); cascade.add(refer('notNull').call([]));
}
if (cascade.isNotEmpty) { if (cascade.isNotEmpty) {
var b = new StringBuffer() var b = StringBuffer()..writeln(field.accept(DartEmitter()));
..writeln(field.accept(new DartEmitter()));
for (var ex in cascade) { for (var ex in cascade) {
b b
..write('..') ..write('..')
..writeln(ex.accept(new DartEmitter())); ..writeln(ex.accept(DartEmitter()));
} }
field = new CodeExpression(new Code(b.toString())); field = CodeExpression(Code(b.toString()));
} }
closureBody.addExpression(field); closureBody.addExpression(field);
@ -259,15 +263,16 @@ class MigrationGenerator extends GeneratorForAnnotation<Orm> {
if (relationship.cascadeOnDelete != false && if (relationship.cascadeOnDelete != false &&
const [RelationshipType.hasOne, RelationshipType.belongsTo] const [RelationshipType.hasOne, RelationshipType.belongsTo]
.contains(relationship.type)) .contains(relationship.type)) {
ref = ref.property('onDeleteCascade').call([]); ref = ref.property('onDeleteCascade').call([]);
}
closureBody.addExpression(ref); closureBody.addExpression(ref);
} }
}); });
}); });
}); });
meth.body = new Block((b) { meth.body = Block((b) {
b.addExpression(_schema.property('create').call([ b.addExpression(_schema.property('create').call([
literal(ctx.tableName), literal(ctx.tableName),
closure.closure, closure.closure,
@ -277,12 +282,12 @@ class MigrationGenerator extends GeneratorForAnnotation<Orm> {
} }
Method buildDownMigration(OrmBuildContext ctx) { Method buildDownMigration(OrmBuildContext ctx) {
return new Method((b) { return Method((b) {
b b
..name = 'down' ..name = 'down'
..annotations.add(refer('override')) ..annotations.add(refer('override'))
..requiredParameters.add(_schemaParam) ..requiredParameters.add(_schemaParam)
..body = new Block((b) { ..body = Block((b) {
var named = <String, Expression>{}; var named = <String, Expression>{};
if (ctx.relations.values.any((r) => if (ctx.relations.values.any((r) =>

View file

@ -43,7 +43,7 @@ FieldElement findPrimaryFieldInList(
var columnAnnotation = columnTypeChecker.firstAnnotationOf(element); var columnAnnotation = columnTypeChecker.firstAnnotationOf(element);
if (columnAnnotation != null) { if (columnAnnotation != null) {
var column = reviveColumn(new ConstantReader(columnAnnotation)); var column = reviveColumn(ConstantReader(columnAnnotation));
// print( // print(
// ' * Found column on ${field.name} with indexType = ${column.indexType}'); // ' * Found column on ${field.name} with indexType = ${column.indexType}');
// print(element.metadata); // print(element.metadata);
@ -66,7 +66,7 @@ Future<OrmBuildContext> buildOrmContext(
BuildStep buildStep, BuildStep buildStep,
Resolver resolver, Resolver resolver,
bool autoSnakeCaseNames, bool autoSnakeCaseNames,
{bool heedExclude: true}) async { {bool heedExclude = true}) async {
// Check for @generatedSerializable // Check for @generatedSerializable
// ignore: unused_local_variable // ignore: unused_local_variable
DartObject generatedSerializable; DartObject generatedSerializable;
@ -88,12 +88,12 @@ Future<OrmBuildContext> buildOrmContext(
var ormAnnotation = reviveORMAnnotation(annotation); var ormAnnotation = reviveORMAnnotation(annotation);
// print( // print(
// 'tableName (${annotation.objectValue.type.name}) => ${ormAnnotation.tableName} from ${clazz.name} (${annotation.revive().namedArguments})'); // 'tableName (${annotation.objectValue.type.name}) => ${ormAnnotation.tableName} from ${clazz.name} (${annotation.revive().namedArguments})');
var ctx = new OrmBuildContext( var ctx = OrmBuildContext(
buildCtx, buildCtx,
ormAnnotation, ormAnnotation,
(ormAnnotation.tableName?.isNotEmpty == true) (ormAnnotation.tableName?.isNotEmpty == true)
? ormAnnotation.tableName ? ormAnnotation.tableName
: pluralize(new ReCase(clazz.name).snakeCase)); : pluralize(ReCase(clazz.name).snakeCase));
_cache[id] = ctx; _cache[id] = ctx;
// Read all fields // Read all fields
@ -105,7 +105,7 @@ Future<OrmBuildContext> buildOrmContext(
// print('${element.name} => $columnAnnotation'); // print('${element.name} => $columnAnnotation');
if (columnAnnotation != null) { if (columnAnnotation != null) {
column = reviveColumn(new ConstantReader(columnAnnotation)); column = reviveColumn(ConstantReader(columnAnnotation));
} }
if (column == null && isSpecialId(ctx, field)) { if (column == null && isSpecialId(ctx, field)) {
@ -117,7 +117,7 @@ Future<OrmBuildContext> buildOrmContext(
if (column == null) { if (column == null) {
// Guess what kind of column this is... // Guess what kind of column this is...
column = new Column( column = Column(
type: inferColumnType( type: inferColumnType(
buildCtx.resolveSerializedFieldType(field.name), buildCtx.resolveSerializedFieldType(field.name),
), ),
@ -125,7 +125,7 @@ Future<OrmBuildContext> buildOrmContext(
} }
if (column != null && column.type == null) { if (column != null && column.type == null) {
column = new Column( column = Column(
isNullable: column.isNullable, isNullable: column.isNullable,
length: column.length, length: column.length,
indexType: column.indexType, indexType: column.indexType,
@ -139,7 +139,7 @@ Future<OrmBuildContext> buildOrmContext(
var ann = relationshipTypeChecker.firstAnnotationOf(el); var ann = relationshipTypeChecker.firstAnnotationOf(el);
if (ann != null) { if (ann != null) {
var cr = new ConstantReader(ann); var cr = ConstantReader(ann);
var rc = ctx.buildContext.modelClassNameRecase; var rc = ctx.buildContext.modelClassNameRecase;
var type = cr.read('type').intValue; var type = cr.read('type').intValue;
var localKey = cr.peek('localKey')?.stringValue; var localKey = cr.peek('localKey')?.stringValue;
@ -157,7 +157,7 @@ Future<OrmBuildContext> buildOrmContext(
isListOfModelType(field.type as InterfaceType)) || isListOfModelType(field.type as InterfaceType)) ||
isModelClass(field.type); isModelClass(field.type);
if (!canUse) { if (!canUse) {
throw new UnsupportedError( throw UnsupportedError(
'Cannot apply relationship to field "${field.name}" - ${field.type} is not assignable to Model.'); 'Cannot apply relationship to field "${field.name}" - ${field.type} is not assignable to Model.');
} else { } else {
try { try {
@ -174,7 +174,7 @@ Future<OrmBuildContext> buildOrmContext(
foreign = await buildOrmContext( foreign = await buildOrmContext(
modelType.element as ClassElement, modelType.element as ClassElement,
new ConstantReader(const TypeChecker.fromRuntime(Orm) ConstantReader(const TypeChecker.fromRuntime(Orm)
.firstAnnotationOf(modelType.element)), .firstAnnotationOf(modelType.element)),
buildStep, buildStep,
resolver, resolver,
@ -184,7 +184,7 @@ Future<OrmBuildContext> buildOrmContext(
if (through != null && through is InterfaceType) { if (through != null && through is InterfaceType) {
throughContext = await buildOrmContext( throughContext = await buildOrmContext(
through.element, through.element,
new ConstantReader(const TypeChecker.fromRuntime(Serializable) ConstantReader(const TypeChecker.fromRuntime(Serializable)
.firstAnnotationOf(modelType.element)), .firstAnnotationOf(modelType.element)),
buildStep, buildStep,
resolver, resolver,
@ -196,20 +196,20 @@ Future<OrmBuildContext> buildOrmContext(
if (ormAnn != null) { if (ormAnn != null) {
foreignTable = foreignTable =
new ConstantReader(ormAnn).peek('tableName')?.stringValue; ConstantReader(ormAnn).peek('tableName')?.stringValue;
} }
foreignTable ??= foreignTable ??=
pluralize(foreign.buildContext.modelClassNameRecase.snakeCase); pluralize(foreign.buildContext.modelClassNameRecase.snakeCase);
} on StackOverflowError { } on StackOverflowError {
throw new UnsupportedError( throw UnsupportedError(
'There is an infinite cycle between ${clazz.name} and ${field.type.name}. This triggered a stack overflow.'); 'There is an infinite cycle between ${clazz.name} and ${field.type.name}. This triggered a stack overflow.');
} }
} }
} }
// Fill in missing keys // Fill in missing keys
var rcc = new ReCase(field.name); var rcc = ReCase(field.name);
String keyName(OrmBuildContext ctx, String missing) { String keyName(OrmBuildContext ctx, String missing) {
var _keyName = var _keyName =
@ -236,7 +236,7 @@ Future<OrmBuildContext> buildOrmContext(
localKey ??= '${rcc.snakeCase}_$foreignKey'; localKey ??= '${rcc.snakeCase}_$foreignKey';
} }
var relation = new RelationshipReader( var relation = RelationshipReader(
type, type,
localKey: localKey, localKey: localKey,
foreignKey: foreignKey, foreignKey: foreignKey,
@ -251,28 +251,31 @@ Future<OrmBuildContext> buildOrmContext(
// 'foreignKey=$foreignKey, localKey=$localKey'); // 'foreignKey=$foreignKey, localKey=$localKey');
if (relation.type == RelationshipType.belongsTo) { if (relation.type == RelationshipType.belongsTo) {
var name = new ReCase(relation.localKey).camelCase; var name = ReCase(relation.localKey).camelCase;
ctx.buildContext.aliases[name] = relation.localKey; ctx.buildContext.aliases[name] = relation.localKey;
if (!ctx.effectiveFields.any((f) => f.name == field.name)) { if (!ctx.effectiveFields.any((f) => f.name == field.name)) {
var foreignField = relation.findForeignField(ctx); var foreignField = relation.findForeignField(ctx);
var foreign = relation.throughContext ?? relation.foreign; var foreign = relation.throughContext ?? relation.foreign;
var type = foreignField.type; var type = foreignField.type;
if (isSpecialId(foreign, foreignField)) if (isSpecialId(foreign, foreignField)) {
type = field.type.element.context.typeProvider.intType; type = field.type.element.context.typeProvider.intType;
var rf = new RelationFieldImpl(name, relation, type, field); }
var rf = RelationFieldImpl(name, relation, type, field);
ctx.effectiveFields.add(rf); ctx.effectiveFields.add(rf);
} }
} }
ctx.relations[field.name] = relation; ctx.relations[field.name] = relation;
} else { } 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}".'; throw 'Cannot infer SQL column type for field "${ctx.buildContext.originalClassName}.${field.name}" with type "${field.type.displayName}".';
}
ctx.columns[field.name] = column; ctx.columns[field.name] = column;
if (!ctx.effectiveFields.any((f) => f.name == field.name)) if (!ctx.effectiveFields.any((f) => f.name == field.name)) {
ctx.effectiveFields.add(field); ctx.effectiveFields.add(field);
}
} }
} }
@ -280,22 +283,30 @@ Future<OrmBuildContext> buildOrmContext(
} }
ColumnType inferColumnType(DartType type) { ColumnType inferColumnType(DartType type) {
if (const TypeChecker.fromRuntime(String).isAssignableFromType(type)) if (const TypeChecker.fromRuntime(String).isAssignableFromType(type)) {
return ColumnType.varChar; return ColumnType.varChar;
if (const TypeChecker.fromRuntime(int).isAssignableFromType(type)) }
if (const TypeChecker.fromRuntime(int).isAssignableFromType(type)) {
return ColumnType.int; return ColumnType.int;
if (const TypeChecker.fromRuntime(double).isAssignableFromType(type)) }
if (const TypeChecker.fromRuntime(double).isAssignableFromType(type)) {
return ColumnType.decimal; return ColumnType.decimal;
if (const TypeChecker.fromRuntime(num).isAssignableFromType(type)) }
if (const TypeChecker.fromRuntime(num).isAssignableFromType(type)) {
return ColumnType.numeric; return ColumnType.numeric;
if (const TypeChecker.fromRuntime(bool).isAssignableFromType(type)) }
if (const TypeChecker.fromRuntime(bool).isAssignableFromType(type)) {
return ColumnType.boolean; return ColumnType.boolean;
if (const TypeChecker.fromRuntime(DateTime).isAssignableFromType(type)) }
if (const TypeChecker.fromRuntime(DateTime).isAssignableFromType(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 (const TypeChecker.fromRuntime(List).isAssignableFromType(type)) }
if (const TypeChecker.fromRuntime(List).isAssignableFromType(type)) {
return ColumnType.jsonb; return ColumnType.jsonb;
}
if (type is InterfaceType && type.element.isEnum) return ColumnType.int; if (type is InterfaceType && type.element.isEnum) return ColumnType.int;
return null; return null;
} }
@ -317,10 +328,10 @@ Column reviveColumn(ConstantReader cr) {
} }
if (columnObj != null) { if (columnObj != null) {
columnType = new _ColumnType(columnObj); columnType = _ColumnType(columnObj);
} }
return new Column( return Column(
isNullable: cr.peek('isNullable')?.boolValue, isNullable: cr.peek('isNullable')?.boolValue,
length: cr.peek('length')?.intValue, length: cr.peek('length')?.intValue,
type: columnType, type: columnType,
@ -329,7 +340,7 @@ Column reviveColumn(ConstantReader cr) {
} }
const TypeChecker relationshipTypeChecker = const TypeChecker relationshipTypeChecker =
const TypeChecker.fromRuntime(Relationship); TypeChecker.fromRuntime(Relationship);
class OrmBuildContext { class OrmBuildContext {
final BuildContext buildContext; final BuildContext buildContext;

View file

@ -17,14 +17,14 @@ var floatTypes = [
]; ];
Builder ormBuilder(BuilderOptions options) { Builder ormBuilder(BuilderOptions options) {
return new SharedPartBuilder([ return SharedPartBuilder([
new OrmGenerator( OrmGenerator(
autoSnakeCaseNames: options.config['auto_snake_case_names'] != false) autoSnakeCaseNames: options.config['auto_snake_case_names'] != false)
], 'angel_orm'); ], 'angel_orm');
} }
TypeReference futureOf(String type) { TypeReference futureOf(String type) {
return new TypeReference((b) => b return TypeReference((b) => b
..symbol = 'Future' ..symbol = 'Future'
..types.add(refer(type))); ..types.add(refer(type)));
} }
@ -42,14 +42,14 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
var ctx = await buildOrmContext(element, annotation, buildStep, var ctx = await buildOrmContext(element, annotation, buildStep,
buildStep.resolver, autoSnakeCaseNames); buildStep.resolver, autoSnakeCaseNames);
var lib = buildOrmLibrary(buildStep.inputId, ctx); var lib = buildOrmLibrary(buildStep.inputId, ctx);
return lib.accept(new DartEmitter()).toString(); return lib.accept(DartEmitter()).toString();
} else { } else {
throw 'The @Orm() annotation can only be applied to classes.'; throw 'The @Orm() annotation can only be applied to classes.';
} }
} }
Library buildOrmLibrary(AssetId inputId, OrmBuildContext ctx) { Library buildOrmLibrary(AssetId inputId, OrmBuildContext ctx) {
return new Library((lib) { return Library((lib) {
// Create `FooQuery` class // Create `FooQuery` class
// Create `FooQueryWhere` class // Create `FooQueryWhere` class
lib.body.add(buildQueryClass(ctx)); lib.body.add(buildQueryClass(ctx));
@ -59,12 +59,12 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
} }
Class buildQueryClass(OrmBuildContext ctx) { Class buildQueryClass(OrmBuildContext ctx) {
return new Class((clazz) { return Class((clazz) {
var rc = ctx.buildContext.modelClassNameRecase; var rc = ctx.buildContext.modelClassNameRecase;
var queryWhereType = refer('${rc.pascalCase}QueryWhere'); var queryWhereType = refer('${rc.pascalCase}QueryWhere');
clazz clazz
..name = '${rc.pascalCase}Query' ..name = '${rc.pascalCase}Query'
..extend = new TypeReference((b) { ..extend = TypeReference((b) {
b b
..symbol = 'Query' ..symbol = 'Query'
..types.addAll([ ..types.addAll([
@ -96,7 +96,7 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
})); }));
// Add values // Add values
clazz.fields.add(new Field((b) { clazz.fields.add(Field((b) {
var type = refer('${rc.pascalCase}QueryValues'); var type = refer('${rc.pascalCase}QueryValues');
b b
..name = 'values' ..name = 'values'
@ -107,23 +107,23 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
})); }));
// Add tableName // Add tableName
clazz.methods.add(new Method((m) { clazz.methods.add(Method((m) {
m m
..name = 'tableName' ..name = 'tableName'
..annotations.add(refer('override')) ..annotations.add(refer('override'))
..type = MethodType.getter ..type = MethodType.getter
..body = new Block((b) { ..body = Block((b) {
b.addExpression(literalString(ctx.tableName).returned); b.addExpression(literalString(ctx.tableName).returned);
}); });
})); }));
// Add fields getter // Add fields getter
clazz.methods.add(new Method((m) { clazz.methods.add(Method((m) {
m m
..name = 'fields' ..name = 'fields'
..annotations.add(refer('override')) ..annotations.add(refer('override'))
..type = MethodType.getter ..type = MethodType.getter
..body = new Block((b) { ..body = Block((b) {
var names = ctx.effectiveFields var names = ctx.effectiveFields
.map((f) => .map((f) =>
literalString(ctx.buildContext.resolveFieldName(f.name))) literalString(ctx.buildContext.resolveFieldName(f.name)))
@ -133,41 +133,41 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
})); }));
// Add _where member // Add _where member
clazz.fields.add(new Field((b) { clazz.fields.add(Field((b) {
b b
..name = '_where' ..name = '_where'
..type = queryWhereType; ..type = queryWhereType;
})); }));
// Add where getter // Add where getter
clazz.methods.add(new Method((b) { clazz.methods.add(Method((b) {
b b
..name = 'where' ..name = 'where'
..type = MethodType.getter ..type = MethodType.getter
..returns = queryWhereType ..returns = queryWhereType
..annotations.add(refer('override')) ..annotations.add(refer('override'))
..body = new Block((b) => b.addExpression(refer('_where').returned)); ..body = Block((b) => b.addExpression(refer('_where').returned));
})); }));
// newWhereClause() // newWhereClause()
clazz.methods.add(new Method((b) { clazz.methods.add(Method((b) {
b b
..name = 'newWhereClause' ..name = 'newWhereClause'
..annotations.add(refer('override')) ..annotations.add(refer('override'))
..returns = queryWhereType ..returns = queryWhereType
..body = new Block((b) => b.addExpression( ..body = Block((b) => b.addExpression(
queryWhereType.newInstance([refer('this')]).returned)); queryWhereType.newInstance([refer('this')]).returned));
})); }));
// Add deserialize() // Add deserialize()
clazz.methods.add(new Method((m) { clazz.methods.add(Method((m) {
m m
..name = 'parseRow' ..name = 'parseRow'
..static = true ..static = true
..returns = ctx.buildContext.modelClassType ..returns = ctx.buildContext.modelClassType
..requiredParameters.add(new Parameter((b) => b ..requiredParameters.add(Parameter((b) => b
..name = 'row' ..name = 'row'
..type = refer('List'))) ..type = refer('List')))
..body = new Block((b) { ..body = Block((b) {
int i = 0; int i = 0;
var args = <String, Expression>{}; var args = <String, Expression>{};
@ -177,11 +177,11 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
if (isSpecialId(ctx, field)) type = refer('int'); if (isSpecialId(ctx, field)) type = refer('int');
var expr = (refer('row').index(literalNum(i++))); var expr = (refer('row').index(literalNum(i++)));
if (isSpecialId(ctx, field)) if (isSpecialId(ctx, field)) {
expr = expr.property('toString').call([]); expr = expr.property('toString').call([]);
else if (field is RelationFieldImpl) } else if (field is RelationFieldImpl) {
continue; continue;
else if (ctx.columns[field.name]?.type == ColumnType.json) { } else if (ctx.columns[field.name]?.type == ColumnType.json) {
expr = refer('json') expr = refer('json')
.property('decode') .property('decode')
.call([expr.asA(refer('String'))]).asA(type); .call([expr.asA(refer('String'))]).asA(type);
@ -193,14 +193,15 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
var isNull = expr.equalTo(literalNull); var isNull = expr.equalTo(literalNull);
expr = isNull.conditional(literalNull, expr = isNull.conditional(literalNull,
type.property('values').index(expr.asA(refer('int')))); type.property('values').index(expr.asA(refer('int'))));
} else } else {
expr = expr.asA(type); expr = expr.asA(type);
}
args[field.name] = expr; args[field.name] = expr;
} }
b.statements b.statements
.add(new Code('if (row.every((x) => x == null)) return null;')); .add(Code('if (row.every((x) => x == null)) return null;'));
b.addExpression(ctx.buildContext.modelClassType b.addExpression(ctx.buildContext.modelClassType
.newInstance([], args).assignVar('model')); .newInstance([], args).assignVar('model'));
@ -230,11 +231,11 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
} }
var expr = var expr =
refer('model').property('copyWith').call([], {name: parsed}); refer('model').property('copyWith').call([], {name: parsed});
var block = new Block( var block =
(b) => b.addExpression(refer('model').assign(expr))); Block((b) => b.addExpression(refer('model').assign(expr)));
var blockStr = block.accept(new DartEmitter()); var blockStr = block.accept(DartEmitter());
var ifStr = 'if (row.length > $i) { $blockStr }'; var ifStr = 'if (row.length > $i) { $blockStr }';
b.statements.add(new Code(ifStr)); b.statements.add(Code(ifStr));
i += relation.foreign.effectiveFields.length; i += relation.foreign.effectiveFields.length;
}); });
@ -242,20 +243,20 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
}); });
})); }));
clazz.methods.add(new Method((m) { clazz.methods.add(Method((m) {
m m
..name = 'deserialize' ..name = 'deserialize'
..annotations.add(refer('override')) ..annotations.add(refer('override'))
..requiredParameters.add(new Parameter((b) => b ..requiredParameters.add(Parameter((b) => b
..name = 'row' ..name = 'row'
..type = refer('List'))) ..type = refer('List')))
..body = new Block((b) { ..body = Block((b) {
b.addExpression(refer('parseRow').call([refer('row')]).returned); b.addExpression(refer('parseRow').call([refer('row')]).returned);
}); });
})); }));
// If there are any relations, we need some overrides. // If there are any relations, we need some overrides.
clazz.constructors.add(new Constructor((b) { clazz.constructors.add(Constructor((b) {
b b
..optionalParameters.add(Parameter((b) => b ..optionalParameters.add(Parameter((b) => b
..named = true ..named = true
@ -263,7 +264,7 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
..type = TypeReference((b) => b ..type = TypeReference((b) => b
..symbol = 'Set' ..symbol = 'Set'
..types.add(refer('String'))))) ..types.add(refer('String')))))
..body = new Block((b) { ..body = Block((b) {
b.statements.addAll([ b.statements.addAll([
Code('trampoline ??= Set();'), Code('trampoline ??= Set();'),
Code('trampoline.add(tableName);'), Code('trampoline.add(tableName);'),
@ -331,12 +332,11 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
}); });
var out = outExprs.reduce((a, b) => a.and(b)); var out = outExprs.reduce((a, b) => a.and(b));
clazz.methods.add(new Method((b) { clazz.methods.add(Method((b) {
b b
..name = 'canCompile' ..name = 'canCompile'
..annotations.add(refer('override')) ..annotations.add(refer('override'))
..requiredParameters ..requiredParameters.add(Parameter((b) => b..name = 'trampoline'))
.add(new Parameter((b) => b..name = 'trampoline'))
..returns = refer('bool') ..returns = refer('bool')
..body = Block((b) { ..body = Block((b) {
b.addExpression(out.returned); b.addExpression(out.returned);
@ -346,16 +346,16 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
// TODO: Ultimately remove the insert override // TODO: Ultimately remove the insert override
if (false && ctx.relations.isNotEmpty) { if (false && ctx.relations.isNotEmpty) {
clazz.methods.add(new Method((b) { clazz.methods.add(Method((b) {
b b
..name = 'insert' ..name = 'insert'
..annotations.add(refer('override')) ..annotations.add(refer('override'))
..requiredParameters.add(new Parameter((b) => b..name = 'executor')) ..requiredParameters.add(Parameter((b) => b..name = 'executor'))
..body = new Block((b) { ..body = Block((b) {
var inTransaction = new Method((b) { var inTransaction = Method((b) {
b b
..modifier = MethodModifier.async ..modifier = MethodModifier.async
..body = new Block((b) { ..body = Block((b) {
b.addExpression(refer('super') b.addExpression(refer('super')
.property('insert') .property('insert')
.call([refer('executor')]) .call([refer('executor')])
@ -404,24 +404,24 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
// Create a Future<T> fetchLinked(T model, QueryExecutor), if necessary. // Create a Future<T> fetchLinked(T model, QueryExecutor), if necessary.
if (false && if (false &&
ctx.relations.values.any((r) => r.type == RelationshipType.hasMany)) { ctx.relations.values.any((r) => r.type == RelationshipType.hasMany)) {
clazz.methods.add(new Method((b) { clazz.methods.add(Method((b) {
b b
..name = 'fetchLinked' ..name = 'fetchLinked'
..modifier = MethodModifier.async ..modifier = MethodModifier.async
..returns = new TypeReference((b) { ..returns = TypeReference((b) {
b b
..symbol = 'Future' ..symbol = 'Future'
..types.add(ctx.buildContext.modelClassType); ..types.add(ctx.buildContext.modelClassType);
}) })
..requiredParameters.addAll([ ..requiredParameters.addAll([
new Parameter((b) => b Parameter((b) => b
..name = 'model' ..name = 'model'
..type = ctx.buildContext.modelClassType), ..type = ctx.buildContext.modelClassType),
new Parameter((b) => b Parameter((b) => b
..name = 'executor' ..name = 'executor'
..type = refer('QueryExecutor')), ..type = refer('QueryExecutor')),
]) ])
..body = new Block((b) { ..body = Block((b) {
var args = <String, Expression>{}; var args = <String, Expression>{};
ctx.relations.forEach((name, relation) { ctx.relations.forEach((name, relation) {
@ -443,9 +443,9 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
: 'model.${localField.name}'; : 'model.${localField.name}';
var cascadeText = var cascadeText =
'..where.${foreignField.name}.equals($queryValue)'; '..where.${foreignField.name}.equals($queryValue)';
var queryText = queryInstance.accept(new DartEmitter()); var queryText = queryInstance.accept(DartEmitter());
var combinedExpr = var combinedExpr =
new CodeExpression(new Code('($queryText$cascadeText)')); CodeExpression(Code('($queryText$cascadeText)'));
// Finally, just call get and await it. // Finally, just call get and await it.
var expr = combinedExpr var expr = combinedExpr
@ -466,12 +466,12 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
// execute in a transaction, and invoke fetchLinked. // execute in a transaction, and invoke fetchLinked.
if (ctx.relations.values.any((r) => r.type == RelationshipType.hasMany)) { if (ctx.relations.values.any((r) => r.type == RelationshipType.hasMany)) {
for (var methodName in const ['get', 'update', 'delete']) { for (var methodName in const ['get', 'update', 'delete']) {
clazz.methods.add(new Method((b) { clazz.methods.add(Method((b) {
var type = ctx.buildContext.modelClassType.accept(DartEmitter()); var type = ctx.buildContext.modelClassType.accept(DartEmitter());
b b
..name = methodName ..name = methodName
..annotations.add(refer('override')) ..annotations.add(refer('override'))
..requiredParameters.add(new Parameter((b) => b ..requiredParameters.add(Parameter((b) => b
..name = 'executor' ..name = 'executor'
..type = refer('QueryExecutor'))); ..type = refer('QueryExecutor')));
@ -500,7 +500,7 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
'@HasMany and @ManyToMany relations require a primary key to be defined on the model.'; '@HasMany and @ManyToMany relations require a primary key to be defined on the model.';
} }
b.body = new Code(''' b.body = Code('''
return super.$methodName(executor).then((result) { return super.$methodName(executor).then((result) {
return result.fold<List<$type>>([], (out, model) { return result.fold<List<$type>>([], (out, model) {
var idx = out.indexWhere((m) => m.$keyName == model.$keyName); var idx = out.indexWhere((m) => m.$keyName == model.$keyName);
@ -521,19 +521,19 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
} }
Class buildWhereClass(OrmBuildContext ctx) { Class buildWhereClass(OrmBuildContext ctx) {
return new Class((clazz) { return Class((clazz) {
var rc = ctx.buildContext.modelClassNameRecase; var rc = ctx.buildContext.modelClassNameRecase;
clazz clazz
..name = '${rc.pascalCase}QueryWhere' ..name = '${rc.pascalCase}QueryWhere'
..extend = refer('QueryWhere'); ..extend = refer('QueryWhere');
// Build expressionBuilders getter // Build expressionBuilders getter
clazz.methods.add(new Method((m) { clazz.methods.add(Method((m) {
m m
..name = 'expressionBuilders' ..name = 'expressionBuilders'
..annotations.add(refer('override')) ..annotations.add(refer('override'))
..type = MethodType.getter ..type = MethodType.getter
..body = new Block((b) { ..body = Block((b) {
var references = ctx.effectiveFields.map((f) => refer(f.name)); var references = ctx.effectiveFields.map((f) => refer(f.name));
b.addExpression(literalList(references).returned); b.addExpression(literalList(references).returned);
}); });
@ -557,11 +557,11 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
if (const TypeChecker.fromRuntime(int).isExactlyType(type) || if (const TypeChecker.fromRuntime(int).isExactlyType(type) ||
const TypeChecker.fromRuntime(double).isExactlyType(type) || const TypeChecker.fromRuntime(double).isExactlyType(type) ||
isSpecialId(ctx, field)) { isSpecialId(ctx, field)) {
builderType = new TypeReference((b) => b builderType = 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) { } else if (type is InterfaceType && type.element.isEnum) {
builderType = new TypeReference((b) => b builderType = TypeReference((b) => b
..symbol = 'EnumSqlExpressionBuilder' ..symbol = 'EnumSqlExpressionBuilder'
..types.add(convertTypeReference(type))); ..types.add(convertTypeReference(type)));
args.add(CodeExpression(Code('(v) => v.index'))); args.add(CodeExpression(Code('(v) => v.index')));
@ -580,20 +580,20 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
builderType = refer('ListSqlExpressionBuilder'); builderType = refer('ListSqlExpressionBuilder');
} else if (ctx.relations.containsKey(field.name)) { } else if (ctx.relations.containsKey(field.name)) {
var relation = ctx.relations[field.name]; var relation = ctx.relations[field.name];
if (relation.type != RelationshipType.belongsTo) if (relation.type != RelationshipType.belongsTo) {
continue; continue;
else { } else {
builderType = new TypeReference((b) => b builderType = TypeReference((b) => b
..symbol = 'NumericSqlExpressionBuilder' ..symbol = 'NumericSqlExpressionBuilder'
..types.add(refer('int'))); ..types.add(refer('int')));
name = relation.localKey; name = relation.localKey;
} }
} else { } else {
throw new UnsupportedError( throw UnsupportedError(
'Cannot generate ORM code for field of type ${field.type.name}.'); 'Cannot generate ORM code for field of type ${field.type.name}.');
} }
clazz.fields.add(new Field((b) { clazz.fields.add(Field((b) {
b b
..name = name ..name = name
..modifier = FieldModifier.final$ ..modifier = FieldModifier.final$
@ -611,9 +611,9 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
} }
// Now, just add a constructor that initializes each builder. // Now, just add a constructor that initializes each builder.
clazz.constructors.add(new Constructor((b) { clazz.constructors.add(Constructor((b) {
b b
..requiredParameters.add(new Parameter((b) => b ..requiredParameters.add(Parameter((b) => b
..name = 'query' ..name = 'query'
..type = refer('${rc.pascalCase}Query'))) ..type = refer('${rc.pascalCase}Query')))
..initializers.addAll(initializers); ..initializers.addAll(initializers);
@ -622,7 +622,7 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
} }
Class buildValuesClass(OrmBuildContext ctx) { Class buildValuesClass(OrmBuildContext ctx) {
return new Class((clazz) { return Class((clazz) {
var rc = ctx.buildContext.modelClassNameRecase; var rc = ctx.buildContext.modelClassNameRecase;
clazz clazz
..name = '${rc.pascalCase}QueryValues' ..name = '${rc.pascalCase}QueryValues'
@ -660,7 +660,7 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
var name = ctx.buildContext.resolveFieldName(field.name); var name = ctx.buildContext.resolveFieldName(field.name);
var type = convertTypeReference(field.type); var type = convertTypeReference(field.type);
clazz.methods.add(new Method((b) { 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) { if (fType is InterfaceType && fType.element.isEnum) {
@ -684,10 +684,10 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
..name = field.name ..name = field.name
..type = MethodType.getter ..type = MethodType.getter
..returns = type ..returns = type
..body = new Block((b) => b.addExpression(value.returned)); ..body = Block((b) => b.addExpression(value.returned));
})); }));
clazz.methods.add(new Method((b) { clazz.methods.add(Method((b) {
Expression value = refer('value'); Expression value = refer('value');
if (fType is InterfaceType && fType.element.isEnum) { if (fType is InterfaceType && fType.element.isEnum) {
@ -702,7 +702,7 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
b b
..name = field.name ..name = field.name
..type = MethodType.setter ..type = MethodType.setter
..requiredParameters.add(new Parameter((b) => b ..requiredParameters.add(Parameter((b) => b
..name = 'value' ..name = 'value'
..type = type)) ..type = type))
..body = ..body =
@ -711,17 +711,18 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
} }
// Add an copyFrom(model) // Add an copyFrom(model)
clazz.methods.add(new Method((b) { clazz.methods.add(Method((b) {
b b
..name = 'copyFrom' ..name = 'copyFrom'
..returns = refer('void') ..returns = refer('void')
..requiredParameters.add(new Parameter((b) => b ..requiredParameters.add(Parameter((b) => b
..name = 'model' ..name = 'model'
..type = ctx.buildContext.modelClassType)) ..type = ctx.buildContext.modelClassType))
..body = new Block((b) { ..body = Block((b) {
for (var field in ctx.effectiveFields) { for (var field in ctx.effectiveFields) {
if (isSpecialId(ctx, field) || field is RelationFieldImpl) if (isSpecialId(ctx, field) || field is RelationFieldImpl) {
continue; continue;
}
b.addExpression(refer(field.name) b.addExpression(refer(field.name)
.assign(refer('model').property(field.name))); .assign(refer('model').property(field.name)));
} }
@ -744,11 +745,11 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
} }
var cond = prop.notEqualTo(literalNull); var cond = prop.notEqualTo(literalNull);
var condStr = cond.accept(new DartEmitter()); var condStr = cond.accept(DartEmitter());
var blkStr = var blkStr =
new Block((b) => b.addExpression(target.assign(parsedId))) Block((b) => b.addExpression(target.assign(parsedId)))
.accept(new DartEmitter()); .accept(DartEmitter());
var ifStmt = new Code('if ($condStr) { $blkStr }'); var ifStmt = Code('if ($condStr) { $blkStr }');
b.statements.add(ifStmt); b.statements.add(ifStmt);
} }
} }

View file

@ -5,7 +5,7 @@ import 'package:angel_orm/angel_orm.dart';
import 'package:source_gen/source_gen.dart'; import 'package:source_gen/source_gen.dart';
import 'orm_build_context.dart'; import 'orm_build_context.dart';
const TypeChecker columnTypeChecker = const TypeChecker.fromRuntime(Column); const TypeChecker columnTypeChecker = TypeChecker.fromRuntime(Column);
Orm reviveORMAnnotation(ConstantReader reader) { Orm reviveORMAnnotation(ConstantReader reader) {
return Orm( return Orm(