Skip to content

Commit a614da9

Browse files
authored
Merge pull request #74 from objectbox/56-generated-model
Model generator issues & improvements
2 parents cde44e6 + ac651fc commit a614da9

18 files changed

+284
-129
lines changed

generator/lib/src/code_chunks.dart

+3-16
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,18 @@
1+
import "dart:convert";
12
import "package:objectbox/src/modelinfo/index.dart";
23
import "package:objectbox/src/bindings/constants.dart" show OBXPropertyType;
34
import "package:source_gen/source_gen.dart" show InvalidGenerationSourceError;
45

56
class CodeChunks {
7+
// TODO ModelInfo, once per DB
68
static String modelInfoLoader() => """
7-
Map<int, ModelEntity> _allOBXModelEntities;
8-
9-
void _loadOBXModelEntities() {
10-
_allOBXModelEntities = {};
11-
ModelInfo modelInfo = ModelInfo.fromMap(||MODEL-JSON||);
12-
modelInfo.entities.forEach((e) => _allOBXModelEntities[e.id.uid] = e);
13-
}
14-
15-
ModelEntity _getOBXModelEntity(int entityUid) {
16-
if (_allOBXModelEntities == null) _loadOBXModelEntities();
17-
if (!_allOBXModelEntities.containsKey(entityUid)) {
18-
throw Exception("entity uid missing in objectbox-model.json: \$entityUid");
19-
}
20-
return _allOBXModelEntities[entityUid];
21-
}
229
""";
2310

2411
static String instanceBuildersReaders(ModelEntity readEntity) {
2512
String name = readEntity.name;
2613
return """
2714
ModelEntity _${name}_OBXModelGetter() {
28-
return _getOBXModelEntity(${readEntity.id.uid});
15+
return ModelEntity.fromMap(${JsonEncoder().convert(readEntity.toMap())});
2916
}
3017
3118
$name _${name}_OBXBuilder(Map<String, dynamic> members) {

generator/lib/src/generator.dart

-3
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,6 @@ class EntityGenerator extends GeneratorForAnnotation<obx.Entity> {
128128
final modelJson = JsonEncoder.withIndent(" ").convert(allModels.toMap());
129129
await File(ALL_MODELS_JSON).writeAsString(modelJson);
130130

131-
// add model-json to the generated code
132-
ret = ret.replaceFirst('||MODEL-JSON||', modelJson);
133-
134131
readEntity = allModels.findEntityByName(element.name);
135132
if (readEntity == null) return ret;
136133

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import "package:objectbox/objectbox.dart";
2+
part "a.g.dart";
3+
4+
@Entity(uid: 1)
5+
class A {
6+
@Id(uid: 11)
7+
int id;
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// **************************************************************************
2+
// EntityGenerator
3+
// **************************************************************************
4+
5+
ModelEntity _A_OBXModelGetter() {
6+
return ModelEntity.fromMap({
7+
"id": "1:1",
8+
"lastPropertyId": "1:11",
9+
"name": "A",
10+
"properties": [
11+
{"id": "1:11", "name": "id", "type": 6, "flags": 1}
12+
]
13+
});
14+
}
15+
16+
A _A_OBXBuilder(Map<String, dynamic> members) {
17+
A r = A();
18+
r.id = members["id"];
19+
return r;
20+
}
21+
22+
Map<String, dynamic> _A_OBXReader(A inst) {
23+
Map<String, dynamic> r = {};
24+
r["id"] = inst.id;
25+
return r;
26+
}
27+
28+
const A_OBXDefs =
29+
EntityDefinition<A>(_A_OBXModelGetter, _A_OBXReader, _A_OBXBuilder);
30+
31+
class A_ {
32+
static final id =
33+
QueryIntegerProperty(entityId: 1, propertyId: 1, obxType: 6);
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import "package:objectbox/objectbox.dart";
2+
part "b.g.dart";
3+
4+
@Entity(uid: 2)
5+
class B {
6+
@Id(uid: 21)
7+
int id;
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// **************************************************************************
2+
// EntityGenerator
3+
// **************************************************************************
4+
5+
ModelEntity _B_OBXModelGetter() {
6+
return ModelEntity.fromMap({
7+
"id": "2:2",
8+
"lastPropertyId": "1:21",
9+
"name": "B",
10+
"properties": [
11+
{"id": "1:21", "name": "id", "type": 6, "flags": 1}
12+
]
13+
});
14+
}
15+
16+
B _B_OBXBuilder(Map<String, dynamic> members) {
17+
B r = B();
18+
r.id = members["id"];
19+
return r;
20+
}
21+
22+
Map<String, dynamic> _B_OBXReader(B inst) {
23+
Map<String, dynamic> r = {};
24+
r["id"] = inst.id;
25+
return r;
26+
}
27+
28+
const B_OBXDefs =
29+
EntityDefinition<B>(_B_OBXModelGetter, _B_OBXReader, _B_OBXBuilder);
30+
31+
class B_ {
32+
static final id =
33+
QueryIntegerProperty(entityId: 2, propertyId: 1, obxType: 6);
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"_note1": "KEEP THIS FILE! Check it into a version control system (VCS) like git.",
3+
"_note2": "ObjectBox manages crucial IDs for your object model. See docs for details.",
4+
"_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.",
5+
"entities": [
6+
{
7+
"id": "1:1",
8+
"lastPropertyId": "1:11",
9+
"name": "A",
10+
"properties": [
11+
{
12+
"id": "1:11",
13+
"name": "id",
14+
"type": 6,
15+
"flags": 1
16+
}
17+
]
18+
},
19+
{
20+
"id": "2:2",
21+
"lastPropertyId": "1:21",
22+
"name": "B",
23+
"properties": [
24+
{
25+
"id": "1:21",
26+
"name": "id",
27+
"type": 6,
28+
"flags": 1
29+
}
30+
]
31+
}
32+
],
33+
"lastEntityId": "2:2",
34+
"lastIndexId": "0:0",
35+
"lastRelationId": "0:0",
36+
"lastSequenceId": "0:0",
37+
"modelVersion": 5,
38+
"modelVersionParserMinimum": 5,
39+
"retiredEntityUids": [],
40+
"retiredIndexUids": [],
41+
"retiredPropertyUids": [],
42+
"retiredRelationUids": [],
43+
"version": 1
44+
}
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
import "package:objectbox/objectbox.dart";
22
part "single_entity.g.dart";
33

4+
/// A dummy annotation to verify the code is generated properly even with annotations unknown to ObjectBox generator.
5+
class TestingUnknownAnnotation {
6+
const TestingUnknownAnnotation();
7+
}
8+
49
@Entity(uid: 1234)
10+
@TestingUnknownAnnotation()
511
class SingleEntity {
612
@Id(uid: 5678)
713
int id;
814

915
@Property(uid: 4321)
16+
@TestingUnknownAnnotation()
1017
String texta;
1118
}

generator/test/cases/single_entity/single_entity.g.dart_expected

+9-46
Original file line numberDiff line numberDiff line change
@@ -2,53 +2,16 @@
22
// EntityGenerator
33
// **************************************************************************
44

5-
Map<int, ModelEntity> _allOBXModelEntities;
6-
7-
void _loadOBXModelEntities() {
8-
_allOBXModelEntities = {};
9-
ModelInfo modelInfo = ModelInfo.fromMap({
10-
"_note1":
11-
"KEEP THIS FILE! Check it into a version control system (VCS) like git.",
12-
"_note2":
13-
"ObjectBox manages crucial IDs for your object model. See docs for details.",
14-
"_note3":
15-
"If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.",
16-
"entities": [
17-
{
18-
"id": "1:1234",
19-
"lastPropertyId": "2:4321",
20-
"name": "SingleEntity",
21-
"properties": [
22-
{"id": "1:5678", "name": "id", "type": 6, "flags": 1},
23-
{"id": "2:4321", "name": "texta", "type": 9}
24-
]
25-
}
26-
],
27-
"lastEntityId": "1:1234",
28-
"lastIndexId": "0:0",
29-
"lastRelationId": "0:0",
30-
"lastSequenceId": "0:0",
31-
"modelVersion": 5,
32-
"modelVersionParserMinimum": 5,
33-
"retiredEntityUids": [],
34-
"retiredIndexUids": [],
35-
"retiredPropertyUids": [],
36-
"retiredRelationUids": [],
37-
"version": 1
38-
});
39-
modelInfo.entities.forEach((e) => _allOBXModelEntities[e.id.uid] = e);
40-
}
41-
42-
ModelEntity _getOBXModelEntity(int entityUid) {
43-
if (_allOBXModelEntities == null) _loadOBXModelEntities();
44-
if (!_allOBXModelEntities.containsKey(entityUid)) {
45-
throw Exception("entity uid missing in objectbox-model.json: $entityUid");
46-
}
47-
return _allOBXModelEntities[entityUid];
48-
}
49-
505
ModelEntity _SingleEntity_OBXModelGetter() {
51-
return _getOBXModelEntity(1234);
6+
return ModelEntity.fromMap({
7+
"id": "1:1234",
8+
"lastPropertyId": "2:4321",
9+
"name": "SingleEntity",
10+
"properties": [
11+
{"id": "1:5678", "name": "id", "type": 6, "flags": 1},
12+
{"id": "2:4321", "name": "texta", "type": 9}
13+
]
14+
});
5215
}
5316

5417
SingleEntity _SingleEntity_OBXBuilder(Map<String, dynamic> members) {

generator/test/generator_test.dart

+29-1
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,42 @@
11
import "dart:io";
22
import "package:test/test.dart";
3+
import 'package:glob/glob.dart' show Glob;
4+
import 'package:path/path.dart';
35

46
import "helpers.dart";
57

8+
Map<String, String> getArgs() {
9+
final result = Map<String, String>();
10+
11+
// accept GENERATOR environment variable as a list of arguments, e.g. GENERATOR=update-expected,target:single_entity
12+
final env = Platform.environment['GENERATOR'] ?? "";
13+
14+
env.split(",").forEach((part) {
15+
final kvPair = part.split(":");
16+
if (kvPair.length < 2) {
17+
result[part] = "";
18+
} else {
19+
result[kvPair[0]] = kvPair.sublist(1).join(":"); // join() just in case there were multiple ":"
20+
}
21+
});
22+
23+
return result;
24+
}
25+
626
void main() async {
727
group("generator", () {
828
tearDown(() {
929
File("objectbox-model.json").deleteSync();
1030
});
1131

12-
testGeneratorOutput("single_entity");
32+
final args = getArgs();
33+
final updateExpected = args["update-expected"] != null;
34+
35+
for (var testCase in Glob("test/cases/*").listSync()) {
36+
final name = basename(testCase.path);
37+
if (args["target"] == null || args["target"] == name) {
38+
testGeneratorOutput(name, updateExpected);
39+
}
40+
}
1341
});
1442
}

generator/test/helpers.dart

+36-23
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ import "package:build/src/analyzer/resolver.dart";
1212
import "package:build_resolvers/src/resolver.dart";
1313

1414
class _InMemoryAssetWriter implements AssetWriter {
15-
String output;
15+
Map<AssetId, String> output;
1616

17-
_InMemoryAssetWriter();
17+
_InMemoryAssetWriter() : output = Map<AssetId, String>();
1818

1919
@override
2020
Future writeAsBytes(AssetId id, List<int> bytes) async {
@@ -23,23 +23,15 @@ class _InMemoryAssetWriter implements AssetWriter {
2323

2424
@override
2525
Future writeAsString(AssetId id, String contents, {Encoding encoding = utf8}) async {
26-
if (output != null) throw Exception("output was set already");
27-
output = contents;
26+
if (output[id] != null) throw Exception("output was set already");
27+
output[id] = contents;
2828
}
2929
}
3030

3131
class _SingleFileAssetReader extends AssetReader {
32-
AssetId id;
33-
34-
_SingleFileAssetReader(this.id) {
35-
if (id.package != "objectbox_generator") {
36-
throw Exception("asset package needs to be 'objectbox_generator', but got '${id.package}'");
37-
}
38-
}
39-
4032
Future<bool> canRead(AssetId id) async => true; //this.id == id;
4133

42-
Stream<AssetId> findAssets(Glob glob, {String package}) => Stream.fromIterable([id]);
34+
Stream<AssetId> findAssets(Glob glob, {String package}) => throw UnimplementedError();
4335

4436
@override
4537
Future<List<int>> readAsBytes(AssetId id) async => utf8.encode(await readAsString(id));
@@ -59,24 +51,45 @@ class _SingleFileAssetReader extends AssetReader {
5951
}
6052
}
6153

62-
Future<String> _buildGeneratorOutput(String caseName) async {
63-
AssetId assetId = AssetId("objectbox_generator", "test/cases/$caseName/$caseName.dart");
54+
Future<Map<AssetId, String>> _buildGeneratorOutput(String caseName) async {
55+
final entities = List<AssetId>();
56+
for (var entity in Glob("test/cases/$caseName/*.dart_testcase").listSync()) {
57+
final path = entity.path.substring(0, entity.path.length - "_testcase".length);
58+
entities.add(AssetId("objectbox_generator", path));
59+
}
60+
6461
var writer = _InMemoryAssetWriter();
65-
var reader = _SingleFileAssetReader(assetId);
62+
var reader = _SingleFileAssetReader();
6663
Resolvers resolvers = AnalyzerResolvers();
6764

68-
await runBuilder(objectboxModelFactory(BuilderOptions.empty), [assetId], reader, writer, resolvers);
65+
await runBuilder(objectboxModelFactory(BuilderOptions.empty), entities, reader, writer, resolvers);
6966
return writer.output;
7067
}
7168

72-
void testGeneratorOutput(String caseName) {
69+
void checkExpectedContents(String path, String contents, bool updateExpected) async {
70+
final expectedFile = File(path);
71+
final expectedContents = await expectedFile.readAsString();
72+
73+
if (updateExpected) {
74+
if (expectedContents != contents) {
75+
print("Updating $path");
76+
}
77+
await expectedFile.writeAsString(contents);
78+
} else {
79+
expect(contents, equals(expectedContents));
80+
}
81+
}
82+
83+
void testGeneratorOutput(String caseName, bool updateExpected) {
7384
test(caseName, () async {
74-
String built = await _buildGeneratorOutput(caseName);
75-
String expected = await File("test/cases/$caseName/$caseName.g.dart_expected").readAsString();
76-
expect(built, equals(expected));
85+
Map<AssetId, String> built = await _buildGeneratorOutput(caseName);
86+
87+
built.forEach((assetId, generatedCode) async {
88+
final expectedPath = assetId.path.replaceAll(".objectbox_model.g.part", ".g.dart_expected");
89+
checkExpectedContents(expectedPath, generatedCode, updateExpected);
90+
});
7791

7892
String jsonBuilt = await File("objectbox-model.json").readAsString();
79-
String jsonExpected = await File("test/cases/$caseName/objectbox-model.json_expected").readAsString();
80-
expect(jsonBuilt, equals(jsonExpected));
93+
checkExpectedContents("test/cases/$caseName/objectbox-model.json_expected", jsonBuilt, updateExpected);
8194
});
8295
}

0 commit comments

Comments
 (0)