MigrationGenerator almost ported

This commit is contained in:
Tobe O 2018-03-25 13:19:10 -04:00
parent 23f776c2f9
commit b57067cae4
5 changed files with 157 additions and 146 deletions

View file

@ -0,0 +1,5 @@
import 'package:code_builder/code_builder.dart';
final TypeReference $void = new TypeReference((b) => b.symbol = 'void');
final Expression override = new CodeExpression(new Code('override'));

View file

@ -2,17 +2,19 @@ import 'dart:async';
import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/element.dart';
import 'package:angel_orm/angel_orm.dart'; import 'package:angel_orm/angel_orm.dart';
import 'package:build/build.dart'; import 'package:build/build.dart';
import 'package:code_builder/dart/core.dart';
import 'package:code_builder/code_builder.dart'; import 'package:code_builder/code_builder.dart';
import 'package:source_gen/source_gen.dart' hide LibraryBuilder; import 'package:source_gen/source_gen.dart' hide LibraryBuilder;
import 'build_context.dart'; import 'build_context.dart';
import 'postgres_build_context.dart'; import 'postgres_build_context.dart';
import 'lib_core.dart' as lib$core;
class MigrationGenerator extends GeneratorForAnnotation<ORM> { class MigrationGenerator extends GeneratorForAnnotation<ORM> {
static final ParameterBuilder _schemaParam = parameter('schema', [ static final Parameter _schemaParam = new Parameter((b) {
new TypeBuilder('Schema'), b
]); ..name = 'schema'
static final ReferenceBuilder _schema = reference('schema'); ..type = new TypeReference((b) => b.symbol = 'Schema');
});
static final Expression _schema = new CodeExpression(new Code('schema'));
/// 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;
@ -37,165 +39,168 @@ class MigrationGenerator extends GeneratorForAnnotation<ORM> {
resolver, autoSnakeCaseNames != false, autoIdAndDateFields != false); resolver, autoSnakeCaseNames != false, autoIdAndDateFields != false);
var lib = generateMigrationLibrary(ctx, element, resolver, buildStep); var lib = generateMigrationLibrary(ctx, element, resolver, buildStep);
if (lib == null) return null; if (lib == null) return null;
return prettyToSource(lib.buildAst()); var emitter = new DartEmitter();
return lib.accept(emitter).toString();
} }
LibraryBuilder generateMigrationLibrary(PostgresBuildContext ctx, Library generateMigrationLibrary(PostgresBuildContext ctx,
ClassElement element, Resolver resolver, BuildStep buildStep) { ClassElement element, Resolver resolver, BuildStep buildStep) {
var lib = new LibraryBuilder() return new Library((lib) {
..addDirective( lib.directives.add([
new ImportBuilder('package:angel_migration/angel_migration.dart')); new Directive.import('package:angel_migration/angel_migration.dart'),
]);
var clazz = new ClassBuilder('${ctx.modelClassName}Migration', lib.body.add(new Class((b) {
asExtends: new TypeBuilder('Migration')); b.name = '${ctx.modelClassName}Migration';
clazz..addMethod(buildUpMigration(ctx, lib))..addMethod(buildDownMigration(ctx)); b.extend = new Reference('Migration');
}));
return lib..addMember(clazz); lib.methods.add(buildUpMigration(ctx, lib));
lib.methods.add(buildDownMigration(ctx));
});
} }
MethodBuilder buildUpMigration(PostgresBuildContext ctx, LibraryBuilder lib) { Method buildUpMigration(PostgresBuildContext ctx, LibraryBuilder lib) {
var meth = new MethodBuilder('up')..addPositional(_schemaParam); return new Method((meth) {
var closure = new MethodBuilder.closure() meth.name = 'up';
..addPositional(parameter('table')); meth.annotations.add(lib$core.override);
var table = reference('table'); meth.requiredParameters.add(_schemaParam);
List<String> dup = []; var closure = new Method((closure) {
bool hasOrmImport = false; closure.requiredParameters.add(new Parameter((b) => b.name = 'table'));
ctx.columnInfo.forEach((name, col) { var table = new Reference('table');
var key = ctx.resolveFieldName(name);
if (dup.contains(key)) List<String> dup = [];
return; bool hasOrmImport = false;
else { ctx.columnInfo.forEach((name, col) {
if (key != 'id' || autoIdAndDateFields == false) { var key = ctx.resolveFieldName(name);
// Check for relationships that might duplicate
for (var rName in ctx.relationships.keys) { if (dup.contains(key))
var relationship = ctx.populateRelationship(rName); return;
if (relationship.localKey == key) return; else {
if (key != 'id' || autoIdAndDateFields == false) {
// Check for relationships that might duplicate
for (var rName in ctx.relationships.keys) {
var relationship = ctx.populateRelationship(rName);
if (relationship.localKey == key) return;
}
}
dup.add(key);
} }
}
dup.add(key); String methodName;
} List<Expression> positional = [literal(key)];
Map<String, Expression> named = {};
String methodName; if (autoIdAndDateFields != false && name == 'id') methodName = 'serial';
List<ExpressionBuilder> positional = [literal(key)];
Map<String, ExpressionBuilder> named = {};
if (autoIdAndDateFields != false && name == 'id') methodName = 'serial'; if (methodName == null) {
switch (col.type) {
case ColumnType.VAR_CHAR:
methodName = 'varchar';
if (col.length != null) named['length'] = literal(col.length);
break;
case ColumnType.SERIAL:
methodName = 'serial';
break;
case ColumnType.INT:
methodName = 'integer';
break;
case ColumnType.FLOAT:
methodName = 'float';
break;
case ColumnType.NUMERIC:
methodName = 'numeric';
break;
case ColumnType.BOOLEAN:
methodName = 'boolean';
break;
case ColumnType.DATE:
methodName = 'date';
break;
case ColumnType.DATE_TIME:
methodName = 'dateTime';
break;
case ColumnType.TIME_STAMP:
methodName = 'timeStamp';
break;
default:
if (!hasOrmImport) {
hasOrmImport = true;
lib.directives.add(new Directive.import('package:angel_orm/angel_orm.dart'));
}
if (methodName == null) { Expression provColumn;
switch (col.type) {
case ColumnType.VAR_CHAR: if (col.length == null) {
methodName = 'varchar'; methodName = 'declare';
if (col.length != null) named['length'] = literal(col.length); provColumn = new CodeExpression(new Code("new ColumnType('${col.type.name}')"));
break; } else {
case ColumnType.SERIAL: methodName = 'declareColumn';
methodName = 'serial'; provColumn = new CodeExpression(new Code("new Column({type: new Column('${col.type.name}'), length: ${col.length})"));
break; }
case ColumnType.INT:
methodName = 'integer'; positional.add(provColumn);
break; break;
case ColumnType.FLOAT:
methodName = 'float';
break;
case ColumnType.NUMERIC:
methodName = 'numeric';
break;
case ColumnType.BOOLEAN:
methodName = 'boolean';
break;
case ColumnType.DATE:
methodName = 'date';
break;
case ColumnType.DATE_TIME:
methodName = 'dateTime';
break;
case ColumnType.TIME_STAMP:
methodName = 'timeStamp';
break;
default:
if (!hasOrmImport) {
hasOrmImport = true;
lib.addDirective(new ImportBuilder('package:angel_orm/angel_orm.dart'));
} }
}
ExpressionBuilder provColumn; var field = table.property(methodName).call(positional, named);
var colType = new TypeBuilder('Column'); var cascade = <Expression Function(Expression)>[];
var columnTypeType = new TypeBuilder('ColumnType');
if (col.length == null) { if (col.defaultValue != null) {
methodName = 'declare'; cascade
provColumn = columnTypeType.newInstance([ .add((e) => e.property('defaultsTo').call([literal(col.defaultValue)]));
literal(col.type.name), }
]);
} else {
methodName = 'declareColumn';
provColumn = colType.newInstance([], named: {
'type': columnTypeType.newInstance([
literal(col.type.name),
]),
'length': literal(col.length),
});
}
positional.add(provColumn); if (col.index == IndexType.PRIMARY_KEY ||
break; (autoIdAndDateFields != false && name == 'id'))
} cascade.add((e) => e.property('primaryKey').call([]));
} else if (col.index == IndexType.UNIQUE)
cascade.add((e) => e.property('unique').call([]));
var field = table.invoke(methodName, positional, namedArguments: named); if (col.nullable != true) cascade.add((e) => e.property('notNull').call([]));
var cascade = <ExpressionBuilder Function(ExpressionBuilder)>[];
if (col.defaultValue != null) { field = cascade.isEmpty
cascade.add((e) => e.invoke('defaultsTo', [literal(col.defaultValue)])); ? field
} : field.cascade((e) => cascade.map((f) => f(e)).toList());
closure.addStatement(field);
});
if (col.index == IndexType.PRIMARY_KEY || ctx.relationships.forEach((name, r) {
(autoIdAndDateFields != false && name == 'id')) var relationship = ctx.populateRelationship(name);
cascade.add((e) => e.invoke('primaryKey', []));
else if (col.index == IndexType.UNIQUE)
cascade.add((e) => e.invoke('unique', []));
if (col.nullable != true) cascade.add((e) => e.invoke('notNull', [])); if (relationship.isBelongsTo) {
var key = relationship.localKey;
field = cascade.isEmpty var field = table.property('integer').call([literal(key)]);
? field // .references('user', 'id').onDeleteCascade()
: field.cascade((e) => cascade.map((f) => f(e)).toList()); var ref = field.property('references').call([
closure.addStatement(field); literal(relationship.foreignTable),
literal(relationship.foreignKey),
]);
if (relationship.cascadeOnDelete != false && relationship.isSingular)
ref = ref.property('onDeleteCascade').call([]);
return closure.addStatement(ref);
}
});
meth.addStatement(_schema.property('create').call([
literal(ctx.tableName),
closure,
]));
});
}); });
ctx.relationships.forEach((name, r) {
var relationship = ctx.populateRelationship(name);
if (relationship.isBelongsTo) {
var key = relationship.localKey;
var field = table.invoke('integer', [literal(key)]);
// .references('user', 'id').onDeleteCascade()
var ref = field.invoke('references', [
literal(relationship.foreignTable),
literal(relationship.foreignKey),
]);
if (relationship.cascadeOnDelete != false && relationship.isSingular)
ref = ref.invoke('onDeleteCascade', []);
return closure.addStatement(ref);
}
});
meth.addStatement(_schema.invoke('create', [
literal(ctx.tableName),
closure,
]));
return meth..addAnnotation(lib$core.override);
} }
MethodBuilder buildDownMigration(PostgresBuildContext ctx) { Method buildDownMigration(PostgresBuildContext ctx) {
return method('down', [ return new Method((b) {
_schemaParam, b.name = 'down';
_schema.invoke('drop', [literal(ctx.tableName)]), b.requiredParameters.add(_schemaParam);
]) b.annotations.add(lib$core.override);
..addAnnotation(lib$core.override); b.body.add(new Code("schema.drop('${ctx.tableName}')"));
});
} }
} }

View file

@ -3,14 +3,13 @@ import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/element.dart';
import 'package:angel_orm/angel_orm.dart'; import 'package:angel_orm/angel_orm.dart';
import 'package:build/build.dart'; import 'package:build/build.dart';
import 'package:code_builder/dart/async.dart';
import 'package:code_builder/dart/core.dart';
import 'package:code_builder/code_builder.dart'; import 'package:code_builder/code_builder.dart';
import 'package:inflection/inflection.dart'; import 'package:inflection/inflection.dart';
import 'package:path/path.dart' as p; import 'package:path/path.dart' as p;
import 'package:recase/recase.dart'; import 'package:recase/recase.dart';
import 'package:source_gen/source_gen.dart' hide LibraryBuilder; import 'package:source_gen/source_gen.dart' hide LibraryBuilder;
import 'build_context.dart'; import 'build_context.dart';
import 'lib_core.dart' as lib$core;
import 'postgres_build_context.dart'; import 'postgres_build_context.dart';
const List<String> RELATIONS = const ['or']; const List<String> RELATIONS = const ['or'];

View file

@ -3,7 +3,6 @@ import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/element.dart';
import 'package:angel_orm/angel_orm.dart'; import 'package:angel_orm/angel_orm.dart';
import 'package:build/build.dart'; import 'package:build/build.dart';
import 'package:code_builder/dart/core.dart';
import 'package:code_builder/code_builder.dart'; import 'package:code_builder/code_builder.dart';
import 'package:path/path.dart' as p; import 'package:path/path.dart' as p;
import 'package:recase/recase.dart'; import 'package:recase/recase.dart';

View file

@ -7,8 +7,8 @@ environment:
sdk: ">=1.19.0" sdk: ">=1.19.0"
dependencies: dependencies:
angel_orm: ^1.0.0-alpha angel_orm: ^1.0.0-alpha
angel_serialize_generator: ^1.0.0-alpha angel_serialize_generator: ^2.0.0
code_builder: ^1.0.0 code_builder: ^3.0.0
inflection: ^0.4.1 inflection: ^0.4.1
meta: ^1.0.0 meta: ^1.0.0
recase: ^1.0.0 recase: ^1.0.0
@ -17,5 +17,8 @@ dev_dependencies:
angel_framework: ^1.0.0 angel_framework: ^1.0.0
angel_migration: ^1.0.0-alpha angel_migration: ^1.0.0-alpha
angel_test: ^1.0.0 angel_test: ^1.0.0
build_runner: ^0.5.0 build_runner: ^0.7.0
test: ^0.12.0 test: ^0.12.0
dependency_overrides:
angel_orm:
path: ../angel_orm