Default values
This commit is contained in:
parent
7db3ec1a36
commit
133b6a3c41
11 changed files with 173 additions and 10 deletions
|
@ -67,6 +67,7 @@ part 'book.g.dart';
|
||||||
abstract class _Book extends Model {
|
abstract class _Book extends Model {
|
||||||
String get author;
|
String get author;
|
||||||
|
|
||||||
|
@DefaultValue('[Untitled]')
|
||||||
String get title;
|
String get title;
|
||||||
|
|
||||||
String get description;
|
String get description;
|
||||||
|
|
|
@ -27,6 +27,7 @@ targets:
|
||||||
_book:
|
_book:
|
||||||
sources:
|
sources:
|
||||||
- "test/models/book.dart"
|
- "test/models/book.dart"
|
||||||
|
- "test/models/goat.dart"
|
||||||
- "test/models/game_pad_button.dart"
|
- "test/models/game_pad_button.dart"
|
||||||
- "test/models/with_enum.dart"
|
- "test/models/with_enum.dart"
|
||||||
_typescript_definition:
|
_typescript_definition:
|
||||||
|
|
|
@ -2,7 +2,7 @@ library angel_serialize_generator;
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
import 'package:analyzer/dart/constant/value.dart';
|
||||||
import 'package:analyzer/dart/element/element.dart';
|
import 'package:analyzer/dart/element/element.dart';
|
||||||
import 'package:analyzer/dart/element/type.dart';
|
import 'package:analyzer/dart/element/type.dart';
|
||||||
import 'package:angel_model/angel_model.dart';
|
import 'package:angel_model/angel_model.dart';
|
||||||
|
@ -48,6 +48,32 @@ TypeReference convertTypeReference(DartType t) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String dartObjectToString(DartObject v) {
|
||||||
|
if (v.isNull) return 'null';
|
||||||
|
if (v.toBoolValue() != null) return v.toBoolValue().toString();
|
||||||
|
if (v.toIntValue() != null) return v.toIntValue().toString();
|
||||||
|
if (v.toDoubleValue() != null) return v.toDoubleValue().toString();
|
||||||
|
if (v.toSymbolValue() != null) return '#' + v.toSymbolValue();
|
||||||
|
if (v.toTypeValue() != null) return v.toTypeValue().name;
|
||||||
|
if (v.toListValue() != null)
|
||||||
|
return 'const [' + v.toListValue().map(dartObjectToString).join(', ') + ']';
|
||||||
|
if (v.toMapValue() != null) {
|
||||||
|
return 'const {' +
|
||||||
|
v.toMapValue().entries.map((entry) {
|
||||||
|
var k = dartObjectToString(entry.key);
|
||||||
|
var v = dartObjectToString(entry.value);
|
||||||
|
return '$k: $v';
|
||||||
|
}).join(', ') +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
if (v.toStringValue() != null) {
|
||||||
|
return literalString(v.toStringValue())
|
||||||
|
.accept(new DartEmitter())
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
throw new ArgumentError(v.toString());
|
||||||
|
}
|
||||||
|
|
||||||
/// Determines if a type supports `package:angel_serialize`.
|
/// Determines if a type supports `package:angel_serialize`.
|
||||||
bool isModelClass(DartType t) {
|
bool isModelClass(DartType t) {
|
||||||
if (t == null) return false;
|
if (t == null) return false;
|
||||||
|
|
|
@ -67,6 +67,14 @@ Future<BuildContext> buildContext(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for @DefaultValue()
|
||||||
|
var defAnn =
|
||||||
|
const TypeChecker.fromRuntime(DefaultValue).firstAnnotationOf(el);
|
||||||
|
if (defAnn != null) {
|
||||||
|
var rev = new ConstantReader(defAnn).revive().positionalArguments[0];
|
||||||
|
ctx.defaults[field.name] = rev;
|
||||||
|
}
|
||||||
|
|
||||||
// Check for alias
|
// Check for alias
|
||||||
Alias alias;
|
Alias alias;
|
||||||
var aliasAnn = aliasTypeChecker.firstAnnotationOf(el);
|
var aliasAnn = aliasTypeChecker.firstAnnotationOf(el);
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:analyzer/dart/constant/value.dart';
|
||||||
import 'package:analyzer/dart/element/element.dart';
|
import 'package:analyzer/dart/element/element.dart';
|
||||||
import 'package:angel_serialize/angel_serialize.dart';
|
import 'package:angel_serialize/angel_serialize.dart';
|
||||||
import 'package:code_builder/code_builder.dart';
|
import 'package:code_builder/code_builder.dart';
|
||||||
|
@ -15,6 +16,9 @@ class BuildContext {
|
||||||
/// A map of field names to resolved names from `@Alias()` declarations.
|
/// A map of field names to resolved names from `@Alias()` declarations.
|
||||||
final Map<String, String> aliases = {};
|
final Map<String, String> aliases = {};
|
||||||
|
|
||||||
|
/// A map of field names to their default values.
|
||||||
|
final Map<String, DartObject> defaults = {};
|
||||||
|
|
||||||
/// A map of fields that have been marked as to be excluded from serialization.
|
/// A map of fields that have been marked as to be excluded from serialization.
|
||||||
final Map<String, Exclude> excluded = {};
|
final Map<String, Exclude> excluded = {};
|
||||||
|
|
||||||
|
|
|
@ -96,6 +96,12 @@ class JsonModelGenerator extends GeneratorForAnnotation<Serializable> {
|
||||||
? 'List'
|
? 'List'
|
||||||
: 'Map';
|
: 'Map';
|
||||||
var defaultValue = typeName == 'List' ? '[]' : '{}';
|
var defaultValue = typeName == 'List' ? '[]' : '{}';
|
||||||
|
var existingDefault = ctx.defaults[field.name];
|
||||||
|
|
||||||
|
if (existingDefault != null) {
|
||||||
|
defaultValue = dartObjectToString(existingDefault);
|
||||||
|
}
|
||||||
|
|
||||||
constructor.initializers.add(new Code('''
|
constructor.initializers.add(new Code('''
|
||||||
this.${field.name} =
|
this.${field.name} =
|
||||||
new $typeName.unmodifiable(${field.name} ?? $defaultValue)'''));
|
new $typeName.unmodifiable(${field.name} ?? $defaultValue)'''));
|
||||||
|
@ -109,6 +115,12 @@ class JsonModelGenerator extends GeneratorForAnnotation<Serializable> {
|
||||||
..name = field.name
|
..name = field.name
|
||||||
..named = true;
|
..named = true;
|
||||||
|
|
||||||
|
var existingDefault = ctx.defaults[field.name];
|
||||||
|
|
||||||
|
if (existingDefault != null) {
|
||||||
|
b.defaultTo = new Code(dartObjectToString(existingDefault));
|
||||||
|
}
|
||||||
|
|
||||||
if (!isListOrMapType(field.type))
|
if (!isListOrMapType(field.type))
|
||||||
b.toThis = true;
|
b.toThis = true;
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -203,18 +203,27 @@ class SerializerGenerator extends GeneratorForAnnotation<Serializable> {
|
||||||
String deserializedRepresentation =
|
String deserializedRepresentation =
|
||||||
"map['$alias'] as ${typeToString(field.type)}";
|
"map['$alias'] as ${typeToString(field.type)}";
|
||||||
|
|
||||||
|
var defaultValue = 'null';
|
||||||
|
var existingDefault = ctx.defaults[field.name];
|
||||||
|
|
||||||
|
if (existingDefault != null) {
|
||||||
|
defaultValue = dartObjectToString(existingDefault);
|
||||||
|
deserializedRepresentation =
|
||||||
|
'$deserializedRepresentation ?? $defaultValue';
|
||||||
|
}
|
||||||
|
|
||||||
// Deserialize dates
|
// Deserialize dates
|
||||||
if (dateTimeTypeChecker.isAssignableFromType(field.type))
|
if (dateTimeTypeChecker.isAssignableFromType(field.type))
|
||||||
deserializedRepresentation = "map['$alias'] != null ? "
|
deserializedRepresentation = "map['$alias'] != null ? "
|
||||||
"(map['$alias'] is DateTime ? (map['$alias'] as DateTime) : DateTime.parse(map['$alias'].toString()))"
|
"(map['$alias'] is DateTime ? (map['$alias'] as DateTime) : DateTime.parse(map['$alias'].toString()))"
|
||||||
" : null";
|
" : $defaultValue";
|
||||||
|
|
||||||
// Serialize model classes via `XSerializer.toMap`
|
// Serialize model classes via `XSerializer.toMap`
|
||||||
else if (isModelClass(field.type)) {
|
else if (isModelClass(field.type)) {
|
||||||
var rc = new ReCase(field.type.name);
|
var rc = new ReCase(field.type.name);
|
||||||
deserializedRepresentation = "map['$alias'] != null"
|
deserializedRepresentation = "map['$alias'] != null"
|
||||||
" ? ${rc.pascalCase}Serializer.fromMap(map['$alias'] as Map)"
|
" ? ${rc.pascalCase}Serializer.fromMap(map['$alias'] as Map)"
|
||||||
" : null";
|
" : $defaultValue";
|
||||||
} else if (field.type is InterfaceType) {
|
} else if (field.type is InterfaceType) {
|
||||||
var t = field.type as InterfaceType;
|
var t = field.type as InterfaceType;
|
||||||
|
|
||||||
|
@ -224,7 +233,7 @@ class SerializerGenerator extends GeneratorForAnnotation<Serializable> {
|
||||||
" ? new List.unmodifiable(((map['$alias'] as Iterable)"
|
" ? new List.unmodifiable(((map['$alias'] as Iterable)"
|
||||||
".where((x) => x is Map) as Iterable<Map>)"
|
".where((x) => x is Map) as Iterable<Map>)"
|
||||||
".map(${rc.pascalCase}Serializer.fromMap))"
|
".map(${rc.pascalCase}Serializer.fromMap))"
|
||||||
" : null";
|
" : $defaultValue";
|
||||||
} 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 = '''
|
||||||
|
@ -233,7 +242,7 @@ class SerializerGenerator extends GeneratorForAnnotation<Serializable> {
|
||||||
return out..[key] = ${rc.pascalCase}Serializer
|
return out..[key] = ${rc.pascalCase}Serializer
|
||||||
.fromMap(((map['$alias'] as Map)[key]) as Map);
|
.fromMap(((map['$alias'] as Map)[key]) as Map);
|
||||||
}))
|
}))
|
||||||
: null
|
: $defaultValue
|
||||||
''';
|
''';
|
||||||
} else if (t.element.isEnum) {
|
} else if (t.element.isEnum) {
|
||||||
deserializedRepresentation = '''
|
deserializedRepresentation = '''
|
||||||
|
@ -243,7 +252,7 @@ class SerializerGenerator extends GeneratorForAnnotation<Serializable> {
|
||||||
(
|
(
|
||||||
map['$alias'] is int
|
map['$alias'] is int
|
||||||
? ${t.name}.values[map['$alias'] as int]
|
? ${t.name}.values[map['$alias'] as int]
|
||||||
: null
|
: $defaultValue
|
||||||
)
|
)
|
||||||
''';
|
''';
|
||||||
} else if (const TypeChecker.fromRuntime(List)
|
} else if (const TypeChecker.fromRuntime(List)
|
||||||
|
@ -254,7 +263,7 @@ class SerializerGenerator extends GeneratorForAnnotation<Serializable> {
|
||||||
deserializedRepresentation = '''
|
deserializedRepresentation = '''
|
||||||
map['$alias'] is Iterable
|
map['$alias'] is Iterable
|
||||||
? (map['$alias'] as Iterable).cast<$arg>().toList()
|
? (map['$alias'] as Iterable).cast<$arg>().toList()
|
||||||
: null
|
: $defaultValue
|
||||||
''';
|
''';
|
||||||
} else if (const TypeChecker.fromRuntime(Map)
|
} else if (const TypeChecker.fromRuntime(Map)
|
||||||
.isAssignableFromType(t) &&
|
.isAssignableFromType(t) &&
|
||||||
|
@ -266,7 +275,7 @@ class SerializerGenerator extends GeneratorForAnnotation<Serializable> {
|
||||||
deserializedRepresentation = '''
|
deserializedRepresentation = '''
|
||||||
map['$alias'] is Map
|
map['$alias'] is Map
|
||||||
? (map['$alias'] as Map).cast<$key, $value>()
|
? (map['$alias'] as Map).cast<$key, $value>()
|
||||||
: null
|
: $defaultValue
|
||||||
''';
|
''';
|
||||||
} else if (const TypeChecker.fromRuntime(Uint8List)
|
} else if (const TypeChecker.fromRuntime(Uint8List)
|
||||||
.isAssignableFromType(t)) {
|
.isAssignableFromType(t)) {
|
||||||
|
@ -281,7 +290,7 @@ class SerializerGenerator extends GeneratorForAnnotation<Serializable> {
|
||||||
(
|
(
|
||||||
map['$alias'] is String
|
map['$alias'] is String
|
||||||
? new Uint8List.fromList(base64.decode(map['$alias'] as String))
|
? new Uint8List.fromList(base64.decode(map['$alias'] as String))
|
||||||
: null
|
: $defaultValue
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
''';
|
''';
|
||||||
|
|
24
angel_serialize_generator/test/default_value_test.dart
Normal file
24
angel_serialize_generator/test/default_value_test.dart
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
import 'models/goat.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('constructor', () {
|
||||||
|
test('int default', () {
|
||||||
|
expect(Goat().integer, 34);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('list default', () {
|
||||||
|
expect(Goat().list, [34, 35]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('from map', () {
|
||||||
|
test('int default', () {
|
||||||
|
expect(GoatSerializer.fromMap({}).integer, 34);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('list default', () {
|
||||||
|
expect(GoatSerializer.fromMap({}).list, [34, 35]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -3,7 +3,6 @@ import 'package:collection/collection.dart';
|
||||||
import 'game_pad_button.dart';
|
import 'game_pad_button.dart';
|
||||||
part 'game_pad.g.dart';
|
part 'game_pad.g.dart';
|
||||||
|
|
||||||
|
|
||||||
@Serializable(autoIdAndDateFields: false)
|
@Serializable(autoIdAndDateFields: false)
|
||||||
class _Gamepad {
|
class _Gamepad {
|
||||||
List<GamepadButton> buttons;
|
List<GamepadButton> buttons;
|
||||||
|
|
12
angel_serialize_generator/test/models/goat.dart
Normal file
12
angel_serialize_generator/test/models/goat.dart
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import 'package:angel_serialize/angel_serialize.dart';
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
part 'goat.g.dart';
|
||||||
|
|
||||||
|
@Serializable(autoIdAndDateFields: false)
|
||||||
|
abstract class _Goat {
|
||||||
|
@DefaultValue(34)
|
||||||
|
int get integer;
|
||||||
|
|
||||||
|
@DefaultValue([34, 35])
|
||||||
|
List<int> get list;
|
||||||
|
}
|
67
angel_serialize_generator/test/models/goat.g.dart
Normal file
67
angel_serialize_generator/test/models/goat.g.dart
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'goat.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonModelGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
@generatedSerializable
|
||||||
|
class Goat implements _Goat {
|
||||||
|
const Goat({this.integer: 34, List<int> this.list: const [34, 35]});
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int integer;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final List<int> list;
|
||||||
|
|
||||||
|
Goat copyWith({int integer, List<int> list}) {
|
||||||
|
return new Goat(integer: integer ?? this.integer, list: list ?? this.list);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator ==(other) {
|
||||||
|
return other is _Goat &&
|
||||||
|
other.integer == integer &&
|
||||||
|
const ListEquality<int>(const DefaultEquality<int>())
|
||||||
|
.equals(other.list, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
return hashObjects([integer, list]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return GoatSerializer.toMap(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// SerializerGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
abstract class GoatSerializer {
|
||||||
|
static Goat fromMap(Map map) {
|
||||||
|
return new Goat(
|
||||||
|
integer: map['integer'] as int ?? 34,
|
||||||
|
list: map['list'] is Iterable
|
||||||
|
? (map['list'] as Iterable).cast<int>().toList()
|
||||||
|
: const [34, 35]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Map<String, dynamic> toMap(_Goat model) {
|
||||||
|
if (model == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return {'integer': model.integer, 'list': model.list};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class GoatFields {
|
||||||
|
static const List<String> allFields = const <String>[integer, list];
|
||||||
|
|
||||||
|
static const String integer = 'integer';
|
||||||
|
|
||||||
|
static const String list = 'list';
|
||||||
|
}
|
Loading…
Reference in a new issue