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_framework/common.dart';
|
||||||
import 'package:angel_serialize/angel_serialize.dart';
|
import 'package:angel_serialize/angel_serialize.dart';
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
part 'book.g.dart';
|
part 'book.g.dart';
|
||||||
|
|
||||||
@serializable
|
@serializable
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
# 2.0.7
|
||||||
|
* Create unmodifiable Lists and Maps.
|
||||||
|
|
||||||
# 2.0.6
|
# 2.0.6
|
||||||
* Support for using `abstract` to create immutable model classes.
|
* Support for using `abstract` to create immutable model classes.
|
||||||
* Add support for custom constructor parameters.
|
* 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`.
|
/// Determines if a [DartType] is a `List` with the first type argument being a `Model`.
|
||||||
bool isListModelType(InterfaceType t) {
|
bool isListModelType(InterfaceType t) {
|
||||||
return t.name == 'List' &&
|
return const TypeChecker.fromRuntime(List).isAssignableFromType(t) &&
|
||||||
t.typeArguments.length == 1 &&
|
t.typeArguments.length == 1 &&
|
||||||
isModelClass(t.typeArguments[0]);
|
isModelClass(t.typeArguments[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines if a [DartType] is a `Map` with the second type argument being a `Model`.
|
/// Determines if a [DartType] is a `Map` with the second type argument being a `Model`.
|
||||||
bool isMapToModelType(InterfaceType t) {
|
bool isMapToModelType(InterfaceType t) {
|
||||||
return t.name == 'Map' &&
|
return const TypeChecker.fromRuntime(Map).isAssignableFromType(t) &&
|
||||||
t.typeArguments.length == 2 &&
|
t.typeArguments.length == 2 &&
|
||||||
isModelClass(t.typeArguments[1]);
|
isModelClass(t.typeArguments[1]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,23 +63,43 @@ class JsonModelGenerator extends GeneratorForAnnotation<Serializable> {
|
||||||
BuildContext ctx, ClassBuilder clazz, LibraryBuilder file) {
|
BuildContext ctx, ClassBuilder clazz, LibraryBuilder file) {
|
||||||
clazz.constructors.add(new Constructor((constructor) {
|
clazz.constructors.add(new Constructor((constructor) {
|
||||||
// Add all `super` params
|
// 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(
|
for (var param in ctx.constructorParameters) {
|
||||||
'super(${ctx.constructorParameters.map((p) => p.name).join(',')})'));
|
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) {
|
for (var field in ctx.fields) {
|
||||||
constructor.optionalParameters.add(new Parameter((b) {
|
constructor.optionalParameters.add(new Parameter((b) {
|
||||||
b
|
b
|
||||||
..name = field.name
|
..name = field.name
|
||||||
..named = true
|
..named = true;
|
||||||
..toThis = 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) {
|
if (it.typeParameters.length == 2) {
|
||||||
var keq = generateEquality(it.typeArguments[0]),
|
var keq = generateEquality(it.typeArguments[0]),
|
||||||
veq = generateEquality(it.typeArguments[1]);
|
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
|
} 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}>()';
|
return nullable ? null : 'const DefaultEquality<${type.name}>()';
|
||||||
|
|
|
@ -169,17 +169,17 @@ class SerializerGenerator extends GeneratorForAnnotation<Serializable> {
|
||||||
if (isListModelType(t)) {
|
if (isListModelType(t)) {
|
||||||
var rc = new ReCase(t.typeArguments[0].name);
|
var rc = new ReCase(t.typeArguments[0].name);
|
||||||
deserializedRepresentation = "map['$alias'] is Iterable"
|
deserializedRepresentation = "map['$alias'] is Iterable"
|
||||||
" ? map['$alias'].map(${rc
|
" ? new List.unmodifiable(map['$alias'].map(${rc
|
||||||
.pascalCase}Serializer.fromMap).toList()"
|
.pascalCase}Serializer.fromMap))"
|
||||||
" : null";
|
" : null";
|
||||||
} else if (isMapToModelType(t)) {
|
} else if (isMapToModelType(t)) {
|
||||||
var rc = new ReCase(t.typeArguments[1].name);
|
var rc = new ReCase(t.typeArguments[1].name);
|
||||||
deserializedRepresentation = '''
|
deserializedRepresentation = '''
|
||||||
map['$alias'] is Map
|
map['$alias'] is Map
|
||||||
? map['$alias'].keys.fold({}, (out, key) {
|
? new Map.unmodifiable(map['$alias'].keys.fold({}, (out, key) {
|
||||||
return out..[key] = ${rc
|
return out..[key] = ${rc
|
||||||
.pascalCase}Serializer.fromMap(map['$alias'][key]);
|
.pascalCase}Serializer.fromMap(map['$alias'][key]);
|
||||||
})
|
}))
|
||||||
: null
|
: null
|
||||||
''';
|
''';
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,12 +11,13 @@ class Author extends _Author {
|
||||||
{this.id,
|
{this.id,
|
||||||
this.name,
|
this.name,
|
||||||
this.age,
|
this.age,
|
||||||
this.books,
|
List<Book> books,
|
||||||
this.newestBook,
|
this.newestBook,
|
||||||
this.secret,
|
this.secret,
|
||||||
this.obscured,
|
this.obscured,
|
||||||
this.createdAt,
|
this.createdAt,
|
||||||
this.updatedAt});
|
this.updatedAt})
|
||||||
|
: this.books = new List.unmodifiable(books ?? []);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final String id;
|
final String id;
|
||||||
|
@ -87,7 +88,9 @@ class Author extends _Author {
|
||||||
}
|
}
|
||||||
|
|
||||||
class Library extends _Library {
|
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
|
@override
|
||||||
final String id;
|
final String id;
|
||||||
|
@ -132,12 +135,13 @@ class Library extends _Library {
|
||||||
class Bookmark extends _Bookmark {
|
class Bookmark extends _Bookmark {
|
||||||
Bookmark(Book book,
|
Bookmark(Book book,
|
||||||
{this.id,
|
{this.id,
|
||||||
this.history,
|
List<int> history,
|
||||||
this.page,
|
this.page,
|
||||||
this.comment,
|
this.comment,
|
||||||
this.createdAt,
|
this.createdAt,
|
||||||
this.updatedAt})
|
this.updatedAt})
|
||||||
: super(book);
|
: this.history = new List.unmodifiable(history ?? []),
|
||||||
|
super(book);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final String id;
|
final String id;
|
||||||
|
|
|
@ -13,7 +13,7 @@ abstract class AuthorSerializer {
|
||||||
name: map['name'],
|
name: map['name'],
|
||||||
age: map['age'],
|
age: map['age'],
|
||||||
books: map['books'] is Iterable
|
books: map['books'] is Iterable
|
||||||
? map['books'].map(BookSerializer.fromMap).toList()
|
? new List.unmodifiable(map['books'].map(BookSerializer.fromMap))
|
||||||
: null,
|
: null,
|
||||||
newestBook: map['newest_book'] != null
|
newestBook: map['newest_book'] != null
|
||||||
? BookSerializer.fromMap(map['newest_book'])
|
? BookSerializer.fromMap(map['newest_book'])
|
||||||
|
@ -69,10 +69,10 @@ abstract class LibrarySerializer {
|
||||||
return new Library(
|
return new Library(
|
||||||
id: map['id'],
|
id: map['id'],
|
||||||
collection: map['collection'] is Map
|
collection: map['collection'] is Map
|
||||||
? map['collection'].keys.fold({}, (out, key) {
|
? new Map.unmodifiable(map['collection'].keys.fold({}, (out, key) {
|
||||||
return out
|
return out
|
||||||
..[key] = BookSerializer.fromMap(map['collection'][key]);
|
..[key] = BookSerializer.fromMap(map['collection'][key]);
|
||||||
})
|
}))
|
||||||
: null,
|
: null,
|
||||||
createdAt: map['created_at'] != null
|
createdAt: map['created_at'] != null
|
||||||
? (map['created_at'] is DateTime
|
? (map['created_at'] is DateTime
|
||||||
|
|
|
@ -13,10 +13,11 @@ class Book extends _Book {
|
||||||
this.title,
|
this.title,
|
||||||
this.description,
|
this.description,
|
||||||
this.pageCount,
|
this.pageCount,
|
||||||
this.notModels,
|
List<double> notModels,
|
||||||
this.camelCaseString,
|
this.camelCaseString,
|
||||||
this.createdAt,
|
this.createdAt,
|
||||||
this.updatedAt});
|
this.updatedAt})
|
||||||
|
: this.notModels = new List.unmodifiable(notModels ?? []);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final String id;
|
final String id;
|
||||||
|
|
Loading…
Reference in a new issue