diff --git a/README.md b/README.md index 9d82c6be..52efb4e3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # serialize + [![Pub](https://img.shields.io/pub/v/angel_serialize.svg)](https://pub.dartlang.org/packages/angel_serialize) [![build status](https://travis-ci.org/angel-dart/serialize.svg)](https://travis-ci.org/angel-dart/serialize) @@ -6,26 +7,29 @@ Source-generated serialization for Dart objects. This package uses `package:sour the time you spend writing boilerplate serialization code for your models. `package:angel_serialize` also powers `package:angel_orm`. -* [Usage](#usage) - * [Models](#models) - * [Field Aliases](#aliases) - * [Excluding Keys](#excluding-keys) - * [Required Fields](#required-fields) - * [Serialization](#serializaition) - * [Nesting](#nesting) - * [ID and Date Fields](#id-and-dates) - * [TypeScript Definition Generator](#typescript-definitions) - * [Constructor Parameters](#constructor-parameters) +- [Usage](#usage) + - [Models](#models) + - [Field Aliases](#aliases) + - [Excluding Keys](#excluding-keys) + - [Required Fields](#required-fields) + - [Serialization](#serializaition) + - [Nesting](#nesting) + - [ID and Date Fields](#id-and-dates) + - [Binary Data](#binary-data) + - [TypeScript Definition Generator](#typescript-definitions) + - [Constructor Parameters](#constructor-parameters) # Usage + In your `pubspec.yaml`, you need to install the following dependencies: + ```yaml dependencies: - angel_model: ^1.0.0 # Only required if using Angel!!! + angel_model: ^1.0.0 angel_serialize: ^2.0.0 dev_dependencies: angel_serialize_generator: ^2.0.0 - build_runner: ^0.8.0 + build_runner: ^1.0.0 ``` With the recent updates to `package:build_runner`, you can build models in @@ -38,6 +42,7 @@ If you want to watch for file changes and re-build when necessary, replace the ` with a call to `watch`. They take the same parameters. # Models + There are a few changes opposed to normal Model classes. You need to add a `@serializable` annotation to your model class to have it serialized, and a serializable model class's name should also start with a leading underscore. @@ -61,13 +66,13 @@ part 'book.g.dart'; @serializable abstract class _Book extends Model { String get author; - + String get title; - + String get description; - + int get pageCount; - + BookType get type; } @@ -78,17 +83,18 @@ enum BookType { } ``` -The following files will be generated: - * `book.g.dart` - * `book.serializer.g.dart` - +The following file will be generated: + +- `book.g.dart` + Producing these classes: - * `Book`: Extends or implements `_Book`; may be `const`-enabled. - * `BookSerializer`: static functionality for serializing `Book` models. - * `BookFields`: The names of all fields from the `Book` model, statically-available. - + +- `Book`: Extends or implements `_Book`; may be `const`-enabled. +- `BookSerializer`: static functionality for serializing `Book` models. +- `BookFields`: The names of all fields from the `Book` model, statically-available. + # Serialization - + You can use the generated files as follows: ```dart @@ -99,18 +105,18 @@ myFunction() { description: 'You will cry after reading this.', pageCount: 1225 ); - + // Easily serialize models into Maps var map = BookSerializer.toMap(warAndPeace); - + // Also deserialize from Maps var book = BookSerializer.fromMap(map); print(book.title); // 'War and Peace' - + // For compatibility with `JSON.encode`, a `toJson` method // is included that forwards to `BookSerializer.toMap`: expect(book.toJson(), map); - + // Generated classes act as value types, and thus can be compared. expect(BookSerializer.fromMap(map), equals(warAndPeace)); } @@ -126,12 +132,15 @@ about the serialized names of keys on your model class. } } ``` + ## Customizing Serialization + Currently, these serialization methods are supported: - * to `Map` - * to JSON - * to TypeScript definitions - + +- to `Map` +- to JSON +- to TypeScript definitions + You can customize these by means of `serializers`: ```dart @@ -139,8 +148,8 @@ You can customize these by means of `serializers`: class _MyClass extends Model {} ``` - ## Aliases + Whereas Dart fields conventionally are camelCased, most database columns tend to be snake_cased. This is not a problem, because we can define an alias for a field. @@ -152,13 +161,13 @@ provide a custom name, or pass `autoSnakeCaseNames`: `false` to the builder; @serializable abstract class _Spy extends Model { /// Will show up as 'agency_id' in serialized JSON. - /// + /// /// When deserializing JSON, instead of searching for an 'agencyId' key, /// it will use 'agency_id'. - /// + /// /// Hooray! String agencyId; - + @Alias('foo') String someOtherField; } @@ -174,6 +183,7 @@ abstract class _OtherCasing extends Model { ``` ## Excluding Keys + In pratice, there may keys that you want to exclude from JSON. To accomplish this, simply annotate them with `@exclude`: @@ -196,7 +206,7 @@ In this case, use `canSerialize` or `canDeserialize`: @serializable abstract class _Whisper extends Model { /// Will never be serialized to JSON - /// + /// /// ... But it can be deserialized @Exclude(canDeserialize: true) String secret; @@ -204,6 +214,7 @@ abstract class _Whisper extends Model { ``` ## Required Fields + It is easy to mark a field as required; just use the `@required` annotation from `package:meta`: @@ -212,7 +223,7 @@ It is easy to mark a field as required; just use the abstract class _Foo extends Model { @required int myRequiredInt; - + @Required('Custom message') int myOtherRequiredInt; } @@ -223,10 +234,12 @@ generated constructor, and serializers will check for its presence, throwing a `FormatException` if it is missing. # Nesting + `angel_serialize` also supports a few types of nesting of `@serializable` classes: -* As a class member, ex. `Book myField` -* As the type argument to a `List`, ex. `List` -* As the second type argument to a `Map`, ex. `Map` + +- As a class member, ex. `Book myField` +- As the type argument to a `List`, ex. `List` +- As the second type argument to a `Map`, ex. `Map` In other words, the following are all legal, and will be serialized/deserialized. You can use either the underscored name of a child class (ex. `_Book`), or the @@ -246,11 +259,11 @@ then you will need to generate `book.g.dart` before, `author.g.dart`, **in a separate build action**. This way, the analyzer can resolve the `Book` type. # ID and Dates + This package will automatically generate `id`, `createdAt`, and `updatedAt` fields for you, in the style of an Angel `Model`. To disable this, set `autoIdAndDateFields` to `false` in the builder constructor. - You can also override `autoIdAndDateFields` per model: ```dart @@ -258,7 +271,13 @@ You can also override `autoIdAndDateFields` per model: abstract class _Skinny extends Model {} ``` +# Binary Data + +`package:angel_serialize` also handles `Uint8List` fields, by means of serialization to +and from `base64` encoding. + # TypeScript Definitions + It is quite common to build frontends with JavaScript and/or TypeScript, so why not generate typings as well? @@ -297,11 +316,13 @@ TypeScript definition. The rationale for this is that if a field (i.e. `password 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 { diff --git a/angel_serialize_generator/CHANGELOG.md b/angel_serialize_generator/CHANGELOG.md index 11a75905..9102b9ca 100644 --- a/angel_serialize_generator/CHANGELOG.md +++ b/angel_serialize_generator/CHANGELOG.md @@ -1,3 +1,9 @@ +# 2.2.0 +* Build to `cache`. +* Only generate one `.g.dart` file. +* Support for `Uint8List`. +* Use `.cast()` for `List`s and `Map`s of *non-`Model`* types. + # 2.1.2 * Add `declare module` to generated TypeScript files. diff --git a/angel_serialize_generator/build.yaml b/angel_serialize_generator/build.yaml index 9b4b07d9..643996df 100644 --- a/angel_serialize_generator/build.yaml +++ b/angel_serialize_generator/build.yaml @@ -7,13 +7,14 @@ builders: - serializerBuilder - typescriptDefinitionBuilder auto_apply: root_package - build_to: source + build_to: cache build_extensions: .dart: - "angel_serialize.g.part" - - ".serializer.g.dart" + - "angel_serialize_serializer.g.part" - ".d.ts" applies_builders: ["source_gen|combining_builder", "source_gen|part_cleanup"] + runs_before: ["angel_orm_generator|angel_orm"] targets: _book: sources: diff --git a/angel_serialize_generator/lib/angel_serialize_generator.dart b/angel_serialize_generator/lib/angel_serialize_generator.dart index 747fb024..04bf794e 100644 --- a/angel_serialize_generator/lib/angel_serialize_generator.dart +++ b/angel_serialize_generator/lib/angel_serialize_generator.dart @@ -1,6 +1,7 @@ library angel_serialize_generator; import 'dart:async'; +import 'dart:typed_data'; import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/type.dart'; @@ -28,10 +29,8 @@ Builder jsonModelBuilder(_) { } Builder serializerBuilder(_) { - return new PartBuilder( - const [const SerializerGenerator()], - '.serializer.g.dart', - ); + return new SharedPartBuilder( + const [const SerializerGenerator()], 'angel_serialize_serializer'); } Builder typescriptDefinitionBuilder(_) { @@ -73,8 +72,9 @@ bool isModelClass(DartType t) { } bool isListOrMapType(DartType t) { - return const TypeChecker.fromRuntime(List).isAssignableFromType(t) || - const TypeChecker.fromRuntime(Map).isAssignableFromType(t); + return (const TypeChecker.fromRuntime(List).isAssignableFromType(t) || + const TypeChecker.fromRuntime(Map).isAssignableFromType(t)) && + !const TypeChecker.fromRuntime(Uint8List).isAssignableFromType(t); } bool isEnumType(DartType t) { diff --git a/angel_serialize_generator/lib/model.dart b/angel_serialize_generator/lib/model.dart index dd9567f7..cf0a94e7 100644 --- a/angel_serialize_generator/lib/model.dart +++ b/angel_serialize_generator/lib/model.dart @@ -180,7 +180,7 @@ class JsonModelGenerator extends GeneratorForAnnotation { var eq = generateEquality(type.typeArguments[0]); return 'const ListEquality<${type.typeArguments[0].name}>($eq)'; } else - return 'const ListEquality<${type.typeArguments[0].name}>()'; + return 'const ListEquality()'; } else if (const TypeChecker.fromRuntime(Map) .isAssignableFromType(type)) { if (type.typeParameters.length == 2) { @@ -188,7 +188,7 @@ class JsonModelGenerator extends GeneratorForAnnotation { veq = generateEquality(type.typeArguments[1]); return 'const MapEquality<${type.typeArguments[0].name}, ${type.typeArguments[1].name}>(keys: $keq, values: $veq)'; } else - return 'const MapEquality()<${type.typeArguments[0].name}, ${type.typeArguments[1].name}>'; + return 'const MapEquality()'; } return nullable ? null : 'const DefaultEquality<${type.name}>()'; diff --git a/angel_serialize_generator/lib/serialize.dart b/angel_serialize_generator/lib/serialize.dart index 70300795..3c388972 100644 --- a/angel_serialize_generator/lib/serialize.dart +++ b/angel_serialize_generator/lib/serialize.dart @@ -128,6 +128,13 @@ class SerializerGenerator extends GeneratorForAnnotation { null : ${t.name}.values.indexOf(model.${field.name}) '''; + } else if (const TypeChecker.fromRuntime(Uint8List) + .isAssignableFromType(t)) { + serializedRepresentation = ''' + model.${field.name} == null ? + null + : base64.encode(model.${field.name}) + '''; } } @@ -237,6 +244,45 @@ class SerializerGenerator extends GeneratorForAnnotation { : null ) '''; + } else if (const TypeChecker.fromRuntime(List) + .isAssignableFromType(t) && + t.typeArguments.length == 1) { + var arg = convertTypeReference(t.typeArguments[0]) + .accept(new DartEmitter()); + deserializedRepresentation = ''' + map['$alias'] is Iterable + ? (map['$alias'] as Iterable).cast<$arg>().toList() + : null + '''; + } else if (const TypeChecker.fromRuntime(Map) + .isAssignableFromType(t) && + t.typeArguments.length == 2) { + var key = convertTypeReference(t.typeArguments[0]) + .accept(new DartEmitter()); + var value = convertTypeReference(t.typeArguments[1]) + .accept(new DartEmitter()); + deserializedRepresentation = ''' + map['$alias'] is Map + ? (map['$alias'] as Map).cast<$key, $value>() + : null + '''; + } else if (const TypeChecker.fromRuntime(Uint8List) + .isAssignableFromType(t)) { + deserializedRepresentation = ''' + map['$alias'] is Uint8List + ? (map['$alias'] as Uint8List) + : + ( + map['$alias'] is Iterable + ? new Uint8List.fromList((map['$alias'] as Iterable).toList()) + : + ( + map['$alias'] is String + ? new Uint8List.fromList(base64.decode(map['$alias'] as String)) + : null + ) + ) + '''; } } diff --git a/angel_serialize_generator/pubspec.yaml b/angel_serialize_generator/pubspec.yaml index 65d0e63b..dfdf9b96 100644 --- a/angel_serialize_generator/pubspec.yaml +++ b/angel_serialize_generator/pubspec.yaml @@ -1,5 +1,5 @@ name: angel_serialize_generator -version: 2.1.2 +version: 2.2.0 description: Model serialization generators, designed for use with Angel. Combine with angel_serialize for flexible modeling. author: Tobe O homepage: https://github.com/angel-dart/serialize @@ -7,7 +7,7 @@ environment: sdk: '>=2.0.0-dev.55 <3.0.0' dependencies: angel_model: ^1.0.0 - angel_serialize: ^2.0.0-alpha + angel_serialize: ^2.0.0 build_config: ">=0.3.0 <2.0.0" code_buffer: ^1.0.0 code_builder: ^3.0.0 diff --git a/angel_serialize_generator/test/enum_test.dart b/angel_serialize_generator/test/enum_test.dart index 15d1dd7e..403804c1 100644 --- a/angel_serialize_generator/test/enum_test.dart +++ b/angel_serialize_generator/test/enum_test.dart @@ -1,3 +1,5 @@ +import 'dart:typed_data'; + import 'package:test/test.dart'; import 'models/with_enum.dart'; @@ -45,4 +47,15 @@ void main() { test('const', () { expect(identical(aWithEnum, aWithEnum2), true); }); + + test('uint8list', () { + var ee = new WithEnum( + imageBytes: + new Uint8List.fromList(new List.generate(1000, (i) => i))); + var eeMap = ee.toJson(); + print(eeMap); + var ef = WithEnumSerializer.fromMap(eeMap); + expect(ee.copyWith(), ee); + expect(ef, ee); + }); } diff --git a/angel_serialize_generator/test/models/author.d.ts b/angel_serialize_generator/test/models/author.d.ts deleted file mode 100644 index 64f0baf1..00000000 --- a/angel_serialize_generator/test/models/author.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -/// -// GENERATED CODE - DO NOT MODIFY BY HAND -declare module 'angel_serialize_generator' { - interface Library { - id?: string; - collection?: LibraryCollection; - created_at?: any; - updated_at?: any; - } - interface LibraryCollection { - [key: string]: Book; - } - interface Bookmark { - id?: string; - history?: number[]; - page: number; - comment?: string; - created_at?: any; - updated_at?: any; - } -} \ No newline at end of file diff --git a/angel_serialize_generator/test/models/author.dart b/angel_serialize_generator/test/models/author.dart index ec955612..6d46748f 100644 --- a/angel_serialize_generator/test/models/author.dart +++ b/angel_serialize_generator/test/models/author.dart @@ -5,11 +5,8 @@ import 'package:angel_serialize/angel_serialize.dart'; import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; import 'book.dart'; - part 'author.g.dart'; -part 'author.serializer.g.dart'; - @serializable abstract class _Author extends Model { @required diff --git a/angel_serialize_generator/test/models/author.g.dart b/angel_serialize_generator/test/models/author.g.dart index caab3506..2a634a59 100644 --- a/angel_serialize_generator/test/models/author.g.dart +++ b/angel_serialize_generator/test/models/author.g.dart @@ -220,3 +220,221 @@ class Bookmark extends _Bookmark { return BookmarkSerializer.toMap(this); } } + +// ************************************************************************** +// SerializerGenerator +// ************************************************************************** + +abstract class AuthorSerializer { + static Author fromMap(Map map) { + if (map['name'] == null) { + throw new FormatException("Missing required field 'name' on Author."); + } + + if (map['age'] == null) { + throw new FormatException("Custom message for missing `age`"); + } + + return new Author( + id: map['id'] as String, + name: map['name'] as String, + age: map['age'] as int, + books: map['books'] is Iterable + ? new List.unmodifiable(((map['books'] as Iterable) + .where((x) => x is Map) as Iterable) + .map(BookSerializer.fromMap)) + : null, + newestBook: map['newest_book'] != null + ? BookSerializer.fromMap(map['newest_book'] as Map) + : null, + obscured: map['obscured'] as String, + createdAt: map['created_at'] != null + ? (map['created_at'] is DateTime + ? (map['created_at'] as DateTime) + : DateTime.parse(map['created_at'].toString())) + : null, + updatedAt: map['updated_at'] != null + ? (map['updated_at'] is DateTime + ? (map['updated_at'] as DateTime) + : DateTime.parse(map['updated_at'].toString())) + : null); + } + + static Map toMap(Author model) { + if (model == null) { + return null; + } + if (model.name == null) { + throw new FormatException("Missing required field 'name' on Author."); + } + + if (model.age == null) { + throw new FormatException("Custom message for missing `age`"); + } + + return { + 'id': model.id, + 'name': model.name, + 'age': model.age, + 'books': model.books?.map((m) => m.toJson())?.toList(), + 'newest_book': BookSerializer.toMap(model.newestBook), + 'created_at': model.createdAt?.toIso8601String(), + 'updated_at': model.updatedAt?.toIso8601String() + }; + } +} + +abstract class AuthorFields { + static const List allFields = const [ + id, + name, + age, + books, + newestBook, + secret, + obscured, + createdAt, + updatedAt + ]; + + static const String id = 'id'; + + static const String name = 'name'; + + static const String age = 'age'; + + static const String books = 'books'; + + static const String newestBook = 'newest_book'; + + static const String secret = 'secret'; + + static const String obscured = 'obscured'; + + static const String createdAt = 'created_at'; + + static const String updatedAt = 'updated_at'; +} + +abstract class LibrarySerializer { + static Library fromMap(Map map) { + return new Library( + id: map['id'] as String, + collection: map['collection'] is Map + ? new Map.unmodifiable( + (map['collection'] as Map).keys.fold({}, (out, key) { + return out + ..[key] = BookSerializer.fromMap( + ((map['collection'] as Map)[key]) as Map); + })) + : null, + createdAt: map['created_at'] != null + ? (map['created_at'] is DateTime + ? (map['created_at'] as DateTime) + : DateTime.parse(map['created_at'].toString())) + : null, + updatedAt: map['updated_at'] != null + ? (map['updated_at'] is DateTime + ? (map['updated_at'] as DateTime) + : DateTime.parse(map['updated_at'].toString())) + : null); + } + + static Map toMap(Library model) { + if (model == null) { + return null; + } + return { + 'id': model.id, + 'collection': model.collection.keys?.fold({}, (map, key) { + return map..[key] = BookSerializer.toMap(model.collection[key]); + }), + 'created_at': model.createdAt?.toIso8601String(), + 'updated_at': model.updatedAt?.toIso8601String() + }; + } +} + +abstract class LibraryFields { + static const List allFields = const [ + id, + collection, + createdAt, + updatedAt + ]; + + static const String id = 'id'; + + static const String collection = 'collection'; + + static const String createdAt = 'created_at'; + + static const String updatedAt = 'updated_at'; +} + +abstract class BookmarkSerializer { + static Bookmark fromMap(Map map, Book book) { + if (map['page'] == null) { + throw new FormatException("Missing required field 'page' on Bookmark."); + } + + return new Bookmark(book, + id: map['id'] as String, + history: map['history'] is Iterable + ? (map['history'] as Iterable).cast().toList() + : null, + page: map['page'] as int, + comment: map['comment'] as String, + createdAt: map['created_at'] != null + ? (map['created_at'] is DateTime + ? (map['created_at'] as DateTime) + : DateTime.parse(map['created_at'].toString())) + : null, + updatedAt: map['updated_at'] != null + ? (map['updated_at'] is DateTime + ? (map['updated_at'] as DateTime) + : DateTime.parse(map['updated_at'].toString())) + : null); + } + + static Map toMap(Bookmark model) { + if (model == null) { + return null; + } + if (model.page == null) { + throw new FormatException("Missing required field 'page' on Bookmark."); + } + + return { + 'id': model.id, + 'history': model.history, + 'page': model.page, + 'comment': model.comment, + 'created_at': model.createdAt?.toIso8601String(), + 'updated_at': model.updatedAt?.toIso8601String() + }; + } +} + +abstract class BookmarkFields { + static const List allFields = const [ + id, + history, + page, + comment, + createdAt, + updatedAt + ]; + + static const String id = 'id'; + + static const String history = 'history'; + + static const String page = 'page'; + + static const String comment = 'comment'; + + static const String createdAt = 'created_at'; + + static const String updatedAt = 'updated_at'; +} diff --git a/angel_serialize_generator/test/models/author.serializer.g.dart b/angel_serialize_generator/test/models/author.serializer.g.dart deleted file mode 100644 index a4970f28..00000000 --- a/angel_serialize_generator/test/models/author.serializer.g.dart +++ /dev/null @@ -1,219 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of angel_serialize.test.models.author; - -// ************************************************************************** -// SerializerGenerator -// ************************************************************************** - -abstract class AuthorSerializer { - static Author fromMap(Map map) { - if (map['name'] == null) { - throw new FormatException("Missing required field 'name' on Author."); - } - - if (map['age'] == null) { - throw new FormatException("Custom message for missing `age`"); - } - - return new Author( - id: map['id'] as String, - name: map['name'] as String, - age: map['age'] as int, - books: map['books'] is Iterable - ? new List.unmodifiable(((map['books'] as Iterable) - .where((x) => x is Map) as Iterable) - .map(BookSerializer.fromMap)) - : null, - newestBook: map['newest_book'] != null - ? BookSerializer.fromMap(map['newest_book'] as Map) - : null, - obscured: map['obscured'] as String, - createdAt: map['created_at'] != null - ? (map['created_at'] is DateTime - ? (map['created_at'] as DateTime) - : DateTime.parse(map['created_at'].toString())) - : null, - updatedAt: map['updated_at'] != null - ? (map['updated_at'] is DateTime - ? (map['updated_at'] as DateTime) - : DateTime.parse(map['updated_at'].toString())) - : null); - } - - static Map toMap(Author model) { - if (model == null) { - return null; - } - if (model.name == null) { - throw new FormatException("Missing required field 'name' on Author."); - } - - if (model.age == null) { - throw new FormatException("Custom message for missing `age`"); - } - - return { - 'id': model.id, - 'name': model.name, - 'age': model.age, - 'books': model.books?.map((m) => m.toJson())?.toList(), - 'newest_book': BookSerializer.toMap(model.newestBook), - 'created_at': model.createdAt?.toIso8601String(), - 'updated_at': model.updatedAt?.toIso8601String() - }; - } -} - -abstract class AuthorFields { - static const List allFields = const [ - id, - name, - age, - books, - newestBook, - secret, - obscured, - createdAt, - updatedAt - ]; - - static const String id = 'id'; - - static const String name = 'name'; - - static const String age = 'age'; - - static const String books = 'books'; - - static const String newestBook = 'newest_book'; - - static const String secret = 'secret'; - - static const String obscured = 'obscured'; - - static const String createdAt = 'created_at'; - - static const String updatedAt = 'updated_at'; -} - -abstract class LibrarySerializer { - static Library fromMap(Map map) { - return new Library( - id: map['id'] as String, - collection: map['collection'] is Map - ? new Map.unmodifiable( - (map['collection'] as Map).keys.fold({}, (out, key) { - return out - ..[key] = BookSerializer.fromMap( - ((map['collection'] as Map)[key]) as Map); - })) - : null, - createdAt: map['created_at'] != null - ? (map['created_at'] is DateTime - ? (map['created_at'] as DateTime) - : DateTime.parse(map['created_at'].toString())) - : null, - updatedAt: map['updated_at'] != null - ? (map['updated_at'] is DateTime - ? (map['updated_at'] as DateTime) - : DateTime.parse(map['updated_at'].toString())) - : null); - } - - static Map toMap(Library model) { - if (model == null) { - return null; - } - return { - 'id': model.id, - 'collection': model.collection.keys?.fold({}, (map, key) { - return map..[key] = BookSerializer.toMap(model.collection[key]); - }), - 'created_at': model.createdAt?.toIso8601String(), - 'updated_at': model.updatedAt?.toIso8601String() - }; - } -} - -abstract class LibraryFields { - static const List allFields = const [ - id, - collection, - createdAt, - updatedAt - ]; - - static const String id = 'id'; - - static const String collection = 'collection'; - - static const String createdAt = 'created_at'; - - static const String updatedAt = 'updated_at'; -} - -abstract class BookmarkSerializer { - static Bookmark fromMap(Map map, Book book) { - if (map['page'] == null) { - throw new FormatException("Missing required field 'page' on Bookmark."); - } - - return new Bookmark(book, - id: map['id'] as String, - history: map['history'] as List, - page: map['page'] as int, - comment: map['comment'] as String, - createdAt: map['created_at'] != null - ? (map['created_at'] is DateTime - ? (map['created_at'] as DateTime) - : DateTime.parse(map['created_at'].toString())) - : null, - updatedAt: map['updated_at'] != null - ? (map['updated_at'] is DateTime - ? (map['updated_at'] as DateTime) - : DateTime.parse(map['updated_at'].toString())) - : null); - } - - static Map toMap(Bookmark model) { - if (model == null) { - return null; - } - if (model.page == null) { - throw new FormatException("Missing required field 'page' on Bookmark."); - } - - return { - 'id': model.id, - 'history': model.history, - 'page': model.page, - 'comment': model.comment, - 'created_at': model.createdAt?.toIso8601String(), - 'updated_at': model.updatedAt?.toIso8601String() - }; - } -} - -abstract class BookmarkFields { - static const List allFields = const [ - id, - history, - page, - comment, - createdAt, - updatedAt - ]; - - static const String id = 'id'; - - static const String history = 'history'; - - static const String page = 'page'; - - static const String comment = 'comment'; - - static const String createdAt = 'created_at'; - - static const String updatedAt = 'updated_at'; -} diff --git a/angel_serialize_generator/test/models/book.d.ts b/angel_serialize_generator/test/models/book.d.ts deleted file mode 100644 index e67cc967..00000000 --- a/angel_serialize_generator/test/models/book.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND -declare module 'angel_serialize_generator' { - interface Book { - id?: string; - author?: string; - title?: string; - description?: string; - page_count?: number; - not_models?: number[]; - camelCase?: string; - created_at?: any; - updated_at?: any; - } -} \ No newline at end of file diff --git a/angel_serialize_generator/test/models/book.dart b/angel_serialize_generator/test/models/book.dart index 790c8926..bb02e464 100644 --- a/angel_serialize_generator/test/models/book.dart +++ b/angel_serialize_generator/test/models/book.dart @@ -4,7 +4,6 @@ import 'package:angel_model/angel_model.dart'; import 'package:angel_serialize/angel_serialize.dart'; import 'package:collection/collection.dart'; part 'book.g.dart'; -part 'book.serializer.g.dart'; @Serializable(serializers: Serializers.all) abstract class _Book extends Model { diff --git a/angel_serialize_generator/test/models/book.g.dart b/angel_serialize_generator/test/models/book.g.dart index 5de4cb2b..464cd4ec 100644 --- a/angel_serialize_generator/test/models/book.g.dart +++ b/angel_serialize_generator/test/models/book.g.dart @@ -102,3 +102,81 @@ class Book extends _Book { return BookSerializer.toMap(this); } } + +// ************************************************************************** +// SerializerGenerator +// ************************************************************************** + +abstract class BookSerializer { + static Book fromMap(Map map) { + return new Book( + id: map['id'] as String, + author: map['author'] as String, + title: map['title'] as String, + description: map['description'] as String, + pageCount: map['page_count'] as int, + notModels: map['not_models'] is Iterable + ? (map['not_models'] as Iterable).cast().toList() + : null, + camelCaseString: map['camelCase'] as String, + createdAt: map['created_at'] != null + ? (map['created_at'] is DateTime + ? (map['created_at'] as DateTime) + : DateTime.parse(map['created_at'].toString())) + : null, + updatedAt: map['updated_at'] != null + ? (map['updated_at'] is DateTime + ? (map['updated_at'] as DateTime) + : DateTime.parse(map['updated_at'].toString())) + : null); + } + + static Map toMap(Book model) { + if (model == null) { + return null; + } + return { + 'id': model.id, + 'author': model.author, + 'title': model.title, + 'description': model.description, + 'page_count': model.pageCount, + 'not_models': model.notModels, + 'camelCase': model.camelCaseString, + 'created_at': model.createdAt?.toIso8601String(), + 'updated_at': model.updatedAt?.toIso8601String() + }; + } +} + +abstract class BookFields { + static const List allFields = const [ + id, + author, + title, + description, + pageCount, + notModels, + camelCaseString, + createdAt, + updatedAt + ]; + + static const String id = 'id'; + + static const String author = 'author'; + + static const String title = 'title'; + + static const String description = 'description'; + + static const String pageCount = 'page_count'; + + static const String notModels = 'not_models'; + + static const String camelCaseString = 'camelCase'; + + static const String createdAt = 'created_at'; + + static const String updatedAt = 'updated_at'; +} diff --git a/angel_serialize_generator/test/models/book.serializer.g.dart b/angel_serialize_generator/test/models/book.serializer.g.dart deleted file mode 100644 index f1eff5f3..00000000 --- a/angel_serialize_generator/test/models/book.serializer.g.dart +++ /dev/null @@ -1,79 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of angel_serialize.test.models.book; - -// ************************************************************************** -// SerializerGenerator -// ************************************************************************** - -abstract class BookSerializer { - static Book fromMap(Map map) { - return new Book( - id: map['id'] as String, - author: map['author'] as String, - title: map['title'] as String, - description: map['description'] as String, - pageCount: map['page_count'] as int, - notModels: map['not_models'] as List, - camelCaseString: map['camelCase'] as String, - createdAt: map['created_at'] != null - ? (map['created_at'] is DateTime - ? (map['created_at'] as DateTime) - : DateTime.parse(map['created_at'].toString())) - : null, - updatedAt: map['updated_at'] != null - ? (map['updated_at'] is DateTime - ? (map['updated_at'] as DateTime) - : DateTime.parse(map['updated_at'].toString())) - : null); - } - - static Map toMap(Book model) { - if (model == null) { - return null; - } - return { - 'id': model.id, - 'author': model.author, - 'title': model.title, - 'description': model.description, - 'page_count': model.pageCount, - 'not_models': model.notModels, - 'camelCase': model.camelCaseString, - 'created_at': model.createdAt?.toIso8601String(), - 'updated_at': model.updatedAt?.toIso8601String() - }; - } -} - -abstract class BookFields { - static const List allFields = const [ - id, - author, - title, - description, - pageCount, - notModels, - camelCaseString, - createdAt, - updatedAt - ]; - - static const String id = 'id'; - - static const String author = 'author'; - - static const String title = 'title'; - - static const String description = 'description'; - - static const String pageCount = 'page_count'; - - static const String notModels = 'not_models'; - - static const String camelCaseString = 'camelCase'; - - static const String createdAt = 'created_at'; - - static const String updatedAt = 'updated_at'; -} diff --git a/angel_serialize_generator/test/models/game_pad.dart b/angel_serialize_generator/test/models/game_pad.dart index 7503cd75..dbc70c39 100644 --- a/angel_serialize_generator/test/models/game_pad.dart +++ b/angel_serialize_generator/test/models/game_pad.dart @@ -1,10 +1,8 @@ import 'package:angel_serialize/angel_serialize.dart'; import 'package:collection/collection.dart'; import 'game_pad_button.dart'; - part 'game_pad.g.dart'; -part 'game_pad.serializer.g.dart'; @Serializable(autoIdAndDateFields: false) class _Gamepad { diff --git a/angel_serialize_generator/test/models/game_pad.g.dart b/angel_serialize_generator/test/models/game_pad.g.dart index 85fd895f..264a009e 100644 --- a/angel_serialize_generator/test/models/game_pad.g.dart +++ b/angel_serialize_generator/test/models/game_pad.g.dart @@ -45,3 +45,39 @@ class Gamepad extends _Gamepad { return GamepadSerializer.toMap(this); } } + +// ************************************************************************** +// SerializerGenerator +// ************************************************************************** + +abstract class GamepadSerializer { + static Gamepad fromMap(Map map) { + return new Gamepad( + buttons: map['buttons'] is Iterable + ? new List.unmodifiable(((map['buttons'] as Iterable) + .where((x) => x is Map) as Iterable) + .map(GamepadButtonSerializer.fromMap)) + : null, + dynamicMap: map['dynamic_map'] is Map + ? (map['dynamic_map'] as Map).cast() + : null); + } + + static Map toMap(Gamepad model) { + if (model == null) { + return null; + } + return { + 'buttons': model.buttons?.map((m) => m.toJson())?.toList(), + 'dynamic_map': model.dynamicMap + }; + } +} + +abstract class GamepadFields { + static const List allFields = const [buttons, dynamicMap]; + + static const String buttons = 'buttons'; + + static const String dynamicMap = 'dynamic_map'; +} diff --git a/angel_serialize_generator/test/models/game_pad.serializer.g.dart b/angel_serialize_generator/test/models/game_pad.serializer.g.dart deleted file mode 100644 index ce27183b..00000000 --- a/angel_serialize_generator/test/models/game_pad.serializer.g.dart +++ /dev/null @@ -1,37 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'game_pad.dart'; - -// ************************************************************************** -// SerializerGenerator -// ************************************************************************** - -abstract class GamepadSerializer { - static Gamepad fromMap(Map map) { - return new Gamepad( - buttons: map['buttons'] is Iterable - ? new List.unmodifiable(((map['buttons'] as Iterable) - .where((x) => x is Map) as Iterable) - .map(GamepadButtonSerializer.fromMap)) - : null, - dynamicMap: map['dynamic_map'] as Map); - } - - static Map toMap(Gamepad model) { - if (model == null) { - return null; - } - return { - 'buttons': model.buttons?.map((m) => m.toJson())?.toList(), - 'dynamic_map': model.dynamicMap - }; - } -} - -abstract class GamepadFields { - static const List allFields = const [buttons, dynamicMap]; - - static const String buttons = 'buttons'; - - static const String dynamicMap = 'dynamic_map'; -} diff --git a/angel_serialize_generator/test/models/game_pad_button.dart b/angel_serialize_generator/test/models/game_pad_button.dart index e3777b75..e2257e9e 100644 --- a/angel_serialize_generator/test/models/game_pad_button.dart +++ b/angel_serialize_generator/test/models/game_pad_button.dart @@ -1,6 +1,5 @@ import 'package:angel_serialize/angel_serialize.dart'; part 'game_pad_button.g.dart'; -part 'game_pad_button.serializer.g.dart'; @Serializable(autoIdAndDateFields: false) abstract class _GamepadButton { diff --git a/angel_serialize_generator/test/models/game_pad_button.g.dart b/angel_serialize_generator/test/models/game_pad_button.g.dart index 262146df..37b94445 100644 --- a/angel_serialize_generator/test/models/game_pad_button.g.dart +++ b/angel_serialize_generator/test/models/game_pad_button.g.dart @@ -36,3 +36,29 @@ class GamepadButton implements _GamepadButton { return GamepadButtonSerializer.toMap(this); } } + +// ************************************************************************** +// SerializerGenerator +// ************************************************************************** + +abstract class GamepadButtonSerializer { + static GamepadButton fromMap(Map map) { + return new GamepadButton( + name: map['name'] as String, radius: map['radius'] as int); + } + + static Map toMap(GamepadButton model) { + if (model == null) { + return null; + } + return {'name': model.name, 'radius': model.radius}; + } +} + +abstract class GamepadButtonFields { + static const List allFields = const [name, radius]; + + static const String name = 'name'; + + static const String radius = 'radius'; +} diff --git a/angel_serialize_generator/test/models/game_pad_button.serializer.g.dart b/angel_serialize_generator/test/models/game_pad_button.serializer.g.dart deleted file mode 100644 index e9cb0dfa..00000000 --- a/angel_serialize_generator/test/models/game_pad_button.serializer.g.dart +++ /dev/null @@ -1,29 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'game_pad_button.dart'; - -// ************************************************************************** -// SerializerGenerator -// ************************************************************************** - -abstract class GamepadButtonSerializer { - static GamepadButton fromMap(Map map) { - return new GamepadButton( - name: map['name'] as String, radius: map['radius'] as int); - } - - static Map toMap(GamepadButton model) { - if (model == null) { - return null; - } - return {'name': model.name, 'radius': model.radius}; - } -} - -abstract class GamepadButtonFields { - static const List allFields = const [name, radius]; - - static const String name = 'name'; - - static const String radius = 'radius'; -} diff --git a/angel_serialize_generator/test/models/with_enum.dart b/angel_serialize_generator/test/models/with_enum.dart index f8c08dc5..39975a73 100644 --- a/angel_serialize_generator/test/models/with_enum.dart +++ b/angel_serialize_generator/test/models/with_enum.dart @@ -1,13 +1,16 @@ +import 'dart:convert'; +import 'dart:typed_data'; import 'package:angel_serialize/angel_serialize.dart'; import 'package:collection/collection.dart'; part 'with_enum.g.dart'; -part 'with_enum.serializer.g.dart'; @Serializable(autoIdAndDateFields: false) abstract class _WithEnum { WithEnumType get type; List get finalList; + + Uint8List get imageBytes; } enum WithEnumType { a, b, c } diff --git a/angel_serialize_generator/test/models/with_enum.g.dart b/angel_serialize_generator/test/models/with_enum.g.dart index 2ffdbd00..5eb07319 100644 --- a/angel_serialize_generator/test/models/with_enum.g.dart +++ b/angel_serialize_generator/test/models/with_enum.g.dart @@ -8,7 +8,7 @@ part of 'with_enum.dart'; @generatedSerializable class WithEnum implements _WithEnum { - const WithEnum({this.type, List this.finalList}); + const WithEnum({this.type, List this.finalList, this.imageBytes}); @override final WithEnumType type; @@ -16,24 +16,85 @@ class WithEnum implements _WithEnum { @override final List finalList; - WithEnum copyWith({WithEnumType type, List finalList}) { + @override + final Uint8List imageBytes; + + WithEnum copyWith( + {WithEnumType type, List finalList, Uint8List imageBytes}) { return new WithEnum( - type: type ?? this.type, finalList: finalList ?? this.finalList); + type: type ?? this.type, + finalList: finalList ?? this.finalList, + imageBytes: imageBytes ?? this.imageBytes); } bool operator ==(other) { return other is _WithEnum && other.type == type && const ListEquality(const DefaultEquality()) - .equals(other.finalList, finalList); + .equals(other.finalList, finalList) && + const ListEquality().equals(other.imageBytes, imageBytes); } @override int get hashCode { - return hashObjects([type, finalList]); + return hashObjects([type, finalList, imageBytes]); } Map toJson() { return WithEnumSerializer.toMap(this); } } + +// ************************************************************************** +// SerializerGenerator +// ************************************************************************** + +abstract class WithEnumSerializer { + static WithEnum fromMap(Map map) { + return new WithEnum( + type: map['type'] is WithEnumType + ? (map['type'] as WithEnumType) + : (map['type'] is int + ? WithEnumType.values[map['type'] as int] + : null), + finalList: map['final_list'] is Iterable + ? (map['final_list'] as Iterable).cast().toList() + : null, + imageBytes: map['image_bytes'] is Uint8List + ? (map['image_bytes'] as Uint8List) + : (map['image_bytes'] is Iterable + ? new Uint8List.fromList( + (map['image_bytes'] as Iterable).toList()) + : (map['image_bytes'] is String + ? new Uint8List.fromList( + base64.decode(map['image_bytes'] as String)) + : null))); + } + + static Map toMap(WithEnum model) { + if (model == null) { + return null; + } + return { + 'type': + model.type == null ? null : WithEnumType.values.indexOf(model.type), + 'final_list': model.finalList, + 'image_bytes': + model.imageBytes == null ? null : base64.encode(model.imageBytes) + }; + } +} + +abstract class WithEnumFields { + static const List allFields = const [ + type, + finalList, + imageBytes + ]; + + static const String type = 'type'; + + static const String finalList = 'final_list'; + + static const String imageBytes = 'image_bytes'; +} diff --git a/angel_serialize_generator/test/models/with_enum.serializer.g.dart b/angel_serialize_generator/test/models/with_enum.serializer.g.dart deleted file mode 100644 index 9d545083..00000000 --- a/angel_serialize_generator/test/models/with_enum.serializer.g.dart +++ /dev/null @@ -1,38 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'with_enum.dart'; - -// ************************************************************************** -// SerializerGenerator -// ************************************************************************** - -abstract class WithEnumSerializer { - static WithEnum fromMap(Map map) { - return new WithEnum( - type: map['type'] is WithEnumType - ? (map['type'] as WithEnumType) - : (map['type'] is int - ? WithEnumType.values[map['type'] as int] - : null), - finalList: map['final_list'] as List); - } - - static Map toMap(WithEnum model) { - if (model == null) { - return null; - } - return { - 'type': - model.type == null ? null : WithEnumType.values.indexOf(model.type), - 'final_list': model.finalList - }; - } -} - -abstract class WithEnumFields { - static const List allFields = const [type, finalList]; - - static const String type = 'type'; - - static const String finalList = 'final_list'; -}