diff --git a/README.md b/README.md index e136de51..73012fbd 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ the time you spend writing boilerplate serialization code for your models. * [Nesting](#nesting) * [ID and Date Fields](#id-and-dates) * [TypeScript Definition Generator](#typescript-definitions) + * [Constructor Parameters](#constructor-parameters) # Usage In your `pubspec.yaml`, you need to install the following dependencies: @@ -252,4 +253,42 @@ interface BookCollection { Fields with an `@Exclude()` that specifies `canSerialize: false` will not be present in the TypeScript definition. The rationale for this is that if a field (i.e. `password`) will -never be sent to the client, the client shouldn't even know the field exists. \ No newline at end of file +never be sent to the client, the client shouldn't even know the field exists. + +# Constructor Parameters +Sometimes, you may need to have custom constructor parameters, for example, when +using depedency injection frameworks. For these cases, `angel_serialize` can forward +custom constructor parameters. + +The following: +```dart +@serializable +abstract class _Bookmark extends _BookmarkBase { + @exclude + final Book book; + + int get page; + String get comment; + + _Bookmark(this.book); +} +``` + +Generates: + +```dart +class Bookmark extends _Bookmark { + Bookmark(Book book, + {this.id, + this.page, + this.comment, + this.createdAt, + this.updatedAt}) + : super(book); + + @override + final String id; + + // ... +} +``` \ No newline at end of file diff --git a/angel_serialize_generator/CHANGELOG.md b/angel_serialize_generator/CHANGELOG.md index c0e55564..77144763 100644 --- a/angel_serialize_generator/CHANGELOG.md +++ b/angel_serialize_generator/CHANGELOG.md @@ -1,5 +1,6 @@ # 2.0.6 * Support for using `abstract` to create immutable model classes. +* Add support for custom constructor parameters. # 2.0.5 * Deserialization now supports un-serialized `DateTime`. diff --git a/angel_serialize_generator/lib/build_context.dart b/angel_serialize_generator/lib/build_context.dart index 140543cf..8b539d95 100644 --- a/angel_serialize_generator/lib/build_context.dart +++ b/angel_serialize_generator/lib/build_context.dart @@ -102,6 +102,9 @@ Future buildContext( } } + // Get constructor params, if any + ctx.constructorParameters.addAll(clazz.unnamedConstructor.parameters); + return ctx; } diff --git a/angel_serialize_generator/lib/context.dart b/angel_serialize_generator/lib/context.dart index 24011ecb..70687761 100644 --- a/angel_serialize_generator/lib/context.dart +++ b/angel_serialize_generator/lib/context.dart @@ -25,6 +25,8 @@ class BuildContext { /// The fields declared on the original class. final List fields = []; + final List constructorParameters = []; + final ConstantReader annotation; /// The name of the field that identifies data of this model type. diff --git a/angel_serialize_generator/lib/model.dart b/angel_serialize_generator/lib/model.dart index 94df3147..dcf54dad 100644 --- a/angel_serialize_generator/lib/model.dart +++ b/angel_serialize_generator/lib/model.dart @@ -61,6 +61,18 @@ class JsonModelGenerator extends GeneratorForAnnotation { void generateConstructor( BuildContext ctx, ClassBuilder clazz, LibraryBuilder file) { clazz.constructors.add(new Constructor((constructor) { + // Add all `super` params + if (ctx.constructorParameters.isNotEmpty) { + for (var param in ctx.constructorParameters) { + constructor.requiredParameters.add(new Parameter((b) => b + ..name = param.name + ..type = convertTypeReference(param.type))); + } + + constructor.initializers.add(new Code( + 'super(${ctx.constructorParameters.map((p) => p.name).join(',')})')); + } + for (var field in ctx.fields) { constructor.optionalParameters.add(new Parameter((b) { b @@ -79,10 +91,24 @@ class JsonModelGenerator extends GeneratorForAnnotation { method ..name = 'copyWith' ..returns = ctx.modelClassType; + + // Add all `super` params + if (ctx.constructorParameters.isNotEmpty) { + for (var param in ctx.constructorParameters) { + method.requiredParameters.add(new Parameter((b) => b + ..name = param.name + ..type = convertTypeReference(param.type))); + } + } var buf = new StringBuffer('return new ${ctx.modelClassName}('); int i = 0; + for (var param in ctx.constructorParameters) { + if (i++ > 0) buf.write(', '); + buf.write(param.name); + } + // Add named parameters for (var field in ctx.fields) { method.optionalParameters.add(new Parameter((b) { diff --git a/angel_serialize_generator/lib/serialize.dart b/angel_serialize_generator/lib/serialize.dart index a5e8f21e..d0abab99 100644 --- a/angel_serialize_generator/lib/serialize.dart +++ b/angel_serialize_generator/lib/serialize.dart @@ -125,9 +125,23 @@ class SerializerGenerator extends GeneratorForAnnotation { ..type = new Reference('Map')), ); + // Add all `super` params + if (ctx.constructorParameters.isNotEmpty) { + for (var param in ctx.constructorParameters) { + method.requiredParameters.add(new Parameter((b) => b + ..name = param.name + ..type = convertTypeReference(param.type))); + } + } + var buf = new StringBuffer('return new ${ctx.modelClassName}('); int i = 0; + for (var param in ctx.constructorParameters) { + if (i++ > 0) buf.write(', '); + buf.write(param.name); + } + for (var field in ctx.fields) { if (ctx.excluded[field.name]?.canDeserialize == false) continue; diff --git a/angel_serialize_generator/test/models/author.dart b/angel_serialize_generator/test/models/author.dart index e88924ba..ed100971 100644 --- a/angel_serialize_generator/test/models/author.dart +++ b/angel_serialize_generator/test/models/author.dart @@ -26,3 +26,14 @@ abstract class _Author extends Model { abstract class _Library extends Model { Map get collection; } + +@serializable +abstract class _Bookmark extends Model { + @exclude + final Book book; + + int get page; + String get comment; + + _Bookmark(this.book); +} diff --git a/angel_serialize_generator/test/models/author.g.dart b/angel_serialize_generator/test/models/author.g.dart index 263e7bf1..bf65832a 100644 --- a/angel_serialize_generator/test/models/author.g.dart +++ b/angel_serialize_generator/test/models/author.g.dart @@ -103,3 +103,42 @@ class Library extends _Library { return LibrarySerializer.toMap(this); } } + +class Bookmark extends _Bookmark { + Bookmark(Book book, + {this.id, this.page, this.comment, this.createdAt, this.updatedAt}) + : super(book); + + @override + final String id; + + @override + final int page; + + @override + final String comment; + + @override + final DateTime createdAt; + + @override + final DateTime updatedAt; + + Bookmark copyWith(Book book, + {String id, + int page, + String comment, + DateTime createdAt, + DateTime updatedAt}) { + return new Bookmark(book, + id: id ?? this.id, + page: page ?? this.page, + comment: comment ?? this.comment, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt); + } + + Map toJson() { + return BookmarkSerializer.toMap(this); + } +} diff --git a/angel_serialize_generator/test/models/author.serializer.g.dart b/angel_serialize_generator/test/models/author.serializer.g.dart index c52af786..66f9d976 100644 --- a/angel_serialize_generator/test/models/author.serializer.g.dart +++ b/angel_serialize_generator/test/models/author.serializer.g.dart @@ -107,3 +107,44 @@ abstract class LibraryFields { static const String updatedAt = 'updated_at'; } + +abstract class BookmarkSerializer { + static Bookmark fromMap(Map map, Book book) { + return new Bookmark(book, + id: map['id'], + page: map['page'], + comment: map['comment'], + createdAt: map['created_at'] != null + ? (map['created_at'] is DateTime + ? map['created_at'] + : DateTime.parse(map['created_at'])) + : null, + updatedAt: map['updated_at'] != null + ? (map['updated_at'] is DateTime + ? map['updated_at'] + : DateTime.parse(map['updated_at'])) + : null); + } + + static Map toMap(Bookmark model) { + return { + 'id': model.id, + 'page': model.page, + 'comment': model.comment, + 'created_at': model.createdAt?.toIso8601String(), + 'updated_at': model.updatedAt?.toIso8601String() + }; + } +} + +abstract class BookmarkFields { + static const String id = 'id'; + + static const String page = 'page'; + + static const String comment = 'comment'; + + static const String createdAt = 'created_at'; + + static const String updatedAt = 'updated_at'; +}