gen migrations

This commit is contained in:
Tobe O 2018-12-09 12:44:16 -05:00
parent c6e00e9c98
commit b2e6c14386
24 changed files with 540 additions and 8 deletions

View file

@ -46,12 +46,19 @@ part 'car.g.dart';
@serializable
@orm
class _Car extends Model {
String make;
String description;
bool familyFriendly;
DateTime recalledAt;
abstract class _Car extends Model {
String get make;
String get description;
bool get familyFriendly;
DateTime get recalledAt;
}
// You can disable migration generation.
@Orm(generateMigrations: false)
abstract class _NoMigrations extends Model {}
```
Models can use the `@Alias()` annotation; `package:angel_orm` obeys it.

View file

@ -1,9 +1,17 @@
const Orm orm = const Orm();
class Orm {
/// The name of the table to query.
///
/// Inferred if not present.
final String tableName;
/// Whether to generate migrations for this model.
///
/// Defaults to [:true:].
final bool generateMigrations;
const Orm({this.tableName});
const Orm({this.tableName, this.generateMigrations: true});
}
class Join {

View file

@ -1,3 +1,6 @@
# 2.0.0-dev.1
* Generate migration files.
# 2.0.0-dev
* Dart 2 updates, and more.

View file

@ -2,11 +2,13 @@ builders:
angel_orm:
import: "package:angel_orm_generator/angel_orm_generator.dart"
builder_factories:
- migrationBuilder
- ormBuilder
auto_apply: root_package
build_to: cache
build_extensions:
.dart:
- ".angel_migration.g.part"
- ".angel_orm.g.part"
required_inputs:
- angel_serialize.g.part

View file

@ -1,4 +1,5 @@
//export 'src/mongodb_orm_generator.dart';
export 'src/migration_generator.dart';
export 'src/orm_build_context.dart';
export 'src/orm_generator.dart';
export 'src/readers.dart';

View file

@ -0,0 +1,246 @@
import 'dart:async';
import 'package:analyzer/dart/element/element.dart';
import 'package:angel_orm/angel_orm.dart';
import 'package:build/build.dart';
import 'package:code_builder/code_builder.dart';
import 'package:dart_style/dart_style.dart';
import 'package:source_gen/source_gen.dart' hide LibraryBuilder;
import 'orm_build_context.dart';
Builder migrationBuilder(BuilderOptions options) {
return new SharedPartBuilder([
new MigrationGenerator(
autoSnakeCaseNames: options.config['auto_snake_case_names'] != false,
autoIdAndDateFields: options.config['auto_id_and_date_fields'] != false)
], 'angel_migration');
}
class MigrationGenerator extends GeneratorForAnnotation<Orm> {
static final Parameter _schemaParam = new Parameter((b) => b
..name = 'schema'
..type = refer('Schema'));
static final Reference _schema = refer('schema');
/// If `true` (default), then field names will automatically be (de)serialized as snake_case.
final bool autoSnakeCaseNames;
/// If `true` (default), then the schema will automatically add id, created_at and updated_at fields.
final bool autoIdAndDateFields;
const MigrationGenerator(
{this.autoSnakeCaseNames: true, this.autoIdAndDateFields: true});
@override
Future<String> generateForAnnotatedElement(
Element element, ConstantReader annotation, BuildStep buildStep) async {
if (element is! ClassElement)
throw 'Only classes can be annotated with @ORM().';
var generateMigrations =
annotation.peek('generateMigrations')?.boolValue ?? true;
if (!generateMigrations) {
return null;
}
var resolver = await buildStep.resolver;
var ctx = await buildOrmContext(
element as ClassElement,
annotation,
buildStep,
resolver,
autoSnakeCaseNames != false,
autoIdAndDateFields != false);
var lib = generateMigrationLibrary(
ctx, element as ClassElement, resolver, buildStep);
if (lib == null) return null;
return new DartFormatter().format(lib.accept(new DartEmitter()).toString());
}
Library generateMigrationLibrary(OrmBuildContext ctx, ClassElement element,
Resolver resolver, BuildStep buildStep) {
return new Library((lib) {
lib.body.add(new Class((clazz) {
clazz
..name = '${ctx.buildContext.modelClassName}Migration'
..extend = refer('Migration')
..methods
.addAll([buildUpMigration(ctx, lib), buildDownMigration(ctx)]);
}));
});
}
Method buildUpMigration(OrmBuildContext ctx, LibraryBuilder lib) {
return new Method((meth) {
meth
..name = 'up'
..annotations.add(refer('override'))
..requiredParameters.add(_schemaParam);
//var closure = new Method.closure()..addPositional(parameter('table'));
var closure = new Method((closure) {
closure
..requiredParameters.add(new Parameter((b) => b..name = 'table'))
..body = new Block((closureBody) {
var table = refer('table');
List<String> dup = [];
ctx.columns.forEach((name, col) {
var key = ctx.buildContext.resolveFieldName(name);
if (dup.contains(key))
return;
else {
if (key != 'id' || autoIdAndDateFields == false) {
// Check for relationships that might duplicate
for (var rName in ctx.relations.keys) {
var relationship = ctx.relations[rName];
if (relationship.localKey == key) return;
}
}
dup.add(key);
}
String methodName;
List<Expression> positional = [literal(key)];
Map<String, Expression> named = {};
if (autoIdAndDateFields != false && name == 'id')
methodName = 'serial';
if (methodName == null) {
switch (col.type) {
case ColumnType.varChar:
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.dateTime:
methodName = 'dateTime';
break;
case ColumnType.timeStamp:
methodName = 'timeStamp';
break;
default:
Expression provColumn;
var colType = refer('Column');
var columnTypeType = refer('ColumnType');
if (col.length == null) {
methodName = 'declare';
provColumn = columnTypeType.newInstance([
literal(col.type.name),
]);
} else {
methodName = 'declareColumn';
provColumn = colType.newInstance([], {
'type': columnTypeType.newInstance([
literal(col.type.name),
]),
'length': literal(col.length),
});
}
positional.add(provColumn);
break;
}
}
var field = table.property(methodName).call(positional, named);
var cascade = <Expression>[];
if (col.defaultValue != null) {
cascade
.add(refer('defaultsTo').call([literal(col.defaultValue)]));
}
if (col.indexType == IndexType.primaryKey ||
(autoIdAndDateFields != false && name == 'id')) {
cascade.add(refer('primaryKey').call([]));
} else if (col.indexType == IndexType.unique) {
cascade.add(refer('unique').call([]));
}
if (col.isNullable != true)
cascade.add(refer('notNull').call([]));
if (cascade.isNotEmpty) {
var b = new StringBuffer()
..writeln(field.accept(new DartEmitter()));
for (var ex in cascade) {
b
..write('..')
..writeln(ex.accept(new DartEmitter()));
}
field = new CodeExpression(new Code(b.toString()));
}
closureBody.addExpression(field);
});
ctx.relations.forEach((name, r) {
var relationship = r;
if (relationship.type == RelationshipType.belongsTo) {
var key = relationship.localKey;
var field = table.property('integer').call([literal(key)]);
// .references('user', 'id').onDeleteCascade()
var ref = field.property('references').call([
literal(relationship.foreignTable),
literal(relationship.foreignKey),
]);
if (relationship.cascadeOnDelete != false &&
const [RelationshipType.hasOne, RelationshipType.belongsTo]
.contains(relationship.type))
ref = ref.property('onDeleteCascade').call([]);
closureBody.addExpression(ref);
}
});
});
});
meth.body = new Block((b) {
b.addExpression(_schema.property('create').call([
literal(ctx.tableName),
closure.closure,
]));
});
});
}
Method buildDownMigration(OrmBuildContext ctx) {
return new Method((b) {
b
..name = 'down'
..annotations.add(refer('override'))
..requiredParameters.add(_schemaParam)
..body = new Block((b) {
b.addExpression(
_schema.property('drop').call([literalString(ctx.tableName)]));
});
});
}
}

View file

@ -1,5 +1,5 @@
name: angel_orm_generator
version: 2.0.0-dev
version: 2.0.0-dev.1
description: Code generators for Angel's ORM. Generates query builder classes.
author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/orm
@ -14,6 +14,7 @@ dependencies:
build: ">=0.12.0 <2.0.0"
build_config: ^0.3.0
code_builder: ^3.0.0
dart_style: ^1.0.0
inflection:
git:
url: https://github.com/thosakwe/dart-inflection.git
@ -24,7 +25,10 @@ dependencies:
source_gen: ^0.9.0
dev_dependencies:
angel_framework: ^2.0.0-alpha
#angel_migration: ^1.0.0-alpha
angel_migration:
git:
url: https://github.com/angel-dart/migration
path: angel_migration
#angel_test: ^1.0.0
build_runner: ^1.0.0
collection: ^1.0.0

View file

@ -1,6 +1,7 @@
library angel_orm.generator.models.author;
import 'package:angel_model/angel_model.dart';
import 'package:angel_migration/angel_migration.dart';
import 'package:angel_orm/angel_orm.dart';
import 'package:angel_serialize/angel_serialize.dart';
part 'author.g.dart';

View file

@ -2,6 +2,27 @@
part of angel_orm.generator.models.author;
// **************************************************************************
// MigrationGenerator
// **************************************************************************
class AuthorMigration extends Migration {
@override
up(Schema schema) {
schema.create('authors', (table) {
table.serial('id')..primaryKey();
table.varChar('name');
table.timeStamp('created_at');
table.timeStamp('updated_at');
});
}
@override
down(Schema schema) {
schema.drop('authors');
}
}
// **************************************************************************
// OrmGenerator
// **************************************************************************

View file

@ -1,5 +1,6 @@
library angel_orm.generator.models.book;
import 'package:angel_migration/angel_migration.dart';
import 'package:angel_model/angel_model.dart';
import 'package:angel_orm/angel_orm.dart';
import 'package:angel_serialize/angel_serialize.dart';

View file

@ -2,6 +2,29 @@
part of angel_orm.generator.models.book;
// **************************************************************************
// MigrationGenerator
// **************************************************************************
class BookMigration extends Migration {
@override
up(Schema schema) {
schema.create('books', (table) {
table.serial('id')..primaryKey();
table.varChar('name');
table.timeStamp('created_at');
table.timeStamp('updated_at');
table.integer('author_id').references('authors', 'id');
table.integer('partner_author_id').references('authors', 'id');
});
}
@override
down(Schema schema) {
schema.drop('books');
}
}
// **************************************************************************
// OrmGenerator
// **************************************************************************

View file

@ -1,5 +1,6 @@
library angel_orm.generator.models.car;
import 'package:angel_migration/angel_migration.dart';
import 'package:angel_model/angel_model.dart';
import 'package:angel_orm/angel_orm.dart';
import 'package:angel_serialize/angel_serialize.dart';

View file

@ -2,6 +2,30 @@
part of angel_orm.generator.models.car;
// **************************************************************************
// MigrationGenerator
// **************************************************************************
class CarMigration extends Migration {
@override
up(Schema schema) {
schema.create('cars', (table) {
table.serial('id')..primaryKey();
table.varChar('make');
table.varChar('description');
table.boolean('family_friendly');
table.timeStamp('recalled_at');
table.timeStamp('created_at');
table.timeStamp('updated_at');
});
}
@override
down(Schema schema) {
schema.drop('cars');
}
}
// **************************************************************************
// OrmGenerator
// **************************************************************************

View file

@ -2,6 +2,26 @@
part of angel_orm_generator.test.models.customer;
// **************************************************************************
// MigrationGenerator
// **************************************************************************
class CustomerMigration extends Migration {
@override
up(Schema schema) {
schema.create('customers', (table) {
table.serial('id')..primaryKey();
table.timeStamp('created_at');
table.timeStamp('updated_at');
});
}
@override
down(Schema schema) {
schema.drop('customers');
}
}
// **************************************************************************
// OrmGenerator
// **************************************************************************

View file

@ -2,6 +2,28 @@
part of angel_orm_generator.test.models.foot;
// **************************************************************************
// MigrationGenerator
// **************************************************************************
class FootMigration extends Migration {
@override
up(Schema schema) {
schema.create('feet', (table) {
table.serial('id')..primaryKey();
table.integer('leg_id');
table.integer('n_toes');
table.timeStamp('created_at');
table.timeStamp('updated_at');
});
}
@override
down(Schema schema) {
schema.drop('feet');
}
}
// **************************************************************************
// OrmGenerator
// **************************************************************************

View file

@ -1,5 +1,6 @@
library angel_orm_generator.test.models.fruit;
import 'package:angel_migration/angel_migration.dart';
import 'package:angel_model/angel_model.dart';
import 'package:angel_orm/angel_orm.dart';
import 'package:angel_serialize/angel_serialize.dart';

View file

@ -2,6 +2,28 @@
part of angel_orm_generator.test.models.fruit;
// **************************************************************************
// MigrationGenerator
// **************************************************************************
class FruitMigration extends Migration {
@override
up(Schema schema) {
schema.create('fruits', (table) {
table.serial('id')..primaryKey();
table.integer('tree_id');
table.varChar('common_name');
table.timeStamp('created_at');
table.timeStamp('updated_at');
});
}
@override
down(Schema schema) {
schema.drop('fruits');
}
}
// **************************************************************************
// OrmGenerator
// **************************************************************************

View file

@ -1,5 +1,6 @@
library angel_orm_generator.test.models.leg;
import 'package:angel_migration/angel_migration.dart';
import 'package:angel_model/angel_model.dart';
import 'package:angel_orm/angel_orm.dart';
import 'package:angel_serialize/angel_serialize.dart';

View file

@ -2,6 +2,27 @@
part of angel_orm_generator.test.models.leg;
// **************************************************************************
// MigrationGenerator
// **************************************************************************
class LegMigration extends Migration {
@override
up(Schema schema) {
schema.create('legs', (table) {
table.serial('id')..primaryKey();
table.varChar('name');
table.timeStamp('created_at');
table.timeStamp('updated_at');
});
}
@override
down(Schema schema) {
schema.drop('legs');
}
}
// **************************************************************************
// OrmGenerator
// **************************************************************************

View file

@ -2,6 +2,30 @@
part of angel_orm_generator.test.models.order;
// **************************************************************************
// MigrationGenerator
// **************************************************************************
class OrderMigration extends Migration {
@override
up(Schema schema) {
schema.create('orders', (table) {
table.serial('id')..primaryKey();
table.integer('customer_id');
table.integer('employee_id');
table.timeStamp('order_date');
table.integer('shipper_id');
table.timeStamp('created_at');
table.timeStamp('updated_at');
});
}
@override
down(Schema schema) {
schema.drop('orders');
}
}
// **************************************************************************
// OrmGenerator
// **************************************************************************

View file

@ -1,5 +1,6 @@
library angel_orm_generator.test.models.tree;
import 'package:angel_migration/angel_migration.dart';
import 'package:angel_model/angel_model.dart';
import 'package:angel_orm/angel_orm.dart';
import 'package:angel_serialize/angel_serialize.dart';

View file

@ -2,6 +2,27 @@
part of angel_orm_generator.test.models.tree;
// **************************************************************************
// MigrationGenerator
// **************************************************************************
class TreeMigration extends Migration {
@override
up(Schema schema) {
schema.create('trees', (table) {
table.serial('id')..primaryKey();
table.declare('rings', new ColumnType('smallint'));
table.timeStamp('created_at');
table.timeStamp('updated_at');
});
}
@override
down(Schema schema) {
schema.drop('trees');
}
}
// **************************************************************************
// OrmGenerator
// **************************************************************************

View file

@ -1,5 +1,6 @@
library angel_orm_generator.test.models.user;
import 'package:angel_migration/angel_migration.dart';
import 'package:angel_model/angel_model.dart';
import 'package:angel_orm/angel_orm.dart';
import 'package:angel_serialize/angel_serialize.dart';

View file

@ -2,6 +2,62 @@
part of angel_orm_generator.test.models.user;
// **************************************************************************
// MigrationGenerator
// **************************************************************************
class UserMigration extends Migration {
@override
up(Schema schema) {
schema.create('users', (table) {
table.serial('id')..primaryKey();
table.varChar('username');
table.varChar('password');
table.varChar('email');
table.timeStamp('created_at');
table.timeStamp('updated_at');
});
}
@override
down(Schema schema) {
schema.drop('users');
}
}
class RoleMigration extends Migration {
@override
up(Schema schema) {
schema.create('roles', (table) {
table.serial('id')..primaryKey();
table.varChar('name');
table.timeStamp('created_at');
table.timeStamp('updated_at');
});
}
@override
down(Schema schema) {
schema.drop('roles');
}
}
class UserRoleMigration extends Migration {
@override
up(Schema schema) {
schema.create('user_roles', (table) {
table.serial('id')..primaryKey();
table.integer('user_id').references('users', 'id');
table.integer('role_id').references('roles', 'id');
});
}
@override
down(Schema schema) {
schema.drop('user_roles');
}
}
// **************************************************************************
// OrmGenerator
// **************************************************************************