diff --git a/angel_serialize_generator/lib/angel_serialize_generator.dart b/angel_serialize_generator/lib/angel_serialize_generator.dart index fb22821e..8287cfae 100644 --- a/angel_serialize_generator/lib/angel_serialize_generator.dart +++ b/angel_serialize_generator/lib/angel_serialize_generator.dart @@ -9,7 +9,6 @@ import 'package:code_builder/code_builder.dart'; import 'package:source_gen/source_gen.dart' hide LibraryBuilder; import 'build_context.dart'; import 'context.dart'; - part 'model.dart'; /// Converts a [DartType] to a [TypeReference]. @@ -21,81 +20,4 @@ TypeReference convertTypeReference(DartType t) { b.types.addAll(t.typeArguments.map(convertTypeReference)); } }); -} - -class JsonModelGenerator extends GeneratorForAnnotation { - final bool autoSnakeCaseNames; - final bool autoIdAndDateFields; - - const JsonModelGenerator( - {this.autoSnakeCaseNames: true, this.autoIdAndDateFields: true}); - - @override - Future generateForAnnotatedElement( - Element element, ConstantReader reader, BuildStep buildStep) async { - if (element.kind != ElementKind.CLASS) - throw 'Only classes can be annotated with a @Serializable() annotation.'; - - var ctx = await buildContext( - element, - serializable, - buildStep, - await buildStep.resolver, - autoSnakeCaseNames != false, - autoIdAndDateFields != false); - - var lib = new File((b) { - generateClass(ctx, b); - }); - - var buf = lib.accept(new DartEmitter()); - return buf.toString(); - } - - /// Generate an extended model class. - void generateClass(BuildContext ctx, FileBuilder file) { - file.body.add(new Class((clazz) { - clazz - ..name = ctx.modelClassNameRecase.pascalCase - ..extend = new Reference(ctx.originalClassName); - - for (var field in ctx.fields) { - clazz.fields.add(new Field((b) { - b - ..name = field.name - ..modifier = FieldModifier.final$ - ..type = convertTypeReference(field.type); - })); - } - - generateConstructor(ctx, clazz, file); - generateChangeMethod(ctx, clazz, file); - })); - } - - /// Generate a constructor with named parameters. - void generateConstructor( - BuildContext ctx, ClassBuilder clazz, FileBuilder file) { - clazz.constructors.add(new Constructor((constructor) { - for (var field in ctx.fields) { - constructor.optionalParameters.add(new Parameter((b) { - b - ..name = field.name - ..named = true - ..toThis = true; - })); - } - })); - } - - /// Generate a `change` method. - void generateChangeMethod( - BuildContext ctx, ClassBuilder clazz, FileBuilder file) { - clazz.methods.add(new Method((method) { - method - ..name = 'change' - ..returns = ctx.modelClassType - ..requiredParameters.add(new Parameter()); - })); - } -} +} \ No newline at end of file diff --git a/angel_serialize_generator/lib/build_context.dart b/angel_serialize_generator/lib/build_context.dart index cce3cd00..5400747d 100644 --- a/angel_serialize_generator/lib/build_context.dart +++ b/angel_serialize_generator/lib/build_context.dart @@ -36,7 +36,9 @@ Future buildContext( fieldNames.add(field.name); // Skip if annotated with @exclude var excludeAnnotation = excludeTypeChecker.firstAnnotationOf(field); - if (excludeAnnotation != null) continue; + + ctx.excluded[field.name] = excludeAnnotation != null; + // Check for alias Alias alias; var aliasAnn = aliasTypeChecker.firstAnnotationOf(field); diff --git a/angel_serialize_generator/lib/context.dart b/angel_serialize_generator/lib/context.dart index 9bf66a9a..b5c99268 100644 --- a/angel_serialize_generator/lib/context.dart +++ b/angel_serialize_generator/lib/context.dart @@ -11,6 +11,9 @@ class BuildContext { /// A map of field names to resolved names from `@Alias()` declarations. final Map aliases = {}; + /// A map of fields that have been marked as to be excluded from serialization. + final Map excluded = {}; + /// A map of "synthetic" fields, i.e. `id` and `created_at` injected automatically. final Map shimmed = {}; diff --git a/angel_serialize_generator/lib/model.dart b/angel_serialize_generator/lib/model.dart index 97a17289..f0f599c5 100644 --- a/angel_serialize_generator/lib/model.dart +++ b/angel_serialize_generator/lib/model.dart @@ -1,2 +1,98 @@ part of angel_serialize_generator; +class JsonModelGenerator extends GeneratorForAnnotation { + final bool autoSnakeCaseNames; + final bool autoIdAndDateFields; + + const JsonModelGenerator( + {this.autoSnakeCaseNames: true, this.autoIdAndDateFields: true}); + + @override + Future generateForAnnotatedElement( + Element element, ConstantReader reader, BuildStep buildStep) async { + if (element.kind != ElementKind.CLASS) + throw 'Only classes can be annotated with a @Serializable() annotation.'; + + var ctx = await buildContext( + element, + serializable, + buildStep, + await buildStep.resolver, + autoSnakeCaseNames != false, + autoIdAndDateFields != false); + + var lib = new File((b) { + generateClass(ctx, b); + }); + + var buf = lib.accept(new DartEmitter()); + return buf.toString(); + } + + /// Generate an extended model class. + void generateClass(BuildContext ctx, FileBuilder file) { + file.body.add(new Class((clazz) { + clazz + ..name = ctx.modelClassNameRecase.pascalCase + ..extend = new Reference(ctx.originalClassName); + + for (var field in ctx.fields) { + clazz.fields.add(new Field((b) { + b + ..name = field.name + ..modifier = FieldModifier.final$ + ..annotations + .add(new Annotation((b) => b.code = new Code('override'))) + ..type = convertTypeReference(field.type); + })); + } + + generateConstructor(ctx, clazz, file); + generateCopyWithMethod(ctx, clazz, file); + })); + } + + /// Generate a constructor with named parameters. + void generateConstructor( + BuildContext ctx, ClassBuilder clazz, FileBuilder file) { + clazz.constructors.add(new Constructor((constructor) { + for (var field in ctx.fields) { + constructor.optionalParameters.add(new Parameter((b) { + b + ..name = field.name + ..named = true + ..toThis = true; + })); + } + })); + } + + /// Generate a `copyWith` method. + void generateCopyWithMethod( + BuildContext ctx, ClassBuilder clazz, FileBuilder file) { + clazz.methods.add(new Method((method) { + method + ..name = 'copyWith' + ..returns = ctx.modelClassType; + + var buf = new StringBuffer('return new ${ctx.modelClassName}('); + int i = 0; + + // Add named parameters + for (var field in ctx.fields) { + method.optionalParameters.add(new Parameter((b) { + b + ..name = field.name + ..named = true + ..type = convertTypeReference(field.type); + })); + + if (i++ > 0) buf.write(', '); + buf.write('${field.name}: ${field.name} ?? this.${field.name}'); + } + + buf.write(');'); + method.body = new Code(buf.toString()); + })); + } +} diff --git a/angel_serialize_generator/test/models/author.dart b/angel_serialize_generator/test/models/author.dart index 66ecac61..9a6e1716 100644 --- a/angel_serialize_generator/test/models/author.dart +++ b/angel_serialize_generator/test/models/author.dart @@ -3,6 +3,7 @@ library angel_serialize.test.models.author; import 'package:angel_framework/common.dart'; import 'package:angel_serialize/angel_serialize.dart'; import 'book.dart'; + part 'author.g.dart'; @serializable @@ -18,4 +19,4 @@ abstract class _Author extends Model { @serializable abstract class _Library extends Model { Map collection; -} \ No newline at end of file +} diff --git a/angel_serialize_generator/test/models/author.g.dart b/angel_serialize_generator/test/models/author.g.dart index ed7c2792..b95c10ac 100644 --- a/angel_serialize_generator/test/models/author.g.dart +++ b/angel_serialize_generator/test/models/author.g.dart @@ -13,32 +13,79 @@ class Author extends _Author { this.age, this.books, this.newestBook, + this.secret, this.createdAt, this.updatedAt}); + @override final String id; + @override final String name; + @override final int age; + @override final List books; + @override final Book newestBook; + @override + final String secret; + + @override final DateTime createdAt; + @override final DateTime updatedAt; + + Author copyWith( + {String id, + String name, + int age, + List books, + Book newestBook, + String secret, + DateTime createdAt, + DateTime updatedAt}) { + return new Author( + id: id ?? this.id, + name: name ?? this.name, + age: age ?? this.age, + books: books ?? this.books, + newestBook: newestBook ?? this.newestBook, + secret: secret ?? this.secret, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt); + } } class Library extends _Library { Library({this.id, this.collection, this.createdAt, this.updatedAt}); + @override final String id; + @override final Map collection; + @override final DateTime createdAt; + @override final DateTime updatedAt; + + Library copyWith( + {String id, + Map collection, + DateTime createdAt, + DateTime updatedAt}) { + return new Library( + id: id ?? this.id, + collection: collection ?? this.collection, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt); + } } diff --git a/angel_serialize_generator/test/models/book.g.dart b/angel_serialize_generator/test/models/book.g.dart index 100d634d..5536383b 100644 --- a/angel_serialize_generator/test/models/book.g.dart +++ b/angel_serialize_generator/test/models/book.g.dart @@ -16,17 +16,42 @@ class Book extends _Book { this.createdAt, this.updatedAt}); + @override final String id; + @override final String author; + @override final String title; + @override final String description; + @override final int pageCount; + @override final DateTime createdAt; + @override final DateTime updatedAt; + + Book copyWith( + {String id, + String author, + String title, + String description, + int pageCount, + DateTime createdAt, + DateTime updatedAt}) { + return new Book( + id: id ?? this.id, + author: author ?? this.author, + title: title ?? this.title, + description: description ?? this.description, + pageCount: pageCount ?? this.pageCount, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt); + } }