Force unmodifiable collections
This commit is contained in:
parent
d4a58cfcef
commit
46d4c24f69
8 changed files with 64 additions and 28 deletions
|
@ -53,6 +53,7 @@ library angel_serialize.test.models.book;
|
|||
|
||||
import 'package:angel_framework/common.dart';
|
||||
import 'package:angel_serialize/angel_serialize.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
part 'book.g.dart';
|
||||
|
||||
@serializable
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
# 2.0.7
|
||||
* Create unmodifiable Lists and Maps.
|
||||
|
||||
# 2.0.6
|
||||
* Support for using `abstract` to create immutable model classes.
|
||||
* Add support for custom constructor parameters.
|
||||
|
|
|
@ -60,16 +60,21 @@ bool isModelClass(DartType t) {
|
|||
}
|
||||
}
|
||||
|
||||
bool isListOrMapType(DartType t) {
|
||||
return const TypeChecker.fromRuntime(List).isAssignableFromType(t) ||
|
||||
const TypeChecker.fromRuntime(Map).isAssignableFromType(t);
|
||||
}
|
||||
|
||||
/// Determines if a [DartType] is a `List` with the first type argument being a `Model`.
|
||||
bool isListModelType(InterfaceType t) {
|
||||
return t.name == 'List' &&
|
||||
return const TypeChecker.fromRuntime(List).isAssignableFromType(t) &&
|
||||
t.typeArguments.length == 1 &&
|
||||
isModelClass(t.typeArguments[0]);
|
||||
}
|
||||
|
||||
/// Determines if a [DartType] is a `Map` with the second type argument being a `Model`.
|
||||
bool isMapToModelType(InterfaceType t) {
|
||||
return t.name == 'Map' &&
|
||||
return const TypeChecker.fromRuntime(Map).isAssignableFromType(t) &&
|
||||
t.typeArguments.length == 2 &&
|
||||
isModelClass(t.typeArguments[1]);
|
||||
}
|
||||
|
|
|
@ -63,23 +63,43 @@ class JsonModelGenerator extends GeneratorForAnnotation<Serializable> {
|
|||
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 param in ctx.constructorParameters) {
|
||||
constructor.requiredParameters.add(new Parameter((b) => b
|
||||
..name = param.name
|
||||
..type = convertTypeReference(param.type)));
|
||||
}
|
||||
|
||||
for (var field in ctx.fields) {
|
||||
if (isListOrMapType(field.type)) {
|
||||
String typeName = const TypeChecker.fromRuntime(List)
|
||||
.isAssignableFromType(field.type)
|
||||
? 'List'
|
||||
: 'Map';
|
||||
var defaultValue = typeName == 'List' ? '[]' : '{}';
|
||||
constructor.initializers.add(new Code('''
|
||||
this.${field.name} =
|
||||
new $typeName.unmodifiable(${field.name} ?? $defaultValue)'''));
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.constructorParameters.isNotEmpty) {
|
||||
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
|
||||
..name = field.name
|
||||
..named = true
|
||||
..toThis = true;
|
||||
..named = true;
|
||||
|
||||
if (!isListOrMapType(field.type))
|
||||
b.toThis = true;
|
||||
else {
|
||||
b.type = convertTypeReference(field.type);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}));
|
||||
|
@ -141,9 +161,11 @@ class JsonModelGenerator extends GeneratorForAnnotation<Serializable> {
|
|||
if (it.typeParameters.length == 2) {
|
||||
var keq = generateEquality(it.typeArguments[0]),
|
||||
veq = generateEquality(it.typeArguments[1]);
|
||||
return 'const MapEquality<${it.typeArguments[0].name}, ${it.typeArguments[1].name}>(keys: $keq, values: $veq)';
|
||||
return 'const MapEquality<${it.typeArguments[0].name}, ${it
|
||||
.typeArguments[1].name}>(keys: $keq, values: $veq)';
|
||||
} else
|
||||
return 'const MapEquality()<${it.typeArguments[0].name}, ${it.typeArguments[1].name}>';
|
||||
return 'const MapEquality()<${it.typeArguments[0].name}, ${it
|
||||
.typeArguments[1].name}>';
|
||||
}
|
||||
|
||||
return nullable ? null : 'const DefaultEquality<${type.name}>()';
|
||||
|
|
|
@ -169,17 +169,17 @@ class SerializerGenerator extends GeneratorForAnnotation<Serializable> {
|
|||
if (isListModelType(t)) {
|
||||
var rc = new ReCase(t.typeArguments[0].name);
|
||||
deserializedRepresentation = "map['$alias'] is Iterable"
|
||||
" ? map['$alias'].map(${rc
|
||||
.pascalCase}Serializer.fromMap).toList()"
|
||||
" ? new List.unmodifiable(map['$alias'].map(${rc
|
||||
.pascalCase}Serializer.fromMap))"
|
||||
" : null";
|
||||
} else if (isMapToModelType(t)) {
|
||||
var rc = new ReCase(t.typeArguments[1].name);
|
||||
deserializedRepresentation = '''
|
||||
map['$alias'] is Map
|
||||
? map['$alias'].keys.fold({}, (out, key) {
|
||||
? new Map.unmodifiable(map['$alias'].keys.fold({}, (out, key) {
|
||||
return out..[key] = ${rc
|
||||
.pascalCase}Serializer.fromMap(map['$alias'][key]);
|
||||
})
|
||||
}))
|
||||
: null
|
||||
''';
|
||||
}
|
||||
|
|
|
@ -11,12 +11,13 @@ class Author extends _Author {
|
|||
{this.id,
|
||||
this.name,
|
||||
this.age,
|
||||
this.books,
|
||||
List<Book> books,
|
||||
this.newestBook,
|
||||
this.secret,
|
||||
this.obscured,
|
||||
this.createdAt,
|
||||
this.updatedAt});
|
||||
this.updatedAt})
|
||||
: this.books = new List.unmodifiable(books ?? []);
|
||||
|
||||
@override
|
||||
final String id;
|
||||
|
@ -87,7 +88,9 @@ class Author extends _Author {
|
|||
}
|
||||
|
||||
class Library extends _Library {
|
||||
Library({this.id, this.collection, this.createdAt, this.updatedAt});
|
||||
Library(
|
||||
{this.id, Map<String, Book> collection, this.createdAt, this.updatedAt})
|
||||
: this.collection = new Map.unmodifiable(collection ?? {});
|
||||
|
||||
@override
|
||||
final String id;
|
||||
|
@ -132,12 +135,13 @@ class Library extends _Library {
|
|||
class Bookmark extends _Bookmark {
|
||||
Bookmark(Book book,
|
||||
{this.id,
|
||||
this.history,
|
||||
List<int> history,
|
||||
this.page,
|
||||
this.comment,
|
||||
this.createdAt,
|
||||
this.updatedAt})
|
||||
: super(book);
|
||||
: this.history = new List.unmodifiable(history ?? []),
|
||||
super(book);
|
||||
|
||||
@override
|
||||
final String id;
|
||||
|
|
|
@ -13,7 +13,7 @@ abstract class AuthorSerializer {
|
|||
name: map['name'],
|
||||
age: map['age'],
|
||||
books: map['books'] is Iterable
|
||||
? map['books'].map(BookSerializer.fromMap).toList()
|
||||
? new List.unmodifiable(map['books'].map(BookSerializer.fromMap))
|
||||
: null,
|
||||
newestBook: map['newest_book'] != null
|
||||
? BookSerializer.fromMap(map['newest_book'])
|
||||
|
@ -69,10 +69,10 @@ abstract class LibrarySerializer {
|
|||
return new Library(
|
||||
id: map['id'],
|
||||
collection: map['collection'] is Map
|
||||
? map['collection'].keys.fold({}, (out, key) {
|
||||
? new Map.unmodifiable(map['collection'].keys.fold({}, (out, key) {
|
||||
return out
|
||||
..[key] = BookSerializer.fromMap(map['collection'][key]);
|
||||
})
|
||||
}))
|
||||
: null,
|
||||
createdAt: map['created_at'] != null
|
||||
? (map['created_at'] is DateTime
|
||||
|
|
|
@ -13,10 +13,11 @@ class Book extends _Book {
|
|||
this.title,
|
||||
this.description,
|
||||
this.pageCount,
|
||||
this.notModels,
|
||||
List<double> notModels,
|
||||
this.camelCaseString,
|
||||
this.createdAt,
|
||||
this.updatedAt});
|
||||
this.updatedAt})
|
||||
: this.notModels = new List.unmodifiable(notModels ?? []);
|
||||
|
||||
@override
|
||||
final String id;
|
||||
|
|
Loading…
Reference in a new issue