Skip to content

Commit 1711f53

Browse files
Merge branch 'gh-392-support-getter-annotation' into 'main'
Support annotations on getters See merge request objectbox/objectbox-dart!69
2 parents 1ac9d42 + d653751 commit 1711f53

File tree

7 files changed

+142
-9
lines changed

7 files changed

+142
-9
lines changed

generator/integration-tests/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ Each subdirectory contains a test case, a complete dart package (with some share
1818
but not removed between test-case runs
1919
* additionally, there may be `[0-9]-pre.dart` command-line apps, which are executed `dart N-pre.dart` - these may be
2020
used to further prepare the environment **before** code generation for the step `N` is issued and `N.dart` test is run
21+
22+
## Development
23+
24+
To enable Dart Analysis, including code auto-complete, temporarily remove the `exclude` for this directory in
25+
the parent [analysis_options.yaml](../analysis_options.yaml).
2126

2227
## Troubleshooting
2328

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# start with an empty project, without a objectbox-model.json
2+
objectbox-model.json
3+
testdata
4+
objectbox.*
5+
# Do not remove native library to allow running with non-globally installed version.
6+
!objectbox.dll
7+
!objectbox.lib
8+
!libobjectbox.*
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import 'dart:io';
2+
3+
import 'package:test/test.dart';
4+
5+
import '../common.dart';
6+
import '../test_env.dart';
7+
import 'lib/lib.dart';
8+
import 'lib/objectbox.g.dart';
9+
10+
void main() {
11+
late TestEnv<AnnotatedGetters> env;
12+
final jsonModel = readModelJson('lib');
13+
final defs = getObjectBoxModel();
14+
final model = defs.model;
15+
16+
setUp(() {
17+
env = TestEnv<AnnotatedGetters>(defs);
18+
});
19+
20+
tearDown(() {
21+
env.close();
22+
});
23+
24+
commonModelTests(defs, jsonModel);
25+
26+
test('project must be generated properly', () {
27+
expect(TestEnv.dir.existsSync(), true);
28+
expect(File('lib/objectbox.g.dart').existsSync(), true);
29+
expect(File('lib/objectbox-model.json').existsSync(), true);
30+
});
31+
32+
test('annotations from getters are read', () {
33+
// @Property on getter changes type, @Index adds index flag
34+
var prop = property(model, 'AnnotatedGetters.prop');
35+
expect(prop.type, OBXPropertyType.Int);
36+
expect(prop.flags, OBXPropertyFlags.INDEXED);
37+
// @Transient on getter ignores property
38+
expect(() => property(model, 'AnnotatedGetters.ignored'),
39+
throwsA(isStateError));
40+
});
41+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import 'package:objectbox/objectbox.dart';
2+
3+
import 'objectbox.g.dart';
4+
5+
/// Test annotations on getters are applied to properties when using
6+
/// getter + setter combos to create synthetic fields.
7+
@Entity()
8+
class AnnotatedGetters {
9+
int _dummyId = 0;
10+
11+
// ID via annotation (not using auto-detected name id)
12+
@Id()
13+
int get customId => _dummyId;
14+
15+
set customId(int value) {
16+
_dummyId = value;
17+
}
18+
19+
int? _dummyProp;
20+
21+
// Customizing property via annotation
22+
@Property(type: PropertyType.int)
23+
@Index()
24+
int? get prop => _dummyProp;
25+
26+
set prop(int? value) => _dummyProp = value;
27+
28+
// Ignore property via annotation
29+
@Transient()
30+
String? get ignored => "";
31+
32+
set ignored(String? value) {}
33+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: objectbox_generator_test
2+
3+
environment:
4+
sdk: '>=2.18.0 <4.0.0'
5+
6+
dependencies:
7+
objectbox: any
8+
9+
dev_dependencies:
10+
objectbox_generator: any
11+
test: any
12+
build_runner: any
13+
build_test: any
14+
io: any
15+
path: any
16+
17+
dependency_overrides:
18+
objectbox:
19+
path: ../../../objectbox
20+
objectbox_generator:
21+
path: ../../

generator/lib/src/entity_resolver.dart

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,13 @@ class EntityResolver extends Builder {
9292

9393
// read all suitable annotated properties
9494
for (var f in classElement.fields) {
95-
if (_transientChecker.hasAnnotationOfExact(f)) {
95+
// The field might be implicitly defined by a getter, aka it is synthetic
96+
// and does not exist in code. So always resolve the actual non-synthetic
97+
// element that exists in code (here a getter) as only it will have any
98+
// annotations.
99+
final annotated = f.nonSynthetic;
100+
101+
if (_transientChecker.hasAnnotationOfExact(annotated)) {
96102
log.info(" Skipping property '${f.name}': annotated with @Transient.");
97103
continue;
98104
}
@@ -112,15 +118,16 @@ class EntityResolver extends Builder {
112118
var flags = 0;
113119
int? propUid;
114120

115-
_idChecker.runIfMatches(f, (annotation) {
121+
// Check for @Id annotation
122+
_idChecker.runIfMatches(annotated, (annotation) {
116123
flags |= OBXPropertyFlags.ID;
117124
if (annotation.getField('assignable')!.toBoolValue()!) {
118125
flags |= OBXPropertyFlags.ID_SELF_ASSIGNABLE;
119126
}
120127
});
121128

122129
// Get info from @Property annotation
123-
_propertyChecker.runIfMatches(f, (annotation) {
130+
_propertyChecker.runIfMatches(annotated, (annotation) {
124131
propUid = annotation.getField('uid')!.toIntValue();
125132
fieldType = propertyTypeFromAnnotation(annotation.getField('type')!);
126133
if (!annotation.getField('signed')!.toBoolValue()!) {
@@ -156,7 +163,8 @@ class EntityResolver extends Builder {
156163
(f.type as ParameterizedType).typeArguments[0].element!.name;
157164
}
158165

159-
final backlinkAnnotations = _backlinkChecker.annotationsOfExact(f);
166+
final backlinkAnnotations =
167+
_backlinkChecker.annotationsOfExact(annotated);
160168
if (backlinkAnnotations.isNotEmpty) {
161169
if (!isToManyRel) {
162170
log.severe(
@@ -196,7 +204,8 @@ class EntityResolver extends Builder {
196204
}
197205

198206
// Index and unique annotation.
199-
processAnnotationIndexUnique(f, fieldType, classElement, prop);
207+
processAnnotationIndexUnique(
208+
f, annotated, fieldType, classElement, prop);
200209

201210
// for code generation
202211
prop.dartFieldType =
@@ -339,12 +348,14 @@ class EntityResolver extends Builder {
339348
idProperty.flags &= ~OBXPropertyFlags.UNSIGNED;
340349
}
341350

342-
void processAnnotationIndexUnique(
343-
FieldElement f, int? fieldType, Element elementBare, ModelProperty prop) {
351+
void processAnnotationIndexUnique(FieldElement f, Element annotatedElement,
352+
int? fieldType, Element elementBare, ModelProperty prop) {
344353
IndexType? indexType;
345354

346-
final indexAnnotation = _indexChecker.firstAnnotationOfExact(f);
347-
final uniqueAnnotation = _uniqueChecker.firstAnnotationOfExact(f);
355+
final indexAnnotation =
356+
_indexChecker.firstAnnotationOfExact(annotatedElement);
357+
final uniqueAnnotation =
358+
_uniqueChecker.firstAnnotationOfExact(annotatedElement);
348359
if (indexAnnotation == null && uniqueAnnotation == null) return;
349360

350361
// Throw if property type does not support any index.

objectbox/CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,20 @@
2626
// You can now just write:
2727
Order_.date.betweenMilliseconds(DateTime(2024, 1), DateTime(2024, 2))
2828
```
29+
* When defining a property with a getter and setter instead of a field, support annotating the
30+
getter to configure or ignore the property [#392](https://github.com/objectbox/objectbox-dart/issues/392)
31+
32+
For example, it is now possible to do this:
33+
```dart
34+
@Property(type: PropertyType.date)
35+
@Index()
36+
DateTime get date => TODO;
37+
set date(DateTime value) => TODO;
38+
39+
@Transient()
40+
int get computedValue => TODO;
41+
set computedValue(int value) => TODO;
42+
```
2943

3044
## 2.4.0 (2023-12-13)
3145

0 commit comments

Comments
 (0)