Force unmodifiable collections

This commit is contained in:
Tobe O 2018-05-15 15:01:13 -04:00
parent d4a58cfcef
commit 46d4c24f69
8 changed files with 64 additions and 28 deletions

View file

@ -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

View file

@ -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.

View file

@ -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]);
}

View file

@ -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 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}>()';

View file

@ -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
''';
}

View file

@ -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;

View file

@ -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

View file

@ -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;