gen@2.4.3

This commit is contained in:
Tobe O 2019-04-08 11:00:04 -04:00
parent 439ced6cab
commit f0cc3bb513
17 changed files with 363 additions and 24 deletions

View file

@ -96,6 +96,12 @@ 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.
- `BookEncoder`: Allows `BookSerializer` to extend `Codec<Book, Map>`.
- `BookDecoder`: Also allows `BookSerializer` to extend `Codec<Book, Map>`.
And the following other features:
- `bookSerializer`: A top-level, `const` instance of `BookSerializer`.
- `Book.toString`: Prints out all of a `Book` instance's fields.
# Serialization

View file

@ -1,3 +1,6 @@
# 2.2.3+1
* Export `json`, `Codec`, and `Converter` from `dart:convert`.
# 2.2.3
* `isNullable` defaults to `true`, and will not change.
* Deprecate `@nullable`.

View file

@ -1,3 +1,4 @@
export 'dart:convert' show json, Codec, Converter;
export 'package:angel_model/angel_model.dart';
export 'package:collection/collection.dart';
export 'package:meta/meta.dart' show required, Required;

View file

@ -1,5 +1,5 @@
name: angel_serialize
version: 2.2.3
version: 2.2.3+1
description: Static annotations powering Angel model serialization. Combine with angel_serialize_generator for flexible modeling.
author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/serialize

View file

@ -1,3 +1,8 @@
# 2.4.3
* Generate `Codec` and `Converter` classes.
* Generate `toString` methods.
* Include original documentation comments from the model.
# 2.4.2
* Fix bug where enums didn't support default values.
* Stop emitting `@required` on items with default values.

View file

@ -30,6 +30,11 @@ class Todo extends _Todo {
return hashObjects([text, completed]);
}
@override
String toString() {
return "Todo(text=$text, completed=$completed)";
}
Map<String, dynamic> toJson() {
return TodoSerializer.toMap(this);
}
@ -39,7 +44,29 @@ class Todo extends _Todo {
// SerializerGenerator
// **************************************************************************
abstract class TodoSerializer {
const TodoSerializer todoSerializer = const TodoSerializer();
class TodoEncoder extends Converter<Todo, Map> {
const TodoEncoder();
@override
Map convert(Todo model) => TodoSerializer.toMap(model);
}
class TodoDecoder extends Converter<Map, Todo> {
const TodoDecoder();
@override
Todo convert(Map map) => TodoSerializer.fromMap(map);
}
class TodoSerializer extends Codec<Todo, Map> {
const TodoSerializer();
@override
get encoder => const TodoEncoder();
@override
get decoder => const TodoDecoder();
static Todo fromMap(Map map) {
return new Todo(
text: map['text'] as String, completed: map['completed'] as bool);

View file

@ -48,6 +48,12 @@ class JsonModelGenerator extends GeneratorForAnnotation<Serializable> {
..modifier = FieldModifier.final$
..annotations.add(new CodeExpression(new Code('override')))
..type = convertTypeReference(field.type);
for (var el in [field.getter, field]) {
if (el?.documentationComment != null) {
b.docs.addAll(el.documentationComment.split('\n'));
}
}
}));
}
@ -55,6 +61,7 @@ class JsonModelGenerator extends GeneratorForAnnotation<Serializable> {
generateCopyWithMethod(ctx, clazz, file);
generateEqualsOperator(ctx, clazz, file);
generateHashCode(ctx, clazz);
generateToString(ctx, clazz);
// Generate toJson() method if necessary
var serializers = annotation.peek('serializers')?.listValue ?? [];
@ -235,6 +242,25 @@ class JsonModelGenerator extends GeneratorForAnnotation<Serializable> {
}));
}
void generateToString(BuildContext ctx, ClassBuilder clazz) {
clazz.methods.add(Method((b) {
b
..name = 'toString'
..returns = refer('String')
..annotations.add(refer('override'))
..body = Block((b) {
var buf = StringBuffer('\"${ctx.modelClassName}(');
var i = 0;
for (var field in ctx.fields) {
if (i++ > 0) buf.write(', ');
buf.write('${field.name}=\$${field.name}');
}
buf.write(')\"');
b.addExpression(CodeExpression(Code(buf.toString())).returned);
});
}));
}
void generateEqualsOperator(
BuildContext ctx, ClassBuilder clazz, LibraryBuilder file) {
clazz.methods.add(new Method((method) {

View file

@ -35,10 +35,53 @@ class SerializerGenerator extends GeneratorForAnnotation<Serializable> {
/// Generate a serializer class.
void generateClass(
List<int> serializers, BuildContext ctx, LibraryBuilder file) {
// Generate canonical codecs, etc.
var pascal = ctx.modelClassNameRecase.pascalCase,
camel = ctx.modelClassNameRecase.camelCase;
if (ctx.constructorParameters.isEmpty) {
file.body.add(new Code('''
const ${pascal}Serializer ${camel}Serializer = const ${pascal}Serializer();
class ${pascal}Encoder extends Converter<${pascal}, Map> {
const ${pascal}Encoder();
@override
Map convert(${pascal} model) => ${pascal}Serializer.toMap(model);
}
class ${pascal}Decoder extends Converter<Map, ${pascal}> {
const ${pascal}Decoder();
@override
${pascal} convert(Map map) => ${pascal}Serializer.fromMap(map);
}
'''));
}
file.body.add(new Class((clazz) {
clazz
..name = '${ctx.modelClassNameRecase.pascalCase}Serializer'
..abstract = true;
clazz..name = '${pascal}Serializer';
if (ctx.constructorParameters.isEmpty) {
clazz
..extend = TypeReference((b) => b
..symbol = 'Codec'
..types.addAll([ctx.modelClassType, refer('Map')]));
// Add constructor, Codec impl, etc.
clazz.constructors.add(Constructor((b) => b..constant = true));
clazz.methods.add(Method((b) => b
..name = 'encoder'
..type = MethodType.getter
..annotations.add(refer('override'))
..body = refer('${pascal}Encoder').constInstance([]).code));
clazz.methods.add(Method((b) => b
..name = 'decoder'
..type = MethodType.getter
..annotations.add(refer('override'))
..body = refer('${pascal}Decoder').constInstance([]).code));
} else {
clazz.abstract = true;
}
if (serializers.contains(Serializers.map)) {
generateFromMapMethod(clazz, ctx, file);

View file

@ -1,5 +1,5 @@
name: angel_serialize_generator
version: 2.4.2
version: 2.4.3
description: Model serialization generators, designed for use with Angel. Combine with angel_serialize for flexible modeling.
author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/serialize
@ -22,6 +22,6 @@ dev_dependencies:
build_runner: ^1.0.0
collection: ^1.0.0
test: ^1.0.0
# dependency_overrides:
# angel_serialize:
# path: ../angel_serialize
dependency_overrides:
angel_serialize:
path: ../angel_serialize

View file

@ -13,7 +13,7 @@ main() {
notModels: [1.0, 3.0],
updatedAt: new DateTime.now());
var serializedDeathlyHallows = deathlyHallows.toJson();
print('Deathly Hallows: $serializedDeathlyHallows');
print('Deathly Hallows: $deathlyHallows');
var jkRowling = new Author(
id: '1',
@ -21,13 +21,13 @@ main() {
age: 51,
books: [deathlyHallows],
newestBook: deathlyHallows);
Map serializedJkRowling = AuthorSerializer.toMap(jkRowling);
Map deathlyHallowsMap = BookSerializer.toMap(deathlyHallows);
print('J.K. Rowling: $serializedJkRowling');
var serializedJkRowling = authorSerializer.encode(jkRowling);
var deathlyHallowsMap = bookSerializer.encode(deathlyHallows);
print('J.K. Rowling: $jkRowling');
var library = new Library(collection: {deathlyHallowsIsbn: deathlyHallows});
var serializedLibrary = LibrarySerializer.toMap(library);
print('Library: $serializedLibrary');
print('Library: $library');
group('serialization', () {
test('serialization sets proper fields', () {
@ -56,7 +56,7 @@ main() {
test('heeds canDeserialize', () {
var map = new Map.from(serializedJkRowling)..['obscured'] = 'foo';
var author = AuthorSerializer.fromMap(map);
var author = authorSerializer.decode(map);
expect(author.obscured, 'foo');
});

View file

@ -53,7 +53,7 @@ void main() {
imageBytes:
new Uint8List.fromList(new List<int>.generate(1000, (i) => i)));
var eeMap = ee.toJson();
print(eeMap);
print(ee);
var ef = WithEnumSerializer.fromMap(eeMap);
expect(ee.copyWith(), ee);
expect(ef, ee);

View file

@ -15,7 +15,10 @@ part 'book.g.dart';
)
abstract class _Book extends Model {
String author, title, description;
/// The number of pages the book has.
int pageCount;
List<double> notModels;
@SerializableField(alias: 'camelCase', isNullable: true)
@ -35,6 +38,7 @@ abstract class _Author extends Model {
List<_Book> get books;
/// The newest book.
_Book get newestBook;
@SerializableField(exclude: true, isNullable: true)

View file

@ -34,6 +34,7 @@ class Book extends _Book {
@override
final String description;
/// The number of pages the book has.
@override
final int pageCount;
@ -100,6 +101,11 @@ class Book extends _Book {
]);
}
@override
String toString() {
return "Book(id=$id, author=$author, title=$title, description=$description, pageCount=$pageCount, notModels=$notModels, camelCaseString=$camelCaseString, createdAt=$createdAt, updatedAt=$updatedAt)";
}
Map<String, dynamic> toJson() {
return BookSerializer.toMap(this);
}
@ -131,6 +137,7 @@ class Author extends _Author {
@override
final List<_Book> books;
/// The newest book.
@override
final _Book newestBook;
@ -197,6 +204,11 @@ class Author extends _Author {
]);
}
@override
String toString() {
return "Author(id=$id, name=$name, age=$age, books=$books, newestBook=$newestBook, secret=$secret, obscured=$obscured, createdAt=$createdAt, updatedAt=$updatedAt)";
}
Map<String, dynamic> toJson() {
return AuthorSerializer.toMap(this);
}
@ -248,6 +260,11 @@ class Library extends _Library {
return hashObjects([id, collection, createdAt, updatedAt]);
}
@override
String toString() {
return "Library(id=$id, collection=$collection, createdAt=$createdAt, updatedAt=$updatedAt)";
}
Map<String, dynamic> toJson() {
return LibrarySerializer.toMap(this);
}
@ -315,6 +332,11 @@ class Bookmark extends _Bookmark {
return hashObjects([id, history, page, comment, createdAt, updatedAt]);
}
@override
String toString() {
return "Bookmark(id=$id, history=$history, page=$page, comment=$comment, createdAt=$createdAt, updatedAt=$updatedAt)";
}
Map<String, dynamic> toJson() {
return BookmarkSerializer.toMap(this);
}
@ -324,7 +346,29 @@ class Bookmark extends _Bookmark {
// SerializerGenerator
// **************************************************************************
abstract class BookSerializer {
const BookSerializer bookSerializer = const BookSerializer();
class BookEncoder extends Converter<Book, Map> {
const BookEncoder();
@override
Map convert(Book model) => BookSerializer.toMap(model);
}
class BookDecoder extends Converter<Map, Book> {
const BookDecoder();
@override
Book convert(Map map) => BookSerializer.fromMap(map);
}
class BookSerializer extends Codec<Book, Map> {
const BookSerializer();
@override
get encoder => const BookEncoder();
@override
get decoder => const BookDecoder();
static Book fromMap(Map map) {
return new Book(
id: map['id'] as String,
@ -398,7 +442,29 @@ abstract class BookFields {
static const String updatedAt = 'updated_at';
}
abstract class AuthorSerializer {
const AuthorSerializer authorSerializer = const AuthorSerializer();
class AuthorEncoder extends Converter<Author, Map> {
const AuthorEncoder();
@override
Map convert(Author model) => AuthorSerializer.toMap(model);
}
class AuthorDecoder extends Converter<Map, Author> {
const AuthorDecoder();
@override
Author convert(Map map) => AuthorSerializer.fromMap(map);
}
class AuthorSerializer extends Codec<Author, Map> {
const AuthorSerializer();
@override
get encoder => const AuthorEncoder();
@override
get decoder => const AuthorDecoder();
static Author fromMap(Map map) {
if (map['name'] == null) {
throw new FormatException("Missing required field 'name' on Author.");
@ -490,7 +556,29 @@ abstract class AuthorFields {
static const String updatedAt = 'updated_at';
}
abstract class LibrarySerializer {
const LibrarySerializer librarySerializer = const LibrarySerializer();
class LibraryEncoder extends Converter<Library, Map> {
const LibraryEncoder();
@override
Map convert(Library model) => LibrarySerializer.toMap(model);
}
class LibraryDecoder extends Converter<Map, Library> {
const LibraryDecoder();
@override
Library convert(Map map) => LibrarySerializer.fromMap(map);
}
class LibrarySerializer extends Codec<Library, Map> {
const LibrarySerializer();
@override
get encoder => const LibraryEncoder();
@override
get decoder => const LibraryDecoder();
static Library fromMap(Map map) {
return new Library(
id: map['id'] as String,

View file

@ -32,6 +32,11 @@ class GamepadButton implements _GamepadButton {
return hashObjects([name, radius]);
}
@override
String toString() {
return "GamepadButton(name=$name, radius=$radius)";
}
Map<String, dynamic> toJson() {
return GamepadButtonSerializer.toMap(this);
}
@ -72,6 +77,11 @@ class Gamepad extends _Gamepad {
return hashObjects([buttons, dynamicMap]);
}
@override
String toString() {
return "Gamepad(buttons=$buttons, dynamicMap=$dynamicMap)";
}
Map<String, dynamic> toJson() {
return GamepadSerializer.toMap(this);
}
@ -81,7 +91,30 @@ class Gamepad extends _Gamepad {
// SerializerGenerator
// **************************************************************************
abstract class GamepadButtonSerializer {
const GamepadButtonSerializer gamepadButtonSerializer =
const GamepadButtonSerializer();
class GamepadButtonEncoder extends Converter<GamepadButton, Map> {
const GamepadButtonEncoder();
@override
Map convert(GamepadButton model) => GamepadButtonSerializer.toMap(model);
}
class GamepadButtonDecoder extends Converter<Map, GamepadButton> {
const GamepadButtonDecoder();
@override
GamepadButton convert(Map map) => GamepadButtonSerializer.fromMap(map);
}
class GamepadButtonSerializer extends Codec<GamepadButton, Map> {
const GamepadButtonSerializer();
@override
get encoder => const GamepadButtonEncoder();
@override
get decoder => const GamepadButtonDecoder();
static GamepadButton fromMap(Map map) {
return new GamepadButton(
name: map['name'] as String, radius: map['radius'] as int);
@ -103,7 +136,29 @@ abstract class GamepadButtonFields {
static const String radius = 'radius';
}
abstract class GamepadSerializer {
const GamepadSerializer gamepadSerializer = const GamepadSerializer();
class GamepadEncoder extends Converter<Gamepad, Map> {
const GamepadEncoder();
@override
Map convert(Gamepad model) => GamepadSerializer.toMap(model);
}
class GamepadDecoder extends Converter<Map, Gamepad> {
const GamepadDecoder();
@override
Gamepad convert(Map map) => GamepadSerializer.fromMap(map);
}
class GamepadSerializer extends Codec<Gamepad, Map> {
const GamepadSerializer();
@override
get encoder => const GamepadEncoder();
@override
get decoder => const GamepadDecoder();
static Gamepad fromMap(Map map) {
return new Gamepad(
buttons: map['buttons'] is Iterable

View file

@ -32,6 +32,11 @@ class Goat implements _Goat {
return hashObjects([integer, list]);
}
@override
String toString() {
return "Goat(integer=$integer, list=$list)";
}
Map<String, dynamic> toJson() {
return GoatSerializer.toMap(this);
}
@ -41,7 +46,29 @@ class Goat implements _Goat {
// SerializerGenerator
// **************************************************************************
abstract class GoatSerializer {
const GoatSerializer goatSerializer = const GoatSerializer();
class GoatEncoder extends Converter<Goat, Map> {
const GoatEncoder();
@override
Map convert(Goat model) => GoatSerializer.toMap(model);
}
class GoatDecoder extends Converter<Map, Goat> {
const GoatDecoder();
@override
Goat convert(Map map) => GoatSerializer.fromMap(map);
}
class GoatSerializer extends Codec<Goat, Map> {
const GoatSerializer();
@override
get encoder => const GoatEncoder();
@override
get decoder => const GoatDecoder();
static Goat fromMap(Map map) {
return new Goat(
integer: map['integer'] as int ?? 34,

View file

@ -29,6 +29,11 @@ class HasMap implements _HasMap {
return hashObjects([value]);
}
@override
String toString() {
return "HasMap(value=$value)";
}
Map<String, dynamic> toJson() {
return HasMapSerializer.toMap(this);
}
@ -38,7 +43,29 @@ class HasMap implements _HasMap {
// SerializerGenerator
// **************************************************************************
abstract class HasMapSerializer {
const HasMapSerializer hasMapSerializer = const HasMapSerializer();
class HasMapEncoder extends Converter<HasMap, Map> {
const HasMapEncoder();
@override
Map convert(HasMap model) => HasMapSerializer.toMap(model);
}
class HasMapDecoder extends Converter<Map, HasMap> {
const HasMapDecoder();
@override
HasMap convert(Map map) => HasMapSerializer.fromMap(map);
}
class HasMapSerializer extends Codec<HasMap, Map> {
const HasMapSerializer();
@override
get encoder => const HasMapEncoder();
@override
get decoder => const HasMapDecoder();
static HasMap fromMap(Map map) {
if (map['value'] == null) {
throw new FormatException("Missing required field 'value' on HasMap.");

View file

@ -41,6 +41,11 @@ class WithEnum implements _WithEnum {
return hashObjects([type, finalList, imageBytes]);
}
@override
String toString() {
return "WithEnum(type=$type, finalList=$finalList, imageBytes=$imageBytes)";
}
Map<String, dynamic> toJson() {
return WithEnumSerializer.toMap(this);
}
@ -50,7 +55,29 @@ class WithEnum implements _WithEnum {
// SerializerGenerator
// **************************************************************************
abstract class WithEnumSerializer {
const WithEnumSerializer withEnumSerializer = const WithEnumSerializer();
class WithEnumEncoder extends Converter<WithEnum, Map> {
const WithEnumEncoder();
@override
Map convert(WithEnum model) => WithEnumSerializer.toMap(model);
}
class WithEnumDecoder extends Converter<Map, WithEnum> {
const WithEnumDecoder();
@override
WithEnum convert(Map map) => WithEnumSerializer.fromMap(map);
}
class WithEnumSerializer extends Codec<WithEnum, Map> {
const WithEnumSerializer();
@override
get encoder => const WithEnumEncoder();
@override
get decoder => const WithEnumDecoder();
static WithEnum fromMap(Map map) {
return new WithEnum(
type: map['type'] is WithEnumType